为什么我必须从继承的 class 中重新声明一个虚函数?

Why must I re-declare a virtual function from an inherited class?

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

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 应该完全了解这些方法。我做错了什么吗?

这样不行。您需要声明要定义的方法,无论它们是否重写虚方法。

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

当您在派生 class 中声明方法时,您对编译器说:

I want to override this method in this class

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

I don't want to override this method; derived class's implementation is the same as the one in the base class

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

I don't want to implement this method in this class

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

为什么必须在基础 class 中派生重写虚方法的一个非常不直观的原因是 C++ 允许将 class 的不同部分放入不同的文件中,进入不同的翻译单位。

对于其他一些语言(我在看 Java 的方向),单个 class 必须放在一个文件中。对于 C++,情况并非如此。 class 在一个翻译单元中声明其某些方法,而在另一个翻译单元中声明其他方法是完全合法的,这些方法可以完全位于某个不同目录中的文件中。

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

现在假设您可以从 class 声明中省略一个重写的虚拟方法。这就产生了一个直接的问题:当编译 class 的构造函数时,编译器有必要知道 class 是否覆盖了来自任何 superclasses 的任何虚拟方法,在为了正确 assemble 正在构建的 class 的虚拟 table 分派。如果没有显式声明,编译器无法知道其他翻译单元是否可能定义重写的虚拟方法。

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

您应该覆盖所有基础 class 纯虚函数,以便能够实例化派生的 class。

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

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

所以你的 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