20170511_请说清楚什么是菱形继承?
1、我们都知道C++中的三大特性——“封装、继承、多态”,但是当我们谈到“继承”时,不免会想起一个很重要的问题:菱形继承。
2、那么菱形继承是什么呢?请看图。
如上图所示,中间类A 和中间类B 共同继承了基类Base,而与此同时,派生类D 又同时继承了A 和B。
意思就是:多个类继承了同一个公共基类,而这些派生类又同时被一个类继承。
这时,会发生什么事情呢?请看代码。
#include<iostream>
using namespace std;
class Base
{
protected:
int _base;
public:
void fun()
cout << "Base::fun" << endl;
};
class A:public Base
{
protected:
int _a;
};
class B : public Base
{
protected:
int _b;
};
class D :public A, public B
{
private:
int _d;
};
int main()
{
D d;
d.fun(); //编译器报错:调用不明确
return 0;
}
这时,我们可以看到,类D的对象d 中保存了两份Base 成员,一份是从中间类A 继承来的,另一份是从中间类B 继承来的。当我们想要调用从Base 继承来的fun() 函数时,就会出现编译错误——调用不明确问题,同时这也造成了数据冗余的问题——明明需要一份就可以了,我们却偏偏保存了两份。
3、那么,我们该如何解决这个问题呢?
- 第一种方法:使用作用域运算符::
int main()
{
D d;
d.A::fun();
d.B::fun();
return 0;
}
这个做法是没有问题,但是没有从根本上解决这个问题,而且还不方便。
- 第二种方法:使用虚继承机制
如上图所示,虚继承,也就是让中间类A 和中间类B 继承基类Base 时前面加上关键字virtual,使其成为虚继承关系。
注意:这里并不是D 使用虚继承,
那么,虚继承是如何解决这个烦人的问题呢?请看图。
此时,我们可以看到,中间类A 和中间类B 不再保存基类Base 中的具体内容了,而是保存了该基类中的具体内容的一份偏移地址,然后将基类中的数据保存在一个公共位置处,这样就实现了“去除调用不明确问题!”
再看代码:
#include<iostream>
using namespace std;
class Base
{
protected:
int _base;
public:
void fun()
cout << "Base::fun" << endl;
};
class A:virtual public Base
{
protected:
int _a;
};
class B :virtual public Base
{
protected:
int _b;
};
class D :public A, public B
{
private:
int _d;
};
int main()
{
D d;
d.fun();//调用从基类Base继承来的函数,而且该函数只有这一份。
return 0;
}
这样的话,就既解决了数据冗余的问题,又解决了直接使用d.func() 来调用基类Base 中的func() 函数。
4、 *虚继承和虚函数是完全不同的两个概念,希望大家不要随意搞混。
想要了解虚函数的同学可以看看另一篇博文《C++的继承&多态》。
http://zimomo.blog.51cto.com/10799874/1752936