Требуется ли определение деструктора производного класса, если деструктор базового класса является виртуальным?
Я пытаюсь следующий пример:
class base // base class
{
public:
std::list<base*> values;
base(){}
void initialize(base *b) {
values.push_front(b);
}
virtual ~base()
{
values.clear();
cout<<"base called"<<endl;
}
};
class derived : public base // derived class
{
public:
~derived(){
cout<<"derived called"<<endl;
}
};
int main()
{
derived *d = new derived;
base *b = new base;
b->initialize(static_cast<base *>(d)); /* filling list */
delete b;
return 0;
}
Q.1) Почему не вызывается деструктор производного класса, как в деструкторе базового класса, который я выполняю values.clear()
?
В.2) Требуется ли определение деструктора производного класса, если деструктор базового класса является виртуальным?
4 ответа
Q1. Потому что вы не удаляете объект типа derived
, Вы только делаете delete b;
, который удаляет base
, Вы также должны позвонить delete d;
,
Также вы должны указать, какой объект отвечает за управление памятью. Ваш дизайн подвержен ошибкам. Вам лучше использовать умный указатель, чтобы предотвратить двусмысленность. Кроме того, чтобы вести себя так, как вы ожидаете, деструктор должен быть:
virtual ~base()
{
for ( int i = 0 ; i < values.size() ; i++ )
delete values[i];
values.clear();
cout<<"base called"<<endl;
}
Конечно, при таком подходе было бы неопределенное поведение, вызывающее delete d;
в вашем main
,
Q2. Нет, определение не требуется.
Почему деструктор производного класса не вызывается, как в деструкторе базового класса, который я выполняю
values.clear();
values.clear()
удаляет все указатели из этого списка. Это не удаляет объекты, на которые указывают; это было бы чрезвычайно опасно, так как в списке нет способа узнать, отвечает ли он за их время жизни или они просто используются для ссылки на объекты, управляемые в других местах.
Если вы хотите, чтобы список владел объектами, вы должны либо удалить их самостоятельно, либо удалив умные указатели, такие как std::unique_ptr<base>
, Если ваш компилятор не поддерживает новые умные указатели, то вам может пригодиться библиотека Boost's Pointer Container.
Требуется ли определение деструктора производного класса. Если деструктор базового класса является виртуальным.
Это нужно только в том случае, если в производном классе есть что-то, что требует очистки. Нет необходимости определять пустой, если для этого нечем заняться.
Вы на самом деле не delete d
так что, конечно, деструктор не вызывается. Либо сделать d статически распределенным (derived d
вместо derived *d = new derived
) или позвоните по телефону delete d
,
Если вы не объявите деструктор в производном классе, будет создан деструктор. Деструктор базового класса будет по-прежнему вызываться, см. FAQ (11.12). Также обратите внимание, что поскольку деструктор базового класса является виртуальным, деструктор производного класса автоматически становится виртуальным (независимо от того, определяете вы его или нет), см. FAQ (20.7).
Как вы думаете, почему должен вызываться деструктор производного класса? Вы только удаляете базу, а это экземпляр базового класса.
Нет, определение деструктора не требуется - вы можете его опустить.