Ich habe versucht, ::delete
eine Klasse in der davon zu fordern operator delete
. Aber der Destruktor wird nicht aufgerufen.
Ich habe eine Klasse definiert, MyClass
deren operator delete
überladen wurde. Das Globale operator delete
ist ebenfalls überlastet. Die Überladung operator delete
von MyClass
ruft die überladene globale auf operator delete
.
class MyClass
{
public:
MyClass() { printf("Constructing MyClass...\n"); }
virtual ~MyClass() { printf("Destroying MyClass...\n"); }
void* operator new(size_t size)
{
printf("Newing MyClass...\n");
void* p = ::new MyClass();
printf("End of newing MyClass...\n");
return p;
}
void operator delete(void* p)
{
printf("Deleting MyClass...\n");
::delete p; // Why is the destructor not called here?
printf("End of deleting MyClass...\n");
}
};
void* operator new(size_t size)
{
printf("Global newing...\n");
return malloc(size);
}
void operator delete(void* p)
{
printf("Global deleting...\n");
free(p);
}
int main(int argc, char** argv)
{
MyClass* myClass = new MyClass();
delete myClass;
return EXIT_SUCCESS;
}
Die Ausgabe ist:
Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...
Tatsächlich:
Es gibt nur einen Aufruf an den destructor , bevor die überladenen Aufruf operator delete
von MyClass
.
Erwartet:
Es gibt zwei Aufrufe an den Destruktor. Eine vor dem Aufruf der überladenen operator delete
von MyClass
. Ein anderer vor dem Aufruf der globalen operator delete
.
c++
delete-operator
expinc
quelle
quelle
MyClass::operator new()
sollte Rohspeicher von (mindestens)size
Bytes zuweisen . Es sollte nicht versucht werden, eine Instanz von vollständig zu erstellenMyClass
. Der Konstruktor vonMyClass
wird nach ausgeführtMyClass::operator new()
. Dann ruft derdelete
Ausdruck inmain()
den Destruktor auf und gibt den Speicher frei (ohne den Destruktor erneut aufzurufen). Der::delete p
Ausdruck hat keine Informationen über die Art derp
Objektpunkte an, dap
eine istvoid *
, kann so nicht den Destruktor aufrufen.::delete p;
verursacht undefiniertes Verhalten, da der Typ von*p
nicht mit dem Typ des zu löschenden Objekts identisch ist (noch eine Basisklasse mit virtuellem Destruktor)void*
der Operand sogar explizit schlecht geformt ist. [expr.delete] / 1 : " Der Operand muss vom Zeiger auf den Objekttyp oder vom Klassentyp sein. [...] Dies impliziert, dass ein Objekt nicht mit einem Zeiger vom Typ void gelöscht werden kann, da void kein Objekttyp ist. * "@OP Ich habe meine Antwort geändert.Antworten:
Sie missbrauchen
operator new
undoperator delete
. Diese Operatoren sind Zuordnungs- und Freigabefunktionen. Sie sind nicht verantwortlich für die Konstruktion oder Zerstörung von Objekten. Sie sind nur für die Bereitstellung des Speichers verantwortlich, in dem das Objekt platziert wird.Die globalen Versionen dieser Funktionen sind
::operator new
und::operator delete
.::new
und::delete
sind neue / delete-Ausdrücke, ebenso wienew
/delete
, die sich darin unterscheiden,::new
und::delete
umgehen klassenspezifischeoperator new
/operator delete
Überladungen.Die neuen / delete-Ausdrücke konstruieren / zerstören und ordnen / freigeben (durch Aufrufen des entsprechenden
operator new
oderoperator delete
vor dem Aufbau oder nach der Zerstörung).Da Ihre Überlastung nur für den Zuordnungs- / Freigabe-Teil verantwortlich ist, sollte sie
::operator new
und::operator delete
anstelle von::new
und aufrufen::delete
.Das
delete
indelete myClass;
ist dafür verantwortlich, den Destruktor aufzurufen.::delete p;
ruft den Destruktor nicht auf, da erp
einen Typ hatvoid*
und der Ausdruck daher nicht wissen kann, welcher Destruktor aufgerufen werden soll. Wahrscheinlich wird Ihr Ersetzter aufgerufen,::operator delete
um die Zuordnung des Speichers aufzuheben, obwohl die Verwendung einesvoid*
as-Operanden für einen Löschausdruck falsch ist (siehe Bearbeitung unten).::new MyClass();
ruft Ihren ersetzten::operator new
auf, um Speicher zuzuweisen, und erstellt ein Objekt darin. Der Zeiger auf dieses Objekt wird in Bezugvoid*
auf den neuen Ausdruck in zurückgegebenMyClass* myClass = new MyClass();
, der dann ein anderes Objekt in diesem Speicher erstellt und die Lebensdauer des vorherigen Objekts beendet, ohne dessen Destruktor aufzurufen.Bearbeiten:
Dank des Kommentars von @ MM zu dieser Frage wurde mir klar, dass ein
void*
Operand für::delete
tatsächlich schlecht geformt ist. ( [expr.delete] / 1 ) Die großen Compiler scheinen jedoch beschlossen zu haben, nur davor zu warnen, nicht vor Fehlern. Sehen Sie sich diese Frage an, bevor es schlecht geformt wurde und::delete
einvoid*
bereits undefiniertes Verhalten verwendet .Daher ist Ihr Programm schlecht geformt und Sie haben keine Garantie dafür, dass der Code tatsächlich das tut, was ich oben beschrieben habe, wenn er noch kompiliert werden konnte.
Wie von @SanderDeDycker unter seiner Antwort ausgeführt, haben Sie auch ein undefiniertes Verhalten, da Sie durch das Erstellen eines anderen Objekts im Speicher, das bereits ein
MyClass
Objekt enthält, ohne zuerst den Destruktor dieses Objekts aufzurufen, gegen [basic.life] / 5 verstoßen, was dies verbietet, wenn das Programm hängt von den Nebenwirkungen des Destruktors ab. In diesem Fall hat dieprintf
Anweisung im Destruktor einen solchen Nebeneffekt.quelle
Ihre klassenspezifischen Überladungen werden falsch ausgeführt. Dies ist in Ihrer Ausgabe zu sehen: Der Konstruktor wird zweimal aufgerufen!
operator new
Rufen Sie im klassenspezifischen den globalen Operator direkt auf:Führen Sie in der Klassenspezifischen Vorgehensweise
operator delete
Folgendes aus:operator new
Weitere Informationen finden Sie auf der Referenzseite.quelle
Siehe CPP-Referenz :
Löschen (und Neu) sind nur für den Teil 'Speicherverwaltung' verantwortlich.
Es ist also klar und zu erwarten, dass der Destruktor nur einmal aufgerufen wird - um die Instanz des Objekts zu bereinigen. Würde es zweimal aufgerufen, müsste jeder Destruktor prüfen, ob es bereits aufgerufen wurde.
quelle
operator delete()
ist Funktion nicht dasselbe wie eine Lösch - Expression. Der Destruktor wird aufgerufen, bevor dieoperator delete()
Funktion aufgerufen wird.