通过菱形继承剖析虚继承解决二义性和数据冗余问题

    什么是菱形继承?

    假设有一个类A,它有两个子类,分别为类B和类C,再有一个类D又继承了B类和C类;如图:


类似于这种,有子类对象包含多份父类对象的继承模型称为菱形继承。

上述菱形继承体系中,类D多重继承了类B和类C,因此,类D含有两份基类A的成员;此种继承方式会造成两个问题:二义性和数据冗余。

先看一段代码:

#include<iostream>
using namespace std;

class A   //基类A
{

public:
	int _a;
};

class B:public A   //子类B
{

public:
	int _b;
};

class C:public A   //子类C
{

public:
	int _c;
};

class D :public B, public C  //多重继承B和C的子类D
{
public:
	int _d;
};

void test()
{
	D d;
	d._a = 10;


}

int main()
{
	test();

	system("pause");
	return 0;
}
看一下编译结果:


可以看到:编译器无法确定_a是属于类B的还是类C的,这就是二义性问题。那如何解决这个问题?

可以通过添加域的访问限定符解决:

void test()
{
	D d;
	d.B::_a = 10;
	d.C::_a = 20;
	d._b = 30;
	d._c = 40;
	d._d = 50;
}

看一下是否赋值成功:


从监视窗口可看到,对类B和类C的_a都成功赋值。


但是还有另一个问题没有解决:数据的冗余。

因此,引入虚拟继承机制;虚继承可以同时解决菱形继承的二义性和数据冗余的问题。

我们需要在类B和类C继承基类A时加入virtual,这样保证了在子对象创建时,只保存了基类A的一份拷贝。

C++使用虚拟继承,解决了从不同路径继承来的相同基类的数据成员在内存中有不同的拷贝造成数据不一致的问题,将共同基类设置为虚基类,这时从不同路径继承的虚基类在内存就只有一个映射。

#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;
};

void test()
{
	D d;
	d._a = 10;
	d._a = 20;
	d._b = 30;
	d._c = 40;
	d._d = 50;
}

int main()
{
	test();

	system("pause");
	return 0;
}
看一下监视窗口:



可以发现_a被赋值为20,并没有10的出现,说明在类D的对象模型中只存在一个_a。

在探索菱形虚拟继承的实现原理前,我们分别看一下派生类D的大小和菱形虚拟继承派生类D的大小:


菱形继承派生类D的大小:



菱形虚拟继承派生类D的大小:



可以看到两者的区别:菱形虚拟继承比菱形继承多了四个字节的大小。

我们可以通过内存看一下菱形虚拟继承中派生类D的对象在内存中的情况:


通过内存可以看出基类A的成员变量_a在类D中只有一份,因此也已经解决了数据冗余的问题;但是,内存中蓝色框中的地址存的是什么呢?

我们称它为虚基表地址。可以看下这个地址存放的是什么:


总图:





下面我们可以看一下菱形继承的对象模型和菱形虚拟继承对象模型:

菱形继承对象模型:

菱形虚拟继承对象模型:

总结:

        虚拟继承虽然解决了菱形继承的产生的二义性和数据冗余问题,但是如同上述的例子为了解决一个int数据冗余,却开辟了两个存放虚基表的空间;而且访问虚基类数据时,要通过虚基表进行间接访问,效率就会比较低,带来了性能上的损耗,所以非必要的时候尽量避免菱形继承,尝试换一种设计模式(但是当数据冗余的程度很大时,使用虚拟继承会更)。



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
通过标识符读取数据服务是汽车UDS诊断协议中的一种功能,通过该功能可以从汽车的电子控制单元(ECU)中读取特定的数据。其中,标识符0x22表示读取数据服务。 该命令的结构是由一个请求帧和一个响应帧组成。请求帧中包含了要读取数据的标识符和数据记录编号等信息。响应帧中则包含了请求的数据。 在进行标识符读取数据服务时,首先需要确定要读取的数据的标识符。标识符是通过制定的标准或者车辆制造商定义的,用于唯一标识某个特定的数据。例如,可以通过标识符来读取车速、发动机转速等信息。 一旦确定了要读取的数据的标识符,就可以构建请求帧发送给对应的ECU。ECU接收到请求后,会根据标识符来查找对应的数据,并将其封装在响应帧中返回给诊断设备。 标识符读取数据服务的深度剖析需要了解不同的车辆制造商和标准对于标识符和其对应数据的定义。在实际使用中,需要根据特定的车辆型号和诊断设备的要求来选择合适的标识符。此外,不同的ECU可能支持不同的标识符和数据,因此在使用过程中需要根据实际情况进行选择。 总之,通过标识符读取数据服务是汽车UDS诊断中的一个重要功能,它能够帮助用户获取车辆的各种数据信息,提供诊断和故障排除的参考依据。在实际应用中,需要根据具体情况选择合适的标识符来读取所需的数据

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值