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

来源:互联网 发布:李健 知乎 编辑:程序博客网 时间:2024/05/22 14:54

印象中,函数调用的时候,参数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
原创粉丝点击