Это еще более важно для интерфейса. Любой пользователь вашего класса, вероятно, будет содержать указатель на интерфейс, а не указатель на конкретную реализацию. Когда они приходят, чтобы удалить его, если деструктор не виртуальный, они будут вызывать деструктор интерфейса (или предоставленный компилятором по умолчанию, если вы его не указали), а не деструктор производного класса. Мгновенная утечка памяти.
Например
class Interface
{
virtual void doSomething() = 0;
};
class Derived : public Interface
{
Derived();
~Derived()
{
// Do some important cleanup...
}
};
void myFunc(void)
{
Interface* p = new Derived();
// The behaviour of the next line is undefined. It probably
// calls Interface::~Interface, not Derived::~Derived
delete p;
}
delete p
вызывает неопределенное поведение. Звонить не гарантируетсяInterface::~Interface
.[expr.delete]/
:... if the static type of the object to be deleted is different from its dynamic type, ... the static type shall have a virtual destructor or the behavior is undefined. ...
. Было бы все еще неопределенным, если бы Derived использовал неявно сгенерированный деструктор.Ответ на ваш вопрос часто, но не всегда. Если ваш абстрактный класс запрещает клиентам вызывать delete по указателю на него (или если он говорит об этом в своей документации), вы можете не объявлять виртуальный деструктор.
Вы можете запретить клиентам вызывать delete для указателя, сделав его деструктором защищенным. Работая так, совершенно безопасно и разумно опустить виртуальный деструктор.
В конечном итоге у вас не останется таблицы виртуальных методов, и в итоге вы дадите сигнал своим клиентам о своем намерении сделать ее не удаляемой через указатель на нее, поэтому у вас действительно есть причина не объявлять ее виртуальной в этих случаях.
[См. Пункт 4 в этой статье: http://www.gotw.ca/publications/mill18.htm ]
источник
Я решил провести небольшое исследование и попытаться обобщить ваши ответы. Следующие вопросы помогут вам решить, какой деструктор вам нужен:
Надеюсь, это поможет.
* Важно отметить, что в C ++ нет способа пометить класс как окончательный (т.е. не подклассифицируемый), поэтому в случае, если вы решите объявить свой деструктор не виртуальным и общедоступным, не забывайте явно предупреждать ваших коллег-программистов против исходя из вашего класса.
Ссылки:
источник
Да, это всегда важно. Производные классы могут выделять память или содержать ссылку на другие ресурсы, которые необходимо будет очистить при уничтожении объекта. Если вы не передадите своим интерфейсам / абстрактным классам виртуальные деструкторы, то каждый раз, когда вы удаляете экземпляр производного класса через дескриптор базового класса, деструктор вашего производного класса не будет вызываться.
Следовательно, вы открываете потенциал для утечек памяти
источник
Это не всегда требуется, но я считаю, что это хорошая практика. Что он делает, так это позволяет безопасно удалить производный объект через указатель базового типа.
Так, например:
плохо сформирован, если
Base
не имеет виртуального деструктора, потому что он попытается удалить объект, как если бы он былBase *
.источник
shared_ptr
он пытается удалить объект, как если бы он былBase *
- он запоминает тип того, с чем вы его создали. См. Ссылочную ссылку, в частности, бит, который говорит: «Деструктор будет вызывать delete с тем же указателем, в комплекте с его исходным типом, даже если у T нет виртуального деструктора или он пуст».Это не только хорошая практика. Это правило № 1 для любой иерархии классов.
Теперь почему. Возьмите типичную иерархию животных. Виртуальные деструкторы проходят виртуальную диспетчеризацию, как и любой другой вызов метода. Возьмите следующий пример.
Предположим, что Animal является абстрактным классом. Единственный способ, которым C ++ знает правильный деструктор для вызова, - это диспетчеризация виртуального метода. Если деструктор не является виртуальным, он просто вызывает деструктор Animal и не уничтожает объекты в производных классах.
Причина создания виртуального деструктора в базовом классе заключается в том, что он просто удаляет выбор из производных классов. Их деструктор становится виртуальным по умолчанию.
источник
Ответ прост, вам нужно, чтобы он был виртуальным, иначе базовый класс не был бы полным полиморфным классом.
Вы бы предпочли удаление, описанное выше, но если деструктор базового класса не является виртуальным, будет вызван только деструктор базового класса, и все данные в производном классе останутся восстановленными.
источник