我正在尝试以下示例:
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()
的基类析构函数一样?
Q.2)如果基类析构函数是虚拟的,是否需要派生类析构函数定义?
答案 0 :(得分:7)
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。不,这个定义不是必需的。
答案 1 :(得分:4)
为什么没有调用派生类的析构函数,就像在基类析构函数中我正在执行
values.clear();
values.clear()
删除此列表中的所有指针。它不会删除被指向的对象;这将是非常危险的,因为该列表无法知道它是否对其生命周期负责,或者它们是否仅用于引用在其他地方管理的对象。
如果您希望列表拥有对象,则必须在删除它们时自行删除它们,或者存储智能指针,例如std::unique_ptr<base>
。如果您的编译器不支持新的智能指针,那么您可能会发现Boost的Pointer Container库很有用。
是否需要派生类析构函数定义。如果基类析构函数是虚拟的。
只有在派生类中有需要清理的东西时才需要它。如果没有什么可以做的话,就没有必要定义一个空的。
答案 2 :(得分:1)
你实际上并不delete d
,所以当然没有调用析构函数。要么静态分配(derived d
而不是derived *d = new derived
),要么致电delete d
。
如果未在派生类中声明析构函数,则将创建默认析构函数。仍将调用基类析构函数,请参阅FAQ(11.12)。另请注意,由于基类析构函数是虚拟的,派生类析构函数是自动虚拟的(无论您是否定义),请参阅FAQ(20.7)。
答案 3 :(得分:0)
为什么你认为应该调用派生类的析构函数?您只删除base,它是基类的实例。
不需要析构函数的定义 - 您可以省略它。