在c++里面有很大一个重点就是继承和多态了,今天就先说一下关于继承的一个小方面。因为在c++当中的话继承有
1.单继承:一个子类只有一个直接父类时称为这个继承关系为单继承
2.多继承:一个子类有两个或以上的父类时称这个继承关系为多继承
例如:单继承
class AA
{
public:
int _aa;
};
class BB :public AA
{
public:
int _bb;
};
这是一个典型的继承虽然他什么也没有,这里BB继承了AA
多继承:
class AA
{
public:
int _aa;
};
class BB
{
public:
int _bb;
};
class CC : public AA, public BB 。。。
{
public:
int _cc;
};
这里的CC同时继承了AA和BB等,不过正是因为有了多继承所以同时带来了一个问题,就是在继承中有时会出现一种特殊的情况就是菱形继承
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;
};
这里BB继承了AA,CC也继承了AA,但是DD同时继承了BB和CC,更直观一点的就是
这样子的话很明显在DD中就会有两份AA中的函数和变量,这样就会带来二义性和数据冗余的问题,那么就得想办法解决这些问题了
首先一种就是我们可以声明我们具体是给哪一个父类中的变量赋值,例如
int main()
{
DD d;
d.BB::_aa = 10;
d.CC::_aa = 20;
return 0;
}
这样就可以赋值成功,不过这样只是解决了二义性的问题,数据冗余的问题并没有解决,所以又引入了一个新的解决办法,叫做虚继承,其关键字为virtual;用法如下:
class AA
{
public:
int _aa;
};
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;
};
这个时候再对对象d中的_aa赋值
int main()
{
DD d;
d._aa = 10;
d._aa = 20;
return 0;
}
通过调试我们发现
当d中的_aa发生变化是不管是BB中_aa还是CC中的_aa都发生了改变,不过再往深的话,我们从监视窗口中并看不出来什么了,我们不清楚他到底是怎么实现的,所以我们只能用另一种方法,即内存来研究它是怎么实现的,来解决二义性和数据冗余的。
首先是不加virtual的,我们看内存可以得到其对象模型为
然后是虚继承的情况
根据其中地址得到其位置的下方存了一个数据,并且这个数据根据观察可以看出来其数据是一个偏移量,并且是当前指针位置到存AA的位置的偏移量;
再来看其切片赋值的时候
发现在给其父类b赋值的时候是连这其指针和偏移量都一起切片赋值过去的
由此我们可以得到几个结论:
1.在虚继承中是把公共的数据重新在内存的最后开辟空间存进去,在其原来的位置存放一个指针,指针所指的位置是关于其到公共数据的偏移量。
2.虚继承是有代价的,并且在其切片赋值的时候,不只是把其数据赋值回去,而是和着其虚继承之后的指针,偏移量等等一起赋值的
3.虚继承在查找数据的时候先要拿指针找偏移量然后再找其数据,牺牲了性能方面,是典型的时间换空间的做法