C++之继承

目录

一、继承的概念

二、继承的定义

2.1 继承到底干了什么事情呢?

2.1构造的顺序

 2.2子类继承不了父类的构造和析构函数

2.3当子类包含别的类的成员时

三、继承权限

3.1.站在子类内部角度

 3.2.站在子类对象的角度

四、派生类向父类的转换(向上转换)

4.1、派生类对象可以赋值给基类的对象 / 基类的指针 / 基类的引用

4.2对于父类的指针指向子类

4.3函数的同名隐藏问题

五、派生类的默认成员函数

 5.1多继承问题

 5.2当基类没有默认的构造函数的时候

5.3、拷贝构造

5.4、operator=重载

 六、继承与友元

七、菱形继承及菱形虚拟继承

7.1二义性:

7.2、菱形继承的二义性的解决方法:

方法一:让访问明确化

方法二:采用菱形虚拟继承

底层空间深度剖析虚拟继承

虚基类在构造的时候比普通的基类优先级要高


一、继承的概念

继承(inheritance)机制是面向对象程序设计使 代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称 派生类。继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。
继承有两大作用:
                1.代码复用
                2.实现多态


二、继承的定义

2.1 继承到底干了什么事情呢?

无论是public,private还是protected,继承相当于把父类的所有信息拷贝进子类中(除了两个东西)

这就是代码复用的体现

子类大小为8,是因为继承了父类的成员

 子类也继承了父类的方法

2.1构造的顺序

我们分别给出父类和子类的构造和析构函数,在实例化子类对象的时候,来观察下构造的先后顺序

 我们可以发现构造子类前会先构造父类,而析构的时候则是反过来的,这就像是一个栈结构一样,先进后出

 2.2子类继承不了父类的构造和析构函数

前面说了就两个东西继承不了,这就是父类的 “生与死”

2.3当子类包含别的类的成员时

三、继承权限

子类在继承父类的时候有三种方式:public、private、protected,首先要知道,不论是什么方式,继承都会继承父类的所有信息(除了两个东西),继承的不同只是改变父类的成员在子类中的属性

那么这三种方式有什么区别吗?

3.1.站在子类内部角度

public继承

就相当于父类被继承下来的所有属性都不发生变化,但是父类的私有成员在子类中是不可见的

protected继承

相当于把父类的public属性变为protected,其他属性不变

private继承

相当于把父类的public和protected属性变为private属性

简单理解就是 private > protected > public

在子类内部,永远都是可以访问父类的公有和保护,私有是不可见的,

这里的私有指的是在父类中就是私有的,和private继承方式的私有没有关系,但确对再一次继承有影响

 3.2.站在子类对象的角度

 对象永远只能访问其公有成员 

 也就是说要是保护或私有继承的话,那么在对象角度就什么也访问不了了

 

四、派生类向父类的转换(向上转换)

  • 4.1、派生类对象可以赋值给基类的对象 / 基类的指针 / 基类的引用

  • 这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。基类对象不能赋值给派生类对象

父类要的所有信息,子类都可以提供,所以子类可直接给父类进行赋值,在赋值的时候,仅仅是把子类中的父类部分给了,就像切片一样

 

 但反过来就不行了,因为子类有多余的成员,而父类不能提供

4.2对于父类的指针指向子类

 父类的指针成员永远只能指向子类里面的父类部分

其实能够在不同类型的情况下直接指向的原因就是因为子类中有父类的部分

4.3函数的同名隐藏问题

我们先来看一个现象

 

 在子类中有fun()函数的时候子类对象想要访问父类中fun函数的重载的时会报错呢,

子类中没有fun()函数的时候,能够正常访问,这是为什么呢?

原因就是同名函数的隐藏

在子类中有一个和父类同名的函数,那么这个子类中的这个函数就会把父类中所有的同名函数全部隐藏掉,

但本质上父类的同名函数还是存在的,也是有方法可以去访问的

加作用域,进行显示,(就比如医院挂号的特殊原因)

五、派生类的默认成员函数

 5.1多继承问题

 构造的顺序和继承的顺序是一样的

要注意子类中还有成员对象的情况

 5.2当基类没有默认的构造函数的时候

 这时候再和刚刚一样就不行了

 这时候继承的类的构造函数需要传参数,我们就需要使用初始化列表的方式传参数

要注意:这里的构造顺序和初始化顺序没有任何关系,只跟继承顺序和成员对象生名的关系有关

  • 5.3、拷贝构造

  •  派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

 

  • 5.4、operator=重载

  • 派生类中的opeartor=必须要调用基类中的operator=完成对父类的赋值

 六、继承与友元

基类当中的友元函数,友元类是不可以被子类继承的,基类友元不能访问子类的私有

例如:你爸的朋友不一定是你的朋友,

七、菱形继承及菱形虚拟继承

7.1二义性:

我们来看下这个代码

这个继承方式形如一个菱形,也形如砖石,所以叫做菱形继承或者砖石继承

 这时候我们来实例化一下E对象

 e去访问m_a的时候会报错,因为产生了二义性,他的两个父类D和C当中都有m_a

7.2、菱形继承的二义性的解决方法:

  • 方法一:让访问明确化

  • 我们可以给访问加上作用域限定符

虽然这样可以解决问题,但这样对空间有很大的浪费,因为D和C中分别都有m_a独自的空间,而对于E类的e对象来说,

我们只要能在E中能够正常使用m_a就行了,所以最好就是在继承中m_a只有一份就行了

这时候我们可能会想到静态,但是静态有很大的弊端,只要有一个修改了,那就全部改变了

  • 方法二:采用菱形虚拟继承

 我们可以在继承的时候加一个virtual关键字

虚拟继承之后,会发现D和C中的m_a是同一个空间,这说明虚拟继承来自父类Base的成员在继承的过程中只有一份,当然这里的只有一份和静态的只有一份还是有着重要的区别的

 下面我们来看看虚拟继承和静态的区别

静态:这里我们把Base类中的m_a写成了静态的

 可以发现,无论是哪个角度,m_a始终都只有一份

 虚拟继承

 我们可以发现,虚拟继承时,D和C中的m_a不同,最终子类E中D和C中的m_a不同

这是为什么呢?我们有必要来探究一下

我们首先要知道,我们在菱形继承中想要达到的目的就是Base(虚基类)在最终E当中只有一份,而我们这里也看到了,在E当中,确实只有一份

出现这种情况的原理是什么呢?

先来看看非虚拟继承类的底层空间是怎么样的

底层空间深度剖析虚拟继承

 我们再分别来看看  D和C中的两个地址  005b9b40  和   005b9b48   是用来干什么的  

 可以发现:E中的D和C不再存储Base的数据,而是存储能够找到Base空间的一个偏移量的空间地址

这也就解释了为什么解决了二义性,因为本质上都是靠偏移量找到了Base的空间

至于节约空间方面,当Base的信息很多的时候,自然节省了不少的空间

虚基类在构造的时候比普通的基类优先级要高

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值