放置外部虚拟表

时间:2012-08-19 06:19:33

标签: c++ visual-c++ gcc compilation vtable

来自Large-Scale C++ Software Design(Lakos),第652页:

  

问题是,#34;编译器在哪个唯一的翻译单元中存放给定类的虚拟表定义?"。 CFRONT(以及许多其他C ++实现)采用的技巧是将外部虚拟表放在翻译单元中,该单元定义出现在类中的词法第一个非内联函数(如果存在)。

最常用的编译器(GCC和Visual C ++)仍然如此吗?还是曾经?

1 个答案:

答案 0 :(得分:5)

GCC恰好记录其行为与问题(http://gcc.gnu.org/onlinedocs/gcc/Vague-Linkage.html)中描述的相同:

  

的VTable

     

C ++虚函数在大多数编译器中使用查找表实现,称为vtable。 vtable包含指向由类提供的虚函数的指针,并且该类的每个对象都包含指向其vtable(或vtable,在某些多继承情况下)的指针。如果类声明任何非内联非纯虚函数,则选择第一个作为类的“键方法”,并且仅在定义键方法的转换单元中发出vtable。

     

注意:如果稍后将所选键方法定义为内联,则仍会在定义它的每个转换单元中发出vtable。确保在类体中内联声明任何内联虚拟内容,即使它们未在那里定义。

但是,即使在多个目标文件中可能存在多个vtable的情况下(如果'密钥方法'原来是内联的,也会发生这种情况),编译器会安排重复这些副本。可能,但如果目标不支持COMDAT,重复项最终可能会使用最终二进制文件中的空格:

  

在ELF系统上使用GNU ld版本2.8或更高版本时   GNU / Linux或Solaris 2,或Microsoft Windows上的副本   这些结构将在链接时被丢弃。这被称为   COMDAT支持。

     

对于不支持COMDAT但支持弱符号的目标,GCC   会用它们。这样一个副本将覆盖所有其他副本,但是   未使用的副本仍将占用可执行文件中的空间。

     

对于不支持COMDAT或弱符号的目标,大多数   具有模糊链接的实体将作为本地符号发出以避免   来自链接器的重复定义错误。这不会发生   然而,内联中的局部静态将具有多个副本   几乎肯定会破坏事物。

FWIW,GCC似乎使用以__ZTV开头的符号作为vtable。

就MSVC而言,VC ++ 10的一些实证测试(我不认为MS记录了这种行为)表明VC似乎没有尝试将vtable限制为单一对象文件。由于Microsoft知道它可以依赖于支持COMDAT部分的链接器,并且因为构造函数是唯一直接使用vtable的函数(所有其他vtable使用是通过对象指针间接的,我相信),看起来VC只是放置了副本实例化构造函数的任何目标文件中的vtable。对于使用编译器生成的ctor的类,可以构造该类型的对象。