2012-08-23 34 views
4

虽然还有其他关于堆栈溢出的问题,它们处理'未定义引用vtable'错误消息。下面的代码要么编译,要么不编译,这取决于无参数构造函数C()是否在线实现。我知道成员函数m()应该是纯虚拟的,这将是修正问题的正确改变。我感到困惑的是,它可以用一种明显无关的变化进行编译。对内联构造函数影响的vtable错误的未定义引用

下面的代码不能用g ++(ubuntu 64位上的4.6.3)进行编译,并产生预期的'未定义的引用vtable for C'消息(对于记录来说仍然是一个可怕的错误信息,考虑到问题是米())

Header.h

#ifndef HEADER_H 
#define HEADER_H 

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

#endif 

Implementation.cpp

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

Main.cpp的

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

以下无关修改允许编译:

  • 对于C :: C来自Implementation.cpp删除非直列实现()
  • 添加琐碎在线实现对C ()to Header.h中的类

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

回答

7

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

以上都不是。这是不是一个错误,它没有什么用的优化做的,而这个特定的问题是标准的范围之内,它是由相关ABI覆盖(这仅仅是一个事实上的标准。)

C::mkey function和你没有在任何地方定义它,这意味着编译器不会发出vtable。

有好为什么代码编译这些变化(如果合并)的原因:

  • 对于C :: C()从Implementation.cpp
删除非直列实施

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

  • 为C()到类添加琐碎直列实施部首。ħ

未在给定的翻译单元称为不会在目标文件中出射,所以使内联函数内联函数装置的构造不是对象文件中,所以对象文件不会引用vtable,并且链接器不需要在链接时查找它。

如果您更改程序,以便直列构造实际使用(通过创建一个main例如C),那么你会回来得到同样的连接错误,因为现在的直列构造函数将在Main.o等的定义vtable是必需的。

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

int main() 
{ 
    C c; 
} 
+1

+1,虽然*为什么建设过程中需要的虚函数表的*复杂的原因很简单:编译器必须设置'vptr'指向它:) –

+0

是的,ABI使它听起来很复杂,但就是这样! :) –

+0

我其实在那里做了一些手势。在这个特殊情况下,它就如此简单,但ABI必须处理所有使其更加复杂的情况。例如,考虑创建派生类型。虽然起初看起来并不明显,但即使没有创建基类型的对象,也需要基类的vtable。 –