一、继承
1.含义:是面向对象程序设计使代码可以复用手段。它允许程序员在保持原有类(基类)特性的基础上进行扩展,增加功能,这样产生新的类,称为派生类。继承呈现了面向对象程序设计的层次结构。
2继承的格式:
3.成员限定符与继承关系
注意点:访问权限出现在类中
继承权限在继承列表中
4.继承方式及权限对应表
5.总结
1) public继承是一个接口继承,保持is-a原则,即就是可以将派生类对象看成是一个基类对象。
2) protected / private继承是一个实现继承。积累的部分成员并非完全成为子类就扣的一部分,是has-a的关系。所以非特殊情 况下不会使用这两种继承关系。在绝大部分情况下使用的是公有继承。
3) class默认的继承方式是private,struct默认的继承方式是public.不过最好写出继承方式。
二、赋值兼容规则(public继承)
1.子类对象可以赋值给父类对象
基类* p=&派生类对象;
基类& r=派生类对象
2.父类对象不能赋值给子类对象
派生类* pd=&基类对象 ;(报错,代码不安全)
3. 父类的指针/引用可以指向子类对象
4.子类的指针/引用不能指向父类对象(可以通过强制类型转换完成,但不能调用成员函数,会崩溃)
派生类* pd=&基类对象:(错误)
派生类* pd=(派生类*)&基类对象;(正确)
三、继承中的作用域
1.在继承体系中基类和派生类都有独立的作用域。
2.基类和派生类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问。(在子类成员函数中,可以使用 基类::基类成员 访问)
同名隐藏:基类和派生类中有相同名称的成员(成员变量||成员函数),且与相同名称成员的类型无关,通过派生类对象调用相同名称的成员时,有限调用派生类自己的,基类的成员被隐藏了。
若要访问基类对象---> 派生类::成员名字
基类与派生类中相同名称的函数不能形成重载。
四、派生类的默认成员函数
继承体系下,派生类中如果没有显式定义这六个默认成员函数,编译器则会合成这六个默认的成员函数。
1.继承体系中派生类对象的构造函数
构造派生类对象:调用派生类构造函数
1) 在派生类构造函数的初始化列表中完成基类对象的构造
2) 在派生类构造函数的初始化列表中构造派生类自己的成员
3) 执行派生类构造函数的函数体
注意:如果基类中有非缺省的构造函数,派生类必须显式给出自己的构造函数
构造函数初始化列表的位置调用基类的构造函数,完成基类对象的构造
如果基类中的构造函数是缺省的,派生类构造函数是否需要显式提供根据要求
2.继承体系中派生类对象的析构函数
销毁派生类对象:调用派生类析构函数
1)在派生类中完成派生类自己资源的释放工作
2) 在派生类构造函数最后一条语句插入:call 基类析构函数
3.继承体系下派生类、基类分别构造函数、析构函数的调用次序
4.实现一个类:不能被继承
1) 基类中构造 :private + 静态创建基类对象&静态销毁基类对象 的方式
2)final 关键字(在c++11下)
eg:class B final { }
5.生成和合成的区别:
1) 生成:不依赖任何东西,只是编译器根据类的定义简单生成基于基础类型的默认成员函数。
2) 合成:必须依赖于基类,编译器根据基类的相应成员函数的行为来合成派生类的默认成员函数。
五、特殊的继承
1.友元关系不能继承,即是基类友元不能访问子类私有的和保护的成员。
2.static静态成员会被继承。无论派生多少个子类,都只有一个static成员实例。
六、不同继承体系中对象的模型
1.对象模型:对象中非静态成员变量在内存中的布局形式,与成员函数无关。
2.单继承: 一个派生类只有一个基类
3.多继承:一个派生类有两个或两个以上的直接基类(每个基类前面都要加上继承权限,且按照继承次序来赋值)
4.菱形继承 :单继承+多继承 的组合
菱形继承将最顶层中的成员变量在其对象模型中存了两份,派生类对象无法直接访问最顶层基类中的成员变量 ,造成了菱形二义性问题和数据冗余问题。
5.虚拟继承:基类在上,派生在下
虚拟继承---解决了菱形继承的二义性和数据冗余问题。
虚拟继承的格式:在继承权限前加上virtual关键字即可。
eg: class B
{
......
}
class D:virtual public B
{
......
}
1)虚拟继承中对象模型:基类部分在下,派生类部分在上
2) 虚拟继承中:对象模型多了4字节,这4字节指向偏移量表格的地址
3) 虚拟继承中;是通过偏移量表格来访问基类成员
4) 虚拟继承中:编译器一定会合成构造函数,并且多传递1。
1作用:区分虚拟继承 (1在构造函数中压栈)