C++以对象作为返回值时编译器的优化,以及临时变量的析构时机
2015-10-06 03:17
417 查看
印象中,函数调用的时候,参数past by value、返回值return by value,总是伴随着对象的复制。
实际上参数传递是这样的,但是返回值有可能不是这样的(虽然大部分都是面临拷贝构造函数的调用),这取决于编译器。
运行结果:
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++确实对此做了优化,但是动态返回值,编译器却无能为力。你无法要求编译器按照每个分支,生成不同的代码。否则在复杂的程序下,生成的可执行文件大小那将无法估计了
二:临时变了的析构时机
临时变量,总是在执行完生成临时变量的那一行代码之后析构
(是不是比较拗口?)
那就这样说吧:生成临时变量之后,遇到第一个分号,析构函数开始调用
实际上参数传递是这样的,但是返回值有可能不是这样的(虽然大部分都是面临拷贝构造函数的调用),这取决于编译器。
#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++确实对此做了优化,但是动态返回值,编译器却无能为力。你无法要求编译器按照每个分支,生成不同的代码。否则在复杂的程序下,生成的可执行文件大小那将无法估计了
二:临时变了的析构时机
临时变量,总是在执行完生成临时变量的那一行代码之后析构
(是不是比较拗口?)
那就这样说吧:生成临时变量之后,遇到第一个分号,析构函数开始调用
相关文章推荐
- 【言简意赅,叼】c++中->怎么用
- More Effective C++ 条款26 限制某个class所能产生的对象数量
- c++ vector实验
- 带你玩转Visual Studio——带你多工程开发
- 解析C++编程中的继承方面的运用
- STL学习记录(九)Maps、Multimaps
- 为什么C++中只有指针和引用才能实现多态?
- 《高质量C++/C编程指南》笔记——内存管理1-2
- C/C++编程必备网址
- C语言编译全过程
- 深入理解c语言指针的奥秘
- 深入学习C语言系列(二): #define与typedef
- VC++动态链接库
- C语言深入学习系列+-+字节对齐and内存管理
- iOS开发分分钟搞定C语言 —— 总结
- C语言简单的一些简单例子
- iOS开发分分钟搞定C语言 —— 宏定义和关键字
- c++进阶
- c++进阶
- 10.5做题——全排列(初赛复习)