C++之继承(下)

目录

多继承

菱形继承 

菱形继承造成的问题如何解决?

继承与组合


多继承

一个子类只有一个直接父类,我们称这种继承方式为单继承。

图示如下。 

一个子类有多个直接父类,我们称这种继承方式为多继承。 

图示如下。

 D公有继承了类B和类C,因此有两个直接父类,所以D的继承方式为多继承。

菱形继承 

多继承会不会造成什么问题呢?我们使用代码来测试一下。

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._A = 10;

	return 0;
}

运行代码,结果如下。

 我们发现,编译器爆出了访问不明确的错误,这是因为什么呢?

我们来分析以下代码,我们定义了A类,B类和C类公有继承了A类,所以也就继承了A类中的_A成员,所以B类和C类中都有一份成员_A,当D类继承B类和C类的时候,同样的也会继承B类和C类中的_A成员,从而在类D中有两份共有的_A成员,所以我们在调用的时候就会有访问不明确的错误。

那么怎样去进行修改呢? 

很简单,如果我们要去访问从B类继承下来的_A,带上B类名加上类作用域限定符即可,访问类C的同理。

代码如下。

还有一种情况,就是比如我们在A类中定义了一个很大的数组,经过上述多继承图例中的继承方式,被子类继承时,最终在D类中肯定会有两个很大的数组,如果我们本身就只想要一个数组,那么这样继承下来就会造成数据冗余。

 本来我们只想要4万多字节,但是因为采用了当前的继承方式我们竟然产生了8万多字节。造成了巨大的资源浪费,产生了数据冗余现象。

所以可以得出结论,上述多继承方式造成了两个主要问题:

1.子类访问父类中的成员变量时,访问不明确。

2.产生了数据冗余。

其实通过上述的现象, 我们为大家讲述了一种神奇的继承方式------菱形继承,是多继承的一种,也是多继承造成的问题。那么如何解决这些问题呢,怎么样保证在最终的子类D只将类A当中的每个变量继承一次呢?

菱形继承造成的问题如何解决?

这就要引入一个概念,虚继承,就是在中间的父类,注意不是最终子类,而是中间父类(对应B和C),使用关键字virtual进行虚继承。代码如下。

我们发现最终A类中的成员只被继承了一次。 我们称类A为虚基类。那么加上virtual关键字之后,底层实现原理是什么呢?我们一起来探讨。

分别对d的每个成员进行赋值,并打开调试窗口中的内存,通过输入d对象的地址来进一步探讨。

 我们首先继承了B类,然后继承了C类,通过内存可以看到,确实是先存储了_B变量,然后再存储了_C变量,可是对应B变量和C变量中的第一行到底是什么呢?

其实它对应的是一个地址,依次输入地址,再次访问内存。

B对象所对应的地址。

 C对象所对应的地址。

两个地址中红框标注的是什么呢?

其实它表示的是一个偏移量,因为我们最终只继承了一份A类(虚基类)的成员变量,也就意味着我们从类B和类C中也之继承了一份,所以这一份是既属于类B也属于类C的,所以我们就要用一个偏移量去访问同属于类B和类C的成员,所以对于c对象而言,偏移量是20字节,对于c对象而言,偏移量是12字节,再次回到初始的对象d的内存分布,两个16进制位为一个字节,b对象偏移20,c对象偏移12字节,依次去找,我们发现刚好可以访问到共同的_A成员。

我们把带有偏移量的表称之为虚基表偏移量为对应的对象到公共成员的距离,通过这个距离可以访问到公共的对象。 

继承与组合

继承:继承是一种is a的关系。比如,基类是一个人类,子类是一个学生,学生是一个人,符合is a关系,所以这种情况下适合使用继承。

组合:组合是一种has a的关系。比如人和头发,人有头发,符合has a的关系,所以适合用组合。

如果完全符合is a推荐使用继承,如果完全符合has a推荐使用组合。既符合is a,又符合has a,推荐使用组合,这是为什么呢?

先看代码。

class A
{

protected:
	int _A;
};

class B:public A
{

private:
	int _B;
};

 这是继承。

class A
{

protected:
	int _A;
};

class B
{

private:
	int _B;
	A a;
};

这是组合。

我们设想一下,同样对于父类A中的protected成员,对于继承而言,在子类B public继承之后也变成了子类的protected成员,此时子类是可以在类中访问这些成员的。但是对于组合就不一样了,父类A只要是protected成员,对于类B而言,都是不能访问的,因为是在类外。所以可以这样理解,只要类A中的protected成员代码做了改动,因为子类B继承了这些成员,所以子类B中的这些protected成员代码也得改动,对于一个项目而言,改动的代码地方是很多的,非常麻烦。但是对于组合而言,因为是不可访问的,所以你protected成员如何改动,对与类B都是没有影响的,不用修改带代码。

综上也就得出了一个结论,继承的耦合度(关联程度)是很高的,组合的耦合度比较低。对于一个工程而言,肯定是模块之间的耦合度越低越好,因为一个模块出错了不影响另一个模块,所以我们推荐使用组合。

但是继承也是不可或缺的,因为多态的实现就是建立在继承的基础上实现的。

好了,以上便是本期的所有内容,继承的理解对于后续多态的学习尤为重要,所以一定要理解继承。

本期内容到结束^_^ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

棠~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值