为什么我必须从继承的类重新声明虚函数?

时间:2016-10-18 22:12:30

标签: c++ inheritance virtual

我正在研究一个简单的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应该完全了解这些方法。我做错了吗?

4 个答案:

答案 0 :(得分:10)

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

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

答案 1 :(得分:3)

在派生类中声明方法时,请对编译器说:

  

我想在此课程中覆盖此方法

因此,如果您未在派生类中声明方法,请说:

  

我不想覆盖这种方法;派生类的实现与基类

中的实现相同

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

  

我不想在这个课程中实现这个方法

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

答案 2 :(得分:0)

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

  • 您无法从派生类定义基类成员函数。

在您的示例中,您尝试定义不是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

答案 3 :(得分:-1)

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

使用其他一些语言(我正在寻找Java的方向),必须将单个类放在一个文件中。 C ++不是这样。对于一个类来说,在一个翻译单元中声明它的一些方法,以及在另一个翻译单元中声明的其他方法完全合法,这些方法可以放在一些不同目录中的文件中。

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

现在假设允许您从类声明中省略重写的虚方法。这就产生了一个直接的问题:编译类的构造函数时,编译器必须知道该类是否覆盖任何超类中的任何虚方法,以便正确组装正在构造的类的虚拟表分派。如果没有明确的声明,编译器无法知道某个其他翻译单元是否可以定义重写的虚拟方法。

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