C++中的继承(下)

1 继承和友元

友元关系不能继承,因为友元不是类的成员,也就是说基类友元不能访问子类私有和保护成员。

2 继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例

3 复杂的菱形继承即菱形虚拟继承

3.1 单继承

单继承中子类只有一个基类。单继承子类对象模型:
在这里插入图片描述

3.2 多继承

多继承中子类至少有两个基类。

class B1
{
public:
	int _b1;
};

class B2
{
public:
	int _b2;
};

class D : public B2, public B1
{
public:
	int _d;
};

int main()
{
	cout << sizeof(D) << endl;

	D d;
	d._b1 = 1;
	d._b2 = 2;
	d._d = 3;
	return 0;
}

多继承子类对象模型:
在这里插入图片描述
注意:如果是多继承,基类中成员在子类中的排列次序与继承列表中基类的先后次序一致。例如上述的B1,B2。

3.3 菱形继承(钻石继承)

class B
{
public:
	void TestFunc()
	{}

	int _b;
};

// 单继承
class C1 : public B
{
public:
	int _c1;
};

// 单继承
class C2 : public B
{
public:
	int _c2;
};

// 多继承
class D : public C1, public C2
{
public:
	int _d;
};

继承模型图:
在这里插入图片描述

上图中C1继承B,C2也继承B,这两种继承方式都是单继承。D继承C1,C2,D为多继承。整体的继承方式为菱形继承。派生类D中包含有两个_b,一个是C1继承基类的,另一个是C2继承基类。
菱形继承存在二义性问题:如果用户在派生类中需要改变_b 的值(d._b = 1),编译器就不知道从哪个基类中继承下来的值,因此产生了二义性。

二义性的解决办法:

  1. 让访问明确化–>对于存在二义性的成员在访问时加基类名称::
    例如,d.C1::_b = 2; d.C2::f();该种解决方式可以让代码通过编译,但是最顶层基类中的成员在底层派生类仍然存储了两份。
  2. 本质上解决是让最顶层基类中成员在最底层中只存储了一份—>虚拟继承

虚拟继承:在继承权限前面加上virtual关键字。
对象模型:
在这里插入图片描述
重要解释:

  • 虚基表指针是指向偏移量表格的指针。上述虚基表的第一部分是派生类对象相对于自己的偏移量,因此为0;第二部分是基类部分相对于对象的偏移量。
  • 派生类的大小=基类成员大小 + 子类新增成员大小 + 4;
  • 从对象前4个字节中取出地址,在该地址上向后偏移4个字节,取出该空间的内容(也就是偏移量offset),让对象地址向后偏移offset个字节,就会拿到子类对象从基类中继承的成员变量。
  • 例如上图中在子类对象模型中要访问基类成员变量_b,先在子类对象的前四个字节取出虚基表,再向后偏移4个字节就在虚基表中得到数字8,最后在子类对象的起始位置向后偏移8字节,得到基类成员变量_b

虚拟继承唯一的作用就是在菱形继承中,解决菱形继承中二义性的问题。

菱形虚拟继承:

class B
{
public:
	void f()
	{}

	int _b;
};

class C1 : virtual public B
{
public:
	int _c1;
};

class C2 : virtual public B
{
public:
	int _c2;
};

class D :  public C1, public C2
{
public:
	int _d;
};
};

在这里插入图片描述
菱形虚拟继承各类分布如上图所示,按照继承的先后顺序,C1类在最上面,下来是C2类,然后是子类新增的成员变量,最后是基类的成员变量。在C1类中虚基表指针0代表相对于自己的偏移量,20字节代表基类成员变量相对于C1类的偏移量,同理C2。注意:虚基表存储在常量区,不能被修改。

C1& c1 = d;
c1._c1 = 1;//直接访问
c1._b = 2; //通过虚基表的偏移量访问

C2& c2 = d;
c2._c2 = 1; //直接访问
c2._b = 2; //通过虚基表的偏移量访问

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值