Open bosthhe1 opened 1 year ago
struct common//默认类的成员变量和函数是共有的
{
common(int id = 10)
:_id(id)
{}
int _id;
};
class student : public common//这里的继承方式是public
{
public:
student(int stud=10, int id = 1013456)
:common(id)//初时化继承的变量,方法和调用匿名函数相似
,_stud(stud)
{}
private:
int _stud;
};
子类因为包含父类的所以信息,所以子类可以初始化父类,但是不属于父类的一部分会丢失,这种叫做切片,图片中id就缺失了,而且切片只能发生在子类到父类中,不能调换,切片同时发生在指针上,将父类类型的指针,指向子类的地址,也会发生切片
继承同时也分为很多种类,单继承和多继承,而多继承就涉及菱形继承 单继承的书写形形式
class B : public A//B类继承A类
{
public:
int _b;
};
多继承的书写方式
class C :public A, public B//中间需要逗号隔开,C中包含A,B类的成员
{
public:
int _c;
};
存在多继承,就必然存在菱形继承,这个D里面继承了B,C,然而B,C又继承了A,所以D中存放了两个A,这里就存在数据冗余,如果A中存在4G的数据,那么D中就存在8G的数据,此时D中就有两个A,虽然两个A名字一样,但是一个A是B的,一个A是C的,所以这两个A也不一样
struct A
{
int _a;
};
class B : public A
{
public:
int _b;
};
class C :public A
{
public:
int _c;
};
class D :public B, public C//D继承了B,C
{
public:
int _d;
};
所以为了解决菱形继承的问题,c++抛出了虚继承的概念
struct A
{
int _a;
};
class B : virtual public A//虚继承A
{
public:
int _b;
};
class C : virtual public A//虚继承A
{
public:
int _c;
};
class D :public B, public C
{
public:
int _d;
};
需要注意的是,在初始化列表中,初始化的顺序是声明的顺序,在继承中先被继承的先声明,所以如果初始化D中,会先初始化A,由于A中是计算的偏移量,大家看到的都是同一个A,所以编译器会优化,A的初始化交给D,所以初始化会先A,再初始化B,再初始化C,最后初始化D 虚继承的原理是将B,C的位置,与A位置的偏移量,保存在一个内存空间中,B,C需要访问A,就需要通过计算与A的偏移量,因为是计算偏移量,所以B,C访问的A为同一个A(这里存在虚基表,访问A时需要查询虚基表)。
一般的继承都需要写继承方式,如果没有写继承方式,那么默认就是私有继承
class B:A
//私有继承
补充,我们看到虚继承然后实现多态的情况下,在地址偏移量上面存在着一个地址很大的值,存的虚基表的偏移量,通过偏移量找到虚表指针,需要总结的是在菱形虚继承中,编译器都会将共有的部分提出来,以防止二义性,所以在菱形虚继承中将A提出来,然后存的偏移量,在菱形虚继承虚函数重写中,编译器会将A的虚表拿出来,然后计算虚表的偏移量
继承和组合的区别
继承是为了将共同信息提取出来,封装到一个类里面,就好像函数复用一样,比如将经常使用到交换,就将交换提取出来封装成swap函数,大家都可以调用,减少代码冗余,继承也和函数类似,但是继承会存在一些权限控制
一般的继承方式我们都是设置成public,很少使用protected和private 虽然父类的成员函数是私有的,不可见的,但是父类的私有成员也会继承给子类,占子类的空间,只不过子类无法访问