首页 文章

多态类中的虚析构函数

提问于
浏览
4

我知道,只要你有一个多态基类,基类就应该定义一个虚析构函数 . 因此,当删除派生类对象的基类指针时,它将首先调用派生类的析构函数 . 如果我在这里错了,请纠正我 .

另外,如果基类析构函数是非虚拟的,则删除指向派生对象的基类指针将是未定义的行为 . 如果我错了,请纠正我 .

so my question is: Why is it exactly, that when the base-class destructor is non-virtual, the object will not be destroyed correctly?

我假设这是因为虚拟函数具有某种表,无论何时调用虚函数,它都会被记忆和参考 . 并且编译器知道当一个对象应该被删除时,它应该首先调用派生的析构函数 .

我的假设是正确的吗?

2 回答

  • 7

    如果在删除对象时,变量的静态类型是bas类型,则将调用基类型的析构函数,但不会调用子类的析构函数(因为它不是虚拟的) .

    因此,将释放由基类分配的资源,但子类分配的资源不会 .

    Thus the object won't be destructed correctly.

    您对该表是正确的:它被称为虚拟方法表或“vtable” . 但是析构函数是非虚拟的结果并不是没有以正确的顺序调用析构函数,而是根本没有调用子类的析构函数!

  • 2

    考虑

    struct Base {
        void f() { printf("Base::f"); }
    };
    struct Derived : Base {
        void f() { printf("Derived::f"); }
    };
    Base* p = new Derived;
    p->f();
    

    这打印 Base::f ,因为 Base::f 不是虚拟的 . 现在对析构函数执行相同的操作:

    struct Base {
        ~Base() { printf("Base::~Base"); }
    };
    struct Derived : Base {
        ~Derived() { printf("Derived::~Derived"); }
    };
    Base* p = new Derived;
    p->~Base();
    

    这打印 Base::~Base . 现在,如果我们将析构函数设置为virtual,那么,与任何其他虚函数一样,将调用对象的动态类型中的最终覆盖 . 析构函数会覆盖基类中的虚析构函数(即使它的"name"不同):

    struct Base {
        virtual ~Base() { printf("Base::~Base"); }
    };
    struct Derived : Base {
        ~Derived() override { printf("Derived::~Derived"); }
    };
    Base* p = new Derived;
    p->~Base();
    

    调用 p->~Base() 实际上调用了 Derived::~Derived() . 由于这是一个析构函数,在它的主体完成执行后,它会自动调用基础和成员的析构函数 . 所以输出是

    Derived::~Derived
    Base::~Base
    

    现在,delete-expression通常等同于析构函数调用,后跟对内存释放函数的调用 . 在这种特殊情况下,表达式

    delete p;
    

    相当于

    p->~Base();
    ::operator delete(p);
    

    因此,如果析构函数是虚拟的,那么这是正确的:它首先调用 Derived::~Derived ,然后在's done. If the destructor isn't虚拟时自动调用 Base::~Base ,可能的结果是只调用了 Base::~Base .

相关问题