当前位置: 首页 > 知识库问答 >
问题:

多态类中的虚拟析构函数

顾高翰
2023-03-14

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

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

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

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

我的假设正确吗?

共有2个答案

黄修永
2023-03-14

考虑

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();

这将打印基数::~基数。现在,如果我们使析构函数成为虚拟函数,那么,与任何其他虚拟函数一样,将调用对象的动态类型的最终覆盖器。析构函数覆盖基类中的虚拟析构函数(即使其“名称”不同):

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

调用 p-

Derived::~Derived
Base::~Base

现在,delete表达式一般相当于一个析构函数调用,后跟一个内存释放函数调用。在这种特殊情况下,表达式

delete p;

相当于

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

因此,如果析构函数是虚拟的,那么这是正确的:它首先调用Derived::~Derived,然后在完成时自动调用Base::~Base。如果析构函数不是虚拟的,那么可能的结果是只调用Base::~Base

柳星晖
2023-03-14

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

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

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

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

 类似资料:
  • 在这些关于C 11/14标准的幻灯片中,在幻灯片15中,作者写道,“许多经典的编码规则不再适用于C 11中”。他提出了三个示例的列表,我同意三法则和内存管理。 然而,他的第二个例子是“具有虚拟成员的虚拟析构函数”(仅此而已)。这是什么意思?我知道必须将基类析构函数声明为虚拟,以便调用正确的析构函数,如果我们有类似的东西 这里很好地解释了:什么时候使用虚拟析构函数? 但是,如果您有虚拟成员,现在在C

  • 我读过一些文章,正如他们所说,主要的虚拟析构函数用例是: > 派生类可能具有来自堆的动态数据分配,即“拥有”该数据对象。所以,他们需要在析构函数中使用一些删除例程。通过基类指针删除需要在所有派生类中声明析构函数,直到那些具有动态数据分配的析构函数(基类也需要它) 此类具有< code >虚拟方法。但这对我来说不清楚。仅仅通过基类指针调用< code >虚拟方法总是会导致派生实现最多的调用。他们唯一

  • 在C 11中,我们能够声明一个析构函数是自动生成的: 此外,我们可以将析构函数声明为纯虚: 我的问题是:如何将析构函数声明为自动生成和纯虚拟?看起来以下语法不正确: 这一个也不是: 也不是这个: 编辑:关于问题目的的一些澄清。基本上,我希望一个空类是不可实例化的基类,但派生类是可实例化的,那么该类必须有一个纯虚拟析构函数。但另一方面,我不想在.cpp文件中提供定义。因此,我需要某种与等效的机制。我

  • 每个人都知道基类的分解器通常必须是虚拟的。但是派生类的析构函数是什么?在 C 11 中,我们有关键字“override”,并且能够显式使用默认析构函数。 在子类的析构函数中同时使用关键字“覆盖”和“=默认”是否正确?在这种情况下,编译器会生成正确的虚拟析构函数吗? 如果是,那么我们是否可以认为这是好的编码风格,我们应该总是这样声明派生类的析构函数,以确保基类析构函数是虚的?

  • 我一直在使用越来越多的C11,我遇到了一些我在任何地方都找不到的东西。当我们从基指针中删除派生类时,我们需要有虚拟析构函数;但有时父析构函数需要“纯”,这在C中是不可能的。所以,我的问题是,默认值是否可以用于虚拟析构函数?我已经试过了,而且效果不错,但我不知道它是否安全,因为互联网上没有任何关于它的信息。 编辑:为了澄清这个问题,我说的是使用