Предположим, что у меня этот код
class Base{
public:
int getVal();
private:
int a, b;
};
class Derived::public Base{
public:
void printVal();
};
int main(){
Base *b = new Derived();
delete b;
}
Я знаю, что виртуальный деструктор будет правильно удалять вещи, но плохо ли удалять с помощью базового указателя (когда нет виртуального деструктора), даже если нет виртуальных функций и нет элементов данных в производном классе? Что произойдет, если это будет сделано?
Ответ 1
Для данных примитивного типа ваш пример, скорее всего, будет работать на практике. Фактически, использование vtable может фактически помешать работе (поэтому здесь может быть какое-то законное использование), но технически undefined, для 5.3-5.4:
Если статический тип операнда [ оператор удаления] отличается от его динамический тип, статический тип должен быть базовым классом операнда динамический тип и статический тип иметь виртуальный деструктор или поведение undefined.
На самом деле все зависит от "кучи" данных в вашем классе, и поскольку в вашем случае нет выделенных кучей элементов (в вашем случае), вы должны быть в порядке, но это определенно запах кода.
Ответ 2
Неправильно ли удалять базовый указатель (если нет виртуального деструктора), даже если в производном классе нет никаких элементов данных и нет элементов данных?
Да.
Поведение undefined независимо от содержимого производного класса.
Что произойдет, если это будет сделано?
Все может случиться.
Ответ 3
Виртуальный дескриптор в производном классе необходим для правильного вызова производного деструктора (полиморфизма), когда производный объект создается с помощью указателя на базовый класс.
Правило CPP с высокой степенью целостности 3.3.2 Написание "виртуального" деструктора для базовых классов. (QACPP 2116)
Оправдание. Если объект когда-либо будет уничтожен с помощью указателя на его базовый класс, то этот базовый класс должен иметь виртуальный деструктор. Если деструктор базового класса не является виртуальным, будет вызван только деструктор для базового класса. В большинстве случаев деструкторы должны быть виртуальными, поскольку обслуживание или повторное использование могут добавлять производные классы, которым требуется виртуальный деструктор.
class Base {};
class Derived : public Base { public: ~Derived() {} };
void foo() {
Derived* d = new Derived; delete d; // correctly calls derived destructor
}
void boo() {
Derived* d = new Derived; Base* b = d; delete b; // problem! does not call derived destructor!
}