Виртуальность деструктора ничего не сломает в существующем коде, если нет других проблем. Это может даже решить некоторые (см. Ниже). Однако класс не может быть разработан как полиморфный, поэтому добавление virtual к его деструктору делает его способным к полиморфизму, что может быть нежелательно. Тем не менее, вы должны иметь возможность безопасно добавлять виртуальность к деструктору, и он не должен вызывать никаких проблем сам по себе.
Объяснение
Полиморфизм позволяет:
class A
{
public:
~A() {}
};
class B : public A
{
~B() {}
int i;
};
int main()
{
A *a = new B;
delete a;
}
Вы можете взять указатель на объект типа A
из класса, который на самом деле имеет тип B
. Это полезно, например, для разделения интерфейсов (например, A
) и реализаций (например, B
). Однако что будет на delete a;
?
Часть объекта a
типа A
уничтожена. А как насчет части типа B
? К тому же у этой части есть ресурсы, и их нужно освободить. Ну вот тут и утечка памяти. Вызывая delete a;
, вы вызываете деструктор типа A
(поскольку a
является указателем на тип A
), в основном вы вызываете a->~a();
. Деструктор типа B
никогда не вызывается. Как это решить?
class A :
{
public:
virtual ~A() {}
};
Добавляя виртуальную диспетчеризацию к деструктору A
(обратите внимание, что, объявляя базовый деструктор виртуальным, он автоматически делает деструкторы всех производных классов виртуальными, даже если они не объявлены как таковые). Затем вызов delete a;
отправит вызов деструктора в виртуальную таблицу, чтобы найти правильный деструктор для использования (в данном случае типа B
). Этот деструктор будет вызывать родительские деструкторы как обычно. Аккуратно, правда?
Возможные проблемы
Как видите, таким образом вы ничего не сломаете. Однако в вашем дизайне могут быть другие проблемы. Например, могла быть ошибка, которая «полагалась» на невиртуальный вызов деструктора, который вы раскрыли, сделав его виртуальным, рассмотрим:
int main()
{
B *b = new B;
A *a = b;
delete a;
b->i = 10; //might work without virtual destructor, also undefined behvaiour
}
В основном нарезка объекта, но поскольку до этого у вас не было виртуального деструктора, B
часть созданного объекта не была уничтожена, поэтому назначение i
может сработать. Если вы сделали деструктор виртуальным, то он не существует и, скорее всего, выйдет из строя или сделает что-то еще (неопределенное поведение).
Подобные вещи могут происходить, и в сложном коде их может быть трудно найти. Но если ваш деструктор вызывает сбои после того, как вы сделали его виртуальным, у вас, вероятно, есть ошибка, подобная этой, где-то там, и она была у вас с самого начала, потому что, как я сказал, просто создание виртуального деструктора не может ничего сломать само по себе.
person
Resurrection
schedule
22.08.2016
shared_ptr
, не нужны. - person Quentin   schedule 22.08.2016Base*
, которое указывает наDerived
, вероятно, вызовет UB. Таким образом, сделать его виртуальным будет означать, что он (должен) вести себя должным образом. - person qxz   schedule 22.08.2016virtual
метод, почти всегда вам нуженvirtual
деструктор (кроме того, если у вас есть единственный виртуальный метод, цена vptr уже оплачена, поэтому добавление дополнительного виртуального метода по сути бесплатно ). - person Matteo Italia   schedule 22.08.2016virtual
в деструктор базового класса. В любом случае, если это полезно в вашем исследовании, обычно, когда у вас нет виртуального деструктора базового класса, происходит следующее: когда вы удаляете производный объект с помощью указателя на базу, производный деструктор не вызывается, и поля, добавленные в производный класс, не уничтожаются. - person Matteo Italia   schedule 22.08.2016override
, как и должно происходить всякий раз, когда вы предполагаете, что функция виртуальная. - person Quentin   schedule 22.08.2016~b1 () { /* do stuff */ }
- person fgrdn   schedule 22.08.2016virtual
каким-то образом вводящие ошибки) указывают на UB, однако OP не предоставил MCVE или какой-либо код, поэтому спекулировать на этом бесполезно. Вопросы, требующие помощи по отладке (почему этот код не работает?), должны включать желаемое поведение, конкретную проблему или ошибку и кратчайший код. необходимо воспроизвести его в самом вопросе. Вопросы без четкой постановки проблемы не будут полезны другим читателям. См .: Как создать минимальный, полный и проверяемый пример. - person underscore_d   schedule 22.08.2016