一、原码分析
1.1 测试代码
为了方便查看拷贝构造函数调用过程,自定义了拷贝构造函数,但啥也没干。
class CTEST { public: int m_nData; //Method: public: CTEST() { printf("0x%p CTEST is constructed ", this); } CTEST(CTEST& oCtest) { printf("0x%p CTEST copy constructor is called, copy object from 0x%p ", this, &oCtest); } ~CTEST() { printf("0x%p CTEST is destructed ", this); } }; CTEST GetCTest() { CTEST oCtest; return oCtest; } int main(int argc, char* argv[]) { printf("***************************Test1*************************** "); CTEST oTest1 = GetCTest(); printf("oTest1 address is 0x%p ", &oTest1); printf(" "); printf("***************************Test2*************************** "); CTEST oTest2; printf("oTest2 address is 0x%p ", &oTest2); oTest2 = GetCTest(); printf(" "); printf("***************************Test3*************************** "); GetCTest(); printf(" "); 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 ", &oTest1); 0040124D lea ecx,[ebp-10h] 00401250 push ecx 00401251 push offset string "oTest1 address is 0x%p " (00427164) 00401256 call printf (004018a0) 0040125B add esp,8 20: 21: printf("***************************Test2*************************** "); 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 ", &oTest2); 00401284 lea edx,[ebp-14h] 00401287 push edx 00401288 push offset string "oTest2 address is 0x%p " (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*************************** "); 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)