【问题标题】:Why I have to redeclare a virtual function while overriding [C++]为什么我必须在覆盖 [C++] 时重新声明一个虚函数
【发布时间】:2010-06-02 13:16:20
【问题描述】:
#include <iostream>
using namespace std;

class Duck {
public:
        virtual void quack() = 0;
};

class BigDuck : public Duck {
public:
  //  void quack();   (uncommenting will make it compile)

};

void BigDuck::quack(){ cout << "BigDuckDuck::Quack\n"; }

int main() {
        BigDuck b;
        Duck *d = &b;
        d->quack();

}

上面的代码无法编译。但是,当我声明虚函数时 在子类中,然后它编译得很好。

如果编译器已经有了子类将要覆盖的函数的签名,那为什么还需要重新声明呢?

有什么见解吗?

【问题讨论】:

  • 你得到什么编译器错误?
  • 尽管在这个例子中我们看到了一个抽象基类,但这个问题通常也是有效的。
  • 我可以,虽然不知道您使用的是什么编译器,但我可能不会得到相同(或任何)错误:)

标签: c++ virtual-functions


【解决方案1】:

需要重新声明是因为:

  • 标准是这样说的。
  • 它通过不爬上层次结构来检查此类函数是否存在,从而使编译器的工作更容易。
  • 您可能希望在层次结构中将其声明为较低。
  • 为了实例化类,编译器必须知道这个对象是具体的。

【讨论】:

  • = 0;在声明的末尾意味着您必须在派生类中定义它。不是你可能想要的。
  • @Ben:您可能决定不想在特定项目中实例化该类 :) 但您是对的。我的意思是,您可能希望实现它甚至更低,而不是在层次结构中实现它
  • 重新声明是需要,因为,[...]它使编译器的工作更容易 - 哦,FFS C++,为什么不让每个人都用汇编编写,它会让编译器的工作方式更容易
【解决方案2】:

如果你改变:

virtual void quack() = 0;

virtual void quack();

无需在 HugeDuck 中实现 quack() 即可编译。

=0;在函数声明的末尾基本上是说所有 BigDucks 都会嘎嘎,但它必须由每个派生的鸭子实现。通过删除 = 0;除非你在 HugeDuck 中实现 quack,否则 BigDuck quack 会被调用。

编辑:澄清 = 0;是说派生类将具有函数的定义。在您的示例中,它期望 HugeDuck 定义 quack(),但正如您所评论的那样,它没有。

顺便说一句,既然所有鸭子都可以嘎嘎叫,也许我们看不到的原始 Duck 类应该实现 quack() 代替?

【讨论】:

  • HugeDuck == BigDuck?这是您提议的第二个派生类,还是一个错字? (或BigDuck 的派生类,或其他)?
  • 是的,但这不是 OP 的问题。问题是为什么需要声明BigDuck::quack()。如果BigDuck::quack()定义,则它也必须在BigDuck 的成员规范中声明,即使quack() 已在Duck 中声明,无论Duck::quack() 是否是纯虚拟的。
【解决方案3】:

因为 C++ 将“声明”与“多态性”分开:任何函数都需要编译器的声明,无论它是否为虚函数。

您的示例还远远不够,它存在“抽象类”问题:无法实例化 BigDuck,因为它的接口中没有 quack 的实现。

概括问题,我们可以声明基函数不是纯虚函数:

class Duck { public: virtual void quack(){} };

class BigDuck : public Duck {}; 

// WRONG: undeclared method definition
void BigDuck::quack(){ cout << "QUACK!"; }

在这里,编译器会抱怨它有一个未声明的符号BigDuck::quack。这与抽象类或任何东西无关。

(注意:gcc 说: error: no 'void BigDuck::q()' member function declared in class 'BigDuck' )

【讨论】:

  • 这不起作用(不再起作用了?)。使用 GCC 11 提供 error: no declaration matches ‘void BigDuck::quack()’
  • 看来您无论如何都必须重新声明(无论是否虚拟),iff 您想在派生类中实现该方法。但我不完全确定它的有效性。
  • 我将添加一个注释,您必须通读代码才能发现代码是无法编译的示例。
【解决方案4】:

Quack() 在您的基类中的定义是“抽象的”——它没有实现。这告诉编译器你的派生类必须实现它。不这样做是编译错误。

【讨论】:

  • 但是编译器肯定知道在实现中期望它。摘录声明的目的是什么?
  • @Matt 语言标准说它是必需的——这与抽象类无关,所有虚函数(实际上是所有函数)都以这种方式工作。
  • 自从我“愤怒地”用 C++ 做任何事情已经有好几年了,但我很确定代码实际上并没有提供 quack() 的实现,而是被忽略(因为编译器不期望方法实现)或实现新方法(隐藏/重载而不是覆盖)。
  • @Galactic 它在派生类中提供了一个实现。它当然不会被忽视!
  • 呃——我撤回我的声明。 :) 我的意思是他写它的方式,但它当然不会编译,所以我的陈述的任何部分都不正确。如果它不能编译,它不会被忽略重载。
【解决方案5】:

BigDuck 可能是另一个抽象类,你可能不想实现 quack,直到你到达基类ReallyBigDuck。

【讨论】:

  • 现在这是能够声明quack的一个很好的论据。但是声明它的理由是什么?
【解决方案6】:

在您提供实现之前,从包含 Pure Virtual Function 的类继承的所有类都是抽象的 - 它们不能被实例化。为了提供这样的实现,您必须在类中声明函数。

【讨论】:

    【解决方案7】:

    在每个类中声明方法将告诉编译器该类为该方法提供了不同的实现。

    另外, 如果您想在堆栈上创建BigDuck 的对象,那么编译器将如何知道quack() 的签名。

    BigDuck aDuck;
    aDuck.quack();
    

    【讨论】:

      猜你喜欢
      • 2017-02-28
      • 2012-07-16
      • 1970-01-01
      • 2021-03-09
      • 2012-04-26
      • 1970-01-01
      • 2018-10-03
      相关资源
      最近更新 更多