小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

潘凱:C 對(duì)象布局及多態(tài)實(shí)現(xiàn)的探索(四)...

 SpringEmpire 2007-09-23

類(lèi)型動(dòng)態(tài)轉(zhuǎn)換和類(lèi)型強(qiáng)制轉(zhuǎn)換

  為了驗(yàn)證前面提到過(guò)的類(lèi)型動(dòng)態(tài)轉(zhuǎn)換(即dynamic_cast轉(zhuǎn)換),以及對(duì)象類(lèi)型的強(qiáng)制轉(zhuǎn)換。我們利用前面定義的C041、C042及C082類(lèi)來(lái)進(jìn)行驗(yàn)證。
  運(yùn)行下列代碼:
c082.C041::c_ = 0x05;
PRINT_VTABLE_ITEM(c041, 0, 0)
PRINT_DETAIL(C041, ((C041)c082))
PRINT_VTABLE_ITEM(((C041)c082), 0, 0)
PRINT_VTABLE_ITEM(c082, 5, 0)
C042 * pt = dynamic_cast<C042*>(&c082);
PRINT_VTABLE_ITEM(*pt, 0, 0)
  第2行和第5行是為了對(duì)照方便而打印原對(duì)象中的信息。第3、4行把C082對(duì)象類(lèi)型進(jìn)行強(qiáng)制轉(zhuǎn)換并分別打印轉(zhuǎn)換后的對(duì)象內(nèi)存信息及虛表信息。第6行我們用dynamic_cast進(jìn)行了一次動(dòng)態(tài)類(lèi)型轉(zhuǎn)換,從子類(lèi)的指針轉(zhuǎn)型為右父類(lèi)的指針,再把指針指向的對(duì)象的信息打印出來(lái)。
  結(jié)果為:
c041   : objadr:0012FA74 vpadr:0012FA74 vtadr:0045B364 vtival(0):0041DF1E
The detail of C041 is 64 b3 45 00 05
((C041)c082) : objadr:0012F2A3 vpadr:0012F2A3 vtadr:0045B364 vtival(0):0041DF1E
c082   : objadr:0012FA50 vpadr:0012FA55 vtadr:0045B36C vtival(0):0041D483
*pt    : objadr:0012FA55 vpadr:0012FA55 vtadr:0045B36C vtival(0):0041D483
  首先我們比較最后兩行,從objadr列我們可以知道pt指向的并不是c082對(duì)象的起始地址,而是指向了c082的第2個(gè)虛表指針的所在地址(因?yàn)樽詈笠恍械膐bjadr值等于倒數(shù)第2行的vpadr的值)。倒數(shù)第二行的vpadr值是c082對(duì)象的第2個(gè)虛表指針(我們?cè)谳敵鰰r(shí)指定了偏移值5)。而這個(gè)地址正是c082對(duì)象中屬于從C042類(lèi)繼承而來(lái)的部分,即在進(jìn)行動(dòng)態(tài)類(lèi)型轉(zhuǎn)換時(shí),除了改變類(lèi)型信息,編譯器還調(diào)整了指針的位置,以確保轉(zhuǎn)換語(yǔ)義的正確性。所以我們可以知道,對(duì)指向有復(fù)雜繼承結(jié)構(gòu)的類(lèi)對(duì)象的指針進(jìn)行類(lèi)型轉(zhuǎn)換(一般在繼承樹(shù)中向上或向下轉(zhuǎn)換)時(shí),必須使用dynamic_cast,它會(huì)正確的處理指針位置的調(diào)整,如果轉(zhuǎn)換是非法的,它會(huì)返回一個(gè)NULL指針。使用dynamic_cast時(shí)記得要做這個(gè)檢查,文中為了簡(jiǎn)略把這些檢查省去了。這種檢查可以通過(guò)宏來(lái)定義,以便于在release版中去掉,提高效率。
  再將((C041)c082)和c082兩行的輸出進(jìn)行對(duì)照,可以發(fā)現(xiàn)對(duì)對(duì)象進(jìn)行向上的類(lèi)型強(qiáng)制轉(zhuǎn)換實(shí)際上編譯器生成了一個(gè)新的臨時(shí)對(duì)象,因?yàn)樗鼈兊膐bjadr列不一樣了,這表明它們已經(jīng)不是同一個(gè)對(duì)象。再觀察c041、((C041)c082)及c082三行的vtadr和vtival(0),前兩行相比是一樣的,而后兩行相比就不一樣了。這也說(shuō)明編譯器在處理強(qiáng)制轉(zhuǎn)換時(shí),實(shí)際上是new了一個(gè)新的C041對(duì)象出來(lái)。因?yàn)閷?duì)象的強(qiáng)制類(lèi)型轉(zhuǎn)換不象指針的動(dòng)態(tài)類(lèi)型轉(zhuǎn)換,指針的動(dòng)態(tài)類(lèi)型轉(zhuǎn)換同時(shí)要確保多態(tài)的語(yǔ)義,所以只需要調(diào)整指針位置。而對(duì)象強(qiáng)制類(lèi)型轉(zhuǎn)換,還要調(diào)整虛表中的條目值,因?yàn)閷?duì)象類(lèi)型轉(zhuǎn)換不需要多態(tài)的行為。c082類(lèi)的第一個(gè)虛表的第一個(gè)條目中存放的是C082::foo()函數(shù)的地址,做了對(duì)象類(lèi)型轉(zhuǎn)換后,應(yīng)該調(diào)整為C041::foo()才對(duì),做這種調(diào)整過(guò)于復(fù)雜,所以編譯器干脆new了一個(gè)新的C041的臨時(shí)對(duì)象出來(lái)。對(duì)比這三行的最后二列即知。我不知道這是否是C++標(biāo)準(zhǔn)規(guī)范中定義的行為,改天查到我再更新上來(lái)。
  在new出新對(duì)象的同時(shí),編譯器還將原對(duì)象中屬于父類(lèi)部分的數(shù)據(jù)成員的值拷貝了過(guò)來(lái)。注意代碼的第1行,c082.C041::c_ = 0x05;,我們先把c082對(duì)象中從C041類(lèi)繼承過(guò)來(lái)的數(shù)據(jù)成員的值改寫(xiě)為0x05,原來(lái)是的值是0x01,由C041的構(gòu)造函數(shù)初始化。我們觀察輸出的第2行,上面說(shuō)了這個(gè)被打印的對(duì)象并非c082而是編譯器new出的來(lái)的臨時(shí)對(duì)象,可以注意到對(duì)象的最后一字節(jié)為0x05,即數(shù)據(jù)成員的值。所以我們知道編譯器除了new出新的臨時(shí)對(duì)象外,還把原對(duì)象中相應(yīng)的數(shù)據(jù)成員的值也復(fù)制了過(guò)來(lái)。
  這和我以前的認(rèn)識(shí)有點(diǎn)偏差,直觀上我一直以為這種轉(zhuǎn)換不會(huì)產(chǎn)生新的對(duì)象,不過(guò)仔細(xì)想想編譯器的這種作法也是對(duì)的,如果不產(chǎn)生新的對(duì)象,就意味著它要象前述的那樣動(dòng)態(tài)的改變虛表中條目的值。但new出臨時(shí)對(duì)象,也意味著使用下列的語(yǔ)句調(diào)用,可能產(chǎn)生意想不到的結(jié)果。
((C041)c082).somefun();
  如果somefun函數(shù)會(huì)改變對(duì)象的狀態(tài),那么上邊的代碼執(zhí)行后,c082的狀態(tài)并不會(huì)被改變。因?yàn)閟omefun實(shí)際改變的是臨時(shí)對(duì)象,在執(zhí)行完后該臨時(shí)對(duì)象就扔掉了。這和直觀的認(rèn)識(shí)有所差異,一般會(huì)認(rèn)為這個(gè)調(diào)用會(huì)作用于c082對(duì)象上。為了驗(yàn)證我們聲明以下兩個(gè)類(lèi)。
struct C010
{
    C010() : c_(0x01) {}
    void foo() { c_ = 0x02; }
    char c_;
};
struct C013 : public C010
{
    C013() : c1_(0x01) {}
    void foo() { c1_ = 0x02; }
    char c1_;
};
  兩個(gè)類(lèi)為繼承關(guān)系,各有一個(gè)同名的普通成員函數(shù),該函數(shù)改寫(xiě)類(lèi)的相應(yīng)成員變量。我們做以下的調(diào)用:
C013 obj;
obj.foo();
((C010)obj).foo();
  第1個(gè)foo調(diào)用,改變的是c1_值,最后一行的調(diào)用改變的是c_的值。直觀上容易認(rèn)為上述代碼執(zhí)行后obj.c_和obj.c1_的值均為0x02。但我們?cè)谡{(diào)試環(huán)境的局部變量窗口中把obj對(duì)象展開(kāi)可以發(fā)現(xiàn)obj.c1_為0x02,但obj.c_為0x01。原因就是前面所說(shuō)的((C010)obj)實(shí)際產(chǎn)生了一個(gè)臨時(shí)對(duì)象,所以最后一行的調(diào)用沒(méi)有作用到obj對(duì)象上。
  更進(jìn)一步的想想,如果我們?cè)谝粋€(gè)類(lèi)上運(yùn)用了單件(singleton)模式,而這個(gè)類(lèi)又有一個(gè)繼承結(jié)構(gòu),當(dāng)在程序中想利用對(duì)對(duì)象進(jìn)行向上轉(zhuǎn)型來(lái)調(diào)用父類(lèi)的方法時(shí),應(yīng)該會(huì)出現(xiàn)編譯時(shí)錯(cuò)誤,因?yàn)楦割?lèi)臨時(shí)對(duì)象無(wú)法構(gòu)造。在這里有個(gè)前提,父類(lèi)的構(gòu)造函數(shù)應(yīng)該用protected進(jìn)行保護(hù),而不是用private,否則子類(lèi)根本無(wú)法構(gòu)造。這種我沒(méi)有驗(yàn)證了,因?yàn)橛眠@種方法調(diào)用實(shí)在是比較蠢的作法,但不排除這種可能性。向上例中最后一行正確的調(diào)用方法應(yīng)該是:
obj.C010::foo();
  這樣就可以調(diào)用到父類(lèi)中被覆蓋的函數(shù),而且也是作用在正確的對(duì)象上。

  (未完待續(xù))  

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1777626


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多