一.继承的基本作用和概念
继承可以在不破坏原有类的基础上添加新的内容,继承后的类我们脚踏子类或者派生类,原有的类叫基类。继承后父类的成员函数+成员变量都会变成子类的一部分。本质上就是在构建新类的同时对基类进行复用。继承的时候不会继承友元关系,同时无论有多少个派生类静态变量只会存在一份。
继承格式:
class people
{
public:
int num;
}
class student: public people //继承
{
public:
char* name;
};
二.继承的多种组合方式
由于类成员有3种访问限定符限定,继承方式也有3种,这就组合出来9种不同的继承方式。
我们只需要记住,对于继承属性由访问限定符和继承方式中,限制最大的决定。比如说,基类声明为private,继承方式为public,那么这个继承属性就是不可见的,因为private限制大于public,再比如,基类内部声明为public继承方式为protect,那么继承下来就变成派生类的protect成员。
三.切片
派生类对象 可以赋值给 基类的对象 ,指针 ,基类。这种操作我们叫切片,但是只能派生类赋值给基类,不能调过来。 之所以支持这种操作是因为,继承的时候首先会构造基类内部的变量放在低地址处,然后在构造子类的变量放在继承下来的父类的下面高地址处。这样的话如果你用指针指向这个类,他指向的位置就相当于继承下来的基类的变量的位置。当你用父类指针指向子类的时候他也能找到继承下来的父类的变量,同时像切蛋糕一样把后面子类的变量切走。
四.隐藏
子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。
五.子类调用的顺序问题
继承的子类会优先构造父类的变量在构造自己本身的变量。析构的时候也是,先析构父类在析构自己。这里有一点是,编译器在处理析构函数的时候会统一处理成destructtor(),这样直接变成隐藏了,这是因为后面多态的需要。
六.单继承和多继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
多继承的存在就会导致一个问题,菱形继承的存在
七.菱形继承的问题
菱形继承本身是存在问题的。存在数据冗余的问题和二义性,此图可以看到_a变量存在两份,数据冗余,同时当你直接同过d类型调用_a的时候他不知道你要调用B里的还是C里的_a。
八.如何解决菱形继承的问题
我们可以使用虚拟继承,使用虚拟继承会让重复继承的变量只保留一份。虚拟继承可以很好的解决数据冗余和二义性的问题。不过一旦使用虚拟继承,内存布局就会发生大幅度变化。当使用虚拟继承,我们已上图为例,他会把虚拟继承的变量或函数指针放在D的最下面。然后B和C的原先存放_a的位置就会存放一个地址,这个地址存放的内容是偏移量。这个偏移量就是当前类首元素地址到虚拟继承的变量的距离。编译器可以通过先找到首元素地址在找到偏移量最后跳转到你想要的值。