Gibt es einen bestimmten Grund, dass dies die Sprache konzeptionell brechen würde, oder einen bestimmten Grund, dass dies in einigen Fällen technisch nicht durchführbar ist?
Die Nutzung wäre mit neuem Betreiber.
Edit: Ich werde die Hoffnung aufgeben, meinen "neuen Operator" und "neuen Operator" klar zu machen und direkt zu sein.
Der Punkt der Frage ist: Warum sind Konstrukteure etwas Besonderes ? Denken Sie natürlich daran, dass die Sprachspezifikationen uns sagen, was legal, aber nicht unbedingt moralisch ist. Was legal ist, wird in der Regel dadurch bestimmt, was logisch mit dem Rest der Sprache übereinstimmt, was einfach und prägnant ist und was von Compilern implementiert werden kann. Die mögliche Begründung des Normungsausschusses, diese Faktoren abzuwägen, ist bewusst und interessant - daher die Frage.
std::make_unique
undstd::make_shared
kann die zugrunde liegende praktische Motivation für diese Frage angemessen lösen. Hierbei handelt es sich um Vorlagenmethoden, dh, Sie müssen die Eingabeargumente im Konstruktor erfassen und dann an den eigentlichen Konstruktor weiterleiten.Antworten:
Zeiger-auf-Member-Funktionen sind nur sinnvoll, wenn Sie mehr als eine Member-Funktion mit derselben Signatur haben. Andernfalls gibt es nur einen möglichen Wert für Ihren Zeiger. Für Konstruktoren ist dies jedoch nicht möglich, da in C ++ unterschiedliche Konstruktoren derselben Klasse unterschiedliche Signaturen haben müssen.
Die Alternative für Stroustrup wäre gewesen, eine Syntax für C ++ zu wählen, bei der Konstruktoren einen anderen Namen als den Klassennamen haben könnten. Dies hätte jedoch einige sehr elegante Aspekte der vorhandenen ctor-Syntax verhindert und die Sprache komplizierter gemacht. Für mich sieht das nach einem hohen Preis aus, nur um eine selten benötigte Funktion zu ermöglichen, die leicht simuliert werden kann, indem die Initialisierung eines Objekts vom ctor auf eine andere
init
Funktion "ausgelagert" wird (eine normale Elementfunktion, für die Zeiger auf Elemente gelten können) erstellt).quelle
memcpy(buffer, (&std::string)(int, char), size)
? (Wahrscheinlich extrem koscher, aber das ist doch C ++.)memcpy(buffer, strlen, size)
. Vermutlich würde es die Versammlung kopieren, aber wer weiß. Ob der Code ohne Absturz aufgerufen werden kann oder nicht, erfordert Kenntnisse über den von Ihnen verwendeten Compiler. Gleiches gilt für die Größenbestimmung. Es wäre stark plattformabhängig, aber im Produktionscode werden viele nicht portierbare C ++ - Konstrukte verwendet. Ich sehe keinen Grund, es zu verbieten.Ein Konstruktor ist eine Funktion, die Sie aufrufen, wenn das Objekt noch nicht vorhanden ist. Es kann sich also nicht um eine Mitgliedsfunktion handeln. Es könnte statisch sein.
Ein Konstruktor wird tatsächlich mit diesem Zeiger aufgerufen, nachdem der Speicher zugewiesen wurde, aber bevor er vollständig initialisiert wurde. Infolgedessen verfügt ein Konstruktor über eine Reihe von privilegierten Funktionen.
Wenn Sie einen Zeiger auf einen Konstruktor hätten, müsste es sich entweder um einen statischen Zeiger handeln, etwa um eine Factory-Funktion, oder um einen speziellen Zeiger auf etwas, das unmittelbar nach der Speicherzuweisung aufgerufen wird. Es konnte keine gewöhnliche Member-Funktion sein und trotzdem als Konstruktor arbeiten.
Der einzige nützliche Zweck, der in den Sinn kommt, ist eine spezielle Art von Zeiger, der an den neuen Operator übergeben werden kann, damit dieser indirekt auf den zu verwendenden Konstruktor zugreifen kann. Ich denke, das könnte praktisch sein, aber es würde eine bedeutende neue Syntax erfordern, und vermutlich lautet die Antwort: Sie haben darüber nachgedacht und es war die Mühe nicht wert.
Wenn Sie nur den allgemeinen Initialisierungscode umgestalten möchten, ist eine normale Speicherfunktion normalerweise eine ausreichende Antwort, und Sie können einen Zeiger auf eine dieser Funktionen erhalten.
quelle
&A::A
funktioniert in keinem der Compiler, die ich ausprobiert habe.)Dies liegt daran, dass es sich nicht um einen Rückgabetyp für einen Konstruktor handelt und Sie keinen Speicherplatz für den Konstruktor im Speicher reservieren. Wie u im Falle einer Variablen während der Deklaration. Zum Beispiel: Wenn Sie eine einfache Variable X schreiben, generiert der Compiler einen Fehler, da der Compiler die Bedeutung dieses Fehlers nicht versteht. Aber wenn Sie Int x schreiben; Dann erfährt der Compiler, dass es sich um eine Variable vom Typ int handelt, sodass Platz für Variablen reserviert wird.
Schlussfolgerung: - Die Schlussfolgerung ist, dass aufgrund des Ausschlusses des Rückgabetyps die Adresse nicht im Speicher abgerufen wird.
quelle
(void)(*fptr)()
deklariert einen Zeiger auf eine Funktion ohne Rückgabewert.Ich nehme eine wilde Vermutung an:
C ++ - Konstruktor und Destruktor sind überhaupt keine Funktionen, sondern Makros. Sie werden in den Bereich eingefügt, in dem das Objekt erstellt wird, und in den Bereich, in dem das Objekt zerstört wird. Es gibt wiederum keinen Konstruktor oder Destruktor, das Objekt ist nur IS.
Tatsächlich denke ich, dass die anderen Funktionen in der Klasse keine Funktionen sind, sondern Inline-Funktionen, die NICHT inline gesetzt werden, weil Sie die Adresse von ihnen übernehmen (der Compiler erkennt, dass Sie darauf zugreifen und den Code nicht in die Funktion und inline setzen optimiert diese Funktion) und wiederum scheint die Funktion "immer noch da" zu sein, obwohl dies nicht der Fall wäre, wenn Sie die Adresse nicht angegeben hätten.
Die virtuelle Tabelle des C ++ - "Objekts" ist nicht wie ein JavaScript-Objekt, über das Sie den Konstruktor abrufen und Objekte daraus zur Laufzeit erstellen können
new XMLHttpRequest.constructor
, sondern vielmehr eine Sammlung von Zeigern auf anonyme Funktionen, die als Schnittstelle für dieses Objekt dienen , mit Ausnahme der Möglichkeit, das Objekt zu erstellen. Und es macht nicht einmal Sinn, das Objekt zu "löschen", denn es ist wie der Versuch, eine Struktur zu löschen. Sie können es nicht: Es ist nur eine Stapelbeschriftung. Schreiben Sie es einfach, wie Sie möchten, unter einer anderen Beschriftung benutze eine Klasse als 4 Ganzzahlen:Es gibt keinen Speicherverlust, es gibt keine Probleme, außer dass Sie effektiv eine Menge Stapelspeicherplatz verschwendet haben, der für die Objektschnittstelle und die Zeichenfolge reserviert ist, aber Ihr Programm wird nicht zerstört (solange Sie nicht versuchen, es zu verwenden) als eine Zeichenfolge immer wieder).
Wenn meine früheren Annahmen richtig sind: Die vollständigen Kosten der Zeichenfolge bestehen nur aus den Kosten für das Speichern dieser 32 Bytes und dem konstanten Zeichenfolge-Speicherplatz: Die Funktionen werden nur zur Kompilierungszeit verwendet und können auch nachher eingebettet und weggeworfen werden Das Objekt wird erstellt und verwendet (Als ob Sie mit einer Struktur gearbeitet hätten und nur direkt ohne Funktionsaufrufe darauf verwiesen hätten, stellen Sie sicher, dass es doppelte Aufrufe anstelle von Funktionssprüngen gibt, dies ist jedoch in der Regel schneller und benötigt weniger Speicherplatz). Wenn Sie eine Funktion aufrufen, ersetzt der Compiler diesen Aufruf im Wesentlichen durch die entsprechenden Anweisungen, mit Ausnahme der von den Sprachentwicklern festgelegten Ausnahmen.
Zusammenfassung: C ++ - Objekte haben keine Ahnung, was sie sind. Alle Tools für die Schnittstelle sind statisch inline und gehen zur Laufzeit verloren. Dies macht das Arbeiten mit Klassen so effizient wie das Füllen von Strukturen mit Daten und das direkte Arbeiten mit diesen Daten, ohne dass Funktionen überhaupt aufgerufen werden (diese Funktionen sind inline).
Dies unterscheidet sich grundlegend von den Ansätzen von COM / ObjectiveC sowie von Javascript, bei denen die Typinformationen dynamisch beibehalten werden. Dies geht zu Lasten des Laufzeit-Overheads, der Speicherverwaltung und der Aufrufe von Konstruktionen, da der Compiler diese Informationen nicht wegwerfen kann. Dies ist erforderlich für den dynamischen Versand. Dies gibt uns die Möglichkeit, zur Laufzeit mit unserem Programm zu "sprechen" und es im laufenden Betrieb mit reflektierbaren Komponenten zu entwickeln.
quelle
struct { int size; const char * data; };
(wie Sie anscheinend annehmen), schreiben Sie 4 * 4 Bytes = 16 Bytes auf eine Speicheradresse, für die Sie auf einem x86-Computer nur 8 Bytes reserviert haben, sodass 8 Bytes über andere Daten geschrieben werden (was Ihren Stapel beschädigen kann) ). Glücklicherweise gibt esstd::string
normalerweise eine direkte Optimierung für kurze Zeichenfolgen. Sie sollte daher für Ihr Beispiel ausreichend groß sein, wenn Sie eine größere Standardimplementierung verwenden.