c++子类析构函数未调用

为什么父类的析构函数定义成virtual后, delete pTest2 就会释放Derived的资源, 否则就只能释放Base的资源, 而delete pTest1就能释放Derived 和Base的资源

class Base
{
public:
    ~Base() {
        cout << "Base destructor!" << endl;
    };
};

class Derived: public Base
{
public:
    ~Derived() {
        cout << "Derived destructor!" << endl;
    };
};

int main() {

    Derived *pTest1 = new Derived();
    Base *pTest2 = new Derived();

    delete pTest1;
    delete pTest2;
    return 0;
}
阅读 6.3k
4 个回答

只能说 cpp 就是这么设计的. 你可说它(被)设计的不好.

sf上有个问题:什么时候不用 virtual destructor?

供参考
https://stackoverflow.com/que...

虚函数意味着每个分配的对象都会通过虚函数表指针增加内存开销。

因此,如果您的程序涉及分配大量的某些对象,那么为了节省每个对象的额外32位,值得避免所有虚函数。

在所有其他情况下,您将节省自己的调试痛苦,使析构函数虚拟化。

以下只是个人理解,如有谬误请指出,谢谢。

首先需要明确两个概念:

第一个是 virtual 的意义,
当我们通过指针调用一个类对象的方法时,编译器只有指针类型和指向地址这两个信息,它只能根据指针类型来推断指向的对象实例在内存里实际长什么样,
当我们调用的是一个 non-virtual 方法时,C++ 只能根据指针类型来判断应该调用的方法,如果这个指针是一个基类指针,即使它指向的是一个子类对象,但编译器不知道啊,所以它只会调用基类中的对应方法。
只有声明了 virtual,C++ 才会在对象实例中创建一个 virtual table,里面包含了这个类对象实际持有的所有 virtual 函数的地址,此时我们通过一个基类指针指向子类对象,调用 virtual 方法,C++ 会到这个虚函数表中调用对应位置的函数指针,自然就能正确调用子类的方法了。

第二个是析构函数的运作方式,
当我们调用一个 class 的析构函数,从这个 class 起,依次往上,每个 base class 的析构函数都会逐个被调用。
这也是为什么 delete 子类指针,子类和父类的析构函数都会被正确调用的原因。

显然,当一个 derived class 对象,经由一个 base class 指针删除时,
如果这个 base class 的析构函数是 non-virtual 的,C++ 就会直接调用 base class 的析构函数,derived class 的析构函数自然得不到执行,
当这个 base class 的析构函数是 virtual 的,C++ 在 vtbl 中找到这个对象实例所持有析构函数的函数指针,并调用它,然后根据析构函数的基本运作方式,子类先调用,基类后调用。

新手上路,请多包涵
class Base
{
public:
    ~Base() {
        ...;
    };
};

Base *pTest2 = ...;
delete pTest2;

仔细看这个代码,执行delete pTest2时,编译器并不知道pTest2指向的是一个Base子类!他只知道指向的是类Base!

delete的语义就是调用析构函数
如果析构函数有virtual修饰,C++保证delete调用子类析构,插在析构前面。

本身一个delete一个指针只会调用本身的析构。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题