2.2、不明确的基类
另外一种引发不明确的方式是从同样的类中继承了两次。如果父类自身有一个共同的父类时会发生。例如,可能Bird与Dog都继承自Animal类,图示如下:
这种层次结构的类类型在c++中是允许的,但名字不明确仍会产生。例如,如果Animal类有一个公共的成员函数叫做sleep(),该成员函数在DogBird对象上不能被调用,因为编译器不知道到底是去调用Dog还是Bird继承的版本。
使用这些“钻石形状”的类层次结构的最好的方式是使最顶部的类是一个所有成员函数声明为干净的virtual的抽象类。因为该类只声明成员函数而不提供定义,没有基类的成员函数可调用,因些在这个层次上就没有不明确。
下面的例子实现了一个钻石高度类层次结构,Animal抽象类有一个干净的virtual eat()成员函数,必须在每一个继承类中进行定义。DogBird类仍然需要显式指出哪个父类的eat()成员函数被使用,但是Dog与Bird产生的任何不明确都有同样的成员函数,而不是因为它们都继承自同样的类。
class Animal
{
public:
virtual void eat() = 0;
};
class Dog : public Animal
{
public:
virtual void bark() { println("Woof!"); }
void eat() override { println("The dog ate."); }
};
class Bird : public Animal
{
public:
virtual void chirp() { println("Chirp!"); }
void eat() override { println("The bird ate."); }
};
class DogBird : public Dog, public Bird
{
public:
using Dog::eat;
};
处理这种钻石开关层次结构顶部类的更好的方式是virtual基类,本章后面会进行解释。
2.3、多重继承的使用
在这一点上,你可能会想为什么程序员想要在代码中处理多重继承。对于多重继承最直接的使用场景就是定义一个类既是某物,也是另外之物。我们在前面的章节中说过,任何真实世界中发现的这种模式都不会好好地转化为代码。
多重继承最鼓舞人心且最简单的使用场景就是混合类的实现。这个我们前面简单提到过,以后还会详细讨论。
另外一个原因就是人们有时使用多重继承来模型化一个基于组件的类。前面我们给出过飞机模拟器的例子。Airplane类有一个引擎,机身,控制器,以及其他部件。Airplane类的典型实现会是让这些部件成为单独的数据成员,可以使用多重继承。飞机类会继承引擎,机身,和控制器,有效地获得所有这些部件的行为与属性。推荐远离这种类型的代码,因为它使用继承把一个清晰的包含关系搞混淆,其应该是用于继承关系的。推荐的解决方案是用一个Airplane类,包含了Engine,Fuselage,与Controls类型的数据成员。