Я подумав, що було б корисно обговорити "невизначене" поведінку або, принаймні, "краху" невизначеної поведінки, що може статися під час видалення через базовий клас (/ структуру) без віртуального деструктора, а точніше без vtable. У коді нижче перелічено кілька простих структур (те саме було б справедливо і для класів).
#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;
*/
}
Я не підказую, потрібні вам віртуальні деструктори чи ні, хоча я взагалі думаю, що це є гарною практикою їх мати. Я просто вказую на причину того, що у вас може виникнути збій, якщо ваш базовий клас (/ структура) не має vtable, а ваш похідний клас (/ структура) робить, а ви видаляєте об'єкт через базовий клас (/ структура) покажчик. У цьому випадку адреса, яку ви передаєте у вільну процедуру купи, є недійсною, і, отже, причиною збоїв.
Якщо запустити вищевказаний код, ви чітко побачите, коли виникає проблема Коли цей покажчик базового класу (/ структура) відрізняється від цього покажчика похідного класу (/ структура), ви збираєтеся зіткнутися з цією проблемою. У наведеному вище прикладі структури a і b не мають vtables. у структур c і d є vtables. Таким чином, покажчик a або b на екземпляр об'єкта ac або d буде зафіксований для обліку vtable. Якщо ви передасте цей a або b покажчик для його видалення, він вийде з ладу через те, що адреса є недійсною для безкоштовного розпорядку купи.
Якщо ви плануєте видалити похідні екземпляри, у яких є vtables з покажчиків базового класу, вам потрібно переконатися, що базовий клас має vtable. Один із способів зробити це - додати віртуальний деструктор, який, можливо, хочете, щоб правильно очистити ресурси.