C++以对象作为返回值时编译器的优化,以及临时变量的析构时机

印象中,函数调用的时候,参数past by value、返回值return by value,总是伴随着对象的复制。


实际上参数传递是这样的,但是返回值有可能不是这样的(虽然大部分都是面临拷贝构造函数的调用),这取决于编译器。

#include<string>  
#include<list>  
#include<iostream>  

using namespace std;  
  
class C  
{  
   public:
   	    C()
   	    {
   	   	    cout<<"C default constructor(),this="<<this<<endl; 
   	    }  
            
        C(const C &c)  
        {  
            cout<<"C const copy constructor(),this="<<this<<",reference="<<&c<<endl;                                  
        }  
          
        C(C &c)  
        {  
            cout<<"C nonconst copy constructor(),this="<<this<<",reference="<<&c<<endl;            
        }  
          
        const C & operator=(const C &c)  
        {  
            cout<<"C assignment(),this="<<this<<endl;  
            return *this;  
        }  
        ~C()  
        {  
            cout<<"C destructor(),this="<<this<<endl;  
        } 
};  
      
      
C test_1(int i)  
{  
	 cout<<"entering test_1"<<endl; 
   C x;  
   C a;           //a会析构
   cout<<"leaving test_1"<<endl; 
   return x;      //return之后栈不清空,x不会析构,即使编译器已经将优化设置成-O0  
  
}  
      
C test_2(int i) 
{  
	 cout<<"entering test_2"<<endl; 
   C x;  
   C a;  
   
   cout<<"leaving test_2"<<endl;
   if(i>0) 
       return x;   
   else  
       return a;    //x和a都会析构,返回的时候,先调用拷贝构造函数,初始化返回值(此处为main里面的z),  
                    //然后再析构a和x  
} 


C test_3(C t)
{
	return t;        //此处导致t的构造和析构
} 

C test_4(C t)
{
	C x=t;
	return x;        //此处导致t的构造和析构,但是x只会构造不会析构
} 

int main()
{
   cout<<"invoking test_1"<<endl;
   C y=test_1(1); //这种调用不会有拷贝构造函数,y直接为test_1函数栈里面生成的对象,编译器优化的结果
   cout<<"end invoke test_1"<<endl;
   
   cout<<"================华丽分割线================="<<endl;
   
   cout<<"invoking test_2"<<endl;
   C z=test_2(1);  //这种情况会调用拷贝构造函数(nonconst版本),初始化z
   cout<<"end invoke test_2"<<endl; 
   
   cout<<"================华丽分割线================="<<endl;
   
   cout<<"invoking test_3"<<endl;
   C a=test_3(y);  
   cout<<"end invoke test_3"<<endl; 
   
   
   cout<<"================华丽分割线================="<<endl;
   
   cout<<"invoking test_4"<<endl;
   C b=test_4(y);  
   cout<<"end invoke test_4"<<endl;  
   
  cout<<"================华丽分割线================="<<endl;
   
   cout<<"开始测试临时变量何时析构"<<endl;
   test_2(1),                         //(注意结束处是逗号)此处返回的C没有指定任何变量,编译器会生成临时变量                                         
   cout<<"结束测试临时变量何时析构"<<endl;//临时变量会再语句的第一个分号处析构,cout完成之后析构
   
   cout<<"================华丽分割线================="<<endl;
   
   cout<<"开始测试临时变量何时析构"<<endl;
   test_2(1);                         //(注意结束处是分号)此处返回的C没有指定任何变量,编译器会生成临时变量                                         
   cout<<"结束测试临时变量何时析构"<<endl;//临时变量会再语句的第一个分号处析构,cout开始之前析构
   
   cout<<"================华丽分割线================="<<endl;
   cout<<"================下面开始析构栈里面的变量了,啦啦啦================="<<endl;
   cout<<"================析构顺序按照入栈的顺序,后进先出,后构造,先析构==========="<<endl;
   return 0;

}
 



运行结果:


AlexdeMacBook-Pro:~ alex$ a.out
invoking test_1
entering test_1
C default constructor(),this=0x7fff5929baa8
C default constructor(),this=0x7fff5929b8d8
leaving test_1
C destructor(),this=0x7fff5929b8d8
end invoke test_1
================华丽分割线=================
invoking test_2
entering test_2
C default constructor(),this=0x7fff5929b8d8
C default constructor(),this=0x7fff5929b8d0
leaving test_2
C nonconst copy constructor(),this=0x7fff5929ba98,reference=0x7fff5929b8d8
C destructor(),this=0x7fff5929b8d0
C destructor(),this=0x7fff5929b8d8
end invoke test_2
================华丽分割线=================
invoking test_3
C nonconst copy constructor(),this=0x7fff5929ba88,reference=0x7fff5929baa8
C nonconst copy constructor(),this=0x7fff5929ba90,reference=0x7fff5929ba88
C destructor(),this=0x7fff5929ba88
end invoke test_3
================华丽分割线=================
invoking test_4
C nonconst copy constructor(),this=0x7fff5929ba78,reference=0x7fff5929baa8
C nonconst copy constructor(),this=0x7fff5929ba80,reference=0x7fff5929ba78
C destructor(),this=0x7fff5929ba78
end invoke test_4
================华丽分割线=================
开始测试临时变量何时析构
entering test_2
C default constructor(),this=0x7fff5929b8d8
C default constructor(),this=0x7fff5929b8d0
leaving test_2
C nonconst copy constructor(),this=0x7fff5929ba70,reference=0x7fff5929b8d8
C destructor(),this=0x7fff5929b8d0
C destructor(),this=0x7fff5929b8d8
结束测试临时变量何时析构
C destructor(),this=0x7fff5929ba70
================华丽分割线=================
开始测试临时变量何时析构
entering test_2
C default constructor(),this=0x7fff5929b8d8
C default constructor(),this=0x7fff5929b8d0
leaving test_2
C nonconst copy constructor(),this=0x7fff5929ba68,reference=0x7fff5929b8d8
C destructor(),this=0x7fff5929b8d0
C destructor(),this=0x7fff5929b8d8
C destructor(),this=0x7fff5929ba68
结束测试临时变量何时析构
================华丽分割线=================
================下面开始析构栈里面的变量了,啦啦啦=================
================析构顺序按照入栈的顺序,后进先出,后构造,先析构===========
C destructor(),this=0x7fff5929ba80
C destructor(),this=0x7fff5929ba90
C destructor(),this=0x7fff5929ba98
C destructor(),this=0x7fff5929baa8
AlexdeMacBook-Pro:~ alex$
AlexdeMacBook-Pro:~ alex$




结论:

一:return by value时候编译器的优化

编译器在能够做优化的时候,会尽量帮你做优化,比如test_1,总是将栈里面的x直接给调用者,避免了多一次的析构和构造。即使在关闭编译器优化的时候,它依然给你做了这个动作。但是在test_2里面,返回值是动态的,随着参数变动而变动,编译器是没有办法得知保留哪个的,于是索性都析构了。


在Effective C++ Item 21,page 94, Don't try to return a reference when you must return an object.

作者说:C++和所有编程语言一样,允许编译器实现者施行最优化,用以改善产出码的效率却不改变其可观察的行为。


g++确实对此做了优化,但是动态返回值,编译器却无能为力。你无法要求编译器按照每个分支,生成不同的代码。否则在复杂的程序下,生成的可执行文件大小那将无法估计了


二:临时变了的析构时机

临时变量,总是在执行完生成临时变量的那一行代码之后析构

(是不是比较拗口?)

那就这样说吧:生成临时变量之后,遇到第一个分号,析构函数开始调用






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值