下面我将从继承实例(代码 ),继承对象模型和原理来分析继承。
单继承
模型
代码
class A
{
public:
int _a;
};
class B:public A
{
public:
int _b;
};
int main()
{
B b;
b._a = 0;
b._b = 1;
return 0;
}
代码分析:
多继承
模型
class A
{
public:
int _a;
};
class B
{
public:
int _b;
};
class C :public A, public B
{
public:
int _c;
};
int main()
{
C c;
c._a = 0;
c._b = 1;
c._c = 2;
return 0;
}
代码分析
多继承我们要注意一个问题:当继承列表发送变化时,那么我们的对象模型也将发生改变。
原则:继承列表先写谁,谁将在低地址。和谁的类在不在前面无关,只于几次列表有关
我将 class C :public A, public B
换成class C :public B, public A
那么c的对象模型将变成下面的格式
菱形继承
模型
class A
{
public:
int _a;
};
class B:public A
{
public:
int _b;
};
class C:public A
{
public:
int _c;
};
class D:public B,public C
{
public:
int _d;
};
int main()
{
D d1;
d1._a = 0;
d1._b = 1;
d1._c = 2;
d1._d = 4;
return 0;
}
代码将不能编译通过,提示
当屏蔽//d1._a = 0;
后可以通过编译,但是我们将无法访问父类的成员。所以我们迫切需要解决它的办法。
我们仔细看看报的错误,访问对象不明确,于是我们可以通过下面的方法进行访问_a:
d1.B::_a = 3;
d1.C::_a = 0;
这样就可以通过编译了。
下面我们研究下它的对象模型
我们通过计算可以得到对象d1的大小为20字节。但是对于我们来说,查找基类的成员变得困难了,有没有一种方式来解决这个问题?
于是虚继承出现了,它解决了菱形继承访问父类成员出现的二义性问题
下面我们一起来看看它是怎么解决二义性问题的:
虚继承
虚继承就是在被继承的前面加上virtual关键字
它是菱形继承的一种改进,所以它的继承方式和菱形继承一样,只是对象模型有区别。
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;
};
int main()
{
D d1;
cout << sizeof(d1) << endl;
d1._a = 0;
d1._b = 1;
d1._c = 2;
d1._d = 4;
return 0;
}
加入虚继承后d1的大小变成24字节,因为C类和B类都多了一个相对于_a的偏移量,所以多了四个字节。虚继承只有一份父类的数据,但是他在别的类中增加了四个字节来获取基类成员相对于别的类的偏移量。这样就解决了在菱形继承的二义性问题,但是它也引入了,程序性能下降的问题,这里就不做详细的说明。