《Effective C++》堪称C++之经典书籍,但是不能因为她是经典而对其唯命是从,相反应该带着自己的脑袋来思考书中的每一条款,只有这样才能有所得。
故事发生在条款27(尽量少做转型动作),其中一段代码所产生的结果令我费解。
书中对其解释如下:“一如你所期望的,这段代码将*this转型为Window,对函数onResize的调用也因此调用了Window::onResize。但恐怕你没有想到,它调用的并不是当前对象上的函数,而是稍早转型动作所建立的一个‘*this对象之base class成分’的暂时副本身上的onResize!”。
难道,static_cast<Window>(*this)会创建一个Window临时对象?于是,我写了下面代码用于试验。
随后,我将可执行文件反汇编,得到程序主函数的汇编代码:
: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!?