C++中的继承(下)

🧸🧸🧸各位大佬大家好,我是猪皮兄弟🧸🧸🧸
在这里插入图片描述

一、如何定义一个不能被继承的类

1.私有父类的构造函数

因为父类构造函数的私有,继承过来的构造函数不可见
C++98通过这种方式在实例化的时候间接做到

class A
{
public:
	A()
	{}
protected:
	int _a;
};

class B:public A
{
};
int main()
{
	B b;//报错
	return 0;
}

2.final关键字

C++11就可以通过final关键字直接实现不可再继承(其他类在继承的时候就直接报错)

class A final
{
//	...
};

final修饰的类也叫做最终类

二、多继承

1.单继承与多继承

单继承:一个子类只有一个直接父类时称这个继承关系时单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

在这里插入图片描述

class Derive:public Base1,public Base2

2.拷贝的顺序与继承顺序

在这里插入图片描述
从上图可以看出,拷贝的顺序和继承的顺序有关

3.成员的地址映射关系

在这里插入图片描述
在这里插入图片描述
因为栈的使用习惯是从高地址向低地址存储,但是操作系统访问内存的顺序是从低地址向高地址,所以存储的位置关系是这样的

4.复杂的菱形继承

C++是有多继承的,多继承就是由两个或者以上直接父类,就有可能导致菱形继承的问题,实际当中是很忌讳菱形继承的,所以后面的语言都不支持多继承。
在这里插入图片描述
在标准库中,也用到过菱形继承

单继承和多继承会引发一个特别复杂的问题:菱形继承
在这里插入图片描述
在这里插入图片描述

菱形继承的问题:
1.数据冗余:Person的数据有两份,重复占用空间,构造的时候需要构造两次,拷贝的时候也要拷贝两次等等,都是浪费
2.二义性:就是因为有相同的数据,所以访问的时候不知道去访问谁(会报字段不明确的错误),除非你这里直接去显示的访问(但是终究还是有构造两次,拷贝两次的浪费问题)

5.菱形继承问题的解决–菱形虚拟继承

问题提出

菱形继承问题的解决需要用到虚继承
虚继承的最上面的那个基类被称作 虚基类

class Student: virtual public Person
{	
protected:
	int _num;
};
class Teacher: virtual public Person
{	
protected:
	int _id;
};

通过虚继承,能让继承下来的重复数据只有一份

他是怎么做的呢?
虚继承会在原来放虚基类成员的地方放一个指针,这个指针指向的表称为虚基表,存的是偏移量,因为菱形继承使用虚继承,需要被放在虚基表中的成员就是虚基类中的(只需要存一个指针,去找到虚基表,就可以找到多个虚基类的成员)
所以同一把虚基类的成员放在最下面,用偏移量来找

验证

验证:
在这里插入图片描述
可以看出两个东西
1.存储虚基类成员的位置换成了指向虚基表的指针
2.虚基类的成员放在了最下面(为什么等会儿说),之所以没看到_a=1是因为被_a=2覆盖了,因为这时的两个_a是同一个。也就解决了菱形继承带来的数据冗余和二义性的问题

然年再继续找到他的B的虚基表,看到存的是14,因为是六进制数,所以应该是20,正好对应_a的位置。所以能看出它存储的是偏移量
在这里插入图片描述

从这么一点数据量来看的话,确实没有节省空间,甚至还多了,但是如果这个公共的资源很大呢?原来需要很大的空间来存储两份,现在只需要两个指针和存储一份

虚继承遇到赋值兼容转换(切割或切片)

虚继承时,如果遇到切割/切片会发生什么
在这里插入图片描述
因为B是虚继承,所以会有一张虚表,他是通过d中从虚表拿到偏移量,找到_a(基类的资源),再进行切片,重新初始化b对象中的虚表。

另外,也因为赋值兼容转换,所以要保证对象的存储模型的一致。

用虚继承可以解决菱形继承的问题,但是还是付出了一些代价的,无论是访问的效率还是复杂程度,但是也是在可接受的范围之内,但是菱形继承,能不用就不用

三、其他问题

1.继承和组合

继承一般都指公有继承
继承是一种is-a的关系,也即是说每个派生类对象都是一个基类对象(人-学生,植物-花)
组合是一种has-a的关系,假设B组合了A,每个B对象中都有一个A对象,组合也是复用,与继承的区别就在于组合受到权限的一些限制,比如继承能用基类的protected,组合就不行(比如轮胎-车 眼睛-头就是组合)

class C
{
};
class D
{
protected:
	C _c;
};
//这就是一个组合 has-a

2.优先选用组合

继承允许根据基类的实现来定义派生类的实现,这种通过生成派生类的复用称为白箱复用(white-box reuse),在继承方式中,基类的内部细节对子类可见,继承一定程度上破坏了基类的封装。基类的改变,对派生类有很大的影响,派生类和基类间的依赖关系很强,耦合度高。

对象组合式类继承之外的一种复用方式,新的更复杂的功能可以通过组装或组合对象的方式来获得,对象组合要求被组合的对象有良好定义的接口,这种看不见底层细节的复用方式称为黑箱复用(black-box reuse).所以组合间没有很强的依赖关系,耦合度低。

优先选用组合有助于保持每个类的封装

但是有些关系就是要用继承(is-a)的,另外要实现多态,也必须要继承。类和类的关系可以用组合就用组合,关系越松散,执行效率越高,短板效应。

比如说A有10个成员,1个公有,9个保护
B继承A,A的任意改变都会改变B,有十种关联关系
组合的话,语法上就限制了保护的成员我用不了,所以不改这个公有就和我没有瓜西,只有一种关联关系。

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猪皮兄弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值