С++ "Виртуальные функции, но не виртуальные деструкторы"
У меня есть базовый класс Media и несколько производных классов, а именно DVD, Book и т.д.
Базовый класс записывается как:
class Media{
private:
int id;
string title;
int year;
public:
Media(){ id = year = 0; title = ""; }
Media(int _id, string _title, int _year): id(_id), title(_title), year(_year) {}
// virtual ~Media() = 0;
void changeID(int newID){ id = newID; }
virtual void print(ostream &out);
};
Дело в том, что без деструктора GCC дает мне кучу предупреждений "класс имеет виртуальные функции, но не виртуальный деструктор", но все еще компилируется, и моя программа работает нормально. Теперь я хочу избавиться от этих раздражающих предупреждений, поэтому я удовлетворяю компилятору, добавляя виртуальный деструктор, результат: он не компилируется с ошибкой:
undefined ссылка на Media::~Media()
Создание виртуального виртуального деструктора не решает проблему.
Итак, что пошло не так?
Ответы
Ответ 1
Вам нужно также определить виртуальный деструктор, а не только добавить его.
//Media.h
class Media{
//....
virtual ~Media() = 0;
};
//Media.cpp
#include "Media.h"
//....
Media::~Media() {};
Причина, по которой вы получаете предупреждения, состоит в том, что все классы, которые должны быть получены, должны иметь виртуальный или защищенный (credit @Steve) деструктор, в противном случае удаление экземпляра с помощью указателя на базовый класс приведет к поведению undefined.
Примечание. У вас есть определение деструкторов, даже если они являются чистыми виртуальными.
Ответ 2
Вы должны реализовать виртуальный деструктор, а не сделать его чистым виртуальным.
Посмотрите этот аналогичный вопрос (идентичный, возможно, с точки зрения ошибки виртуального деструктора, а не предупреждение) для получения дополнительной информации.
EDIT: более общее решение, в ответ на комментарий LuchianGrigore (спасибо, что указали)
Вы также можете сделать деструктор чистым виртуальным и реализовать, поскольку он указан в вышеупомянутом вопросе.
Использование виртуальных деструкторов в ваших классах должно состоять в том, чтобы предотвратить создание экземпляра базового класса (т.е. когда у вас нет других чистых виртуальных методов для создания абстрактного класса).
Ответ 3
Дело в том, что без деструктора GCC дает мне кучу предупреждений "класс имеет виртуальные функции, но не виртуальный деструктор", но все еще компилируется, а моя программа отлично работает
Это раздражающее предупреждение в Modern С++, но в старом объектно-стиле С++ оно обычно корректно.
Проблема заключается в том, как разрушаются ваши объекты. Простой тест:
#include <iostream>
class Base {};
class Derived: public Base { public: ~Derived() { std::cout << "Aargh\n"; } };
int main() {
Base* b = new Derived();
Derived* d = new Derived();
delete d;
delete b;
}
Отпечатки:
Aargh
Да, только один раз.
Проблема заключается в том, что при вызове delete
для переменной типа Base*
вызывается метод Base::~Base()
. Если это virtual
, тогда вызов динамически отправляется в окончательный метод (на основе динамического типа), в этом случае Derived::~Derived()
, но если это не так, то Derived::~Derived()
никогда не вызывается, поэтому никогда не выполняется.
Поэтому, если вы хотите вызвать delete
(или использовать интеллектуальные указатели, которые делают это для вас) в базовых типах, вам нужно добавить virtual ~Base() {}
в свои определения классов. Вот почему gcc предупреждает вас, когда вы создаете полиморфный класс без деструктора virtual
.
Примечание: время изменилось, и с тех пор я реализовал -Wdelete-non-virtual-dtor
в Clang, и он также был реплицирован в gcc.
-Wnon-virtual-dtor
полезен для библиотек (как он предупреждает о базовом классе), но может иметь более высокую ложную положительную скорость; с другой стороны -Wdelete-non-virtual-dtor
срабатывает на сайте вызова и имеет значительно более низкие значения ложных срабатываний (которые вы обычно можете обойти, используя peppering final
, чтобы удалить "полиморфное" свойство класса).
Ответ 4
То, что вы закомментировали, - это чисто виртуальное объявление для деструктора. Это означает, что функция должна быть переопределена в производном классе, чтобы иметь возможность создавать экземпляр объекта этого класса.
То, что вы хотите, - это просто определение деструктора как виртуальной функции:
virtual ~Media() {}
Ответ 5
Объявление об отказе сначала, а затем
Попробуйте добавить следующую строку после объявления класса
Media::~Media(){}