【问题标题】:Why does the linker give me an error in the virtual case but not in the non-virtual case?为什么链接器在虚拟情况下给我一个错误,但在非虚拟情况下却没有?
【发布时间】:2012-05-04 19:41:32
【问题描述】:

当我有这样的事情时:

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 函数,否则它编译得很好并且不会给我一个未定义的参考链接错误。但是,在派生类中实现的未实现的基类虚函数的规则是什么,就像在第一个失败的代码 sn-p 中一样?

【问题讨论】:

标签: c++ inheritance virtual


【解决方案1】:

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

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

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

【讨论】:

  • 如果Palace Chan 不知道如何声明纯虚拟的东西,我想我会添加它。virutal void rat() = 0;
【解决方案2】:

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

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

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

上面的代码是完全不同的场景。在您不调用非纯虚函数的情况下,您不会收到编译器/链接器错误,因为尽管它在类定义中声明,但您根本没有调用它。

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

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

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

【讨论】:

    【解决方案3】:

    因为A::rat() 不是纯虚拟,它必须有一个实现。

    【讨论】:

    • 准确,但更多信息会使这个答案更好。
    • 可能纯虚拟是什么意思,为什么它是相关的,以及为什么第二个例子可以编译而第一个没有。
    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

      因为 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?

      【讨论】:

        猜你喜欢
        • 2014-04-12
        • 2019-11-02
        • 2013-12-28
        • 1970-01-01
        • 1970-01-01
        • 2016-10-23
        • 2011-11-05
        • 2019-06-26
        • 2015-02-15
        相关资源
        最近更新 更多