https://stackoverflow.com/a/6614369/1091587快速概述了在读取使用“gcc3”类型名称修改编译的程序的符号表时出现的析构函数类型(D0,D1,D2)。还有相应的构造函数C0 / C1 / C2。使用g ++ - 4.7(可能更早),会出现一个新的ctor / dtor对,即C5 / D5,但只作为调试符号。
$ cat i.cpp
class X { public: virtual ~X() {}; };
int main(void) { X x; return 0; };
$ g++ -c i.cpp
$ nm i.o | grep 5
0000000000000000 n _ZN1XC5Ev
0000000000000000 n _ZN1XD5Ev
$ c++filt -n _ZN1XC5Ev _ZN1XD5Ev
X::X()
X::~X()
demangler源将D5对象称为“gnu_v3_object_dtor_group”,但究竟什么是dtor组,它有什么用呢? clang ++ - 3.3不会发出它,http://gcc.gnu.org/ml/gcc-patches/2011-11/msg00383.html表明它可能与gcc中的新事务内存功能有关。
答案 0 :(得分:2)
This LLVM patch和this GCC bug提供了更多背景信息。通过跟踪链接,我发现Bug 3187 - gcc lays down two copies of constructors似乎是它的全部起源:
两个(有时三个)相同的构造函数和析构函数副本 已经奠定了。链接器不会失败,但会生成二进制文件 比我们现实世界的例子大20%(
)
如果您搜索“PR c ++ / 3187”(e.g.),您可以在gcc-patches ML上找到很多讨论。基本上,C5 / D5本身不是构造函数/析构函数,而是包含两个或更多“基本”构造函数/析构函数的COMDAT group。这可以确保组中的函数全部用在最终二进制文件中,或者全部丢弃(以强制执行“一个定义规则”)。
上述错误中的讨论结果似乎是:
对于任何类,实现都可以选择使用一个comdat 构造函数/析构函数或使用C5 / D5 comdat。我可能会这样做 基于任何盈利标准的决定。如果使用C5 / D5 comdat 规则是
- C5 comdat必须有C1和C2。
- 如果一个类有一个虚拟析构函数,那么D5 comdat必须有D0,D1和D2
- 如果类具有非虚拟析构函数,则D5 comdat必须只有D1和D2析构函数。即使是这样也是如此 实现使用D0而不是调用D1 + _ZdlPv来实现 “delete * x”
您可以通过以下方式查看comdats:使用readelf -G
转储文件:
COMDAT group section [ 1] `.group' [_ZN1XD5Ev] contains 2 sections:
[Index] Name
[ 10] .text._ZN1XD2Ev
[ 12] .text._ZN1XD0Ev
COMDAT group section [ 2] `.group' [_ZN1XC5Ev] contains 1 sections:
[Index] Name
[ 14] .text._ZN1XC2Ev
(这与GCC 4.6有关,这可能与上述定义不符)