【发布时间】:2020-06-09 13:20:24
【问题描述】:
在以Itanium C++ ABI 为模型的 C++ ABI 实现中,随后是许多其他处理器的 ABI,虚拟析构函数实际上占用了两个 vtable 插槽。除了执行你所期望的“完整对象析构函数”之外,还有第二个条目用于“删除析构函数”,它调用第一个,然后删除对象的内存。
这种方法存在一个问题,这在小型内存系统中可能会造成麻烦:动态内存管理器已链接,即使没有其他代码使用它。当应用程序中的任何地方都没有调用删除时,这是死代码。这是因为 C++ 编译器/链接器通常无法检测到 vtable 中的插槽未被从任何地方调用,因此无法删除相关代码。显然,如果删除析构函数可以以不涉及 vtable 条目的不同方式实现,并允许编译器/链接器省略这个死代码,那会更好。
当然可以实现自定义void operator delete(void *) {} 来防止链接器引入动态内存代码,但这仍然不能阻止删除析构函数代码完全发出。
因此我的问题是:有没有更好的方法来实现删除析构函数?我的想法是返回指向内存块开头的指针,以便从完整的对象析构函数中删除。如果要在销毁后删除内存块,则返回的地址可以由调用operator delete的非虚拟函数使用。本质上,让完整的对象析构函数返回内存地址将允许删除析构函数是非虚拟的,因此有资格消除死代码。
但我想我一定忽略了一些东西,这使得这个相当简单的解决方案变得不可能。但那会是什么?有人可以为我解释一下 Itanium ABI 中的设计决策吗?
编辑:我找到了提供部分答案的信息here:
上面的答案包含这样的解释:
当某个类定义了自己的操作符 delete 时,选择要调用的特定操作符 delete 就像从类析构函数内部查找一样。这样做的最终结果是,对于具有虚拟析构函数的类,delete 的行为就好像它是一个虚函数(尽管形式上是该类的静态成员)。
显然,Itanium API 选择使其表现像一个虚函数的方式是使调用它的析构函数成为一个实际虚函数。 p>
但是,这不是实现它的唯一方法。链接文章的中心是一个使用带有隐藏参数的单个虚函数的实现,但该解决方案会产生与我在上面描述的相同的不良行为。如果类有自定义实现,则另一个实现可能是让完整的对象析构函数返回运算符 delete() 的地址,否则返回 nullptr。这样可以避免我上面描述的问题。
所以,在稍加修改的形式中,我的问题仍然存在。
【问题讨论】:
-
这不是一个重复,但仍然感谢您的链接。我不是问为什么有多个析构函数,而是为什么删除析构函数需要是虚拟的,这会产生一些不良后果,如上所述。
标签: c++ memory-management linker virtual-destructor itanium