为什么我在C++中派生类中必须重新声明被覆盖的函数?

21

假设我有以下代码:

class Iinterface
{
  virtual void abstractFunction()=0;
};

class Derived : public Iinterface
{
  void abstractFunction(); // Do I need this line?
};

Derived::abstractFunction()
{
  // implementation here
}
如果我不添加这行代码,编译错误会显示Derived中未声明abstractFunction。我使用的是VS 2008。
我不确定为什么需要这条代码(请勿将此与在类声明之外提供的函数定义混淆),只要我从Iinterface继承,应该很明显我已经声明了abstractFunction。这是Visual Studio的问题还是C++标准所强制的?
4个回答

8
如果所有派生类中都隐含基类纯虚拟函数的声明,那么您将永远无法创建一个关于基类纯虚拟函数的抽象派生类。而是,所有的派生类都会产生链接错误。这将极其反直觉和令人困惑,并使语言表达能力降低。
此外,这甚至没有意义:是否存在抽象派生类的问题必须在编译时任何地方得知。覆盖者的实现通常只在单个翻译单元中提供,因此无法将您实际上要重写该函数的事实传递给程序的其他部分。

你总结得更好了 :) - Maksim Satsikau
我同意,这可能比我的冗长回答更易懂,+1。 - OmnipotentEntity
无法使用C++中缺少的显式“abstract”关键字来确定派生类是否为抽象类。 - Mert Mertce

3
  1. : 如果你想创建一个class Derived的对象
  2. 不是: 如果你想保持class Derived也是抽象的
  3. 不是: 如果存在一个已经覆盖了函数的中间类
    例如,在IinterfaceDerived之间有一个覆盖了abstractFunction()class Intermediate; 所以现在class Derived可以选择是否覆盖相同的函数。

编辑: 随着问题标题的更改,

为什么我在派生类中必须重新声明已被覆盖的函数?

这是因为C++编译器语法要求class(或namespace或文件)的每个成员函数必须在class(或namespace或文件)的内部声明。无论是virtual还是普通函数。
没有一个很好的理由来打破这种一致性,只是针对virtual函数。


1
我认为OP已经了解了覆盖纯虚基函数的机制。她想知道为什么派生类中需要声明 - Kerrek SB
@KerrekSB 谢谢,这正是我想表达的。 - atoMerz
@KerrekSB,似乎问题的标题刚刚被编辑了。我会修改答案。 - iammilind

1
=0结尾的函数被称为已删除函数,这在您不想使用某些构造函数(例如具有已删除复制构造函数的unique_ptr)的对象时非常有用。
如果一个virtual函数被删除,则按照标准,该类将成为抽象类型。因为在大多数情况下,类的原型和类的函数体是分开的文件,这意味着除非您在原型中明确概述您正在覆盖已删除的虚拟函数,否则您不会覆盖已删除的虚拟函数。编译器不能简单地推断出您在完全不同的文件中看到实现后想要将函数放入其中。
请记住,原型/实现思想并不是编写代码的唯一方法,您还可以直接将实现放在类中(如果代码足够小且您想内联函数)。为此,您需要再次明确覆盖已删除的虚拟函数。因此,因为您无论如何都需要覆盖它,所以在原型中明确覆盖它是很有道理的。否则该函数仍将被删除。
举个具体的例子:假设您有一个List.hpp、List.cpp和main.cpp。
在List.hpp中,您有一个抽象类和一个从抽象类继承的常规类。在主函数中,您是#include "List.hpp"而不是List.cpp,对吗?因此,编译器不知道该文件中有什么(直到尝试编译它)。如果您没有覆盖已删除的虚函数,则编译器认为您只是尝试实例化抽象类并引发错误。
另一方面,如果您正在编译List.cpp,则编译器也会引发错误,这次是因为您尝试编写的函数实际上未被定义。因为Base::deletedFunction()Derived::deletedFunction()不同。

谢谢。这是编译器的限制还是C++的限制? - atoMerz
1
C++ 的限制。虽然我不愿称之为限制。只是你必须明确表达它。在我看来,这有助于你长期发展。 - OmnipotentEntity

0

是的,纯虚函数的整个目的就是强制你在派生类中重写它;在C++中,这个声明必须是显式的。


我知道这一点,并且我正在覆盖它,但我不明白为什么我需要在类声明中重新编写函数的原型。为什么VS不能自己推断出来呢? - atoMerz
VS认为您忘记了那个方法 :) - whd
因为类定义驱动对象层次结构设计,如果您愿意;正如上面所解释的那样,您是否选择包括覆盖声明,告诉编译器相应的方法代码是否应该存在。 - Maksim Satsikau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接