C++中強迫隱式轉換this對象到基類所引發的思考

    《Effective C++》堪稱C++之經典書籍,但是不能因爲她是經典而對其唯命是從,相反應該帶着自己的腦袋來思考書中的每一條款,只有這樣纔能有所得。

    故事發生在條款27(儘量少做轉型動作),其中一段代碼所產生的結果令我費解。

class Window
{
public:
    virtual void onResize() { . . . }
    . . .
};

class SpecialWindow : public Window
{
public:
    virtual void onResize()
    {
        static_cast<Window>(*this).onResize();
        . . .
    }
    . . .
};

    書中對其解釋如下:“一如你所期望的,這段代碼將*this轉型爲Window,對函數onResize的調用也因此調用了Window::onResize。但恐怕你沒有想到,它調用的並不是當前對象上的函數,而是稍早轉型動作所建立的一個‘*this對象之base class成分’的暫時副本身上的onResize!”。

    難道,static_cast<Window>(*this)會創建一個Window臨時對象?於是,我寫了下面代碼用於試驗。

000001    class Window
000002    {
000003    protected:
000004        int _w , _h;
000005    public:
000006        Window(const int w = 0, const int h = 0)
000007            : _w(w) ,
000008             _h(h)
000009        {
000010        }
000011        virtual void OnResize()
000012        {
000013            _w += 10;
000014            _h += 10;
000015            cout << _w << ',' << _h << endl;
000016        }
000017    };
000018    
000019    class SpecialWindow : public Window
000020    {
000021    public:
000022        virtual void OnResize()
000023        {
000024            static_cast<Window>(*this).OnResize();
000025    //        Window::OnResize();
000026            cout << _w << ',' << _h << endl;
000027        }
000028    };
000029    
000030    int _tmain(int argc, _TCHAR* argv[])
000031    {
000032        SpecialWindow sw;
000033        sw.OnResize();
000034        return 0;
000035    }

     隨後,我將可執行文件反彙編,得到程序主函數的彙編代碼:

:00401000 BA0A000000         mov edx, 0000000A                       ;ecx中爲對象基址
:00401005 015104                  add dword ptr [ecx+04], edx          ;<-_w += 10
:00401008 015108                  add dword ptr [ecx+08], edx          ;<-_h += 10
:0040100B 8B4104                  mov eax, dword ptr [ecx+04]

;以下代碼僅執行了Window::onResize()中的cout << _w << ',' << _h << endl

* Reference To: MSVCP80.?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z, Ord:0799h
                                  |
:0040100E 8B1538204000            mov edx, dword ptr [00402038]
:00401014 8B4908                          mov ecx, dword ptr [ecx+08]
:00401017 52                                    push edx
:00401018 51                                    push ecx

* Reference To: MSVCP80.?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A, Ord:0675h
                                  |
:00401019 8B0D50204000            mov ecx, dword ptr [00402050]
:0040101F 50                                    push eax

* Reference To: MSVCP80.??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z, Ord:0311h
                                  |
:00401020 FF1544204000            Call dword ptr [00402044]
:00401026 50                                   push eax
:00401027 E8D4010000              call 00401200
:0040102C 83C404                       add esp, 00000004
:0040102F 8BC8                            mov ecx, eax

* Reference To: MSVCP80.??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z, Ord:0311h
                                  |
:00401031 FF1544204000            Call dword ptr [00402044]
:00401037 8BC8                             mov ecx, eax

* Reference To: MSVCP80.??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z, Ord:0318h
                                  |
:00401039 FF1540204000            Call dword ptr [00402040]
:0040103F C3                                  ret

    如果將static_cast用被註釋掉的Window::onResize替換,則得到下面反彙編代碼:

* Reference To: MSVCP80.?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z, Ord:0799h
                                  |
:00401000 8B1538204000            mov edx, dword ptr [00402038]
:00401006 56                                   push esi
:00401007 8BF1                              mov esi, ecx                                ;由於後面需要用ecx保存函數調用參數
:00401009 B90A000000                mov ecx, 0000000A                   ;所以將對象基址存於esi中
:0040100E 014E04                         add dword ptr [esi+04], ecx      ;_w += 10
:00401011 014E08                         add dword ptr [esi+08], ecx      ;_h += 10
:00401014 8B4E08                         mov ecx, dword ptr [esi+08]
:00401017 8B4604                         mov eax, dword ptr [esi+04]
:0040101A 52                                   push edx
:0040101B 51                                   push ecx

;下列代碼在同一對象上調用了兩次輸出,第一次在_w和_h增10後,第二次緊接在第一次之後
;說明了先調用Window::onResize再繼續SpecialWindow::onResize中輸出代碼

* Reference To: MSVCP80.?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A, Ord:0675h
                                  |
:0040101C 8B0D50204000            mov ecx, dword ptr [00402050]
:00401022 50                                     push eax

* Reference To: MSVCP80.??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z, Ord:0311h
                                  |
:00401023 FF1544204000           Call dword ptr [00402044]
:00401029 50                                  push eax
:0040102A E8B1010000              call 004011E0
:0040102F 83C404                       add esp, 00000004
:00401032 8BC8                           mov ecx, eax

* Reference To: MSVCP80.??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z, Ord:0311h
                                  |
:00401034 FF1544204000            Call dword ptr [00402044]
:0040103A 8BC8                             mov ecx, eax

* Reference To: MSVCP80.??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z, Ord:0318h
                                  |
:0040103C FF1540204000            Call dword ptr [00402040]
:00401042 A138204000                 mov eax, dword ptr [00402038]
:00401047 8B4E08                         mov ecx, dword ptr [esi+08]
:0040104A 8B5604                         mov edx, dword ptr [esi+04]
:0040104D 50                                  push eax
:0040104E 51                                  push ecx

* Reference To: MSVCP80.?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A, Ord:0675h
                                  |
:0040104F 8B0D50204000            mov ecx, dword ptr [00402050]
:00401055 52                                    push edx

* Reference To: MSVCP80.??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z, Ord:0311h
                                  |
:00401056 FF1544204000            Call dword ptr [00402044]
:0040105C 50                                  push eax
:0040105D E87E010000              call 004011E0
:00401062 83C404                        add esp, 00000004
:00401065 8BC8                            mov ecx, eax

* Reference To: MSVCP80.??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z, Ord:0311h
                                  |
:00401067 FF1544204000            Call dword ptr [00402044]
:0040106D 8BC8                             mov ecx, eax

* Reference To: MSVCP80.??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z, Ord:0318h
                                  |
:0040106F FF1540204000            Call dword ptr [00402040]
:00401075 5E                                   pop esi
:00401076 C3                                  ret

    通過分析對比得知,static_cast<Window>(*this)將對象自身切割爲了基類對象,在此後調用onResize過程中,對象便認爲自己只是一個Window對象了,所以得到了上面的反彙編結果。

    但是,目前還不知如何解釋第一種情況下輸出結果爲:10,10 [換行] 0 ,0。因爲反彙編代碼中只調用了一次cout!?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章