继承

1.什么是继承
继承就是在原有类的基础上进行拓展,增加功能,实现了代码复用,体现了面向对象语言的层次结构。
2.继承的定义
继承即通过派生权限(方式)由基类派生一个类,子类中丰富了基类中的功能
《继承》
————————-如这张图所示,这就是定义一个派生类的方式。访问权限有三种,分别是
public(类内类外都可以访问)、
protect(类内可以访问、子类中可以访问、类外不能访问)、
private(只有类内可以访问、子类中也不能访问、类外更不能访问)
————————-同样的,继承方式也有同上的三种
下面的成员变量指的是包括成员函数和成员变量在内的所有成员
我们来讨论一下这三种继承方式有何区别———->
public:
①:基类中的public和protect修饰的成员变量通过public继承到子类后依然是其在基类中的访问权限,即基类中的protect成员变量通过继承到子类后在子类中依然是protect权限。
②:基类中的private修饰的成员变量在子类中不可见。
protect:
①:基类中public修饰的成员变量继承到子类中降级为protect,protect继承到子类中依然为protect。
②:基类中的private修饰的成员变量在子类中不可见。
private:
①:基类中public修饰的成员变量继承到子类中降级为private,protect继承到子类降级为private。
②:基类中的private修饰的成员变量在子类中不可见。
注意:
1.基类中的private成员变量在继承到子类时都是不可见的,但这不代表没有继承,继承是将基类中的所有成员都继承到子类,可以通过sizeof的方式进行验证,这里的不可见指的是子类中存在这个成员变量但是基类不允许子类去访问和修改它,即对于子类来说是不可见的。
2.class默认的继承方式是private,struct默认是public。

看到这里那么基类和子类的区别就出来了即子类实际是对基类完成了一份拷贝后再丰富自己的内容。
那么我们就可以得到下面的推论,派生类对象 可以赋值给基类的对象 / 基类的指针/ 基类的引用。而反过来则不行,道理很简单,说白了派生类在内存中的大小肯定大于或等于基类(一般是大于,如果等于说明派生类和基类一样,那么创建这个派生类就没有什么意义了),所以大给小赋值没有问题,反过来如果是小给大赋值那么一定不会都赋值进去,那么剩余的部分就没办法了,但可以强制类型转换进行赋值(但强烈不推荐!)。

基类和子类分别代表着不同的作用域:
如果我们在基类和子类中创建相同名字的成员变量,在创建子类对象后访问到的是基类中的成员变量还是子类中的成员变量呢?这种现象叫做隐藏,通过测试会发现由子类创建的对象优先访问的是子类中的同名成员变量,如果我们向访问基类中的同名成员变量需要加访问限定符,即

S.Person::id = 10;

这样就把基类中的id修改为10了。
注意:这里隐藏的定义是同名即构成同名隐藏,与变量的类型、成员函数的参数列表和返回值都没有关系。

那么派生类在定义完成时其内也有六个默认函数,同普通类中的六个默认函数相同–>
派生类对象在使用这些函数时都要先调用基类中的对用函数完成对继承下来的基类部分的初始化。
注意:
1、派生类中会生成无参的默认构造函数,若基类中自定义了非默认构造函数(无参、全缺省),在派生类中也必须自定义构造函数并在初始化列表对其初始化
2.拷贝构造函数与构造函数类似
3.赋值运算符
4.析构
5.取地址
6.const取地址
说明:在创建一个派生类对象时,首先调用派生类的构造函数,并在初始化列表完成对继承下来的基类部分内容的初始化,在初始化派生类以及的内容,在析构时遵循后创建的先析构,所以先调用派生类对象的析构函数,后再对基类对象调用析构函数。这是创建派生类对象的过程!仔细理解!!!

在继承的时候基类的友元函数是不会发生继承的。因为友元函数不属于类啊!
而static修饰的基类成员变量会继承到其派生类中而且依然共用一个地址,所有的子类中都拥有这个静态变量,和static的定义没有出入,则整个继承体系里面只有一个这样的成员。

菱形继承和菱形虚拟继承

单继承和多继承的区别:多继承即一个子类继承于两个基类拥有这两个基类中的属性,特殊的一种继承关系是菱形继承,首先画一张图来看单继承的对象模型:
《继承》
即派生类中从父类继承下来的部分在上,独有的部分在下。
这个不难理解,我们再来看看菱形继承的对象模型:
《继承》
C1和C2分别继承自基类B,D同时继承C1和C2,这样构成了菱形继承,但是这种情况很特殊,即D类中有两个_b对象,_这样会造成数据冗余,也是造成二义性的原因,我们可以通过访问限定符来指定访问_b,_但并没有根本性的解决这个问题,所以有了虚拟继承的概念,即用virtual关键字修饰C1、C2函数,使他们只继承一次B中的_b_成员,我们再来通过观察内存中的情况看看菱形虚拟继承的对象模型:
《继承》
通过这张图我们可以看出b在d对象中只保存了一份,而在C1和C2中分别增加了两个虚基表指针通过访问虚基表来找到b,虚基表中存放的就是自身的偏移量和相对基类偏移量,这两个虚基表指针都可以访问到b,一般情况下是通过C1的虚基表指针找到b。这样就解决了菱形继承中存在的缺陷。
看到这就体现出了c++语法的复杂性,所以我们一般不建议设计出多继承,更不建议设计菱形继承。

点赞