organic

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

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


 

posted on 2015-05-16 00:32  organic  阅读(1024)  评论(0编辑  收藏  举报