1.1 測試代碼
爲了方便查看拷貝構造函數調用過程,自定義了拷貝構造函數,但啥也沒幹。
class CTEST
{
public:
int m_nData;
//Method:
public:
CTEST()
{
printf("0x%p CTEST is constructed\n", this);
}
CTEST(CTEST& oCtest)
{
printf("0x%p CTEST copy constructor is called, copy object from 0x%p\n", this, &oCtest);
}
~CTEST()
{
printf("0x%p CTEST is destructed\n", this);
}
};
CTEST GetCTest()
{
CTEST oCtest;
return oCtest;
}
int main(int argc, char* argv[])
{
printf("***************************Test1***************************\n\n");
CTEST oTest1 = GetCTest();
printf("oTest1 address is 0x%p\n", &oTest1);
printf("\n");
printf("***************************Test2***************************\n\n");
CTEST oTest2;
printf("oTest2 address is 0x%p\n", &oTest2);
oTest2 = GetCTest();
printf("\n");
printf("***************************Test3***************************\n\n");
GetCTest();
printf("\n");
getchar();
return 0;
}
運行結果
1.2
CTEST oTest1 = GetCTest();
用返回對象定義賦值對象時,oTest1的構造函數並不會被調用,而是傳遞其對象的指針作爲隱含參數給GetCTest()函數,
GetCTest()會在函數對象返回時調用其拷貝構造函數,利用返回對象對其初始化。
1.3 oTest2 = GetCTest();
用返回對象賦值對象時,與定義賦值不同,並不會傳遞其對象的指針給GetCTest()函數,而是產生了一個臨時對象作爲隱含參
數傳遞給GetCTest()函數,GetCTest()函數執行完畢後,利用臨時對象給oTest2對象賦值(即淺拷貝,而不是調用其拷貝構造函數,如
果有資源指針,可能會造成資源泄露,有興趣的朋友可以深入研究下這個問題)。
1.4 GetCTest();
單獨調用GetCTest()函數和1.3類似,也會產生臨時對象,只是調用結束後會析構掉。
二、深入分析
2.1 GetCTest()反彙編分析
7: CTEST GetCTest()
8: {
9: CTEST oCtest;
00401074 lea ecx,[ebp-10h]
00401077 call @ILT+5(CTEST::CTEST) (0040100a)
0040107C mov dword ptr [ebp-4],1
10:
11: return oCtest;
00401083 lea eax,[ebp-10h]
00401086 push eax //壓入oCtest對象指針
00401087 mov ecx,dword ptr [ebp+8] //取賦值對象的指針,該指針在調用GetCTest()函數時隱式傳入
0040108A call @ILT+20(CTEST::CTEST) (00401019) //調用賦值對象的拷貝構造函數
0040108F mov ecx,dword ptr [ebp-14h]
00401092 or ecx,1
00401095 mov dword ptr [ebp-14h],ecx
00401098 mov byte ptr [ebp-4],0
0040109C lea ecx,[ebp-10h]
0040109F call @ILT+15(CTEST::~CTEST) (00401014) //返回對象oCtest析構
004010A4 mov eax,dword ptr [ebp+8] //返回賦值對象的指針
12: }
通過以上反彙編代碼的分析,可以看出GetCTest()函數在調用時編譯器偷偷摸摸的傳入了賦值對象的指針,而返回對象的函數實際上在返回時已經將返回對象析構了,其返回的是賦值對象的指針,只是在析構前利用返回對象其賦值對象進行拷貝構造了。
2.2 代碼反彙編分析
17: CTEST oTest1 = GetCTest();
0040123A lea eax,[ebp-10h]
0040123D push eax //壓入oTest1的指針,以供GetCTest拷貝構造對象
0040123E call @ILT+0(GetCTest) (00401005)
00401243 add esp,4
00401246 mov dword ptr [ebp-4],0
18: printf("oTest1 address is 0x%p\n", &oTest1);
0040124D lea ecx,[ebp-10h]
00401250 push ecx
00401251 push offset string "oTest1 address is 0x%p\n" (00427164)
00401256 call printf (004018a0)
0040125B add esp,8
20:
21: printf("***************************Test2***************************\n\n");
0040126B push offset string "***************************Test2"... (00427114)
00401270 call printf (004018a0)
00401275 add esp,4
22: CTEST oTest2;
00401278 lea ecx,[ebp-14h] //調用oTest2的構造函數
0040127B call @ILT+5(CTEST::CTEST) (0040100a)
00401280 mov byte ptr [ebp-4],1
23: printf("oTest2 address is 0x%p\n", &oTest2);
00401284 lea edx,[ebp-14h]
00401287 push edx
00401288 push offset string "oTest2 address is 0x%p\n" (004270f8)
0040128D call printf (004018a0)
00401292 add esp,8
24: oTest2 = GetCTest();
00401295 lea eax,[ebp-1Ch] //壓入臨時對象的指針
00401298 push eax
00401299 call @ILT+0(GetCTest) (00401005)
0040129E add esp,4
004012A1 mov dword ptr [ebp-28h],eax //保存GetCTest返回的對象地址到[ebp-28h]
004012A4 mov ecx,dword ptr [ebp-28h]
004012A7 mov edx,dword ptr [ecx] //拷貝GetCTest返回的對象的m_nData參數至oTest2對象的m_nData
004012A9 mov dword ptr [ebp-14h],edx
004012AC lea ecx,[ebp-1Ch] //臨時對象析構
004012AF call @ILT+15(CTEST::~CTEST) (00401014)
26:
27: printf("***************************Test3***************************\n\n");
004012C1 push offset string "***************************Test3"... (004270ac)
004012C6 call printf (004018a0)
004012CB add esp,4
28: GetCTest();
004012CE lea eax,[ebp-20h] //壓入臨時對象的指針
004012D1 push eax
004012D2 call @ILT+0(GetCTest) (00401005)
004012D7 add esp,4
004012DA lea ecx,[ebp-20h] //臨時對象析構
004012DD call @ILT+15(CTEST::~CTEST) (00401014)