一、继承存在的意义:
所谓继承就是从先辈处得到属性和特征。类的继承就是新类从已经有类得到其已有的特性,新类称之为派生类,已有类被称之为基类。
继承机制是面向对象程序设计使代码可以复用的重要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。
简单的继承关系例:
体会:在原有的基础上进行扩展、增加功能。
二、继承定义的格式:
class 派生类(子类):继承类型 基类(父类)
继承类型:
1.public继承是一个接口继承,保持 is-a 原则,每个父类可用的成员子类也可以使用,因为每个子类对象也是一个父类对象。
2.protect/private 继承是一个实现继承,基类的部分成员并非完全成为子类的一部分,是has-a的关系原则,所以非特殊情况下不会使用这两种继承关系,在绝大多数的场景下使用的都是公有继承。
三、派生类的默认成员函数调用顺序:
继承关系中构造函数的调用顺序:
基类构造函数 -> 派生类中对象构造函数 -> 派生类构造函数
基类关系中析构函数的调用顺序:
派生类构造函数 -> 派生类中对象构造函数 -> 基类构造函数
四、赋值兼容规则(public继承):
1.子类对象可以给父类对象赋值,反之则不行;
2.父类的指针/引用可以指向子类对象,而子类的指针/引用不能直接指向父类对象(可以通过强制类型转换完成)。
五、继承体系的作用域:
1.继承体系中,子类的作用域和父类的作用域属于两个作用域(在子类中不能访问父类的私有成员足以说明)
2.同名隐藏,如果子类中包含了和父类相同名字的成员,则子类成员将屏蔽对父类成员的直接访问,如果想在子类中直接访问父类的同名成员,就必须是用访问限定符。
六、菱形继承(即钻石继承)
多继承:一个子类有两个或两个以上父类时的继承关系。
菱形继承:
#include<iostream>
using namespace std;
class B
{
public:
int _b;
};
class C1 : public B
{
public:
int _c1;
};
class C2 : public B
{
public:
int _c2;
};
class D:public C1,public C2
{
public:
int _d;
};
int main()
{
D num;
num._b = 1;
system("pause");
return 0;
}
编译报错:
继承结构中C1 C2都继承自基类B,D类多重继承了C1类和C2类,因此D类会有两份B类成员(数据和方法),D类对象 num会包含B基类的两个子对象,将会导致编译错误。因为编译器并不知道是调用C1类的成员变量_b,还是调用C2类的成员变量_b,即调用方法是不明确的,这就是菱形存在的二义性和数据冗余问题。
如何解决?
使用虚继承,即在继承体系中,将其成员变量的数据实例共享给也从这个基类直接或间接派生的其他类。使用时用 virtual 声明继承关系即可。
#include<iostream>
using namespace std;
class B
{
public:
int _b;
};
class C1 :virtual public B
{
public:
int _c1;
};
class C2 :virtual public B
{
public:
int _c2;
};
class D:public C1,public C2
{
public:
int _d;
};
int main()
{
D num;
num._b = 1;
system("pause");
return 0;
}
这样我们就可以顺利通过编译,解决了二义性问题。
虚继承原理