【问题标题】:Virtual destructor in polymorphic classes多态类中的虚拟析构函数
【发布时间】:2015-06-04 12:11:15
【问题描述】:

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

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

所以我的问题是:为什么当基类析构函数是非虚拟的时,对象不会被正确销毁?

我假设这是因为虚函数有某种表,每当调用虚函数时,它都会被记住和查阅。并且编译器知道当一个对象应该被删除时,它应该首先调用派生的析构函数。

我的假设正确吗?

【问题讨论】:

  • 你读过这个吗:stackoverflow.com/questions/461203/…
  • @EdChum 是的,我确实读过它,但它并没有完全回答我的假设。这就是我问的原因,但我在路上想通了。

标签: c++ inheritance polymorphism


【解决方案1】:

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

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

因此对象不会被正确销毁。

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

【讨论】:

  • 啊,是的,有道理。这是因为虚拟表包含虚拟功能。这样,当析构函数是虚拟的时,c++ 就知道要调用哪个析构函数。当它不是虚拟的时候,它只会调用静态类型的析构函数。
  • 正确,+1。但是,从技术上讲,vtable 只是实现虚拟调用的一种可能方式(尽管它是迄今为止最常见的一种)。从标准 C++ 的角度来看,只有规定的行为很重要。它的实现方式完全取决于编译器/平台 ABI。这就是为什么标准只是说“行为未定义”——在不同的虚拟调度实现下,可能会发生“静态类型析构函数”以外的事情。
【解决方案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。现在,如果我们将析构函数设为虚拟,那么与任何其他虚拟函数一样,对象的动态类型中的最终覆盖器将被调用。析构函数会覆盖基类中的虚拟析构函数(即使它的“名称”不同):

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,然后在完成后自动调用Base::~Base。如果析构函数不是虚拟的,则可能的结果是只调用了Base::~Base

【讨论】:

  • 您的 Derived 结构不会从您的 Base 结构继承。 Base* p = new Derived 不会编译。
  • @LudwigSchulze 哎呀。适合我在凌晨 2 点写答案。我会解决的
猜你喜欢
  • 2014-02-23
  • 1970-01-01
  • 2012-06-26
  • 2016-02-01
  • 2014-12-10
  • 2013-11-01
  • 2018-06-29
  • 1970-01-01
  • 2013-07-06
相关资源
最近更新 更多