1. 继承的基本概念
1.1 继承的定义
从既有类产生新类的一个过程
class 子类(派生类)
: public/protected/private 父类(基类) // 类派生列表
{
// 数据成员
// 成员函数
};
派生类产生的过程
1. 吸收基类的成员
2. 改造基类的成员
3. 添加自己新的成员
1.2 继承的局限
不能从基类继承下来的:
- 构造函数
- 析构函数
- 友元关系
- 基类的operator new/delete/=运算符
1.3 派生方式对基类成员的访问权限
- 不管以什么继承方式,基类中的私有成员都不能在派生类中被访问
- 不管以什么继承方式,基类中的除了私有成员不能在派生类中被访问外,其他的都可以被访问
- 不管以什么继承方式,派生类对象只能访问公有继承基类中的公有成员,其他的都不能被派生类对象访问
protected继承和private继承的区别
1. protected的继承可以无限继承下去,但是private继承只有一代
2. 如果不写继承方式,默认是私有继承
2. 单继承下派生类对象的创建与销毁
2.1 派生类对象的构造
当创建派生类对象时,为了完成从基类吸收过来的数据成员的初始化,不管基类与派生类有没有显式写出构造函数,肯定默认会调用无参的,有时又会没有无参的构造函数,所以在派生类的构造函数的初始化列表中,最好显式的写出基类构造函数
派生类构造函数的功能(执行顺序):
1. 完成对象所占整块内存的开辟,由系统在调用构造函数时自动完成
2. 调用基类的构造函数完成基类数据成员的初始化
3. 若派生类中函数const成员、子对象成员或引用成员,则必须在初始化列表中完成初始化
4. 派生类构造函数体执行
2.2 派生类对象的销毁
派生类对象销毁的时候,会执行派生类的析构函数,然后 基类析构函数会自动调用
析构函数执行顺序:
1. 先调用派生类的析构函数
2. 再调用派生类中子对象成员的析构函数
3. 最后调用基类的析构函数
3. 多继承
3.1 多继承基础
- 对于多继承而言,每个基类前面都要加上继承方式,否则就是默认继承方式(私有继承)
- 多继承下,基类构造函数的执行顺序与其在派生类构造函数初始化列表中没有关系,只与基类被继承的顺序有关
class D
: public A
, public C
, public B
{
};
3.2 多继承问题
1. 成员函数访问冲突
解决方式:使用类名 + 作用域限定符
D d;
d.A::print();
d.B::print();
d.C::print();
2. 数据成员的存储二义性
解决方式:虚拟继承
class A // 8B
{
private:
long _val;
}
// 虚基指针
class B // 16B
: virtual public A
{
};
class C // 16B
: virtual public A
{
};
class D // 24B
: public B
, public C
{
}
4. 单继承下基类与派生类之间的转化
类型适应:派生类对象可以适用于基类对象。一个类可以适用于另外一个类的所有场景
// 1. 可以将派生类对象赋值给基类对象
base = derived;
/* Base &operaotr=(const Base &rhs)
base.operator=(derived);
const Base &rhs = derived; */
// 2. 基类的引用可以绑定到派生类对象
Base &ref = derived;
// 3. 基类的指针可以指向派生类对象
Base *pbase = &derived;
// 反过来都不行
5. 派生类对象间的复制控制
总结:在派生类的拷贝构造函数和赋值运算符函数中要显式调用基类的拷贝和赋值函数