c++ - 为什么删除析构函数除了普通析构函数还要占用第二个vtable槽?

标签 c++ memory-management linker virtual-destructor itanium

在以 Itanium C++ ABI 为模型的 C++ ABI 实现中,紧随其后的是其他处理器的许多 ABI,虚拟析构函数实际上占用了两个 vtable 插槽。除了执行您所期望的“完整对象析构函数”之外,“删除析构函数”还有第二个条目,它调用第一个,然后删除对象的内存。

这种方法存在一个问题,这在小型内存系统中可能会令人讨厌:动态内存管理器是链接的,即使没有其他代码使用它。当应用程序中的任何地方都没有调用删除时,这是死代码。这是因为 C++ 编译器/链接器通常无法检测到 vtable 中的插槽未被从任何地方调用,因此无法删除相关代码。显然,如果删除析构函数可以以不涉及 vtable 条目的不同方式实现,并允许编译器/链接器省略这个死代码,那会更好。

当然可以实现自定义 void operator delete(void *) {}以防止链接器引入动态内存代码,但这仍然不能阻止删除析构函数代码完全发出。

因此我的问题是:没有更好的方法来实现删除析构函数吗?我的想法是返回指向内存块开头的指针,以便从完整的对象析构函数中删除。如果要在销毁后删除内存块,则返回的地址可以由调用 operator delete 的非虚拟函数使用。 .本质上,让完整的对象析构函数返回内存地址将允许删除析构函数是非虚拟的,因此有资格消除死代码。

但我想我一定忽略了一些东西,这使得这个相当简单的解决方案变得不可能。但那会是什么?有人可以为我解释安腾 ABI 中的设计决策吗?

编辑:我找到了提供部分答案的信息 here :

最佳答案包含以下解释:

When some class defines its own operator delete, the selection of a specific operator delete to call is done as if it was looked up from inside the class destructor. The end result of that is that for classes with virtual destructor operator delete behaves as if it were a virtual function (despite formally being a static member of the class).



显然,Itanium API 选择让它表现得像一个虚函数的方式是让调用它的析构函数成为一个实际的虚函数。

然而,这并不是实现它的唯一方法。链接文章的中心是一个使用带有隐藏参数的单个虚函数的实现,但该解决方案会产生与我在上面描述的相同的不良行为。如果类有自定义实现,则另一个实现可能是让完整的对象析构函数返回运算符 delete() 的地址,否则返回 nullptr。这将避免我上面描述的问题。

因此,以某种修改的形式,我的问题仍然存在。

最佳答案

与销毁类时的普通虚函数不同,必须以正确的顺序调用其析构函数和其所有父级的析构函数。

在技​​术上可以将所有函数调用合并为一个函数调用,但需要了解每个析构函数的实现或对象的完整结构。基本上是对通话进行去虚拟化。编译器并不擅长。不太确定所有细节,因为考虑到所有可能的过度虚拟对象,这是一个非常复杂的问题。

关于c++ - 为什么删除析构函数除了普通析构函数还要占用第二个vtable槽?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60400866/

相关文章:

c++ - 在 C++ 命令行程序中显示可由用户编辑的变量的默认文本?

c++ - Variadic 模板候选者不匹配

c++ - 在C++中连接两个字符

C++ 类数组内存重新分配

c++ - 如何使用 C++ 标准库进行静态链接?

C++ Linux 如何使用 libtar

c++ - 为什么 div、ldiv 和 lldiv 不是模板?

php - 安全使用 PHP ini_set "memory_limit"

c - realloc 不是重新分配内存

linker - Cmake基本库链接问题