浅谈C++中的临时对象
2017-10-10 07:52
232 查看
本文主要总结了在C++开发过程中所遇到的临时对象产生的相关问题,如有不足或有误之处还希望读者朋友们能够及时之处。
三种常见的临时对象创建的情况:
以值的方式给函数传参
类型转换
函数需要返回对象时
一、以值的方式给函数传参
1、按值传递
按值传递时,首先将需要传给函数的参数,调用拷贝构造函数创建一个副本,所有在函数里的操作都是针对这个副本,也正是因为这个原因,在函数体力对该副本进行任何操作都不会影响原参数。
2、按引用传递
引用则不然,它是对象本身,只是一个别名而已。可以参考指针和引用什么时候用?来理解引用。
3、例子
运行结果:
通过以上程序可以看到调用了拷贝构造函数,这是tm在传给GetSum函数做参数时调用的。此时调用拷贝构造函数创建了一个副本,为GetSum函数体内使用。通过运行结果可以看到,在GetSum函数体内对tm副本进行的修改并没有影响到tm本身。
可以按照如下方式修改,函数声明和定义都需要改:
运行结果:
通过传递常量引用,减少了一次临时对象的创建。这个改动也许很小,但对多继承的对象来说在构建时要递归调用所有基类的构造函数,这对于性能来说是个很大的消耗。
二、类型转换生成的临时对象
运行结果:
main函数创建了两个对象,但输出却调用了三次构造函数。关键在
解决办法:
输出结果:
此时,“=”号由原本的赋值变成了构造。对sum的构造推迟了,当定义TempObj sum时,在main的栈中为sum对象创建了一个预留空间,而我们用1000调用构造函数时,此时的构造函数是在sum预留的空间中进行的,因此减少了一次临时对象的创建。
三、函数返回一个对象
当函数需要返回一个对象,它会在栈中创建一个临时对象,存储函数的返回值。
运行结果:
分析:
上述代码
注意,第二步创建的返回对象是难以避免的,此处如果是返回引用,在函数里创建的局部对象,在返回时就被销毁了,这时若再引用该对象就会产生未知错误。
可以直接操作返回对象,具体如下:
也可以结合上面讲到的类型转换生成临时对象解决办法进行优化,具体修改如下:
运行结果如下:
分析发现减少了一次构造函数调用tmp,一次拷贝构造函数(tmp拷贝给返回对象)调用和一次赋值运算符重载函数调用。这是因为返回对象直接使用编译器为sum预留的空间,所以减少了返回临时对象的生成,返回对象即是sum,返回对象的创建即是sum对象的创建。
三种常见的临时对象创建的情况:
以值的方式给函数传参
类型转换
函数需要返回对象时
一、以值的方式给函数传参
1、按值传递
按值传递时,首先将需要传给函数的参数,调用拷贝构造函数创建一个副本,所有在函数里的操作都是针对这个副本,也正是因为这个原因,在函数体力对该副本进行任何操作都不会影响原参数。
2、按引用传递
引用则不然,它是对象本身,只是一个别名而已。可以参考指针和引用什么时候用?来理解引用。
3、例子
#include <iostream> using namespace std; class TempObj { public: TempObj(int m = 0, int n = 0); TempObj(TempObj& to) { cout << "Copy Constructor" << endl; m_Number = to.m_Number; m_Size = to.m_Size; } ~TempObj() { cout << "Deconstructor" << endl; } int GetSum(TempObj ts); public: int m_Number; int m_Size; }; TempObj::TempObj(int m,int n) { cout << "Default constructor" << endl; m_Number = m; m_Size = n; cout << "m_Number=" << m_Number <<" "<< "m_Size=" << m_Size << endl; } int TempObj::GetSum(TempObj ts) { int tmp = ts.m_Number + ts.m_Size; ts.m_Number = 1000; return tmp; } void main() { TempObj tm(10,20); cout << "sum=" << tm.GetSum(tm) << endl; cout << "tm.m_Number=" << tm.m_Number << endl; }
运行结果:
通过以上程序可以看到调用了拷贝构造函数,这是tm在传给GetSum函数做参数时调用的。此时调用拷贝构造函数创建了一个副本,为GetSum函数体内使用。通过运行结果可以看到,在GetSum函数体内对tm副本进行的修改并没有影响到tm本身。
可以按照如下方式修改,函数声明和定义都需要改:
int GetSum(TempObj& ts); int TempObj::GetSum(TempObj& ts) { int tmp = ts.m_Number + ts.m_Size; ts.m_Number = 1000;//此时通过ts这个引用对象本身 return tmp; }
运行结果:
通过传递常量引用,减少了一次临时对象的创建。这个改动也许很小,但对多继承的对象来说在构建时要递归调用所有基类的构造函数,这对于性能来说是个很大的消耗。
二、类型转换生成的临时对象
#include <iostream> using namespace std; class TempObj { public: TempObj(int m = 0, int n = 0); TempObj(TempObj& to) { cout << "Copy Constructor" << endl; m_Number = to.m_Number; m_Size = to.m_Size; } ~TempObj() { //cout << "Deconstructor" << endl; } int GetSum(TempObj &ts); public: int m_Number; int m_Size; }; TempObj::TempObj(int m,int n) { cout << "Default constructor" << endl; m_Number = m; m_Size = n; cout << "m_Number=" << m_Number <<" "<< "m_Size=" << m_Size << endl; } int TempObj::GetSum(TempObj &ts) { int tmp = ts.m_Number + ts.m_Size; ts.m_Number = 1000; return tmp; } void main() { TempObj tm(10,20),sum; sum = 1000; cout << "sum=" << tm.GetSum(sum) << endl; }
运行结果:
main函数创建了两个对象,但输出却调用了三次构造函数。关键在
sum = 1000;本身1000和sum类型不符,但编译器为了通过编译以1000为参数调用构造函数创建了一个临时对象。
解决办法:
void main() { TempObj tm(10,20); TempObj sum = 1000; cout << "sum=" << tm.GetSum(sum) << endl; }
输出结果:
此时,“=”号由原本的赋值变成了构造。对sum的构造推迟了,当定义TempObj sum时,在main的栈中为sum对象创建了一个预留空间,而我们用1000调用构造函数时,此时的构造函数是在sum预留的空间中进行的,因此减少了一次临时对象的创建。
三、函数返回一个对象
当函数需要返回一个对象,它会在栈中创建一个临时对象,存储函数的返回值。
#include <iostream> using namespace std; class TempObj { public: TempObj(int m = 0);//default constructor TempObj(TempObj& to) { cout << "copy constructor" << endl; m_Number = to.m_Number; } TempObj& operator=(TempObj& to) { cout << "operator=(TempObj& to)" << endl; m_Number = to.m_Number; return *this; } ~TempObj() { } public: int m_Number; }; TempObj::TempObj(int m) { cout << "default constructor" << endl; m_Number = m; cout << "m_Number=" << m_Number << endl; } TempObj Double(TempObj& to) { TempObj tmp; tmp.m_Number = to.m_Number*2; return tmp; } void main() { TempObj tm(10), sum; cout << endl; sum = Double(tm); cout << endl; cout << "sum.m_Number=" << sum.m_Number << endl; }
运行结果:
分析:
上述代码
sum = Double(tm);这条语句生成了两个对象。第一,显示创建了一个tmp临时对象(
TempObj tmp;);其次,将temp对象返回,返回过程中调用了拷贝构造函数创建一个返回对象(
return tmp;);第三,将返回结果通过调用赋值运算符重载函数赋值给sum(
sum = Double(tm);),该步骤没有创建对象。可参考C++赋值运算符重载函数和拷贝构造函数进行理解。
注意,第二步创建的返回对象是难以避免的,此处如果是返回引用,在函数里创建的局部对象,在返回时就被销毁了,这时若再引用该对象就会产生未知错误。
可以直接操作返回对象,具体如下:
TempObj Double(TempObj& to) { return to.m_Number*2; }
也可以结合上面讲到的类型转换生成临时对象解决办法进行优化,具体修改如下:
#include <iostream>
using namespace std;
class TempObj {
public:
TempObj(int m = 0);//default constructor
TempObj(TempObj& to)
{
cout << "copy constructor" << endl;
m_Number = to.m_Number;
}
TempObj& operator=(TempObj& to)
{
cout << "operator=(TempObj& to)" << endl;
m_Number = to.m_Number;
return *this;
}
~TempObj()
{
}
public:
int m_Number;
};
TempObj::TempObj(int m)
{
cout << "default constructor" << endl;
m_Number = m;
cout << "m_Number=" << m_Number << endl;
}
TempObj Double(TempObj& to) { return to.m_Number*2; }
void main()
{
TempObj tm(10);
cout << endl;
TempObj sum = Double(tm);
cout << endl;
cout << "sum.m_Number=" << sum.m_Number << endl;
}
运行结果如下:
分析发现减少了一次构造函数调用tmp,一次拷贝构造函数(tmp拷贝给返回对象)调用和一次赋值运算符重载函数调用。这是因为返回对象直接使用编译器为sum预留的空间,所以减少了返回临时对象的生成,返回对象即是sum,返回对象的创建即是sum对象的创建。
相关文章推荐
- 关于C++临时对象的一道题目
- c++临时对象的来源即成本
- C++临时对象销毁时间
- C++ 临时对象的生存周期
- C++中的临时对象(拷贝构造函数)(上)
- 理解临时对象的来源(More Effective C++_19(效率))
- C++以对象作为返回值时编译器的优化,以及临时变量的析构时机
- C++中的临时对象
- C++中临时对象的产生
- printf和wprintf、printf输出结束标识符、c++按值返回临时对象是否是const的实验
- 浅谈c++继承与对象模型
- 笔记--c++临时对象与const
- C++中的临时对象
- c++ l临时对象是const
- (C/C++学习笔记)临时对象深入理解
- C++中的临时对象(http://blog.chinaunix.net/u/11680/showart_316503.html)
- c++ 临时对象的来源
- 浅谈C++中派生类对象的内存布局
- C++中的临时对象
- C++中的临时对象(拷贝构造函数)(下)