C++什么时候销毁 vtable?

When is a vtable destroy in C++?

本文关键字:vtable 什么时候 C++      更新时间:2023-10-16

基类的析构函数中调用虚函数是一种很好的做法吗?">vtable在毁灭时间中存在吗?是否建议在析构函数中调用虚函数?

我可以在基类的析构函数中调用虚函数吗?

从析构函数或构造函数调用虚函数是一种不好的做法。请参阅标准(强调我的):

12.7 建造和破坏

成员函数,包括虚函数 (10.3),可以在构造或销毁 (12.6.2) 期间调用。 当从构造函数或析构函数直接或间接调用虚函数时,包括 在构造或销毁类的非静态数据成员期间,以及 调用应用是正在构建或销毁的对象(调用它x),调用的函数是最终覆盖器 在构造函数或析构函数的类中,而不是在派生更多的类中重写它。如果虚拟 函数调用使用显式类成员访问 (5.2.5),对象表达式引用完整的x的对象或该对象的基类子对象之一,但不是x或其基类子对象之一, 行为未定义

您可以在许多来源中找到此建议,包括斯科特·迈耶斯(Scott Meyers)的
《有效C++:改进程序和设计的55种特定方法》(第9项:在构建或破坏期间切勿调用虚函数。

或赫伯·萨特的
C++编码标准:101 条规则、指南和最佳实践(49.避免在构造函数和析构函数中调用虚函数)。

是的,当您调用析构函数时,对象将有一个指向其 vtable 的指针。

该标准明确表示可以在析构函数中调用虚函数,并说明了会发生什么。 人们一致认为,即使允许这样做,也是一种不好的做法,因为它本质上是脆弱的代码,会导致看似无辜的更改令人惊讶。

如果你有从Der继承DerDer,它继承自Base,它们都覆盖成员函数void member(),并且你在Der的析构函数中,并调用member(),你正在调用Der::member(),而不是DerDer::member(),因为你对象的DerDer部分已经消失了,已经被销毁了。 基类可能会无意中引用派生类中的数据,例如:

struct Base {
int *ip;
Base(int *ip): ip(ip) {}
virtual void useInt() { std::cout << *ip << std::endl; }
~Base() { useInt(); }
};
struct Der: Base {
int theInt;
Der(): Base(&theInt) {}
void useIntPointer() override { std::cout << theInt << std::endl; }
};

当类型为Der的对象被删除时,存在"未定义的行为":首先调用Der的隐式析构函数,然后调用Base的显式析构函数,Base::~Base。 在这一点上,Base::ip指的是已经被摧毁的Der成员。