大多数派生 class 如果未实现虚函数则无法编译,但如果一个基 class 未实现虚函数则可以编译

Most derived class cannot compile if virtual function not implemented, but can compile if one base class does not implement the virtual function

我有一个包含 4 个 classes 的 C++ 程序:Person、Student、Employee 和 PartTimeStudent。

Student 和 Employee 均派生自 Person,而 PartTimeStudent 派生自所有 3 个 classes(使其成为最派生的 class)。 Person、Student 和 Employee 也有一个名为 VDescribe() 的派生函数。

请看下面的代码:

class Person
{
    ...    
    virtual void VDescribe();
    ...
};

class Student : virtual public Person
{
    ...    
    virtual void VDescribe();
    ...
};

class Employee : virtual public Person
{
    ...    
    virtual void VDescribe();
    ...
};

class PartTimeStudent : virtual public Person,
    virtual public Student,
    virtual public Employee
{
    ...
};

注意:在上面的代码片段中,我省略了构造函数、析构函数和成员变量,因为它们与手头的问题无关。

当我尝试编译代码时,出现以下错误:

override of virtual function "Person::VDescribe" is ambiguous

'PartTimeStudent': ambiguous inheritance of 'void Person::VDescrive(void)'

'PartTimeStudent': ambiguous inheritance of 'void Person::VDescribe(void)'

但是,只有当学生和员工都实现了 VDescribe() 时才会发生这种情况。如果其中一个 classes 没有实现 VDescribe(),则编译成功。我仍然收到警告,例如如果我从 Employee 中省略 VDescribe() ,则会出现以下警告:

'PartTimeStudent': inherits 'Student::Student::VDescribe' via dominance

请问为什么会出现这种情况?我想知道如果所有 3 classes 实现 VDescribe(),为什么 PartTimeStudent 无法编译,但如果 Student 或 Employee 没有该函数,PartTimeStudent 仍然可以编译。

两次覆盖

想象一下 StudentEmployee 实现 VDescribePartTimeStudent 不实现它的 scanario。您希望这段代码的行为如何:

PartTimeStudent pts;
pts.VDescribe();

应该调用 VDescribe 的哪个实现?这是模棱两可的,这正是编译错误的原因。

一次覆盖

Employee不覆盖VDescribe时,情况有点不同。 PartTimeStudent则有以下功能可以继承:

  • Person::VDescribeStudent::VDescribe 覆盖,来自 Student
  • Person::VDescribe 未覆盖,来自 Employee.
  • Person::VDescribe 未覆盖,来自 Person.

在这种情况下,Student::VDescribe 覆盖 Person::VDescribe 并且是一个明确的覆盖程序,因此编译器能够使用它。但是,它警告您存在未通过此覆盖的替代继承路径。这个警告在实践中不是很有用,是我经常禁用的少数警告之一。


如果您希望您的代码在 StudentEmployee 都覆盖 VDescribe 的情况下也能编译,您实际上也必须在 PartTimeStudent 中覆盖它。然后该函数将有一个明确的最终覆盖程序,代码将编译得很好。您可以使用限定名称调用一个或两个继承的实现。示例:

void PartTimeStudent::VDescribe()
{
  Student::VDescribe();
  if (isWorking()) Employe::VDescribe();
}