5

虽然还有其他关于堆栈溢出的问题处理“未定义对 vtable 的引用”错误消息。以下代码编译或不编译取决于无参数构造函数 C() 是否内联实现。我知道成员函数 m() 应该是纯虚拟的,这将是为了解决问题而做出的正确更改。令我困惑的是,它可以通过明显不相关的更改进行编译。

以下代码不能使用 g++ 编译(在 ubuntu 64 位上为 4.6.3)并产生预期的“未定义对 C 的 vtable 的引用”消息(考虑到问题出在 m() 上,这仍然是一个可怕的错误消息)

头文件.h

#ifndef HEADER_H
#define HEADER_H

class C
{
  public:
    C();
    virtual void m();
};

#endif

实现.cpp

#include "Header.h"
C::C() {}

主文件

#include "Header.h"
int main()
{
   return 0;
}

以下不相关的更改允许编译:

  • 从 Implementation.cpp 中删除 C::C() 的非内联实现
  • 将 C() 的简单内联实现添加到 Header.h 中的类

为什么这允许编译?这是编译器错误、优化器问题还是标准惊喜的黑暗角落?

4

1 回答 1

7

这是编译器错误、优化器问题还是标准惊喜的黑暗角落?

以上都不是。这不是错误,与优化无关,而且这个特定问题超出了标准的范围,它被相关的ABI所涵盖(这只是一个事实上的标准。)

C::m关键函数,您还没有在任何地方定义它,这意味着编译器不会发出 vtable。

代码与这些更改一起编译有很好的(如果复杂的话)原因:

  • 从 Implementation.cpp 中删除 C::C() 的非内联实现

由于ABI文档2.6中描述的一些复杂的原因,在构建时需要vtables。因此,构造函数的定义创建了对 vtable 的引用,链接器会告诉您在链接时缺少该引用。如果删除构造函数的定义,则没有对 vtable 的引用。

  • 将 C() 的简单内联实现添加到 Header.h 中的类

未在给定翻译单元中调用的内联函数不会在目标文件中发出,因此使函数内联意味着构造函数不在目标文件中,因此目标文件不引用 vtable,并且链接器不需要在链接时查找它。

如果您更改程序以便实际使用内联构造函数(例如通过创建Cin main),那么您将再次得到相同的链接器错误,因为现在将在 in 中定义内联构造Main.o函数,因此需要 vtable。

class C
{
  public:
    C() { }  // inline
    virtual void m();
};

int main()
{
    C c;
}
于 2012-08-23T13:34:56.430 回答