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!?

你可能感兴趣的:(C++,c,汇编,basic,Class,reference)