如果基类析构函数是虚拟的,那么派生类析构函数定义是否必需?

4

我正在尝试以下示例:

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;
}

问题1:为什么派生类的析构函数没有被调用,而我的基类析构函数中执行了 values.clear()

问题2:如果基类析构函数是虚拟的,是否需要定义派生类的析构函数?


3
由于您从未使用 delete 删除对象,因此未调用派生类的析构函数! - Bo Persson
4个回答

7

问题一:因为您没有删除类型为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;将产生未定义的行为。

Q2. 不需要定义。


@ShashankJain values.clear() 不会删除向量包含的对象的内存。 - Luchian Grigore
1
好的,它确实会删除向量中包含的对象的内存,但在这种情况下,包含的对象是指针:指针被销毁,而不是它们所指向的对象。 - Luc Touraille

4
为什么派生类的析构函数没有被调用,因为在基类的析构函数中我执行了values.clear(); values.clear()会从列表中移除所有指针。它不会删除被指向的对象;这是非常危险的,因为列表无法知道它是否负责它们的生命周期,还是它们只是用于引用其他地方管理的对象。
如果您想让列表拥有这些对象,则必须在删除它们时自己删除它们,或者存储智能指针,例如std::unique_ptr<base>。如果您的编译器不支持新的智能指针,则可能会发现Boost的Pointer Container库很有用。
派生类的析构函数定义是否需要,如果基类的析构函数是虚拟的。
只有当派生类中有需要清理的内容时才需要。如果没有任何需要做的事情,就不需要定义一个空的析构函数。

1

你实际上没有 delete d,所以析构函数不会被调用。要么将d静态分配(使用derived d代替derived *d = new derived),要么调用delete d

如果你在派生类中不声明析构函数,那么默认的析构函数将被创建。基类的析构函数仍然会被调用,请参见FAQ(11.12)。还要注意,由于基类的析构函数是虚拟的,所以无论你是否定义了一个,派生类的析构函数都是自动虚拟的,请参见FAQ(20.7)。


0

为什么您认为应该调用派生类的析构函数?您只删除基类,它是基类的实例。

不需要定义析构函数-您可以省略它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接