C++淺析——返回對象的函數

一、原碼分析
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)

發佈了27 篇原創文章 · 獲贊 2 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章