文章目录
一、菱形继承是什么?
在C++中存在多继承,即一个子类可以继承于多个父类,此时如果继承的父类又同时继承一个父类,那么就会引起菱形继承,继承结构如下图所示:
代码表示:
class A
{
public:
A(const string& name = "", int age = 0)
:_name(name),
_age(age)
{}
protected:
string _name;
int _age;
};
class B : public A
{
public:
B(const string& name = "", int age = 0, int id = 0)
:A(name, age),
_id(id)
{}
protected:
int _id;
};
class C : public A
{
public:
C(const string& name = "", int age = 0, const string& sex = "")
:A(name, age),
_sex(sex)
{}
protected:
string _sex;
};
class D : public B, public C
{
public:
D(const string& name = "", int age = 0, int id = 0, const string& sex = "", const string& hobby = "")
:B(name, age, id),
C(name, age, sex),
_hobby(hobby)
{}
protected:
string _hobby;
};
知道了什么是菱形继承,那么菱形继承有什么危害呢?菱形继承造成了数据的二义性和降低了性能,通过上面的代码可以知道,B、C都继承自A,那么B、C中都各有一份_name和_age,此时D再同时继承于B、C,那么D中不就有两份_name和_age了吗,这不是我们所期望看到的,虽然是可以通过指定类域的方法来区别使用父类中的数据:
B::_name
C::_name
但这并不合理,如果说一个人可以拥有多个名字,但是年龄、性别、身份证号码这些固定的属性通过菱形继承获得了多份,这就不合适了。
二、虚继承
那么菱形继承该如何解决呢?此时就引入了虚继承,现来看一下虚继承怎么用:
class A
{
public:
int _a;
};
class B : virtual public A
{
public:
int _b;
};
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
在继承方式前面加上 virtual 即可,此时该继承就是虚继承,来看一些不是虚继承的内存是怎样的:
可以看到在继承的B、C中各自有各自继承自A的值,而虚继承下的内存又是怎样的呢?如下:
可以看到在继承的B、C中只存了自己本身的值,而另外存了一个地址,来看看该地址所指向的内容是什么:
以上是B中所存的地址中指向的内容,通过该地址我们找到了一块区域,该区域内的14转换为十进制就是20,20表示的是从0x00B8FB5C该位置到0x00B8FB70的偏移量,C中也是同样的,也就是说想要找到B中继承自A的值,直接通过偏移量就可以了,此时数据不存在二义性,因为只有一份。
三、组合
如果说继承是is a的关系,那么组合就是has a的关系,即在一个类中存在另外一个类作为成员变量,代码表示:
class A
{
public:
void func();
protected:
int _a;
};
class B
{
private:
A _a;
int _b;
};
以上就是一个组合,B可以直接使用A中的公开函数func(),根据func()的实现也可以间接使用保护成员_a,组合相比继承存在一点优势,打个比方,如果以上代码是继承,那么B继承于A,B可以直接使用A中的公开和保护成员,那么如果此时A修改了_a的名字,而B又使用了_a,那么B也要跟着修改,而对于组合来说并没有影响,总的来说就是组合相对于继承的优势就是低耦合、高内聚。