【问题标题】:Why must I re-declare a virtual function from an inherited class?为什么我必须从继承的类中重新声明虚函数?
【发布时间】:2017-02-28 08:38:39
【问题描述】:

我正在编写一个简单的 C++ 程序,并且很难理解我遇到的编译器错误。该问题是由于我试图从基类创建派生类引起的。我在下面发布了具有相同结构的代码,但更改了名称。

BaseClass.h

#ifndef BASECLASS_H
#define BASECLASS_H

class BaseClass {

    public:
        BaseClass(void);

        virtual int method1(void) = 0;
        virtual int method2(void) = 0;
        virtual float method3(void) = 0;

};

#endif // BASECLASS_H

DerivedClass.h

#ifndef DERIVEDCLASS_H
#define DERIVEDCLASS_H

#include "DerivedClass.h"

class DerivedClass: public BaseClass
{

    public:
        DerivedClass(void);     
};

#endif // DERIVEDCLASS_H

DerivedClass.cpp

#include "DerivedClass.h"

DerivedClass::DerivedClass(void)
{
}

int DerivedClass::method1(void)
{
  // TODO
} 

int DerivedClass::method2(void)
{
  // TODO
}

float DerivedClass::method3(void) 
{
  // TODO
}

在尝试编译时,所有虚拟方法都出现以下错误:

no 'int DerivedClass::methodX()' member function declared in class 'DerivedClass'

只要我在“DerivedClass.h”中声明这些方法,错误就会消失,因为编译器现在知道这些方法。

但是,我很困惑。为什么必须在 DerivedClass.h 中重新声明纯虚函数?当我#include DerivedClass.h 时,它将自动包含 BaseClass.h,因此我假设我的 DerivedClass.cpp 应该完全了解这些方法。我做错了什么吗?

【问题讨论】:

  • 每个派生类都必须覆盖其基类的虚函数。简单的规则。旁注:当你有一个不带参数的函数时,你不需要放 void。
  • @DeiDei 简单,但不完全正确
  • 正确,这就是我在 DerivedClass.cpp 中尝试做的事情。但是,为什么要重新声明 DerivedClass.h 中的函数呢?编译器应该已经期待方法定义了吗?
  • @krzaq 显然它有点复杂,但如果你懒得学很多东西,它只会解决你的问题,所以我想我会提一下。
  • @DeiDei imo 这是一个明显的简化。

标签: c++ inheritance virtual


【解决方案1】:

这种方式是行不通的。您需要声明要定义的方法,无论它们是否覆盖虚拟方法。

这不仅仅是语言的不合理要求。如果没有这个,您将无法定义部分虚拟类,即您可以拥有BaseSubtype,它具有method1() 的通用实现,但需要从它派生的类来实现method2()method3()

【讨论】:

    【解决方案2】:

    当你在派生类中声明一个方法时,你对编译器说:

    我想覆盖这个类中的这个方法

    所以如果你不在派生类中声明一个方法,你说:

    我不想重写这个方法;派生类的实现与基类的实现相同

    在您的情况下,基类将它们声明为纯虚拟,因此在这种情况下可以解释:

    我不想在这个类中实现这个方法

    如果你试图定义一个方法但没有声明它,你就会自相矛盾。编译器会检测到这一点(以保护您免受自己的疏忽)。

    【讨论】:

      【解决方案3】:

      必须在基类中派生重写的虚方法的一个非常不直观的原因源于 C++ 允许将类的不同部分放入不同的文件中,放入不同的翻译单元中。

      对于其他一些语言(我正在研究 Java 的方向),必须将单个类放在单个文件中。这不适用于 C++。一个类在一个翻译单元中声明它的一些方法,而在另一个翻译单元中声明其他方法是完全合法的,这些翻译单元可能完全位于某个不同目录的文件中。

      每个这样的文件都被单独编译。在编译一个翻译时,C++ 编译器不知道任何其他翻译单元、任何其他可能包含同一类的其他部分的文件。

      现在假设你可以在类声明中省略一个被覆盖的虚方法。这就产生了一个直接的问题:在编译类的构造函数时,编译器必须知道该类是否覆盖了来自任何超类的任何虚拟方法,以便为正在构造的类正确组装虚拟表调度。如果没有显式声明,编译器就无法知道其他翻译单元是否会定义一个被覆盖的虚方法。

      这就是为什么重写的虚方法必须显式包含在类的声明中。总结:因为 C++formally specifies phase 9, the linkage phase,在早期阶段进行实际编译时,必须显式声明重写的方法。

      【讨论】:

      • 您在回答中混淆了声明和定义。
      【解决方案4】:

      您应该重写所有基类纯虚函数,以便能够实例化派生类。

      • 您不能从派生类定义基类成员函数。

      在您的示例中,您尝试定义不是 DerivedClass 成员的 method1 和 method2 和 method3!您必须在派生类中自己声明它们。编译器不会为你做这件事。

      所以你的 Derivedclass.h 看起来像:

      #ifndef DERIVEDCLASS_H
      #define DERIVEDCLASS_H
      
      #include "BaseClass.h"
      
      class DerivedClass: public BaseClass
      {
      
          public:
              DerivedClass(void); 
      
              virtual int method1(void); // not pure function
              virtual int method2(void);
              virtual float method3(void);
      };
      
      #endif // DERIVEDCLASS_H
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-12
        • 2021-03-09
        • 1970-01-01
        • 2014-06-29
        • 2012-07-16
        相关资源
        最近更新 更多