Ich dachte, es wäre vorteilhaft, das "undefinierte" Verhalten oder zumindest das "abgestürzte" undefinierte Verhalten zu diskutieren, das beim Löschen durch eine Basisklasse (/ struct) ohne virtuellen Destruktor oder genauer gesagt ohne vtable auftreten kann. Der folgende Code listet einige einfache Strukturen auf (dasselbe gilt für Klassen).
#include <iostream>
using namespace std;
struct a
{
~a() {}
unsigned long long i;
};
struct b : a
{
~b() {}
unsigned long long j;
};
struct c : b
{
~c() {}
virtual void m3() {}
unsigned long long k;
};
struct d : c
{
~d() {}
virtual void m4() {}
unsigned long long l;
};
int main()
{
cout << "sizeof(a): " << sizeof(a) << endl;
cout << "sizeof(b): " << sizeof(b) << endl;
cout << "sizeof(c): " << sizeof(c) << endl;
cout << "sizeof(d): " << sizeof(d) << endl;
// No issue.
a* a1 = new a();
cout << "a1: " << a1 << endl;
delete a1;
// No issue.
b* b1 = new b();
cout << "b1: " << b1 << endl;
cout << "(a*) b1: " << (a*) b1 << endl;
delete b1;
// No issue.
c* c1 = new c();
cout << "c1: " << c1 << endl;
cout << "(b*) c1: " << (b*) c1 << endl;
cout << "(a*) c1: " << (a*) c1 << endl;
delete c1;
// No issue.
d* d1 = new d();
cout << "d1: " << d1 << endl;
cout << "(c*) d1: " << (c*) d1 << endl;
cout << "(b*) d1: " << (b*) d1 << endl;
cout << "(a*) d1: " << (a*) d1 << endl;
delete d1;
// Doesn't crash, but may not produce the results you want.
c1 = (c*) new d();
delete c1;
// Crashes due to passing an invalid address to the method which
// frees the memory.
d1 = new d();
b1 = (b*) d1;
cout << "d1: " << d1 << endl;
cout << "b1: " << b1 << endl;
delete b1;
/*
// This is similar to what's happening above in the "crash" case.
char* buf = new char[32];
cout << "buf: " << (void*) buf << endl;
buf += 8;
cout << "buf after adding 8: " << (void*) buf << endl;
delete buf;
*/
}
Ich schlage nicht vor, ob Sie virtuelle Destruktoren benötigen oder nicht, obwohl ich im Allgemeinen denke, dass es eine gute Praxis ist, sie zu haben. Ich möchte nur auf den Grund hinweisen, warum Sie möglicherweise abstürzen, wenn Ihre Basisklasse (/ struct) keine vtable und Ihre abgeleitete Klasse (/ struct) hat und Sie ein Objekt über eine Basisklasse (/ struct) löschen. Zeiger. In diesem Fall ist die Adresse, die Sie an die freie Routine des Heapspeichers übergeben, ungültig und somit der Grund für den Absturz.
Wenn Sie den obigen Code ausführen, sehen Sie deutlich, wann das Problem auftritt. Wenn sich dieser Zeiger der Basisklasse (/ struct) von diesem Zeiger der abgeleiteten Klasse (/ struct) unterscheidet, tritt dieses Problem auf. Im obigen Beispiel haben Struktur a und b keine vtables. Die Strukturen c und d haben vtables. Somit wird ein a- oder b-Zeiger auf eine ac- oder d-Objektinstanz festgelegt, um die vtable zu berücksichtigen. Wenn Sie diesen a- oder b-Zeiger zum Löschen übergeben, stürzt er ab, da die Adresse für die freie Routine des Heaps ungültig ist.
Wenn Sie abgeleitete Instanzen mit vtables aus Basisklassenzeigern löschen möchten, müssen Sie sicherstellen, dass die Basisklasse über eine vtable verfügt. Eine Möglichkeit, dies zu tun, besteht darin, einen virtuellen Destruktor hinzuzufügen, mit dem Sie möglicherweise ohnehin die Ressourcen ordnungsgemäß bereinigen möchten.