继承和派生是对同一问题的不同角度来看
继承:保持已有类的特性而构造新类的过程(侧重在保持已有类的特征——实现代码的重用)
派生:在已有类的基础上新增自己的特性(在原有基础上强调个性的方面——新的问题,原有的问题无法实现)
(一)继承
1.定义语法
单继承:只从一个直接基类继承
class 派生类名:继承方式 基类名
{
成员声明(新增的函数);
}
class Derived:public Base
{
Derived ();
~ Derived();
}
多继承
class 派生类名:继承方式1 基类名1,继承方式2 基类名2,...
{
成员声明(新增的函数);
}
class Derived:public1 Base1, public2 Base2
{
Derived ();
~ Derived();
}
2.继承方式
(1)为什么要区分不同的继承方式
- 派生类成员对基类成员的访问权限
- 通过派生类对象对基类成员的访问权限
(2)继承方式
继承方式 | 继承的访问控制public | 继承的访问控制protected | 继承的访问控制private | 派生类的成员函数 | 派生类的对象 |
---|---|---|---|---|---|
公有继承 | 不变 | 不变 | 不可以直接访问 | 可以直接访问基类public和protected;但是不能直接访问private | 只能访问public |
私有继承 | 变成private | 变成private | 不可直接访问 | 可以直接访问基类public和protected;但是不能直接访问private | 不能直接访问从基类继承的任何成员 |
保护继承 | 变成protected | 变成protected | 不可直接访问 | 可以直接访问基类public和protected;但是不能直接访问private | 不能直接访问从基类继承的任何成员 |
用处:派生类需要私有成员会有公有访问接口
protected成员的特点与作用
- 对建立其所在类对象的模块来说,与private成员的性质相同
- 对于其派生类来说,它与public成员的性质相同
- 既实现了数据隐藏,又方便继承,实现了代码重用
3.类型转换
(1)基本原则
a.公有派生类对象可以被当做基类的对象使用
派生类对象可以隐含转换为基类对象
派生类对象可以初始化基类的引用
派生类的指针可以隐含转换为基类的指针
b.通过基类对象名,指针只能使用从基类继承的成员
(二)派生
1.相关概念
派生方式 | 内容 |
---|---|
吸收基类成员 | 默认包含除了构造和析构函数之外的所有成员;可以用using语句继承基类构造函数 |
改造基类成员 | 构造同名成员,基类中的成员被覆盖 |
添加新的成员 |
2.派生类的构造函数
每个类的构造函数负责自己类的初始化,派生类把基类所有的数据成员都继承了过来,基类的构造函数不被继承,派生类需要定义自己的构造函数。
(1)语法
a.可用using语句继承基类构造函数,只能初始化从基类继承的成员using B::B;
b.不继承基类的构造函数
- 派生类新增成员:派生类定义构造函数初始化
- 继承来的成员:自动调用基类构造函数进行初始化
- 派生类的构造函数需要给基类的构造函数传递参数
c.单继承时构造函数定义的语法
派生类名::派生类名(基类所需的形参,本类成员所需的形参):
基类名(参数表),本类成员初始化列表
{
}
【当基类有默认构造函数是,默认调用默认的构造函数,派生类构造函数应为基类构造函数提供参数】
d.多继承且有对象成员时派生的构造函数定义语法
派生类名::派生类名(形参表):
基类名1(参数),基类名2(参数),...,基类名n(参数)
本类成员(含对象成员)初始化列表
{
//其他初始化
}
3.构造函数的执行顺序
(1)调用基类构造函数
顺序按照他们被继承时声明的顺序(从左到右)
(2)对初始化列表的成员进行初始化
- 顺序按照在类中定义的数学
- 对象成员初始化时自动调用其所属类的构造函数
(3)执行派生类的构造函数体中的内容
4.派生类的复制构造函数
(1)若派生类没有声明复制构造函数
- 编译器需要生成一个隐含的复制构造函数
- 先调用基类的复制构造函数
- 再为派生类新增的成员执行复制
(2)若派生类定义了复制构造函数 - 一般都要为基类的复制构造函数的传递参数
- 复制构造函数只能接受一个参数,既用来初始化派生类定义的成员,也将被传递给基类的复制构造函数
- 基类的复制构造函数形参类型是基类对象的引用,实参可以是派生类对象的引用
5.派生类的析构函数
析构函数不需要被继承,派生类如果需要,要自行声明析构函数。
声明方法与无继承关系时类的析构函数相同。
不需要现实地调用基类的析构函数,系统会自动隐式调用。
先执行派生类析构函数的函数体,再调用基类的析构函数。
【注意执行顺序】
6.访问从基类继承的成员
(1)当派生类与基类中有相同成员时
- 若未特别限定,则通过派生类对象使用的是派生类的同名成员
- 如果要通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符(::)来限定
(2)二义性问题
问题描述:从不同基类继承了同名成员,但是在派生类中没有定义同名成员“派生类指针->成员名”
存在二义性问题
解决方式:用类名限定;或者使用同名隐藏;
7.虚基类
问题描述:派生类从多个基类派生,而这些基类又有共同基类,则在方式次共同基类中的成员是,将产生冗余,并有可能产生因冗余带来的不一致性
解决方式:以virtual说明基类的继承方式,需要在第一继承时就要将共同基类设计为虚基类
class B1:virtual public B
作用:
主要用来结局多继承时了能发生的对同一基类继承多次而产生的二义性问题
为最远的派生类提供唯一的基类成员,而不产生多次复制