Chapter7 继承与派生
7.1 类的继承与派生
7.1.1
类的继承,是新的类从已有类得到已有的属性。
类的派生,是从已有类产生新类的过程。
原有的类:基类,产生的新类:派生类。
7.1.2 派生类的定义
多继承:一个派生类可以同时有多个基类。
单继承:一个派生类只有一个直接基类。
交通工具——汽车——卡车
间接基类 直接基类 派生类
类的继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。
7.1.3 派生类生成过程
1 吸收基类成员
2 改造基类成员。两个方面,一个是对基类成员的访问控制,另一个是对基类数据或函数成员的覆盖或隐藏。其中,隐藏是在派生类中声明一个和基类成员同名的成员。
3 添加新的成员
7.2 访问控制
基类成员的访问属性(public,protected,private)由继承方式控制。基类的自身成员可以对基类中任何一个其他成员进行访问,但基类的对象只能访问该类的公有成员。
7.2.1 公有继承
基类的公有成员和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。
7.2.2 私有继承
基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可直接访问。
7.2.3 保护继承
基类的公有成员和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可直接访问。
7.3 类型兼容规则
在需要基类对象的任何地方,都可以使用公有派生类的对象来代替,之后派生类的对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
替代所指的情况:
1 派生类的对象可以隐含转化为基类对象。
2 派生类的对象可以初始化基类的引用。
3 派生类的指针可以隐含转换为基类的指针。
7.4 派生类的构造和析构函数
7.4.1 构造函数
派生类构造函数的执行
(1)调用基类构造函数,他们被继承时声明的顺序。
(2) 对派生类新增的成员对象初始化,以他们在类中声明的顺序。
(3) 初始化新增的其他成员。
7.4.2 拷贝构造函数
给基类的拷贝构造函数传参时可利用类型兼容规则。
7.4.3 析构函数
析构函数执行与构造函数正好相反,是一个出栈的过程。
7.5 派生类成员的标识与访问
在派生类中,成员按访问属性可分为:
(1)不可访问成员
(2)保护成员
(3)私有成员
(4)公有成员
7.5.1 作用域分辨符
隐藏规则:若存在多个具有包含关系的作用域 ,则内层标识符隐藏外层同名标识符。
在类的派生层次结构中,基类的成员和派生新增的成员都具有类作用域且相互包含,基类在外层,派生类在内层。
只有在相同作用域中定义的函数才可以重载。
调用非静态成员函数总是针对特定的对象,执行函数调用时需将调用该函数的对象指针作为隐含参数传递给被调函数来初始化this指针。
7.5.2 虚基类
将共同基类设置为虚基类,这时从不同路径继承过来的同名数据成员在内存中只有一个副本,同一个函数名也只有一个映射。
虚基类的成员由最远派生类的构造函数通过调用虚基类的构造函数进行初始化,其中最远派生类是指建立对象时所指定的类。
构造一个类的对象的一般顺序:
(1)虚基类的构造函数
(2)基类的构造函数
(3)成员对象的构造函数
(4)本类构造函数
7.8
7.8.1 组合与继承
组合:整体——部分
继承:特殊——一般
7.8.2 派生类对象的内存布局
1 单继承
class Base {...};
class Derived:public Base {...};
Base *pba = new Base();
Derived *pd = new Derived();
Base *pbb = pd;
/*pba->|Base 数据成员| //Base对象
pbb,pd->|Base数据成员|//Derived对象
|Derived新增数据成员|*/
2 多继承
class Base1 {...};
class Base2 {...};
class Derived:public Base1,public Base2 {...};
Base1 *pb1a = new Base1();
Base2 *pb2a = new Base2();
Derived *pd = new Derived();
Base1 *pb1b = pd;
Base2 *pb2b = pd;
/*pb1a->|Base1数据成员| //Base1对象
pb2a->|Base2数据成员| //Base2对象
pb1b,pd->|Base1数据成员| //Derived对象
pb2b-> |Base2数据成员|
|Derived新增数据成员|*/
3 虚继承
class Base0 {...};
class Base1:virtual public Base0 {...};
class Base2:virtual public Base0 {...};
class Derived:public Base1,public Base2 {...};
Base *pb0a = new Base0();
Base1 *pb1a = new Base1();
Base2 *pb2a = new Base2();
Derived *pd = new Derived();
Base1 *pb1b = pd;
Base2 *pb2b = pd;
Base0 *pb0b = pb1b;
/*pb0a->|Base0数据成员|//Base0对象
pb1a->|Base0指针| //Base1对象
|Base1新增数据成员|
|Base0数据成员|
pb2a->|Base0指针| //Base2对象
|Base2新增数据成员|
|Base0数据成员|
pb1b,pd->|Base0指针|//Derived对象
|Base1新增数据成员|
pb2b-> |Base0指针|
|Base2新增数据成员|
|Derived新增数据成员|
pb0b-> |Base0数据成员|
7.1 类的继承与派生
7.1.1
类的继承,是新的类从已有类得到已有的属性。
类的派生,是从已有类产生新类的过程。
原有的类:基类,产生的新类:派生类。
7.1.2 派生类的定义
多继承:一个派生类可以同时有多个基类。
单继承:一个派生类只有一个直接基类。
交通工具——汽车——卡车
间接基类 直接基类 派生类
类的继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。
7.1.3 派生类生成过程
1 吸收基类成员
2 改造基类成员。两个方面,一个是对基类成员的访问控制,另一个是对基类数据或函数成员的覆盖或隐藏。其中,隐藏是在派生类中声明一个和基类成员同名的成员。
3 添加新的成员
7.2 访问控制
基类成员的访问属性(public,protected,private)由继承方式控制。基类的自身成员可以对基类中任何一个其他成员进行访问,但基类的对象只能访问该类的公有成员。
7.2.1 公有继承
基类的公有成员和保护成员的访问属性在派生类中不变,而基类的私有成员不可直接访问。
7.2.2 私有继承
基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可直接访问。
7.2.3 保护继承
基类的公有成员和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可直接访问。
7.3 类型兼容规则
在需要基类对象的任何地方,都可以使用公有派生类的对象来代替,之后派生类的对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
替代所指的情况:
1 派生类的对象可以隐含转化为基类对象。
2 派生类的对象可以初始化基类的引用。
3 派生类的指针可以隐含转换为基类的指针。
7.4 派生类的构造和析构函数
7.4.1 构造函数
派生类构造函数的执行
(1)调用基类构造函数,他们被继承时声明的顺序。
(2) 对派生类新增的成员对象初始化,以他们在类中声明的顺序。
(3) 初始化新增的其他成员。
7.4.2 拷贝构造函数
给基类的拷贝构造函数传参时可利用类型兼容规则。
7.4.3 析构函数
析构函数执行与构造函数正好相反,是一个出栈的过程。
7.5 派生类成员的标识与访问
在派生类中,成员按访问属性可分为:
(1)不可访问成员
(2)保护成员
(3)私有成员
(4)公有成员
7.5.1 作用域分辨符
隐藏规则:若存在多个具有包含关系的作用域 ,则内层标识符隐藏外层同名标识符。
在类的派生层次结构中,基类的成员和派生新增的成员都具有类作用域且相互包含,基类在外层,派生类在内层。
只有在相同作用域中定义的函数才可以重载。
调用非静态成员函数总是针对特定的对象,执行函数调用时需将调用该函数的对象指针作为隐含参数传递给被调函数来初始化this指针。
7.5.2 虚基类
将共同基类设置为虚基类,这时从不同路径继承过来的同名数据成员在内存中只有一个副本,同一个函数名也只有一个映射。
虚基类的成员由最远派生类的构造函数通过调用虚基类的构造函数进行初始化,其中最远派生类是指建立对象时所指定的类。
构造一个类的对象的一般顺序:
(1)虚基类的构造函数
(2)基类的构造函数
(3)成员对象的构造函数
(4)本类构造函数
7.8
7.8.1 组合与继承
组合:整体——部分
继承:特殊——一般
7.8.2 派生类对象的内存布局
1 单继承
class Base {...};
class Derived:public Base {...};
Base *pba = new Base();
Derived *pd = new Derived();
Base *pbb = pd;
/*pba->|Base 数据成员| //Base对象
pbb,pd->|Base数据成员|//Derived对象
|Derived新增数据成员|*/
2 多继承
class Base1 {...};
class Base2 {...};
class Derived:public Base1,public Base2 {...};
Base1 *pb1a = new Base1();
Base2 *pb2a = new Base2();
Derived *pd = new Derived();
Base1 *pb1b = pd;
Base2 *pb2b = pd;
/*pb1a->|Base1数据成员| //Base1对象
pb2a->|Base2数据成员| //Base2对象
pb1b,pd->|Base1数据成员| //Derived对象
pb2b-> |Base2数据成员|
|Derived新增数据成员|*/
3 虚继承
class Base0 {...};
class Base1:virtual public Base0 {...};
class Base2:virtual public Base0 {...};
class Derived:public Base1,public Base2 {...};
Base *pb0a = new Base0();
Base1 *pb1a = new Base1();
Base2 *pb2a = new Base2();
Derived *pd = new Derived();
Base1 *pb1b = pd;
Base2 *pb2b = pd;
Base0 *pb0b = pb1b;
/*pb0a->|Base0数据成员|//Base0对象
pb1a->|Base0指针| //Base1对象
|Base1新增数据成员|
|Base0数据成员|
pb2a->|Base0指针| //Base2对象
|Base2新增数据成员|
|Base0数据成员|
pb1b,pd->|Base0指针|//Derived对象
|Base1新增数据成员|
pb2b-> |Base0指针|
|Base2新增数据成员|
|Derived新增数据成员|
pb0b-> |Base0数据成员|