您好,登錄后才能下訂單哦!
這篇文章主要介紹“c++中class遇上union會(huì)怎么樣”,在日常操作中,相信很多人在c++中class遇上union會(huì)怎么樣問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”c++中class遇上union會(huì)怎么樣”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
由于面向?qū)ο蟮拇嬖冢?在代碼中常常有這樣一種用于存儲(chǔ)屬性的類,類A,類B, 類C,類B繼承自類A,類C繼承自類B。 而類A, 類B, 類C等這些類的實(shí)例都是從socket層傳過(guò)來(lái)的。
作者在設(shè)計(jì)時(shí)為了代碼的復(fù)用性, 采用了如下設(shè)計(jì):
union object {
class A a;
class B b;
class C c;
};
//read_objectX_from_socket函數(shù)為偽碼, 其實(shí)現(xiàn)為逐個(gè)讀出某個(gè)類的成員, 至于這個(gè)函數(shù)為什么會(huì)是這樣實(shí)現(xiàn), 這是socket層上的另一個(gè)設(shè)計(jì)問(wèn)題了, 暫且不談
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);
}
從上面看出作者對(duì)于C++中的成員變量的內(nèi)存布局相當(dāng)有信心, 才會(huì)想到使用union的方式來(lái)復(fù)用代碼。
假設(shè)這三個(gè)類的成員定義如下:class A {int a;}, class B : public A {int b;}, class C : public B {int c;}.
那么此union中的內(nèi)存布局其實(shí)就是A::a, B::b, C::c。
object::a所占的空間就是A::a在union中所占的內(nèi)存
object::b所占的空間就是A::a和B::b在union中所占的內(nèi)存
object::c所占的空間就是A::a和B::b和B::c在union中所占的內(nèi)存
設(shè)計(jì)者巧妙的利用了union的重疊特性和class的繼承特性來(lái)完成了代碼復(fù)用。
大約在上學(xué)的時(shí)候我也喜歡去hack內(nèi)存布局(當(dāng)然沒(méi)有這種用法這么巧妙), 后來(lái)我便漸漸不大喜歡這種做法了.
因?yàn)檫@種代碼雖然寫起來(lái)有種炫技的自豪感, 但事實(shí)上一旦出了bug是極難發(fā)現(xiàn)的, 人類在匯編語(yǔ)言基礎(chǔ)之上又發(fā)明了高級(jí)語(yǔ)言, 我想也正是因?yàn)樗麄冇X(jué)得人們需要更多的規(guī)則來(lái)幫人們減少出錯(cuò)的可能性, 所以我后來(lái)便一直主張寫出更多可以讓編譯器檢查出錯(cuò)誤的代碼。
同事踩的坑也正驗(yàn)證了hack內(nèi)存布局易錯(cuò)不易查的事實(shí)。 由于某種偷懶原因, 他實(shí)現(xiàn)了class D : public B {int d;}, class F : public C, public D { int f;}。而readF的實(shí)現(xiàn)代碼如下:
void readF(union object &o)
{
readC(o);
readD(o);
read_objectF_from_socket(o.c);
}
在實(shí)現(xiàn)readD時(shí)代碼看起來(lái)依然正常運(yùn)行, 但是在實(shí)現(xiàn)readF時(shí), 明明看到有數(shù)據(jù)讀入,但是類F中繼承自C和D的成員總是莫名其妙亂掉(當(dāng)然這不是我發(fā)現(xiàn)的, 這個(gè)bug只是我事后知道的罷了)。
此時(shí)重新看一下union, 那么C::c和D::d占用的是同一片內(nèi)存, 那么其實(shí)在readF中調(diào)用readC和readD時(shí), 覆蓋的總是同一塊內(nèi)存。
再看class F的內(nèi)存布局應(yīng)該是A::a, b::b, C::c, D::d, f, 也就是說(shuō)整個(gè)readF執(zhí)行下來(lái), 其實(shí)F::D::d這個(gè)變量從來(lái)就沒(méi)被操作過(guò), 也就不可能賦值, 由于棧中的隨機(jī)數(shù), 所以F::D::d這個(gè)變量也就變得隨機(jī)了, 由于object::C::c所占的內(nèi)存總是會(huì)被readC和readD同時(shí)操作。因此看上去數(shù)據(jù)也不那么的有跡可尋。
當(dāng)?shù)谝谎劭吹竭@種設(shè)計(jì)時(shí), 雖然覺(jué)得不妥, 但是我并沒(méi)有找到一種可以不通過(guò)hack內(nèi)存來(lái)達(dá)到最大代碼復(fù)用的方式。
在下班回來(lái)的路上, 我總覺(jué)得應(yīng)該可以不通過(guò)hack內(nèi)存來(lái)達(dá)到同樣的目的, 終于在快到住的地方時(shí)被我想到的了。
其實(shí)很簡(jiǎn)單, 之所以想不到一方面是我不常用C++, 另一方面大概是之前看了這段代碼, 一時(shí)間先入為主, 思維沒(méi)有緩過(guò)勁來(lái)。
其實(shí)只需要按照C++最常規(guī)的dynamic_cast就可以完成代碼的最大復(fù)用。
代碼接口大概實(shí)現(xiàn)如下:
void readA(class A *a)
{
//read a members
}
....
void readD(class F *a)
{
readB(a);
//read D members
}
由于是dynamic_cast, 因此編譯器可以幫我們自動(dòng)去計(jì)算每個(gè)成員的偏移量, 避免了手動(dòng)hack可能出現(xiàn)的各種錯(cuò)誤。
btw,使用dynamic_cast的方式僅僅能依靠編譯器發(fā)現(xiàn)這種hack內(nèi)存時(shí)容易出現(xiàn)的bug。 在碰到類似class F這種使用多重繼承機(jī)制的類時(shí),編譯器僅僅會(huì)報(bào)語(yǔ)法錯(cuò)誤,并不能對(duì)其父類的readX函數(shù)進(jìn)行復(fù)用。
到此,關(guān)于“c++中class遇上union會(huì)怎么樣”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。