继承
通过继承,我们可以用原有类型来定义一个新类型,定义的新类型既包含了原有类型的成员,也能自己添加新的成员,而不用将原有类的内容重新书写一遍。原有类型称为“基类”或“父类”,在它的基础上建立的类称为“派生类”或“子类”。
继承形式
class 派生类
: public/protected/private(继承的方式) 基类
{
};
派生类的生成过程包含三个步骤(重点)
- 吸收基类成员
- 改造基类成员
- 添加自己的新成员
继承的局限
不论以何种方式继承,以下基类的特征是不能继承下来的。
1. 构造函数
2. 析构函数
3. 用户重载的operator new和operator delete运算符
4. 用户重载的operator=运算符
5. 友元关系
派生方式对基类的访问权限
派生类继承了基类的全部成员变量和成员方法(除了构造和析构之外的成员方法),但是这些成员的访问属性,在派生过程中是可以调整的。派生(继承)方式有3种,分别是
1. public(公有)继承
2. protected(保护型)继承
3. private(私有)继承
总结
private成员是私有成员,只能被本类的成员函数所访问,派生类和类外都不能访问。
public成员是公有成员,在本类、派生类和外部都可访问。
protected成员是保护成员,只能在本类和派生类中访问,是一种区分血缘关系内外有别的成员。
- 不管以什么继承方式,派生类内部都不能访问基类的私有成员。
- 不管以什么继承方式,派生类内部除了基类的私有成员不可以访问外,其他的都可以访问。
- 不管以什么继承方式,派生类对象除了公有继承基类中的公有成员可以访问外,其他的一律不能访问
protected继承与private继承的区别
proctected继承可以无限的继承下去,但是private继承一次就终止。(千秋万代,断子绝孙)
派生类对象的构造
- 如果派生类有显式定义构造函数,而基类没有显示定义构造函数,则创建派生类的对象时,派生类相应的构造函数会被自动调用,此时都自动调用了基类缺省的无参构造函数
- 如果派生类没有显式定义构造函数而基类有显示定义构造函数,则基类必须拥有默认构造函数
- 如果派生类有构造函数,基类有默认构造函数,则创建派生类的对象时,基类的默认构造函数会自动调用,如果你想用基类的有参构造函数,必须要在派生类构造函数的初始化列表中显示调用基类的有参构造函数。
- 如果派生类和基类都有构造函数,但基类没有默认的无参构造函数,即基类的构造函数均带有参数,则派生类的每一个构造函数必须在其初始化列表中显示的去调用基类的某个带参的构造函数。如果派生类的初始化列表中没有显示调用则会出错,因为基类中没有默认的构造函数。
总结
必须将基类构造函数放在派生类构造函数的初试化列表中,以调用基类构造函数完成基类数据成员的初始化。派生类构造函数实现的功能,或者说调用顺序为:
1. 完成对象所占整块内存的开辟,由系统在调用构造函数时自动完成。
2. 调用基类的构造函数完成基类成员的初始化。
3. 若派生类中含对象成员、const成员或引用成员,则必须在初始化表中完成其初始化。
4. 派生类构造函数体执行。
派生类的销毁
当派生类对象被删除时,派生类的析构函数被执行。析构函数同样不能继承,因此,在执行派生类析构函数时,基类析构函数会被自动调用。执行顺序是先执行派生类的析构函数,再执行基类的析构函数,这和执行构造函数时的顺序正好相反。当考虑对象成员时,继承机制下析构函数的调用顺序:
1. 先调用派生类的析构函数
2. 再调用派生类中成员对象的析构函数
3. 最后调用普通基类的析构函数
多基继承(多基派生)
多基继承和单基继承的派生类构造函数完成的任务和执行顺序并没有本质不同,唯一一点区别在于:首先要执行所有基类的构造函数,再执行派生类构造函数中初始化表达式的其他内容和构造函数体。各基类构造函数的执行顺序与其在初始化表中的顺序无关,而是由定义派生类时指定的基类顺序决定的。析构函数的执行顺序同样是与构造函数的执行顺序相反。但在使用多基继承过程中,会产生两种二义性。
派生类继承基类的时候,如果是多继承,那么多个基类的构造函数的执行顺序只与多个基类被继承的先后顺序有关,与基类的构造函数在派生类的初始化列表中无关。
成员名冲突的二义性
一般来说,在派生类中对基类成员的访问应当具有唯一性,但在多基继承时,如果多个基类中存在同名成员的情况,造成编译器无从判断具体要访问的哪个基类中的成员,则称为对基类成员访问的二义性问题。
解决方案:
1. 添加命名空间+作用域。
2. virtual虚拟继承
派生类和基类的相互转化
基类—>派生类 √
1. 可以将派生类对象赋值给基类对象
2. 可以将基类的引用绑定到派生类的对象
3. 可以将基类的指针指向派生类对象
派生类—>基类 ×
1. 不可以将基类对象赋值给派生类对象
2. 不可以将派生类的引用绑定到基类的对象
3. 不可以将派生类的指针指向基类对象
向上转型:从派生类向基类进行转换,都是可以的。
向下转型:从基类向派生类进行转换,本来语法是不支持的,但是C++支持强转,所以又支持了。但是向下转型存在不安性。(有安全的,也有不安全的向下转型)
派生类对象间的赋值控制
1、如果派生类没有显示定义拷贝构造函数或者赋值运算符函数,但是基类定义类拷贝构造函数与赋值运算符函数,那么用一个已经存在的派生类对象初始化一个刚刚创建的派生类对象,或者用一个已经存在的派生类对象给另外一个已经存在的派生类对象进行赋值,那么两个派生类部分执行缺省的行为,而两个基类部分执行基类的拷贝构造函数或者赋值运算符函数。
2、如果派生类显示定义拷贝构造函数或者赋值运算符函数,基类也定义类拷贝构造函数与赋值运算符函数,那么用一个已经存在的派生类对象初始化一个刚刚创建的派生类对象,或者用一个已经存在的派生类对象给另外一个已经存在的派生类对象进行赋值,那么两个派生类部分执行派生类的拷贝构造函数或者派生类的赋值运算符函数,而两个基类部分就不会再自动执行基类的拷贝构造函数或者赋值运算符函数,除非在派生类的拷贝构造函数或者赋值运算符函数中显示执行基类的拷贝构造函数或者赋值运算符函数。
禁止复制(禁止使用拷贝构造函数和赋值运算符函数)
- 将拷贝构造函数和赋值运算符函数设置为私有.
- 将拷贝构造函数和赋值运算符函数删除.
- 派生类中不显示定义拷贝构造函数和赋值运算符函数.