多重继承
到目前为止,我们提出的所有继承示例都是单继承 - 也就是说,每个继承的类都有一个且只有一个父类。但是,C ++提供了执行多重继承的功能。 多重继承使派生类可以从多个父级继承成员。
假设我们想编写一个程序来跟踪一群老师。老师是一个人。但是,教师也是一名雇员(如果为自己工作,他们就是自己的雇主)。可以使用多重继承来创建从Person和Employee继承属性的Teacher类。要使用多重继承,只需指定每个基类(就像在单继承中一样),用逗号分隔。
#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)
{
}
};
多重继承的问题
虽然多继承似乎是单继承的简单扩展,但多继承引入了许多问题,这些问题可能显著增加程序的复杂性,并使它们成为维护的噩梦。我们来看看其中的一些情况。
首先,当多个基类包含具有相同名称的函数时,可能会导致歧义。例如:
#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;
}
当c54G.getID() 被编译,编译器会检查是否WirelessAdapter包含了一个名为的getID()函数。它没有。然后编译器查看是否有任何父类具有名为getID()的函数。看到这里的问题?问题是c54G实际上包含两个getID()函数:一个继承自USBDevice,一个继承自NetworkDevice。因此,此函数调用是不明确的,如果您尝试编译它,将收到编译器报错。
但是,有一种方法可以解决此问题:您可以明确指定要调用的版本:
int main()
{
WirelessAdapter c54G(5442, 181742);
std::cout << c54G.USBDevice::getID();
return 0;
}
虽然这种解决方法非常简单,但是当您的类继承自其他类本身的四个或六个基类时,您可以看到事情会变得复杂。当您继承更多类时,命名冲突的可能性会呈指数级增长,并且每个命名冲突都需要明确解决。
其次,更严重的是钻石问题,作者喜欢称之为“厄运之钻”。当类乘法继承自两个类时,会发生这种情况,每个类都从一个基类继承。这导致菱形的遗传模式。
例如,考虑以下一组类:
class PoweredDevice
{
};
class Scanner: public PoweredDevice
{
};
class Printer: public PoweredDevice
{
};
class Copier: public Scanner, public Printer
{
};
Scanner和Printer都是PoweredDevice,因此它们来自PoweredDevice。但是,Copier具有Scanner和Printer的功能。
在此上下文中出现了许多问题,包括Copier是否应该有一个或两个PoweredDevice副本,以及如何解决某些类型的模糊引用。虽然大多数这些问题都可以通过显式范围来解决,但是为了处理增加的复杂性而添加到类中的维护开销可能会导致开发时间急剧增加。我们将在下一课中详细讨论解决钻石问题的方法。
多重继承比它带来的价值更麻烦吗?
事实证明,使用多重继承可以解决的大多数问题也可以使用单继承来解决。许多面向对象的语言(例如Smalltalk,PHP)甚至不支持多重继承。许多相对现代的语言(如Java和C#)将类限制为普通类的单继承,但允许接口类的多重继承(我们将在后面讨论)。在这些语言中不允许多重继承的驱动思想是,它只会使语言过于复杂,并最终导致比修复更多的问题。
许多作者和有经验的程序员认为,由于它带来的许多潜在问题,应该不惜一切代价避免C ++中的多重继承。作者不同意这种方法,因为有时候和多种情况下,多重继承是最好的方法。但是,应该非常明智地使用多重继承。
有趣的是,你已经使用了多重继承编写的类而不知道它:iostream库对象std :: cin和std :: cout都是使用多重继承实现的!
规则:避免多重继承,除非替代方案导致更复杂。