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