继承进阶(多继承)

多继承概念:

一个子类有两个或两个以上的父类,我们把这种继承关系叫作多继承

注意:当子类进行多继承时,要在每一个父类前加上继承权限,如果不加class默认继承权限为private,struct默认继承权限为public。因而如果不加继承权限可能会出现我们非预期的结果。

多继承时父类在子类中的内存排布顺序

因此可得,子类的内存布局是按照继承的先后顺序来确定的

菱形继承

 菱形继承存在的问题:

1、二义性问题

即A类中的成员会在D类中存在两份,如果我们在类D中直接访问类A的成员,将会出现访问不明确

2、存在着数据冗余的情况

类A中的成员一般情况下都是很多的,如果D类中存在两份会造成严重的资源浪费。

菱形继承问题的解决

虚拟继承

class A {
public:
	int a1;
	int a2;
	int a3;
};
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;
};
void Test1() {
	D x;
	x.a1= 6;
	x.a2 = 7;
	x.a3 = 8;
	x.b = 4;
	x.c = 3;
	x.d = 2;
}
int main() {
	Test1();
	return 0;
}

 我们可以看一下x对象的大小

 如果不是菱形虚拟继承,而只是普通的菱形继承,那么对象x的大小应该为36。(类

A被存储了两份为24字节再加上类C类B类D的内的非继承成员12个字节共36字节)

原理:

 类B和类C是虚拟继承的类A,在内存上前4个字节表示为一个指针,指针指向一个名为虚基表 的地方,虚基表内放的是偏移量,当我们在对象X访问D继承A类的成员时,我们会先访问到B类或者C类的虚基表指针,然后得到偏移量后,找到A类的成员,由于B类和C类的虚基表指针通过虚基表内的偏移量找到的是同一个A类成员,因而不存在二义性问题。

继承总结

1.public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
2.组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象
3.优先使用对象组合,而不是类继承
4.继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。
5.继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。
6.对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。 组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
7.实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。
 

猜你喜欢

转载自blog.csdn.net/m0_56910081/article/details/124662938