多重继承(MI),说简单点就是一个派生类继承了多个基类。
在面向对象程序设计时,我们需要MI,比如:在一个家庭中,孩子同时继承了父亲和母亲的一些特性;如果用单继承该怎么弄?
我们每天使用的类iostream就是多重继承出来的,MI在iostream中的作用是代码重用。
MI在日常编码中用的很少,主要原因是:
1. 一些“特性”被加入标准C++(尤其是模板)后,改变了我们的对程序的认识,MI的地位逐渐下降。 比如:一个类需要容器A中的属性,也需要容器B中的属性,使用MI可以达到目的;模板的加入改变了创建容器的方法,所以在这种情况下,减少了使用MI的动力。
2. 使用MI时容易出现“菱形继承”
比如:
class A {public: int data;};
class B: public A{};
class C: public A{};
class D: public B, public C{};
这时,D继承的是B中的data还是C中的data呢?
接口的MI是没有争议的,接口隐藏了数据和方法。(接口中所有的函数都是纯virtual函数,析构函数除外)。
有时,我们需要一个“菱形继承”,如何解决“菱形继承”问题呢?
1. 虚继承 + using B::data, 显示的调用B::data
#include <iostream>
using namespace std;
class A
{
public: int data;
};
class B: virtual public A{};
class C: virtual public A{};
class D: public B, public C
{
public:
using B::data;
};
int main()
{
D d;
d.data;
return 1;
}
2. 虚继承 + 隐藏或覆盖(继承的函数为虚函数)
按照作用域的规则,在继承层次结构中类B比A处于“更高的派生层次”,那么名字B::func就比名字A::func占优势(dominate)。
#include <iostream>
using namespace std;
class A
{
public:
void func()
{
cout<<"A"<<endl;
}
virtual ~A() {}
};
class B: virtual public A
{
public:
void func()
{
cout<<"B"<<endl;
}
};
class C: virtual public A
{
};
class D: public B, public C
{
};
int main()
{
D d;
d.func();
return 1;
}
如果在MI的层次结构中没有“菱形”的继承结构出现,MI将是相当简单的。如果菱形继承结构出现,就需要通过引入虚基类来消除重复子对象。这不仅增加了混乱,而且使接下来的表达方式变得更加复杂和低效(虚继承会带来额外的开销和不同的意义)。