当前位置: 首页 > 知识库问答 >
问题:

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

诸彬郁
2023-03-14

我正在开发一个简单的 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

派生类.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'

一旦我在“衍生类”中声明了这些方法,错误就会消失,因为编译器现在知道这些方法了。

然而,我很困惑。为什么有必要重新声明派生类中的纯虚函数?当我#包括派生类. h时,它将自动包括BaseClass. h,因此我认为我的DerivedClass.cpp应该完全了解这些方法。我做了什么不正确的事情吗?

共有3个答案

董飞
2023-03-14

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

对于其他一些语言(我正朝着Java的方向看),单个类必须放在单个文件中。对于C来说,这是不正确的。一个类在一个翻译单元中声明它的一些方法,而在另一个翻译单元中声明其他方法是完全合法的,这些方法可能在某个不同目录的文件中。

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

现在让我们假设您可以从类声明中省略重写的虚拟方法。这就产生了一个直接的问题:在编译类的构造函数时,编译器需要知道该类是否重写了任何超类中的任何虚拟方法,以便正确地为正在构建的类组装虚拟表调度。如果没有显式声明,编译器无法知道其他翻译单元是否可以定义重写的虚拟方法。

这就是为什么重写的虚方法必须显式包含在类的声明中。总之:因为C正式指定了第9阶段,即链接阶段,在早期阶段进行的实际编译中,重写的方法必须显式声明。

夏烨霖
2023-03-14

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

我想在这个类中重写这个方法

因此,如果您没有在派生类中声明方法,则说:

我不想覆盖这个方法;派生类的实现与基类中的实现相同

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

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

如果你试图定义一个方法却不声明它,你就自相矛盾了。编译器会检测到这一点(以防止您自己的疏忽)。

羊舌兴德
2023-03-14

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

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

 类似资料:
  • 问题内容: 有时我们有几个类,这些类的某些方法具有相同的签名,但是与声明的Java接口不对应。例如,和(在中的其他几个 )中都有一个方法 现在,假设我希望对具有该方法的对象进行一些操作。然后,我想有一个接口(或者自己定义),例如 这样我可以写: 但是,可悲的是,我不能: 此演员表将是非法的。编译器 知道 这 是不是 一个,因为类没有宣布实现该接口...... 然而“实际上”实现它 。 有时这会带来

  • 见 映射类继承层次结构 对于这个部分。

  • 我在为继承另一个类属性的类定义构造函数时遇到困难 我在第 18 行(方面遇到了问题。 我收到的错误发生在我申报< code >运输的地点。 如代码所示,运输类是主体类,继承 这个错误发生在int 最后一个错误发生在两个字符串变量上。

  • FAQs in section [20]: [20.1] 什么是“虚成员函数”? [20.2] C++ 怎样同时实现动态绑定和静态类型? [20.3] 虚成员函数和非虚成员函数调用方式有什么不同? [20.4] 析构函数何时该时虚拟的? [20.5] 什么是“虚构造函数(virtual constructor)”? 20.1 什么是“虚成员函数”? 从面向对象观点来看,它是 C++ 最重要的特征:

  • 问题内容: 在我看到的所有支持可选参数的编程语言中,都有一个模仿,即可选参数必须出现在声明的末尾。可选项目后不得包含必需的参数。是什么原因呢?我想这可能是编译器/解释器的要求。 问题答案: 好吧,如果它们在最前面,您将如何检测何时停止供应它们?唯一的方法是 在 可选参数 之后 变量类型是否不同。有点不可思议的要求,因此您只需将它们强制设置为最后是有意义的(省去了用于检测“最终”可选参数的复杂规则的

  • 问题内容: 定义如下: 定义如下: 为什么要重新定义?并且由于它在界面中,如何重新定义它? 问题答案: 在Java 1.3中,添加了所有功能之后,它仅与无关,而与的其他实现无关。这样-这是 不 存在于和被定义。界面中还有其他3个选项。(英语Javadoc不再在线,尽管仍然可以下载,所以这里没有参考。如果搜索“ WindowConstants Java 1.3”,您将获得日语版的Javadoc-但由