一、原码分析
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)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架