3

当我有这样的事情时:

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 函数。然而,在派生类中实现的未实现的基类虚函数的规则是什么,就像在第一个失败的代码片段中一样?

4

5 回答 5

5

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

A没有虚函数时,A::rat任何地方都没有引用,因此不会出现编译错误。

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

于 2012-05-04T19:48:22.853 回答
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.

现在你应该得到一个链接器错误。

于 2012-05-04T19:46:25.343 回答
2

因为A::rat()不是纯虚的,所以必须实现。

于 2012-05-04T19:47:37.060 回答
2

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

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

有一个特别的警告:一个virtual函数(除非它是纯的)总是被认为是 ODR 使用的。

于 2012-05-04T19:50:26.130 回答
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)。内联函数应在使用它的每个翻译单元中定义。

因此,在非虚拟情况下,如果不使用它,则不需要定义。但是在非纯虚的情况下,即使没有在代码中显式引用,仍然被认为是被标准使用的,所以需要对其进行定义。

另见如果它不是纯的,则使用虚拟成员函数?.

于 2012-05-04T20:02:26.653 回答