溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

c++中class遇上union會(huì)怎么樣

發(fā)布時(shí)間:2022-01-05 17:06:23 來(lái)源:億速云 閱讀:101 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要介紹“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í)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI