今天小生学习了虚继承,做了一下总结,不足之处请多多批评指正:
首先,菱形继承有一个父类例如class AA,再有两个子类分别继承AA,如class BB:public AA、class CC:public AA,最后,将BB、CC作为父类继承给子类DD,这就形成了菱形继承。请看以下代码:
class AA
{
public:
int _aa;
};
class BB: public AA
{
public:
int _bb;
};
class CC: public AA
{
public:
int _cc;
};
class DD:public BB,public CC
{
public:
int _dd;
};
AA类中包含一个int变量_aa,BB、CC分别继承了AA,各自又都有一个int型变量,DD继承了BB和CC,它自己又另外有一个自己的int型变量,请往下看:
void Test1()
{
DD d;
d._aa = 1;
d._bb = 2;
d._cc = 3;
d._dd = 4;
}
在Test1中定义了一个DD类型的d,分别给d._aa、d._bb、d._cc,d._dd赋值,运行代码发现出现了以下错误:
对_aa的访问不明确,为什么呢?是因为d中包含了一个BB类型和一个CC类型,而它们都继承了AA类型,所以d中包含了两个_aa,当我们直接用d._aa访问时,编译器不知道我们访问的是哪一个,所以出错,这既是菱形继承的二义性,坚决这个问题的一种方法是指定类域,如下图代码:
上图代码运行后得到右面的结果,大家可以看到,我们给_aa赋值时指定了给BB类型 中的_aa赋值,而CC中的_aa并不会发生变化。这种方法虽然解决了菱形继承的二义性,确没有解决代码冗余的问题,下面给大家介绍另一种方法虚继承,它能够同时解决了这两个问题。虚继承中我们用到了关键字virtual,请看以下代码:
class BB:virtual public AA
{
public:
int _bb;
};
class CC:virtual public AA
{
public:
int _cc;
};
class DD:public BB,public CC
{
public:
int _dd;
};
BB与CC在继承AA时在public AA前面加了virtual这个关键字,这样我们再运行下面这段代码时发现原来的错误没有了。
void Test1()
DD d;
d._aa = 1;
d._bb = 2;
d._cc = 3;
d._dd = 4;
}
这是为什么呢,因为加上virtual后我,DD型变量d在内存中存储方式方式的变化,下图为大家展示了二者的不同:
大家从内存中可以看到前后发生的变化,_aa只有一个了,所以解决了二义性和代码冗余的问题,大家可能会觉得这里占用的内存反而用的多了,并且_bb、_CC上面都多了一个地址,那我们看看这两个地址里面到底有什么:
我们可以看到_bb上面的这个地址里面存了一个十六进制数字14,_cc上面的地址里面存了一个十六进制的数字0c,这是什么呢,这其实是d中包含的BB、CC类型各自的地址到_a地址的偏移量,请看以下代码:
DD* pd = new DD;
BB* pb = pd;
继承有复制兼容性规则,而虚继承也要满足这个规则,如果没有这两个指针,上述代码运行时_aa与_bb,不在一起,这里也就无法截断赋值,赋值兼容性规则就不满足了,而有了这两个指针,我们这样赋值时,通过这两个指针找到了偏移量,加上偏移量就可以访问到_aa,也就满足了复制兼容性规则。