一、继承和派生
1.继承的概念
- 继承是一种创建新类的方式,新建的类可以继承一个或多个类。可以理解子承父业
- 所以继承描述的是类和类之间的关系
- 新建的类被称之为派生类(子)类,之前就存在的类被称之为基(父)类
2.继承和派生
- 继承和派生是同一个过程,从不同的角度来看的
- 一个新类从已经存在的类哪里获取其已有的特性,这个叫继承(儿子继承父亲的财产)
- 从已存在的类产生一个子类,这个叫派生(父亲把财产给儿子)
3.继承分类
- 新建的类继承一个类,这个是单继承
- 新建的类继承多个类,这个叫多继承
二、继承方式
1.如何用一个类去继承另一个类
示例:
class A
{};
class B:public A //单继承
{};
class D
{};
class E
{};
class F:public D,public E//多继承
{};
中文表示: class 派生类名:继承方式1 基类名1,继承方式2 基类名2,......
{类成员声明;};
//这里的继承方式就是类中的三种访问属性,每种访问属性的继承方式都是有不同点的
//1、基类私有成员,不管用什么方式继承,都不能访问
//2、其他的成员访问属性和继承方式,两者看谁更严格就按严格的来
// public < protected < private
三、继承之后
1.派生类的构成
- 派生类会继承除基类构造析构函数之外所有成员变量和成员函数
- 可以在派生类中添加新的成员,通过派生类的对象调用
- 如果派生列中添加的成员名和基类成员名相同,那么派生类会隐藏基类的成员,可以通过 <基类名::基类成员名> 来访问,如果是继承的多个基类,而多个基类中也有同名的,也是通过此种方法调用同名的成员
2.派生类和基类的关系
- 派生类是基类对象,但是基类对象不是派生类对象。
也就是所,派生类的对象,可以当作基类对象来使用 - 派生类 派生对象名;
基类 基类对象名‘
基类对象名=派生对象名;//允许
派生对象名=基类对象名;//错误
派生列继承了基类的所有成员,所以派生对象可以个基类赋值,因为基类中有的数据,派生类也有,但是反过来不行,派生类有的数据,基类不一定有,因为,派生类可以添加新的数据
3.派生类的构造析构顺序
- 派生类对象在实例化的时候会调用基类的构造函数的,先基类后派生类,释放就是先父亲后儿子,因为栈结构,先进后出
- 如果是多继承与单继承构造讯息一致,区别在于,在构造基类时有多个基类,妈妈会按照基类在初始化列表的声明顺序依次调用基类的构造函数。
- 所以在写继承时,要确保基类有可以调用的构造函数
class A
{
public:
}
class B:public A
{
public:
}
//这样如果实例化B类对象,那么会调用A的默认构造,A有自己写默认构造,那么就调用自己写的
class A
{
public:
A(int a)
{
}
}
class B:public A
{
public:
}
//这样实例化B类对象,那么就会报错,会提示A类没有合适的构造函数调用,所以需要初始化列表调用父类的单参构造
//带参构造
B():A(1)
{
//需要这样写上初始化列表调用带参构造
}
四、菱形继承
结构如上图,类B,C分别继承A,类D继承类B,C,这样继承会导致类D继承两份类A的成员,在类D对象,想要访问类A的成员的时候,会导致访问不明确。因为类B,C各自继承类A的成员
1.解决方法
- 通过类名::成员名来确定调用那个成员
- 通过虚继承:在继承方式的前面加上关键子virtual,虚继承之后会使在虚继承类中多个指针,但是在最后的D类中,不会再继承两份A的成员,只继承一份。
class A
{
int a = 1;
};
class B :public A
{
int c = 2;
};
class C :public A
{
int d = 3;
};
class D :public B, public C
{
int e = 4;
};
//他们的大小分别为 A:4 B:8 C:8 D:20
虚继承后
class A
{
int a = 1;
};
class B :virtual public A
{
int c = 2;
};
class C :virtual public A
{
int d = 3;
};
class D :public B, public C
{
int e = 4;
};
他们大小分别是A:4 B:12 C:12 D:24