"virtual"对C++析构函数有何影响?
How "virtual" impact on destructor in C++?
官方解释中的虚函数是:
虚函数是您希望在派生类中重新定义的成员函数。使用指针或对基类的引用引用派生类对象时,可以为该对象调用虚拟函数并执行派生类的函数版本。
请先看代码:
#include<iostream>
using namespace std;
class A
{
public:
A(){cout << "A()" << endl;}
~A(){cout << "~A()" << endl;}
};
class B:public A
{
public:
B(): A(){cout << "B()" << endl;}
~B(){cout << "~B()" << endl;}
};
int main()
{
A * pt = new B;
delete pt;
}
输出为:
A()
B()
~A()
我的问题是:
基类的析构函数- 不能被派生类继承,那么为什么我们要让基类的析构函数是虚拟的呢?
- 对于上面的代码,我知道这会导致问题(这里不调用 B 类的析构函数(。我从谷歌和堆栈溢出搜索了很多文章或问题,他们都告诉我 base 的析构函数应该是虚拟的,但析构函数上的"虚拟"如何工作?我的意思是,有/没有"虚拟"的核心代码级别与析构函数有什么区别?
如果A
的析构函数不是虚拟的,delete pt;
会导致未定义的行为。
将A
的析构函数设为虚拟的原因是允许使用delete pt;
删除B
对象。
这样做的理由是,当编译器看到delete pt;
时,一般来说,它无法知道pt
是否指向B
对象,因为该决定可能直到运行时才做出。因此,您需要查找对象的一些运行时属性(在本例中为 vtable(以找出要调用的正确析构函数。
其他一些注释/答案表明,原始代码的定义行为是不调用 B 的析构函数或其他东西。然而,这是错误的。你只是看到未定义行为的症状,可能是那个或其他任何东西。
如果析构函数标记为虚拟,则当您调用delete
时,将调用已分配对象的动态类型的析构函数。 在您的示例中,堆上对象的静态类型为 A,而动态类型为 B。
由于尚未将析构函数标记为虚拟,因此不会进行运行时调度,并且将调用 A 的析构函数。 这是错误的,应该修复。 如果计划以多态方式使用类,请确保它的析构函数是虚拟的,以便派生类的实例可以释放它们已获取的任何资源。
它可以帮助想象如何实现vtables。
具有虚拟方法的类将指向函数指针表的指针作为其第一个元素。
方法上的 virtual 意味着在虚函数表中有一个条目。
对于方法,继承的类在重写时将替换条目。
对于析构函数,条目实际上是"如何在此对象上调用 delete"。 所有下属类都会自动覆盖它。 它将delete base_ptr
的呼唤在概念上变成了if (base_ptr) base_ptr->vtable->deleter(base_ptr);
。
然后派生的删除器实际上是(几乎(delete static_cast<derived*>(ptr);
这执行通常的删除调用所做的事情,它按顺序调用析构函数。
如果不这样做,你会留下不确定的行为。 通常,UB 是调用基类 dtor 的。
如果函数不virtual
,C++运行时将直接调用mangled函数。例如,在上面的代码中,析构函数可能会被修改为_ZNK3AXXXXXXXXX
(假名(。所以当你调用delete pt
时,运行时会执行_ZNK3AXXXXXXXXX
。
但是,如果函数virtual
,则结果会有所不同。就像@Yakk说的,一个有虚函数的类会有一个vtable
,其条目是函数指针。它可能位于此类地址空间的顶部,也可能位于底部,具体取决于类模型的实现。任何虚函数的调用都将查找此表。如果 dtorvirtual
,派生类中的函数将覆盖表中的相应条目。使用指针或引用调用它时,C++运行时将调用表中的函数。再看看你的例子。pt
指向的对象条目已被类B
覆盖,所以当你调用delete pt
时,将被调用 dtor 的被覆盖版本。这就是区别。
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- C++核心准则 C35 对于接口类"A base class destructor should be either public and virtual, or protected and nonv
- 为什么擦除方法会影响结束方法
- 内联如何影响模块接口中的成员函数
- 为什么返回类型的'const'限定符对标有 __forceinline/内联的函数没有影响?
- 在容量内调整矢量大小时的性能影响
- 当我从下面的代码中删除关键字 virtual 时,它可以正常工作,否则会出现错误。在这里"virtual"字的意义是什么?
- 重载运算符的范围是什么?它是否会影响作为类成员的集合的插入函数?
- 未达到的情况会影响开关外壳性能
- 循环仅对第一行正常工作.其他行不受 for 的影响
- 处理影响跨不同线程共享对象的定时回调的最佳方法是什么?
- 模板如何影响C++中隐式声明的规则?
- 命名空间信息会影响C++的可读性
- [[可能]]和[[不太可能]]影响程序汇编的简单示例?
- 如何保护非托管应用程序中的字符串不受进程转储的影响
- 检查nullptr是否100%保护内存布局不受segfault影响
- 为什么 std::set.erase(first, last) 会影响从中获取 (first, last) 的容器?
- 发布代码的 gdb 堆栈跟踪可读性如何影响 x64?
- QSql查询行受影响的结果
- "virtual"对C++析构函数有何影响?