2010-02-16 15 views
13

Ich habe this one question asking the same thing gefunden, allerdings wurde nur der 'neue' Teil beantwortet, also geht es hier nochmal.Warum muss der Löschoperator statisch sein?

Warum muss der Löschoperator statisch sein? Irgendwie macht das keinen Sinn. Der neue Operator macht durchaus Sinn, genauso wie der Konstruktor nicht virtuell sein kann, genauso wenig wie der neue Operator. Der Destruktor kann (und sollte) jedoch virtuell sein, wenn Sie Vererbung verwenden, um die Zerstörung von Objekten zu ermöglichen, die (als Polymorphismus) als Basisklasse verwendet werden.

Ich verstehe, dass, wenn der Löschen-Operator aufgerufen wird, das Objekt bereits zerstört wurde, so dass kein "das" existiert. Dennoch macht es immer noch Sinn, den Löschoperator mit dem gleichen Operator wie beim virtuellen Destruktor auf den neuen Operator abzustimmen, der das Objekt erstellt hat.

Dies ist, was ich

class A 
{ 
    public: 
    virtual ~A() {} 
}; 

class B : public A 
{ 
    public: 
    void* operator new (size_t sz); 
    void operator delete (void* ptr, size_t sz); 
}; 

jetzt bedeuten, wenn wir genannt habe

A *ptr = new B(); 
delete ptr; // <-- fail 

A der Delete-Operator (default) tun sollte, da es statisch ist und es nicht bekannt ist (für alles andere als der triviale Fall hier) zur Kompilierzeit, welcher delete-operator der richtige ist.

Ich habe jedoch ein kleines Testprogramm mit dem obigen Code (nur malloc/free in den neuen/löscht-Operatoren und print-Anweisung in delete), und kompiliert es mit g ++. Es wurde ganz unerwartet ausgeführt, um die Ausgabe in Bs Löschoperator zu erzeugen.

Meine (echte) Frage ist diese: Gibt es eine implizite "Virtualität" für den Löschoperator? Ist es nur statisch im Nicht-diesem-Zeiger-Sinn? Oder ist das nur eine g ++ Funktion?

Ich fing an, durch die C++ - Spezifikation zu suchen, aber ich muss zugeben, ich war etwas überwältigt davon, so dass jede Hilfe geschätzt wurde.

Antwort

16

Die Antwort in den Sprachregeln ist wirklich in 12.5 [class.free].

Wenn Sie über einen Zeiger auf eine Basisklasse löschen, muss der Destruktor virtuell sein oder Sie erhalten undefiniertes Verhalten. Andernfalls muss die Implementierung den dynamischen Typ des zu löschenden Objekts ermitteln.

12,5/4 sagt, dass, wenn die delete nicht durch :: dann die Freigabe-Funktion durch Nachschlagen delete im Rahmen der dynamischen Art der virtuellen Destruktor bestimmt wird vorangestellt wird. Dies gewährleistet eine virtuell ähnliche Suche, auch wenn immer eine static Mitgliedsfunktion ist.

Raw Zuweisung und Freigabe passieren konzeptionell außerhalb der Lebensdauer des Objekts so durch die Zeit, die Freigabe-Funktion aufgerufen werden, gibt es nicht mehr ein Objekt einen virtuellen Suchmechanismus zu schaffen, aber die Suchregeln sicherzustellen, dass operator delete ein dynamisches hat (virtual-lite!) Suchmechanismus. Das bedeutet, dass der Operator delete static sinnvollerweise sein kann, ohne den Kontakt mit dem dynamischen Objekt des ursprünglichen Objekts zu verlieren.

+0

Ausgezeichnet, danke! Ich habe in 3.7.3 gewühlt ... – falstro

+3

Es gibt auch relevante Sachen in 5.3.5 sowie 3.7.3 und 12.5. Obwohl es als Nachschlagewerk in Rechnung gestellt wird, scheint es, dass Sie es lesen müssen, um es zu bedecken, sonst würden Sie nie wissen, ob es einen kleinen Absatz gab, der für etwas relevant ist, das Sie in einem völlig anderen Abschnitt nachschlagen schauend. –

1

Operator ist nur zum Aufheben der Speicherfreigabe, und Speicher wird für das am weitesten abgeleitete Klassenobjekt als Ganzes freigegeben - in einer Aktion - genauso wie mit new-Operator ist es für das gesamte am meisten abgeleitete Klassenobjekt zugeordnet - Das Objekt der Klasse wurde als Argument in new Class Konstrukt übergeben.

Aus diesem Grunde, wenn Sie delete ptr; der delete Operator tun immer nur einmal für die tatsächlichen meisten abgeleitete Klasse des Objekts gelöscht werden und die Daten auf welcher Klasse aufgerufen wird, wird er entweder von der V-Tabelle abgeleitet wird, wenn der virtuelle destructor ist Gegenwart oder der Typ des Zeigers, wenn es keinen virtuellen Destruktor gibt. Deshalb gibt es keine implizite Virtualität für den Operator delete - die gesamte Virtualität endet am Punkt des Destruktoraufrufs.

+0

@roe: Der am weitesten abgeleitete Klassenoperator delete wird genauso aufgerufen wie der am weitesten abgeleitete Operator new. Wenn Sie also verschiedene Bänke haben, erhalten Sie Objekte verschiedener Klassen, die von verschiedenen Banken zugewiesen werden, aber nur als ganze Objekte wird es niemals vorkommen, dass ein Unterobjekt von einer Bank kommt und der abgeleitete Klassenteil des Objekts von einem anderen stammt. – sharptooth

+0

Der Operator, der verwendet wird, wird also aus der Klasse abgeleitet, aus der das tatsächliche Objekt besteht (d. H. Es sucht nach einer virtuellen Tabelle oder wie es auch funktioniert), klingt wie eine implizierte Virtualität für den Operator? Angenommen, ich habe eine Klasse C, die von B abstammt: A * ptr = neues C; delete ptr; 'ruft immer noch B's delete-Operator auf, was sich anhört, als wäre es ziemlich virtuell für mich. Sorry für den Kommentar Spamming, ich versuche nur, meinen Kopf darum zu wickeln. – falstro

+0

@roe: löschen und neue Operatoren werden vererbt. Da Sie sie in Klasse B überladen haben, wird sie auch von C benutzt. Sehen Sie das schöne Diagramm hier: http://msdn.microsoft.com/en-us/library/c5at8eya.aspx – sharptooth

Verwandte Themen