为什么链接器在虚拟情况下给出错误但在非虚拟情况下却没有?

时间:2012-05-04 19:41:32

标签: c++ inheritance virtual

当我有这样的事情时:

class A
{
  virtual void rat();
};

class B : public A
{
  virtual void rat() { ; } //implemented!
};

int main(int argc, char **argv)
{
  A *a = new B;
  delete a;
}

我收到链接器错误:

除非我让基础老鼠变成纯粹的虚拟。

然而,当我有这个:

class A
{
  public:
  void rat();
};

int main(int argc, char **argv)
{
  A a;
}

这个编译很好,并没有给我一个未定义的引用链接错误,除非我明确尝试在我的main(a.rat();)中调用rat函数。未实现的基类虚函数的规则是什么,然而,在第一个失败的代码片段中,它是在派生类中实现的?

5 个答案:

答案 0 :(得分:5)

当两个类都定义虚函数时,C ++编译器需要为类AB构建vtables。要构建A的vtable,编译器需要A::rat() - 这是引用的来源。

A没有虚函数时,无法从任何地方引用A::rat,因此您不会收到编译错误。

我相信你知道,你可以通过使A::rat成为纯虚拟来修复这个错误,从而为vtable提供所需的值(在这种情况下,值为零)。

答案 1 :(得分:4)

需要实现每个非纯虚函数。

class A 
{ 
   public: void rat(); 
};

int main(int argc, char **argv) 
{ 
  A a; 
}

上面的代码完全是一个不同的场景。在你不调用非纯虚函数的情况下,你既没有得到编译器/链接器错误,也没有调用它,尽管它在类定义中声明了它。

现在在下面的代码中,编译器只检查rat()中是否存在名为A的成员函数。

A a;
a.rat();   // Compiler passes. But linker bombs.

现在您应该收到链接器错误。

答案 2 :(得分:2)

由于A::rat() 不是纯虚拟,因此必须具有实现。

答案 3 :(得分:2)

标准法律术语 ODR使用:必须定义使用ODR的功能,否则会出错。

将函数标记为ODR使用的规则非常复杂,基本上它意味着函数以某种方式使用。在第二个示例中,未使用该函数,因此没有必要。

有一个特别警告virtual函数(除非它是纯粹的)始终被视为ODR使用。

答案 4 :(得分:2)

因为C ++标准要求它实现。来自C ++03§10.3/ 8:

  

在类中声明的虚函数应在该类中定义或声明为纯(10.4),或两者兼有;但不需要诊断(3.2)。

因此,您需要将其声明为纯(在右括号后面加= 0后缀)或定义其实现。

至于在非虚拟情况下不调用函数时没有出现错误的原因,请参阅C ++03§3.2/ 2-3(强调我的)

  

2)表达式可能被评估,除非它出现在需要整数常量表达式的位置   (见5.19),是sizeof运算符(5.3.3)的操作数,或者是typeid运算符的操作数,   表达式不指定多态类类型的左值(5.2.8)。对象或非超载   如果函数名称出现在可能已评估的表达式中,则使用虚拟成员函数是   如果它不纯,则使用。 [...]

     

3)每个程序都应该包含每个非内联函数或使用的对象的一个​​定义   程序;无需诊断。该定义可以在程序中明确显示,可以在中找到   标准或用户定义的库,或(在适当时)它是隐式定义的(见12.1,12.4和12.8)。   内联函数应在每个使用它的翻译单元中定义。

因此,在非虚拟情况下,如果未使用,则不需要定义。但是在非纯虚拟情况下,即使它没有在代码中明确引用,它仍然被认为是由标准使用,因此它的定义是必需的。

另见A virtual member function is used if it is not pure?