《Effective C++》堪稱C++之經典書籍,但是不能因爲她是經典而對其唯命是從,相反應該帶着自己的腦袋來思考書中的每一條款,只有這樣纔能有所得。
故事發生在條款27(儘量少做轉型動作),其中一段代碼所產生的結果令我費解。
{
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臨時對象?於是,我寫了下面代碼用於試驗。
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
* 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!?