免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4289 | 回复: 6
打印 上一主题 下一主题

[C++] 函数返回一个对象,在GCC和VC下的不同表现 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-03-19 05:00 |只看该作者 |倒序浏览
  1. #include <cstdlib>
  2. #include <iostream>
  3. #include <string>


  4. using namespace std;

  5. class A {
  6. public:

  7.     A(int in = 0) : i(in) {
  8.         cout << "int constructor: in value[" << in << "]" << endl;
  9.     }

  10.     A(A& inA)
  11.     {
  12.         cout << "copy constructor: in value[" << inA.getI() << "]" << endl;
  13.         this->i = inA.getI();
  14.     }

  15.     ~A() {
  16.     }

  17.     void setI(int inI) {
  18.         this->i = inI;
  19.     }

  20.     int getI() const {
  21.         return this->i;
  22.     }

  23.     A operator*(const A& inA) {  //重载“乘”, 函数以值返回一个对象
  24.         A _ans;
  25.         _ans.setI(this->getI() * inA.getI());
  26.         cout << "Fun  in: [" << &_ans << "]" << endl;
  27.         return _ans;  //suppose调用copy构造函数, 返回临时对象
  28.     }
  29. public:
  30.     int j;
  31.     int i;
  32. };

  33. int main() {
  34.     A a1(10);
  35.     A a2(20);
  36.     const A& ptr = a1 * a2;
  37.     cout << "Fun out: [" << &ptr << "]" << endl;
  38.     cout << ptr.i << endl;
  39.     return 0;
  40. }
复制代码
有一小段程序在VC下表现不一致

GCC output:
int constructor: in value[10]
int constructor: in value[20]
int constructor: in value[0]
Fun  in: [0xbfc0afc4]
Fun out: [0xbfc0afc4]
200

VC output
int constructor: in value[10]
int constructor: in value[20]
int constructor: in value[0]
Fun  in: [0xbfc78dc4]
copy constructor: in value[200]  <----调用了copy构造函数
Fun out: [0xbfc78de8]    <----不一样的地址
200


按照C++ primer里面讲的, VC的输出应该是最符合经典的。
但是GCC为什么有那样的输出? 退出函数之后栈对象难道还能存活?

论坛徽章:
14
巨蟹座
日期:2013-11-19 14:09:4615-16赛季CBA联赛之青岛
日期:2016-07-05 12:36:0515-16赛季CBA联赛之广东
日期:2016-06-29 11:45:542015亚冠之全北现代
日期:2015-07-22 08:09:472015年辞旧岁徽章
日期:2015-03-03 16:54:15巨蟹座
日期:2014-12-29 08:22:29射手座
日期:2014-12-05 08:20:39狮子座
日期:2014-11-05 12:33:52寅虎
日期:2014-08-13 09:01:31巳蛇
日期:2014-06-16 16:29:52技术图书徽章
日期:2014-04-15 08:44:01天蝎座
日期:2014-03-11 13:06:45
2 [报告]
发表于 2011-03-19 08:52 |只看该作者
按照C++标准(而不是你说的什么C++ primer),临时对象的生存期一直到其引用结束为止。
比如 foo function() {……}
function(); // 返回的匿名临时对象到此语句结束时被释放
foo& f = function(); // 返回的匿名临时对象到f离开作用域后被释放

对于你的代码,g++的输出非常正常,没什么好说的;对于VC++,则是因为它没有能对“具名临时对象”做“返回值优化”,所以它多了个拷贝构造函数。

另外,你写的代码这个这个……,你原来是搞java的吧?!

论坛徽章:
0
3 [报告]
发表于 2011-03-19 09:42 |只看该作者
按照C++标准(而不是你说的什么C++ primer),临时对象的生存期一直到其引用结束为止。
比如 foo function ...
bruceteen 发表于 2011-03-19 08:52



C++ primer是本书啊...里面说reference延长了临时对象的生存期,以前一直任何返回一个对象会直接调用copy函数。
另外请问你对VC的解释具体是什么意思,不是很明白。

还有我以前啥也不搞,菜鸟刚学。

谢谢了

论坛徽章:
0
4 [报告]
发表于 2011-03-19 12:35 |只看该作者
这里实现的语义是一样的,只不过不同的编译器做法不同,我理解的GCC可能稍微只能一些。

在乘法的子函数里面,操作的对象实际上是调用它的函数(main)里面的局部变量。 我把dump出来的汇编贴上来

  1. int main() {
  2. 8048754:        8d 4c 24 04                  lea    0x4(%esp),%ecx
  3. 8048758:        83 e4 f0                     and    $0xfffffff0,%esp
  4. 804875b:        ff 71 fc                     pushl  -0x4(%ecx)
  5. 804875e:        55                           push   %ebp
  6. 804875f:        89 e5                        mov    %esp,%ebp
  7. 8048761:        56                           push   %esi
  8. 8048762:        53                           push   %ebx
  9. 8048763:        51                           push   %ecx
  10. 8048764:        83 ec 3c                     sub    $0x3c,%esp //GCC 预留了0x3c个byte的空间用作main的局部变量
  11.     A a1(10);
  12. 8048767:        c7 44 24 04 0a 00 00         movl   $0xa,0x4(%esp)
  13. 804876e:        00
  14. 804876f:        8d 45 dc                     lea    -0x24(%ebp),%eax   // a1就是ebp-24了
  15. 8048772:        89 04 24                     mov    %eax,(%esp)
  16. 8048775:        e8 76 01 00 00               call   80488f0 <_ZN1AC1Ei>
  17.     A a2(20);
  18. 804877a:        c7 44 24 04 14 00 00         movl   $0x14,0x4(%esp)
  19. 8048781:        00
  20. 8048782:        8d 45 d4                     lea    -0x2c(%ebp),%eax //a2就是 ebp-2c了
  21. 8048785:        89 04 24                     mov    %eax,(%esp)
  22. 8048788:        e8 63 01 00 00               call   80488f0 <_ZN1AC1Ei>
  23.     const A& ptr = a1 * a2;
  24. 804878d:        8d 45 cc                     lea    -0x34(%ebp),%eax //这是GCC生成的内部匿名对象,作为参数传递了
  25. 8048790:        8d 55 d4                     lea    -0x2c(%ebp),%edx //a2
  26. 8048793:        89 54 24 08                  mov    %edx,0x8(%esp)
  27. 8048797:        8d 55 dc                     lea    -0x24(%ebp),%edx  //a1
  28. 804879a:        89 54 24 04                  mov    %edx,0x4(%esp)
  29. 804879e:        89 04 24                     mov    %eax,(%esp)
  30. 80487a1:        e8 be 01 00 00               call   8048964 <_ZN1AmlERKS_>
  31. 80487a6:        83 ec 04                     sub    $0x4,%esp
  32. 80487a9:        8d 45 cc                     lea    -0x34(%ebp),%eax
  33. 80487ac:        89 45 e4                     mov    %eax,-0x1c(%ebp)
  34.     cout << "Fun out: [" << &ptr << "]" << endl;

  35. 然后看乘法的代码
  36. A operator*(const A& inA) {  //重载“乘”, 函数以值返回一个对象
  37. 8048964:        55                           push   %ebp
  38. 8048965:        89 e5                        mov    %esp,%ebp
  39. 8048967:        57                           push   %edi
  40. 8048968:        56                           push   %esi
  41. 8048969:        53                           push   %ebx
  42. 804896a:        83 ec 2c                     sub    $0x2c,%esp
  43. 804896d:        8b 5d 08                     mov    0x8(%ebp),%ebx  //现在的ebp是原来的esp(也就是老的EBP-0x3c,所以现在ebx指向的就是main里面那个匿名对象。
  44.         A _ans;
  45. 8048970:        89 d8                        mov    %ebx,%eax 关键在这里,对局部变量进行构造的时候,GCC直接对main里面那个局部变量构造了。所以GCC省去了copy 构造这一步。
  46. 8048972:        c7 44 24 04 00 00 00         movl   $0x0,0x4(%esp)
  47. 8048979:        00
  48. 804897a:        89 04 24                     mov    %eax,(%esp)
  49. 804897d:        e8 6e ff ff ff               call   80488f0 <_ZN1AC1Ei>
复制代码

论坛徽章:
0
5 [报告]
发表于 2011-03-19 18:46 |只看该作者
虽然很多人可能会觉得非常不能接受,但是这的确是一个标准规定可以强制进行的优化。详见 C++98/03 12.2 的第 15 条。

简单地说就是函数返回一个具有自动生存期的对象的时候,或者一个没有绑定到引用的临时对象被用于另一个对象的 copy constructor 的时候,相应的复制都可以被省略掉。而不用管对象的 copy constructor 中到底做了什么事情。

论坛徽章:
0
6 [报告]
发表于 2011-03-19 21:48 |只看该作者
回复 4# wangjl_sdu


谢谢了 很helpful and informative

论坛徽章:
1
2015年迎新春徽章
日期:2015-03-04 09:56:11
7 [报告]
发表于 2011-03-20 08:23 |只看该作者
很好,很少返回对象啊。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP