所谓继承,就是从先辈得到的属性和行为特征。类的继承就是新的类从已有类那里得到已有的特征。从另一个角度来看问题,从已有类产生新类的过程就是类的派生。类的继承和派生机制使程序员无须修改已有的类,只需在已有类的基础上,通过增加少量代码或者修改少量代码的方法得到新类,从而较好地解决代码”复用”的问题。
简单来说就是父类子类之间建立联系,享有公共部分,实现各自本质不同的东西
-赋值兼容规则–public继承
1. 子类对象可以赋值给父类对象(切割/切片)
2.父类对象不可以赋值给子类对象
(父类中有的子类都有当子类赋值给父类对象是父类只会接收子类继承它的部分其它部分没有权限访问这就是(切片) 子类中有父类中没有的所以父类对象不能赋值给子类对象)
3. 父类的指针/引用可以指向子类对象(但只能访问到子类继承它的成员,其它成员不能访问)
4. 子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)
(子类的内存空间比父类的内存空间要大 如果子类的指针强行访问父类的话会造成内存越界)
继承又分为:单继承丶多继承和菱形继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承。
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承.
菱形继承:多个类继承了同一个公共基类,而这些派生类又同时被一个类继承。
然而菱形继承也存在一些问题:
class A
{
public:
int _a;
};
class B : public A
{
public:
int _b;
};
class C: public A
{
public:
int _c;
};
class D: public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 0;
d.C::_a = 1;
d._b = 2;
d._c = 3;
d._d = 4;
cout<<sizeof(B)<<endl;
B b;
b = d;
system("pause");
return 0;
}
}
由图可见:菱形继承存在二义性和数据冗余问题 class A在类中有两个,这可能不是我们想要的结果,增加调用的困难,同时也会浪费内存资源
为了解决上述问题我们可以采用虚继承的方式
#include<iostream>
using namespace std;
class A
{
public:
int _a;
};
class B :virtual public A
{
public:
int _b;
};
class C :virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 0;
d._b = 1;
d.C::_a = 2;
d._c = 3;
d._d = 4;
cout << sizeof(D) << endl;
system("pause");
return 0;
}
通过内存我们发现此时_a的对象只有一个就是C,这样二义性就解决了,同时我们发现派生类B中的_a消失,这样就解决了数据的冗余的问题。
第一个偏移20个字节刚好到基类AA,第二个偏移12个字节刚好到基类AA.
但调试后最终结果为24 这是因为多出来的字节要用于存放偏移地址。这也是虚继承未解决问题所付出的代价。
总结:
1. 虚继承解决了在菱形继承体系里面子类对象包含多份父类对象的数据冗余&浪费空间的问题。
2. 虚继承体系看起来好复杂,在实际应用我们通常不会定义如此复杂的继承体系。一般不到万不得已都不要定义菱形结构的虚继承体
系结构,因为使用虚继承解决数据冗余问题也带来了性能上的损耗。