为啥虚拟析构函数需要删除运算符
Posted
技术标签:
【中文标题】为啥虚拟析构函数需要删除运算符【英文标题】:Why is delete operator required for virtual destructors为什么虚拟析构函数需要删除运算符 【发布时间】:2015-10-19 14:39:59 【问题描述】:在使用 g++ 的独立上下文中(没有标准库,例如在操作系统开发中)会发生以下现象:
class Base
public:
virtual ~Base()
;
class Derived : public Base
public:
~Derived()
;
int main()
Derived d;
链接时它会声明如下内容:undefined reference to operator delete(void*)
这显然意味着 g++ 正在生成对删除运算符的调用,即使动态内存分配为零。如果析构函数不是虚拟的,则不会发生这种情况。
我怀疑这与为类生成的 vtable 有关,但我不完全确定。 为什么会这样?
如果由于缺少动态内存分配例程而不能声明删除操作符,有解决办法吗?
EDIT1:
为了成功重现我使用的 g++ 5.1 中的问题:
g++ -ffreestanding -nostdlib foo.cpp
【问题讨论】:
我无法重现这个简单示例的问题。你确定你没有遗漏什么吗? @RobinKrahl 您是否尝试将 -ffreestanding 添加到 g++ 命令行。如果有任何删除操作符的调用,请检查反汇编转储。 在我的 Linux Mint 上使用 g++ 4.8.4 编译。使用g++ Testing.cpp -ffreestanding
。但是使用 clang 3.5.0 我得到了一堆链接器错误。
也许是一个愚蠢的问题:-nostdlib 做什么? (删除运算符 delete(void*) ?)
@DieterLücking 它跳过链接标准 C++ 库(STL、默认运算符、个性、异常处理、堆栈展开等)
【参考方案1】:
因为删除析构函数。当您在具有虚拟析构函数的对象上调用 delete obj
时实际调用的函数。它调用完整的对象析构函数(链接基本对象析构函数——您实际定义的析构函数),然后调用operator delete
。这样一来,在所有使用delete obj
的地方,只需要发出一个调用,并且还用于调用operator delete
,其指针与从operator new
返回的指针相同,符合ISO C++ 的要求(尽管这也可以通过dynamic_cast
以更高的成本完成)。
它是 GCC 使用的 Itanium ABI 的一部分。
我认为你不能禁用它。
【讨论】:
感谢您的回答。同意@Yakk。现在我明白会发生什么。您认为是否有解决方法 @Felipe 因为删除析构函数只会从delete
调用,并且由于您没有,因此您可以实现自己的delete(void*)
并让它什么都不做或生成运行时错误。
@Felipe 不,删除析构函数只能用delete
调用
@Ediac 的想法是提供一个虚拟函数来消除链接器错误,因为无论如何都不应该调用该函数。我建议让它生成一个错误,以便很容易检测到是否有人修改了代码并在将来尝试使用delete
。
我认为您没有(明确地)提到的另一个原因是:可以在派生类中重载operator delete
;标准 [class.free]p4 要求 delete
动态(有效)调度如果delete
操作数的静态类型具有虚拟 dtor。也就是说,如果你定义了一个虚拟析构函数,你实际上也会得到一个虚拟的operator delete
。 Live example【参考方案2】:
在 C++20 中现在有一个修复:P0722R3。 static void operator delete(T*, std::destroying_delete_t)
释放函数。它本质上映射到破坏性析构函数。
你可以让它不调用::operator delete
,比如:
class Base
public:
void operator delete(Base* p, std::destroying_delete_t)
// Shouldn't ever call this function
std::terminate(); // Or whatever abort-like function you have on your platform
// The default implemenation without any overrides basically looks like:
// p->~Base(); ::operator delete(p);
// Which is why the call to `operator delete` is generated
virtual ~Base()
;
class Derived : public Base
public:
// Calls Base::operator delete in deleting destructor, so no changes needed
~Derived()
;
int main()
Derived d;
删除析构函数是在您执行delete ptr_to_obj;
时调用的析构函数。它只能由delete
表达式调用,所以如果你的代码中没有,这应该没问题。如果你这样做了,你可以用::delete ptr_to_obj;
替换它们,删除析构函数将不再被调用(它的目的是为类调用覆盖的operator delete
,而::delete
只会调用全局::operator delete
)
【讨论】:
以上是关于为啥虚拟析构函数需要删除运算符的主要内容,如果未能解决你的问题,请参考以下文章