您的位置:首页 > 编程语言 > C语言/C++

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

2015-10-06 03:17 417 查看
印象中,函数调用的时候,参数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++确实对此做了优化,但是动态返回值,编译器却无能为力。你无法要求编译器按照每个分支,生成不同的代码。否则在复杂的程序下,生成的可执行文件大小那将无法估计了

二:临时变了的析构时机

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

(是不是比较拗口?)

那就这样说吧:生成临时变量之后,遇到第一个分号,析构函数开始调用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: