So far, all of the examples of inheritance we’ve presented have been single inheritance -- that is, each inherited class has one and only one parent. However, C++ provides the ability to do multiple inheritance. Multiple inheritance enables a derived class to inherit members from more than one parent.
到目前为止,所有的继承的例子但是单继承的。就是说每一个例子中被继承的类只有一个。而多继承允许一个派生类从多个基类继承。
Let’s say we wanted to write a program to keep track of a bunch of teachers. A teacher is a person. However, a teacher is also an employee (they are their own employer if working for themselves). Multiple inheritance can be used to create a Teacher class that inherits properties from both Person and Employee. To use multiple inheritance, simply specify each base class (just like in single inheritance), separated by a comma.
假设我们现在想写一个程序来跟踪一堆老师。一个老师是一个人,然而一个老师也是一个雇员。多继承可以用来创建一个派生的Teacher类,继承已有的Person类和Employee类。要使用多继承的话,只需要简单的指定要继承的基类,用逗号把他们隔开。
#include <string>
class Person
{
private:
std::string m_name;
int m_age;
public:
Person(std::string name, int age)
: m_name(name), m_age(age)
{
}
std::string getName() { return m_name; }
int getAge() { return m_age; }
};
class Employee
{
private:
std::string m_employer;
double m_wage;
public:
Employee(std::string employer, double wage)
: m_employer(employer), m_wage(wage)
{
}
std::string getEmployer() { return m_employer; }
double getWage() { return m_wage; }
};
// Teacher publicly inherits Person and Employee
class Teacher: public Person, public Employee
{
private:
int m_teachesGrade;
public:
Teacher(std::string name, int age, std::string employer, double wage, int teachesGrade)
: Person(name, age), Employee(employer, wage), m_teachesGrade(teachesGrade)
{
}
};
Problems with multiple inheritance
While multiple inheritance seems like a simple extension of single inheritance, multiple inheritance introduces a lot of issues that can markedly increase the complexity of programs and make them a maintenance nightmare. Let’s take a look at some of these situations.
多继承看起来是单继承的一种简单拓展,实际上多继承引入了会极大的增加程序复杂度的一堆问题,让维护它们的工作变成噩梦。我们接下来会看几个情镜。
First, ambiguity can result when multiple base classes contain a function with the same name. For example:
首先是会造成在函数调用时候的歧义。
#include <iostream>
class USBDevice
{
private:
long m_id;
public:
USBDevice(long id)
: m_id(id)
{
}
long getID() { return m_id; }
};
class NetworkDevice
{
private:
long m_id;
public:
NetworkDevice(long id)
: m_id(id)
{
}
long getID() { return m_id; }
};
class WirelessAdapter: public USBDevice, public NetworkDevice
{
public:
WirelessAdapter(long usbId, long networkId)
: USBDevice(usbId), NetworkDevice(networkId)
{
}
};
int main()
{
WirelessAdapter c54G(5442, 181742);
std::cout << c54G.getID(); // Which getID() do we call?
return 0;
}
When c54G.getID()
is compiled, the compiler looks to see if WirelessAdapter contains a function named getID(). It doesn’t. The compiler then looks to see if any of the parent classes have a function named getID(). See the problem here? The problem is that c54G actually contains TWO getID() functions: one inherited from USBDevice, and one inherited from NetworkDevice. Consequently, this function call is ambiguous, and you will receive a compiler error if you try to compile it.
当c54G.getID()
在被编译的时候,编译器首先去看派生类是否有一个函数叫getID,在这里它没有。所以编译器就会去看他的基类是不有这个函数。知道问题了吧?问题在于派生类实际上有两个getID函数继承了下来,一个从USBDevice继承,一个从NetWorkDevice继承。因此这个函数调用时是有歧义的,在你编译的时候你会收到来自编译器的一大堆亲切抱怨。
However, there is a way to work around this problem: you can explicitly specify which version you meant to call:
然而,这也是有办法解决的(事情总是有办法的对吗?),你可以清楚的指定你要用哪个版本的基类函数。
int main()
{
WirelessAdapter c54G(5442, 181742);
std::cout << c54G.USBDevice::getID();
return 0;
}
While this workaround is pretty simple, you can see how things can get complex when your class inherits from four or six base classes, which inherit from other classes themselves. The potential for naming conflicts increases exponentially as you inherit more classes, and each of these naming conflicts needs to be resolved explicitly.
哈哈,这是不是很简单,你只需要在用的时候加一个类名限定就可以。你越是继承太多的基类,你就越有可能发生名字冲突的问题,需要你明确的指示哪个基类函数被调用。
Second, and more serious is the diamond problem, which your author likes to call the “diamond of doom”. This occurs when a class multiply inherits from two classes which each inherit from a single base class. This leads to a diamond shaped inheritance pattern.
再就是会产生一个很严重的问题叫菱形问题。很多作者喜欢把它叫做毁灭之钻。这个问题发生在一个类从两个类继承,而这被继承的这两个类有继承了同一个类。这就是菱形的继承模式。
For example, consider the following set of classes:
class PoweredDevice
{
};
class Scanner: public PoweredDevice
{
};
class Printer: public PoweredDevice
{
};
class Copier: public Scanner, public Printer
{
};
Scanners and printers are both powered devices, so they derived from PoweredDevice. However, a copy machine incorporates the functionality of both Scanners and Printers.
There are many issues that arise in this context, including whether Copier should have one or two copies of PoweredDevice, and how to resolve certain types of ambiguous references. While most of these issues can be addressed through explicit scoping, the maintenance overhead added to your classes in order to deal with the added complexity can cause development time to skyrocket. We’ll talk more about ways to resolve the diamond problem in the next lesson.
在这种情况下,会有很多的问题。Copier到底应该有几分PoweredDevice的拷贝?怎么解决特定类型的带有歧义的访问呢?虽然大部分的问题可以通过明确的类名限定来解决。维护成本也随着你增加的复杂性而增加,像火箭上天一样。我们会在下一节讨论更多的解决菱形问题的东西。
As it turns out, most of the problems that can be solved using multiple inheritance can be solved using single inheritance as well. Many object-oriented languages (eg. Smalltalk, PHP) do not even support multiple inheritance. Many relatively modern languages such as Java and C# restrict classes to single inheritance of normal classes, but allow multiple inheritance of interface classes (which we will talk about later). The driving idea behind disallowing multiple inheritance in these languages is that it simply makes the language too complex, and ultimately causes more problems than it fixes.
大部分用多继承解决的问题也可以用单继承来完成。很多面向对象的语言都不支持多继承。很多相对现代的编程语言,比如Java和C#都限制类只能从一个父类直接继承。但是允许一个接口多继承。这背后的考虑是因为多继承会带来太多的语言复杂度,而且会导致产生的问题比解决的多。
Many authors and experienced programmers believe multiple inheritance in C++ should be avoided at all costs due to the many potential problems it brings. Your author does not agree with this approach, because there are times and situations when multiple inheritance is the best way to proceed. However, multiple inheritance should be used extremely judiciously.
很多作者和有经验的程序员认为多继承在C++中应该被避免使用,因为会产生很多潜在的问题。编者不同意这个观点,因为有很多时候和很多情境下,多继承是解决问题的最好办法。然而,多继承应该极其审慎地被使用。
As an interesting aside, you have already been using classes written using multiple inheritance without knowing it: the iostream library objects std::cin and std::cout are both implemented using multiple inheritance!
有趣的是,你已经使用过那种使用了多继承的对象而自己不知道。cin和cout就是使用多继承实现的两个对象。
Rule: Avoid multiple inheritance unless alternatives lead to more complexity.
除非不用它的话会造成更多麻烦, 避免使用多继承。