CSDN首页 空间 新闻 论坛 Blog 下载 读书 网摘 搜索 .NET Java 视频 接项目 求职 在线学习 买书 程序员 通知
英特尔®游戏设计大赛100美元现金周周送 专题改版:Java Web 专题
CSDN社区
搜索 收藏 打印 关闭
CSDN社区 >  C/C++ >  C++ 语言

引用与拷贝构造函数,----不经意之间发现了一个很奇怪的现象!!!

楼主fishsward(.)2003-09-04 16:58:24 在 C/C++ / C++ 语言 提问

class   HowMany2  
  {  
  string   name;  
      public:     static   int   objectCount;  
   
        public:  
                    HowMany2(const   string   &   id   =""):name(id)  
  {  
  ++objectCount;  
  print("howmant2()");  
  }  
   
  ~HowMany2(void)  
                    {  
  --objectCount;  
  print("xigou   --howmany2()");  
                    }  
   
  HowMany2(const   HowMany2   &   h):name(h.name   )  
  {  
  name+=   "copy";  
  ++objectCount;  
  print("HowMany2(const   howmany2   &)--");  
  }  
  HowMany2   &   operator   =(const   HowMany2   &   h)  
  {  
  cout<<"operater   =*****************************"<<endl;  
  name=h.name   ;  
  return   *this;  
  }  
  void   print(const   string   &   msg)const  
                    {  
                                  if(msg.size   ()!=0)  
  std::cout<<msg<<endl;  
  std::cout<<'\t'<<name<<":"<<"objectCount="<<objectCount<<endl;  
                    }  
      }  
   
  HowMany2   x("x");  
  HowMany2   &   getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
          HowMany2   h=getX();  
  }  
  程序的结果对于大家来说,肯定是小菜一碟,但是,对getx()作如下修改,结果  
  竟然不变!!!  
  HowMany2     getX()  
  {  
        return   x;  
  }  
  是不是很奇怪啊?????????  
  函数的返回值,定义成"引用"和"非引用",竟然答案一样??????  
  问题点数:100、回复次数:19Top

1 楼reinhard_liu(reinhard)回复于 2003-09-04 17:06:51 得分 50

因为你定义的copy   constructor   可能启动了系统的NRV(named   return   value)优化  
  所以  
  HowMany2     getX()  
  {  
        return   x;  
  }  
  会变成如下的c   伪吗  
  HowMany2   getX(Howmany2   &   _result)  
  {  
      return   _result.HowMany2(x);(返回值得中间临时变量没有了,优化掉了)  
  }  
  Top

2 楼reinhard_liu(reinhard)回复于 2003-09-04 17:12:17 得分 0

p.s.   sorry   优化后的代码我笔误现在修正如下  
   
  返回value   的优化  
  //c   伪代码  
  void   getX(Howmany2&   _result)  
  {  
      return   _result.HowMany2(x);(返回值得中间临时变量没有了,优化掉了)  
  }  
  返回value   的无优化  
  void   getX(Howmany2&   _result)  
  {  
      Howmany2   temp;  
      Howmany2.Howmany2(x);  
      return   _result.operator=(x);(返回值得中间临时变量有了)  
  }  
  Top

3 楼reinhard_liu(reinhard)回复于 2003-09-04 17:14:18 得分 0

p.s.   又打错了     sorry   :)  
  第2个伪代码  
  Howmany2.Howmany2(x);   //改为   temp.Howmany2(x);Top

4 楼xueweizhong(薛卫忠)回复于 2003-09-04 17:27:51 得分 30

返回引用只有在以下情况才和返回值有差别:  
   
  1)返回值被引用    
       
      T&   t   =   f(...);  
       
      这种情况叫做   reference's   direct-binding  
      会出现在以下情况里:  
      1>ref-para   binding  
          void   g(T&);  
          g(f(...));  
   
      2>ctor   binding  
          struct   Foo  
          {      
              Foo(T&);  
              ...  
            };  
          Foo   foo(f(...));  
        3)其他一些情况。            
   
  2)   返回值被取地址  
        T*   pt   =   &f(...);  
   
  其他情况下返回的引用当值处理,没有任何优越性,  
  倒是会导致一些常见错误,  
  如返回函数调用栈上对象的引用(bad-ref)。  
  Top

5 楼fishsward(.)回复于 2003-09-04 17:30:00 得分 0

我是楼主,现在说一下我的理解:  
   
  to   :     reinhard_liu(reinhard  
          您的意思是   返回value时,把临时变量优化掉了,我不认为这样  
   
  结合输出结果,我的理解:,  
  1.返回value时  
              编译器还是构造了"x的拷贝",此拷贝就是您说的临时变量,它被创建  
      在h中  
  2.返回引用时  
              编译器在函数返回的那一刹那间,没有创建临时变量,它返回的是全局  
      对象x的引用,然后用"x的引用"构造h  
  --------也就是说,两者都调用了拷贝构造函数  
   
  如果改成如下:  
  HowMany2   x("x");  
  HowMany2   &   getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
        getX();  
  }  
  和---------------------------------------------  
  HowMany2   x("x");  
  HowMany2     getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
          getX();  
  }  
  答案就不同,前者一共只有一次构造函数的调用,而后者却有两次构造函数的调用  
   
   
   
   
   
  Top

6 楼reinhard_liu(reinhard)回复于 2003-09-04 17:34:20 得分 0

经过NRV   优化少了一次constructor的调用所以结果会和你的猜想有区别Top

7 楼eric8231(1328cire)回复于 2003-09-04 17:35:22 得分 10

由于程序是   HowMany2   h=getX();     所以在调用getX();   时,不论getX();返回的是引用还是值,getX();的结果都将作为   对象h   的拷贝构造函数的参数。    
   
  由于拷贝构造函数的参数定义为“const   HowMany2   &”,所以在两种情况下编译器都会将那个静态对象x的引用传到函数中。    
  Top

8 楼fishsward(.)回复于 2003-09-04 17:44:06 得分 0

我是楼主,同意xueweizhong(薛卫忠)   的观点,  
                  不同意reinhard_liu(reinhard)的观点,因为返回value时  
                    "临时变量"并没有被优化掉  
   
  我的理解:  
  1    
    HowMany2   x("x");  
  HowMany2   &   getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
          HowMany2   h=getX();  
  }  
  此时,getx()返回的是全局对象x的引用,然后用此引用,通过拷贝构造函数对  
  h进行初始化.  
  --------------------------------  
  2  
  HowMany2   x("x");  
  HowMany2     getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
          HowMany2   h=getX();  
  }  
  此时在getx()返回前,构造了x的拷贝,编译器直接将此"x的拷贝"(也就是您说的临时对象)创建  
  在h中  
  以上两种情况都调用两次构造函数,结果相同!!!  
   
  但是如下情况,就不相同了:  
  1.  
  HowMany2   x("x");  
  HowMany2   &   getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
        getX();  
  }  
  -------------------------------------  
  2.  
  HowMany2   x("x");  
  HowMany2     getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
            getX();  
  }  
  前者调用两次构造函数,后者只调用一次构造函数  
   
   
   
     
  Top

9 楼reinhard_liu(reinhard)回复于 2003-09-04 17:55:58 得分 0

-------------------------------------------  
  但是如下情况,就不相同了:  
  1.  
  HowMany2   x("x");  
  HowMany2   &   getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
        getX();  
  }  
  -------------------------------------  
  2.  
  HowMany2   x("x");  
  HowMany2     getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
            getX();  
  }  
  前者调用两次构造函数,后者只调用一次构造函数  
   
  返回reference   调用两次constructor?  
  Top

10 楼reinhard_liu(reinhard)回复于 2003-09-04 17:59:39 得分 0

从语义上说   返回   reference   肯定要比返回value多一次constructor,但是实际上因为优化和具体使用的限制可能两者接近啊Top

11 楼fishsward(.)回复于 2003-09-04 18:04:12 得分 0

to     reinhard_liu(reinhard:  
      不好意思,刚好写反了,前者调用1次构造函数,后者只调用2   次构造函数  
        并且跟优化好像关系不大!  
  Top

12 楼reinhard_liu(reinhard)回复于 2003-09-04 18:16:57 得分 0

如果你的编译器支持NRV的情况那么在返回值被接受的情况下  
  Howmany2   h=getX();  
  优化和不优化肯定会有一次constructor的调用差异,你仔细考虑一下我的话  
  NRV优化与否也看具体的编译器和实际的上下文,并且只有有COPY   CONSTRUCTOR的CLASS才优化  
  我现在也看的有点混乱了  
  你把你的观点提清楚一点,好么?Top

13 楼fishsward(.)回复于 2003-09-04 18:33:05 得分 0

to     reinhard_liu(reinhard)   :  
   
          其实关于此问题,我有自己的理解,我把问题提出来,是因为我不敢确认!  
  对于优化,我觉得编译器不大可能把返回value的"临时对象"优化掉!  
  1  
  HowMany2   x("x");  
  HowMany2   &   getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
        getX();  
  }  
  getx()返回的是"引用",所以没有"临时对象",所以只有"HowMany2   x("x")"的一次构造函数的调用  
  -------------------------------------  
  2.  
  HowMany2   x("x");  
  HowMany2     getX()  
  {  
        return   x;  
  }  
  void   main()  
  {  
            getX();  
  }  
  getx返回的是value,调用它后,在即将返回的那一刹那,构造x的拷贝,由于生存期已到,  
  这个"临时对象"(x的拷贝)马上释放.所以一共调用两次构造函数  
   
   
  Top

14 楼reinhard_liu(reinhard)回复于 2003-09-04 20:21:41 得分 0

关于临时对象优化的NRV   不是我发明出来的,所以我想你可以参考一下<<深度探索c++对象模型>>,至于你个问题到底有没有优化,我不敢说,不过我觉得你的程序应该参考一下这个说法,从语义上讲你的说法是对的,但是c++的优化是为了效率的考虑,当然也有过NRV之后效率反而下降的例子,更甚至有过出错的目标代码。对于NRV的争论很多,我也不甚了解。  
  我认为对于c++的理解我还处于初级阶段,也希望有高手来给这个问题一个基本圆满的解答。  
  :)Top

15 楼lth_81(上海男生)回复于 2003-09-04 20:22:51 得分 10

环境vc.net  
   
  结论1:  
  2种情况下都只用了一次copy   constructor,因此都用了nrv优化。  
  结论2:  
  返回引用或者说返回的是值在这里都不会构成任何的影响,因为变量h不是引用或者指针。  
  结论3:  
  如果是getx()这样单独调用的话,不会调用copy   constructor,可能原因是没有必要返回,在外层,更本没有对getx()的返回值操作。  
  另外一个问题是(注意在这里getx()返回的是一个局部变量):  
  (1)引用返回的getx()可以这样用getx().member   =   123;因为它发挥的根本就是局部变量的一个引用。  
  (2)返回值的getx()如果调用getx().member   =   123;是会报错的。因为其中用到了一个隐含的由编译器产生的常量,因为是常量所以不能对其进行修改。  
   
  可见,返回引用和返回值是有区别的。问题在于外层接受的变量类型。如果外层接受的变量是引用类型,那么其实不会调用copy   constructor也不会有什么nrv的操作;相反如果外层接受的变量不是引用类型的,那么会调用copy   constructor。  
   
  用到的例子代码:  
  #include   <iostream>  
  #include   <conio.h>  
  using   namespace   std;  
   
  class   test{  
  public:  
  int   y;  
  test()   {   cout   <<   "default   constructor"   <<   endl;   }  
  test(test&   tst)   {   cout   <<   "copy   constructor"   <<   endl;   }  
   
  test&   operator=(const   test&   i)   {   cout   <<   "operator"   <<   endl;}  
  };  
   
   
  test&   getx()  
  {  
  test   x;//这里返回的是局部变量,更能说明问题  
  x.y   =   123;  
   
  return   x;  
  }  
   
  int   main()  
  {  
  test&   y   =   getx();  
   
  system("pause");  
   
  }Top

16 楼xueweizhong(薛卫忠)回复于 2003-09-04 21:21:48 得分 0

看了你们在这边的讨论,又去翻看了C++STD98/chapter   8  
  得出以下结论:  
   
  1)  
      在没有NRV的情况下:  
      调用   T   t   =   getX();    
      如果   getX()返回引用,将只有一个copy-ctor  
      如果   getX()返回值     ,将有两个copy-ctor    
      (if   NRV,   only   1   copy-ctor,   but   it's   not   STD   WAY)  
                                                                    ---------(#)  
  2)  
      如果copy-ctor含有side-effect,  
      那么两次调用和一次调用的导致的程序运行结果肯定是不同的。  
   
  3)  
      从2)的意义上说,返回引用和返回值还是有些不同的。  
      但同时由(#),也说明是否使用NRV是会影响程序结果的。  
   
  4)  
      如果允许使用NRV,认为程序执行结果不会变,  
      那么在这个意义上我们也认为  
      返回引用和返回值的差别(在这个用法中)也不会影响程序执行结果。  
     
  5)  
      所以C++98不允许使用NRV。  
    (对之避而不谈)  
   
  其他:(^-^)  
      我不是很明白   lth_81(上海男生)   在说什么?再次请教。  
  Top

17 楼fishsward(.)回复于 2003-09-05 13:31:12 得分 0

继续聆听大家的高见Top

18 楼eric8231(1328cire)回复于 2003-09-05 18:16:36 得分 0

>>函数的返回值,定义成"引用"和"非引用",竟然答案一样  
   
  结果一样的原因并不是由于返回“引用”和返回“值”的区别,而是由于函数getX()的调用形式:  
   
  1.   如果是   HowMany2   h=getX();    
   
  那么不论是返回“引用”还是“值”都没有必要产生临时变量,因为可以直接将那个静态变量作为拷贝构造函数的参数。这与NRV优化似乎没有关系。  
   
  2.   如果是   getX();    
   
  我在Dev-c++编译的结果是:当返回“引用”时没有临时变量;返回“值”时产生临时变量。  
   
  我想,编译器这样做是由于当返回“值”时,虽然这个值以后不会用到,但仍然产生出一个临时变量来“承接”这个值。  
   
  但我相信在第二种调用情况下产生的任何临时变量都不是必要的,担由于编译器需要有一些一致性的规则,所以这个临时变量仍然被构造出来。  
   
   
   
  Top

19 楼yndfcd(YNDFCD)回复于 2003-09-05 21:09:37 得分 0

为什么在VC6。0下得到的程序调用了两次constructor,只是调用了一次destructor呢?Top

20 楼fishsward(.)回复于 2003-09-05 22:51:07 得分 0

upTop

相关问题

  • 引用和拷贝构造函数?
  • C++[类与结构][引用与指针]虚构造函数与虚等拷贝构造函数等问题
  • C++拷贝构造函数
  • 拷贝构造函数??
  • 拷贝构造函数
  • 为什么拷贝构造函数的参数是一个引用?
  • 关于构造函数和拷贝构造函数的对比。
  • 构造函数和拷贝构造函数
  • 派生类的拷贝构造函数
  • 关于C++的拷贝构造函数

关键词

  • c++
  • 函数
  • 拷贝
  • 编译器
  • 优化
  • howmany2
  • getx
  • 调用
  • 构造
  • 引用

得分解答快速导航

  • 帖主:fishsward
  • reinhard_liu
  • xueweizhong
  • eric8231
  • lth_81

相关链接

  • C/C++ Blog
  • C/C++类图书
  • C/C++类源码下载

广告也精彩

反馈

请通过下述方式给我们反馈
反馈
网站简介|广告服务|VIP资费标准|银行汇款帐号|网站地图|帮助|联系方式|诚聘英才|English|问题报告
世纪乐知(北京)网络技术有限公司 版权所有, 京 ICP 证 020026 号
北京创新乐知广告有限公司 提供技术支持
Copyright © 2000-2007, CSDN.NET, All Rights Reserved
GongshangLogo