c++学习--作为函数返回值时拷贝构造函数的流程解析

https://blog.csdn.net/pshizhsysu/article/details/40741291


一个对象在创建时,一定会调用某个构造函数进行构造。如果函数的参数是以按值传递,则会调用参数类型的拷贝构造函数对形参进行构造;如果返回值是值,则会对返回给函数调用者的对象调用拷贝构造函数进行构造。看下面的使用情况

[cpp]  view plain  copy
  1. struct A{  
  2.     A(){ cout << "A()" << endl; }  
  3.     A(const A&){ cout << "A(const A&)" << endl; }  
  4.     void print(){ cout << "print()" << endl; }  
  5. }  
  6. void f(A a){ a.print(); }  
  7. A g(){ A a; return a; }  
  8. void h(){ g().print(); }  

上面的代码中,f 函数是按值传递,那么当这样子调用函数f 时 ’f(a1)‘ ,f 函数的参数a是如何被构造的呢,就是通过调用拷贝构造函数a(a1)对a进行构造。g()函数是返回一个值,在h()函数中 'g().print()' 的 'g()' 实质上是一个匿名的A对象(假设这个匿名对象叫a1),这个对象是如何被构造的呢,就是通过调用拷贝构造函数a1(a)进行构造的。

返回值

case 1:以下代码没错

[cpp]  view plain  copy
  1. struct A{  
  2.     A(){}  
  3.     A(const A&){}  
  4. };  
  5. A g(){ const A a;  return a; }  
  6. const A f(){ A a; return a; }  
case 2:以下代码有错,因为a 是const A类型,那么返回给g()函数的调用者的匿名对象调用的拷贝构造函数是 A(const A&),但该函数未定义。

[cpp]  view plain  copy
  1. struct A{  
  2.     A(){}  
  3.     A(A&){}  
  4. };  
  5. A g(){ const A a;  return a; } // Error  
  6. const A f(){ A a; return a; } // OK  
case 3:

[cpp]  view plain  copy
  1. struct A{  
  2.     A(){ cout << "A()" << endl; }  
  3.     A(const A&){ cout << "A(const A&)" << endl; }  
  4.     ~A(){ cout << "~A()" << endl; }  
  5.     void print(){ cout << "print()" << endl; }  
  6. };  
  7. A g(){ const A a; return a; }  
  8. int main()  
  9. {  
  10.     g().print();  
  11.     return 0;  
  12. }  
输出

A()

print()

~A()

对于输出,我感到很奇怪。我觉得输出应该是 A() A(const A&) ~A() print() ~A()。我的想法是,g 函数中的a 在g 函数退出时应该就要被析构,所以应该在print 函数前,而a析构前应该要用它作为参数构造返回的匿名对象,所以~A()之前应该有一个A(const A&),而匿名对象在main()函数结束时也应该被析构,所以最后应该也有一个~A()。

虽然上面的输出没有 A(const A&),但是还是有调用拷贝构造函数的,编译器做了优化,没有执行它的函数体,我们可以通过下面的代码进行验证

[cpp]  view plain  copy
  1. struct A{  
  2.     A(){ cout << "A()" << endl; }  
  3.     A(A&){ cout << "A(A&)" << endl; } // changed  
  4.     ~A(){ cout << "~A()" << endl; }  
  5.     void print(){ cout << "print()" << endl; }  
  6. };  
  7. A g(){ const A a; return a; } // Error  
  8. int main()  
  9. {  
  10.     g().print();   
  11.     return 0;  
  12. }  
当改了拷贝构造函数后,编译通不过,提示找不到拷贝构造函数 A(const A&),原因就是case 2。
继续看下面的代码

[cpp]  view plain  copy
  1. struct A{  
  2.     A(){ cout << "A()" << endl; }  
  3.     A(const A&){ cout << "A(const A&)" << endl; }  
  4.     ~A(){ cout << "~A()" << endl; }  
  5.     void print(){ cout << "print()" << endl; }  
  6. };  
  7. const A g(){ A a; cout << (void*)(&a) << endl; return a; }  
  8. void f(const A& a1){ cout << (void*)(&a1) << endl; }  
  9. int main()  
  10. {  
  11.     f(g());  
  12. }  

输出

A()

0x22ac2f
0x22ac2f

~A()

惊奇地发现,a1与a居然在同一地址上,也就是它们是同一个对象。这个时候我们可以大胆地推测,返回值会匹配拷贝构造函数,但是编译器做了优化后不会执行函数体。编译器应该是这样子认为的:析构g 函数中的a,再拷贝构造一个a1,效率不好,还不如把a直接给a1好了。但是要值得注意的是,拷贝构造函数还是一定要严格匹配的,而且虽然a和a1是同一个对象,但是修饰不一样,a是A类型,a1是const A类型,所以a可以调用print函数,a1不能调用print函数。

在 explicit 文章中也提到过, A  a  = A();只调用了默认构造函数,没有调用拷贝构造函数(但还是匹配拷贝构造函数的原型),编译器应该是认为,A()这个匿名对象,因为匿名的原因,其他地方也无法用到它了,既然要用它来拷贝构造另一个对象,不如把它直接给用一个对象好了。但是,A a = a1;就是调用拷贝构造函数了,因为a1还可能被用到。

但是,当返回的值不是函数声明的返回值类型却有相应该的转换构造函数时,情况怎样?且看下面代码

[cpp]  view plain  copy
  1. struct B{  
  2.     B(){ cout << "B()" << endl;}  
  3.     ~B(){ cout << "~B()" << endl; }  
  4. };  
  5. struct A{  
  6.     A(){ cout << "A()" << endl; }  
  7.     A(const A&){ cout << "A(const A&)" << endl; }  
  8.     A(const B&){cout << "A(const B&)" << endl; }  
  9.     ~A(){ cout << "~A()" << endl; }  
  10.     void print(){ cout << "print()" << endl; }  
  11. };  
  12. A g(){ B b; cout << (void*)&b << endl; return b; }  
  13. void f(const A& a){ cout << (void*)(&a) << endl; }  
  14.   
  15. int main()   
  16. {     
  17.     A a = g();  
  18.     cout << (void*)&a << endl;  
  19. }  
输出:

B()
0x22abff
A(const B&)
~B()
0x22ac2f
~A()
可以看出,a与b指向不同的对象了。由上面的代码我们可以进一步肯定,函数function()返回时的一般步骤为

调用相应的构造函数构造function()返回的匿名对象 -> 析构function()中的对象 -> function()结束 -> 主调函数继续执行



  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值