重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
这篇文章主要介绍“c++中class遇上union会怎么样”,在日常操作中,相信很多人在c++中class遇上union会怎么样问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”c++中class遇上union会怎么样”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
成都创新互联公司-专业网站定制、快速模板网站建设、高性价比邱县网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式邱县网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖邱县地区。费用合理售后完善,十余年实体公司更值得信赖。
由于面向对象的存在, 在代码中常常有这样一种用于存储属性的类,类A,类B, 类C,类B继承自类A,类C继承自类B。 而类A, 类B, 类C等这些类的实例都是从socket层传过来的。
作者在设计时为了代码的复用性, 采用了如下设计:
union object {
class A a;
class B b;
class C c;
};
//read_objectX_from_socket函数为伪码, 其实现为逐个读出某个类的成员, 至于这个函数为什么会是这样实现, 这是socket层上的另一个设计问题了, 暂且不谈
void readA(union object &o)
{
read_objectA_from_socket(o.a);
}
void readB(union object &o)
{
readA(o);
read_objectB_from_socket(o.b);
}
void readC(union object &o)
{
readB(o);
read_objectC_from_socket(o.c);
}
从上面看出作者对于C++中的成员变量的内存布局相当有信心, 才会想到使用union的方式来复用代码。
假设这三个类的成员定义如下:class A {int a;}, class B : public A {int b;}, class C : public B {int c;}.
那么此union中的内存布局其实就是A::a, B::b, C::c。
object::a所占的空间就是A::a在union中所占的内存
object::b所占的空间就是A::a和B::b在union中所占的内存
object::c所占的空间就是A::a和B::b和B::c在union中所占的内存
设计者巧妙的利用了union的重叠特性和class的继承特性来完成了代码复用。
大约在上学的时候我也喜欢去hack内存布局(当然没有这种用法这么巧妙), 后来我便渐渐不大喜欢这种做法了.
因为这种代码虽然写起来有种炫技的自豪感, 但事实上一旦出了bug是极难发现的, 人类在汇编语言基础之上又发明了高级语言, 我想也正是因为他们觉得人们需要更多的规则来帮人们减少出错的可能性, 所以我后来便一直主张写出更多可以让编译器检查出错误的代码。
同事踩的坑也正验证了hack内存布局易错不易查的事实。 由于某种偷懒原因, 他实现了class D : public B {int d;}, class F : public C, public D { int f;}。而readF的实现代码如下:
void readF(union object &o)
{
readC(o);
readD(o);
read_objectF_from_socket(o.c);
}
在实现readD时代码看起来依然正常运行, 但是在实现readF时, 明明看到有数据读入,但是类F中继承自C和D的成员总是莫名其妙乱掉(当然这不是我发现的, 这个bug只是我事后知道的罢了)。
此时重新看一下union, 那么C::c和D::d占用的是同一片内存, 那么其实在readF中调用readC和readD时, 覆盖的总是同一块内存。
再看class F的内存布局应该是A::a, b::b, C::c, D::d, f, 也就是说整个readF执行下来, 其实F::D::d这个变量从来就没被操作过, 也就不可能赋值, 由于栈中的随机数, 所以F::D::d这个变量也就变得随机了, 由于object::C::c所占的内存总是会被readC和readD同时操作。因此看上去数据也不那么的有迹可寻。
当第一眼看到这种设计时, 虽然觉得不妥, 但是我并没有找到一种可以不通过hack内存来达到最大代码复用的方式。
在下班回来的路上, 我总觉得应该可以不通过hack内存来达到同样的目的, 终于在快到住的地方时被我想到的了。
其实很简单, 之所以想不到一方面是我不常用C++, 另一方面大概是之前看了这段代码, 一时间先入为主, 思维没有缓过劲来。
其实只需要按照C++最常规的dynamic_cast就可以完成代码的最大复用。
代码接口大概实现如下:
void readA(class A *a)
{
//read a members
}
....
void readD(class F *a)
{
readB(a);
//read D members
}
由于是dynamic_cast, 因此编译器可以帮我们自动去计算每个成员的偏移量, 避免了手动hack可能出现的各种错误。
btw,使用dynamic_cast的方式仅仅能依靠编译器发现这种hack内存时容易出现的bug。 在碰到类似class F这种使用多重继承机制的类时,编译器仅仅会报语法错误,并不能对其父类的readX函数进行复用。
到此,关于“c++中class遇上union会怎么样”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注创新互联网站,小编会继续努力为大家带来更多实用的文章!