学堂在线C++程序设计第九章学习笔记
继承与派生
继承的基本概念和语法
继承与派生
- 继承与派生是同一过程从不同的角度看
- 保持已有类的特性而构造新类的过程称为
继承
- 在已有类的基础上新增自己的特性而产生新类的过程称为
派生
- 保持已有类的特性而构造新类的过程称为
- 被继承的已有类称为
基类
- 派生出的新类称为
派生类
- 直接参与派生出某类的基类称为
直接基类
- 基类的基类甚至更高层的基类称为
间接基类
继承的目的
- 实现设计与代码重用
派生的目的
- 当新的问题出现,原有程序无法解决时,需要对原有程序进行改造
单继承时候派生类的语法
class 派生类名:继承方式 基类名
{
}
多继承时语法
class 派生类名:继承方式 基类名, 继承方式 基类名
{
}
派生类的构成
- 吸收基类成员
- 默认情况下派生类包含了全部基类中除构造和析构函数之外的所有成员
- C++11 规定可以用using语句继承基类构造函数
- 改造基类成员
- 添加新的成员
继承方式
公有继承
不同继承方式区别
派生类成员
对基类成员的访问权限- 通过
派生类对象
对基类成员的访问权限
三种继承方式
- 公有
- 私有
- 保护
公有继承
- 继承的访问控制
- 基类的
public
和protected
成员 :访问属性在派生类保持不变 - 基类的
private
成员:不可直接访问
- 基类的
- 访问权限
- 派生类中的成员函数:可以直接访问基类中的
public
和protected
成员,但不能直接访问基类的private
成员 - 通过派生类的对象:只能访问
public
成员
- 派生类中的成员函数:可以直接访问基类中的
私有继承和保护继承
私有继承
- 继承的访问控制
- 基类的
public
和protected
成员:变成private
成员 - 基类的
private
成员:不可直接访问
- 基类的
- 访问权限
- 派生类中的成员函数:可以直接访问基类中的
public
和protected
成员,但不能直接访问基类的private
成员 - 通过派生类的对象:不能访问任何成员
- 派生类中的成员函数:可以直接访问基类中的
保护继承
- 继承的访问控制
- 基类的
public
和protected
成员:变成private
成员,都已变成protected
成员 - 基类的
private
成员:不可直接访问
- 基类的
- 访问权限
- 派生类中的成员函数:可以直接访问基类中的
public
和protected
成员,但不能直接访问基类的private
成员 - 通过派生类的对象:不能直接访问
- 派生类中的成员函数:可以直接访问基类中的
protected 成员的特点与作用
- 对建立其所在类对象的模块来说,它与private的性质相同
- 对于其派生类来说,它与public性质相同
- 既实现了数据隐藏,又方便继承,实现代码重用
基类与派生类转换
转换
- 公有派生类对象可以被当做基类的对象使用,反之则不可
- 派生类的对象可以隐含转换为基类对象
- 派生类对象可以初始化基类的引用
- 派生类的指针可以隐含转换为基类的指针
- 通过基类对象名,指针只能使用从基类继承的成员
派生类的构造和析构
派生类的构造函数
- 基类的构造函数不被继承
- 派生类需要定义自己的构造函数
C++11 规定
- 可用 using 语句 继承基类构造函数
- 但是 只能 初始化从基类继承的成员
- 语法形式:
- using B::B
若不继承构造函数
- 派生类新增成员:派生类定义构造函数初始化
- 继承来的成员:自动调用基类构造函数进行初始化
- 派生类的构造函数需要给基类的构造函数传递参数
单继承 A继承与B
class A:public B{
A(int a, int b);
}
A::A(int a, int b): B(b),a(a) {
}
多继承 A 继承 B,C
class A:public B,public C{
A(int a, int b, int c);
}
A::A(int a, int b, int c): B(b), C(c), a(a) {
}
- 当基类有默认构造函数时
- 派生类构造函数可以不向基类构造函数传参
- 构造派生类对象,基类默认构造函数将被调用
- 如需执行基类中带参数的构造函数
- 派生类需要向基类构造函数传参
派生类的复制构造函数
若派生类没有声明复制构造函数
- 编译器会在需要时生成一个隐含的复制构造函数
- 先调用基类的复制构造函数
- 再为派生类新增的成员执行复制
若声明复制构造函数
- 一般都要为基类的复制构造函数传递参数
- 复制构造函数只能接受一个参数,既用来初始化派生类定义的成员,也将传递给基类的复制构造函数
- 基类的复制构造函数形参类型是基类对象的引用,实参可以是派生类对象的引用
派生类的析构函数
- 析构函数不被继承,派生类如果需要,要自行声明析构函数
- 声明方法与无继承关系时类的析构函数相同
- 不需要显式调用基类的析构函数,系统会隐式调用
- 限制性派生类析构函数,在执行基类析构函数
派生类成员的标识和访问
访问从基类继承的成员
当派生类与基类中有相同成员时
- 若未特别限定,则通过派生类对象使用的是派生类中的同名成员
- 如果通过派生类对象访问基类中被隐藏的同名成员,应使用基类名和作用域操作符::
class B{
public:
f();
}
class A:public B{
public:
f();
}
class main() {
A a();
a.f(); //调用派生类 f 函数
a.B::f(); //调用基类 f 函数
}
二义性问题
- 如果从不同基类继承了同名成员,但是在派生类中没有定义同名成员 存在二义性
- 派生类对象名或引用名.成员名
- 派生类指针->成员名
- 解决方式:用类名限定
虚基类
要解决的问题:
- 当派生类从多个基类派生,而这些基类又有共同基类,则在访问此共同基类中的成员时,将产生冗余,并可能带来不一致性
虚基类声明
- 以
virtual
说明基类继承方式 - 例如: class B1:virtual public B
作用
- 主要用来解决多继承时可能发生的对同一基类继承多次而产生的二义性问题
- 为最远的派生类提供唯一的基类成员,而不重复产生多次复制
注意
- 在第一级继承时就要将共同基类设计为虚基类
虚基类及派生类构造函数
- 建立对象时候所指定的类称为
最远派生类
- 虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化的
- 在整个继承结构中,直接或间接继承虚基类的所有派生类,都必须在构造函数的成员初始化表中为虚基类的构造函数列出参数。如果未列出,则表示调用该虚基类的默认构造函数
- 在建立对象时,只有 最远派生类 的构造函数调用虚基类的构造函数,其他类对虚基类构造函数的调用被忽略