在函數(shù)調(diào)用的時(shí)候,無論是參數(shù)為對(duì)象還是返回一個(gè)對(duì)象,都將產(chǎn)生一個(gè)臨時(shí)對(duì)象。這個(gè)筆記就是為了學(xué)習(xí)這個(gè)臨時(shí)對(duì)象的產(chǎn)生過程而寫。
本代碼的詳細(xì)例子見實(shí)例代碼Ex.01
Ok,先讓我們定義一個(gè)類:
class CExample
{
public:
int m_nFirstNum;
int m_nSecNum;
int GetSum();
bool SetNum(int nFirst, int nSec);
CExample(){} // 空構(gòu)造,不實(shí)現(xiàn)任何功能
virtual ~CExample(){} // 空析構(gòu)
};
// 定義的函數(shù)實(shí)現(xiàn)部分
int CExample::GetSum()
{
return m_nFirstNum+m_nSecNum;
}
先讓我們看一下對(duì)象的創(chuàng)建過程
CExample objExp1;
// 00401393 lea ecx,[ebp-18h] // 第一個(gè)對(duì)象
// 00401396 call @ILT+20(CExample::CExample)
// 0040139B mov dword ptr [ebp-4],0 用來統(tǒng)計(jì)當(dāng)前對(duì)象個(gè)數(shù)
CExample objExp2;
// 004013A2 lea ecx,[ebp-24h] // 第二個(gè)對(duì)象
// 004013A5 call @ILT+20(CExample::CExample)
// 004013AA mov byte ptr [ebp-4],1
CExample objExp3;
// 004013AE lea ecx,[ebp-30h] // 第三個(gè)對(duì)象
// 004013B1 call @ILT+20(CExample::CExample)
// 004013B6 mov byte ptr [ebp-4],2
上面創(chuàng)建了三個(gè)對(duì)象,它們的過程都非常相似,將一個(gè)局部變量地址給了ECX寄存器,然后調(diào)用構(gòu)造函數(shù)。
先讓我們看看,構(gòu)造函數(shù)都干啥了:
12: CExample::CExample()
13: {
00401540 push ebp
00401541 mov ebp,esp
00401543 sub esp,44h
00401546 push ebx
00401547 push esi
00401548 push edi
00401549 push ecx // 保存寄存器環(huán)境
0040154A lea edi,[ebp-44h]
0040154D mov ecx,11h
00401552 mov eax,0CCCCCCCCh
00401557 rep stos dword ptr [edi]
00401559 pop ecx // 填充完CC以后,恢復(fù)ECX內(nèi)容
0040155A mov dword ptr [ebp-4],ecx
0040155D mov eax,dword ptr [ebp-4] // 取到this指針
00401560 mov dword ptr [eax],offset CExample::`vftable' // 讓this指針指向虛表
15: }
00401566 mov eax,dword ptr [ebp-4]
00401569 pop edi
0040156A pop esi
0040156B pop ebx
0040156C mov esp,ebp
0040156E pop ebp
0040156F ret
我們知道,我們?cè)?/span>C代碼中,實(shí)現(xiàn)的是空構(gòu)造,沒有添加任何功能,可是反匯編的時(shí)候,發(fā)現(xiàn),函數(shù)應(yīng)該有個(gè)參數(shù)(是this指針),定位虛表的時(shí)候,是又構(gòu)造完成讓this指向虛表的工作的。
1、 傳遞一個(gè)對(duì)象的過程:
bool SetExpFun(CExample objExp)
{
g_objExp.SetNum(objExp.m_nFirstNum, objExp.m_nSecNum);
return true;
}
這是我們樣例程序中,一個(gè)對(duì)象作為參數(shù)的情況。我們編寫如下的調(diào)用代碼:
SetExpFun(objExp1);
反匯編代碼如下:
004013C8 sub esp,0Ch // 申請(qǐng)臨時(shí)對(duì)象空間
004013CB mov ecx,esp // 讓ECX指向臨時(shí)申請(qǐng)的對(duì)象
004013CD mov dword ptr [ebp-34h],esp // 賦值一份this
004013D0 lea eax,[ebp-18h] // 獲取第一個(gè)對(duì)象的this指針
004013D3 push eax // 傳遞參數(shù)
004013D4 call @ILT+45(CExample::CExample) // 使用了拷貝構(gòu)造所以有上面的參數(shù)
004013D9 mov dword ptr [ebp-48h],eax // 產(chǎn)生一個(gè)臨時(shí)對(duì)象并保存它的this指針
004013DC call @ILT+15(SetExpFun) (00401014) // 調(diào)用函數(shù)
004013E1 add esp,0Ch
上面代碼中,有兩處函數(shù)調(diào)用,一個(gè)是我們已經(jīng)非常熟悉的調(diào)用構(gòu)造函數(shù),另一個(gè)事調(diào)用我們需要的setExpFun函數(shù),當(dāng)然,通過上面的注釋,我們很容易就能知道,在這里創(chuàng)建了一個(gè)臨時(shí)的對(duì)象,而且貌似調(diào)用構(gòu)造函數(shù)的時(shí)候還傳遞了一個(gè)參數(shù)(參數(shù)是我們定義的第一個(gè)對(duì)象: objExp1)。
是的,很明顯這里是個(gè)拷貝構(gòu)造,讓我們先來看下它的調(diào)用過程。
拷貝構(gòu)造
{
004011F0 push ebp
004011F1 mov ebp,esp
004011F3 sub esp,44h
004011F6 push ebx
004011F7 push esi
004011F8 push edi
004011F9 push ecx ; 保存臨時(shí)對(duì)象的this指針
004011FA lea edi,[ebp-44h]
004011FD mov ecx,11h
00401202 mov eax,0CCCCCCCCh
00401207 rep stos dword ptr [edi]
00401209 pop ecx ; 找到調(diào)用時(shí)傳遞的臨時(shí)對(duì)象的this指針
0040120A mov dword ptr [ebp-4],ecx
0040120D mov eax,dword ptr [ebp-4]
00401210 mov ecx,dword ptr [ebp+8] ; 參數(shù)對(duì)象的this指針,ECX中是虛表
00401213 mov edx,dword ptr [ecx+4] ; 取出參數(shù)對(duì)象的第一個(gè)成員
00401216 mov dword ptr [eax+4],edx ; 并賦值給臨時(shí)對(duì)象的第一個(gè)成員
00401219 mov eax,dword ptr [ebp-4]
0040121C mov ecx,dword ptr [ebp+8]
0040121F mov edx,dword ptr [ecx+8] ; 取到參數(shù)對(duì)象的第二個(gè)成員
00401222 mov dword ptr [eax+8],edx ; 并賦值給臨時(shí)對(duì)象的第二個(gè)成員
00401225 mov eax,dword ptr [ebp-4] ; 設(shè)置臨時(shí)對(duì)象的虛表
00401228 mov dword ptr [eax],offset CExample::`vftable'
0040122E mov eax,dword ptr [ebp-4] ; 返回一個(gè)臨時(shí)對(duì)象
00401231 pop edi
00401232 pop esi
00401233 pop ebx
00401234 mov esp,ebp
00401236 pop ebp
00401237 ret 4
}
從上面的代碼不難看出,我們這個(gè)拷貝構(gòu)造直接在參數(shù)中改寫的數(shù)據(jù),等出來這個(gè)函數(shù),我們main函數(shù)中:
004013C8 sub esp,0Ch
申請(qǐng)的臨時(shí)對(duì)象空間中就是一個(gè)完整的對(duì)象了。
好現(xiàn)在我們繼續(xù)跟蹤調(diào)用傳參的代碼:
16: bool SetExpFun(CExample objExp)
17: {
004012C0 push ebp
004012C1 mov ebp,esp
004012C3 push 0FFh
004012C5 push offset __ehhandler$?SetExpFun@@YA_NVCExample@@@Z
004012CA mov eax,fs:[00000000]
004012D0 push eax
004012D1 mov dword ptr fs:[0],esp
004012D8 sub esp,44h
004012DB push ebx
004012DC push esi
004012DD push edi
004012DE lea edi,[ebp-50h]
004012E1 mov ecx,11h
004012E6 mov eax,0CCCCCCCCh
004012EB rep stos dword ptr [edi]
004012ED mov dword ptr [ebp-4],0 ; 計(jì)數(shù)對(duì)象數(shù)量
18: g_objExp.SetNum(objExp.m_nFirstNum, objExp.m_nSecNum);
004012F4 mov eax,dword ptr [ebp+0Ch] ; 直接引用臨時(shí)對(duì)象的成員
004012F7 push eax
004012F8 mov ecx,dword ptr [ebp+10h]
004012FB push ecx
004012FC mov ecx,offset g_objExp ; 傳遞this指針
00401301 call @ILT+0(CExample::SetNum)
19: return true;
00401306 mov byte ptr [ebp-10h],1
0040130A mov dword ptr [ebp-4],0FFFFFFFFh ; 清空臨時(shí)對(duì)象計(jì)數(shù)
00401311 lea ecx,[ebp+8] ; 取到臨時(shí)對(duì)象的this指針
00401314 call @ILT+40(CExample::~CExample)
00401319 mov al,byte ptr [ebp-10h]
20: }
0040131C mov ecx,dword ptr [ebp-0Ch]
0040131F mov dword ptr fs:[0],ecx
00401326 pop edi
00401327 pop esi
00401328 pop ebx
00401329 add esp,50h
0040132C cmp ebp,esp
0040132E call __chkesp (00401610)
00401333 mov esp,ebp
00401335 pop ebp
00401336 ret
2、 返回一個(gè)對(duì)象的過程:
CExample GetExpFun()
{
return g_objExp;
}
編寫如下的調(diào)用代碼:
// 下面是返回對(duì)象的情況
objExp2 = GetExpFun();
調(diào)試下這個(gè)程序:
59: objExp2 = GetExpFun();
004013E4 lea ecx,[ebp-40h] ; 返回的臨時(shí)對(duì)象空間是進(jìn)入main函數(shù)的時(shí)候,提前分配好的。
004013E7 push ecx ; 先將對(duì)象壓棧
004013E8 call @ILT+25(GetExpFun) ; 調(diào)用函數(shù)
11: CExample GetExpFun()
12: {
00401190 push ebp
00401191 mov ebp,esp
00401193 sub esp,44h
00401196 push ebx
00401197 push esi
00401198 push edi
00401199 lea edi,[ebp-44h]
0040119C mov ecx,11h
004011A1 mov eax,0CCCCCCCCh
004011A6 rep stos dword ptr [edi]
004011A8 mov dword ptr [ebp-4],0
13: return g_objExp;
004011AF push offset g_objExp (0042af80)
004011B4 mov ecx,dword ptr [ebp+8] ; 引用傳進(jìn)來的參數(shù)對(duì)象指針
004011B7 call @ILT+45(CExample::CExample) ; 調(diào)用構(gòu)造創(chuàng)建對(duì)象
004011BC mov eax,dword ptr [ebp-4]
004011BF or al,1
004011C1 mov dword ptr [ebp-4],eax ; 更新對(duì)象個(gè)數(shù)
004011C4 mov eax,dword ptr [ebp+8] ; 返回……
14: }
004011C7 pop edi
004011C8 pop esi
004011C9 pop ebx
004011CA add esp,44h
004011CD cmp ebp,esp
004011CF call __chkesp
004011D4 mov esp,ebp
004011D6 pop ebp
004011D7 ret
004013ED add esp,4
004013F0 mov dword ptr [ebp-4Ch],eax ; 保存臨時(shí)對(duì)象的指針
004013F3 mov edx,dword ptr [ebp-4Ch]
004013F6 mov dword ptr [ebp-50h],edx
004013F9 mov byte ptr [ebp-4],3
004013FD mov eax,dword ptr [ebp-50h] ; 這里重載的 = 運(yùn)算符,因此將副本壓棧做復(fù)制操作
00401400 push eax
00401401 lea ecx,[ebp-24h] ; 得到第二個(gè)對(duì)象的this指針
00401404 call @ILT+10(CExample::operator=)
00401409 mov byte ptr [ebp-4],2
0040140D lea ecx,[ebp-40h] ; 使用完成,釋放臨時(shí)對(duì)象
00401410 call @ILT+40(CExample::~CExample)
printf("%d\r\n", objExp2.GetSum());
OK,只要搗鼓明白了這個(gè)臨時(shí)對(duì)象,那我們的好多問題都可以解決了。