1.什么是临时对象?
临时对象是 生命周期非常短的对象,通常在表达式中为了满足某种特定需求而创建,并在表达式求值完成后立即销毁
l临时对象是一个右值,可以用左值来接收
2.临时对象的出现场景
- 函数返回值
- 参数值传递
- 中间算术表达式
- 隐式类型转换
- 匿名对象
2.1函数返回值
people get_people()
{
people p;
return p; //p为局部对象脱离作用域后析构 return的是p的拷贝临时对象
}
编译器通过RVO可能会直接将p在临时对象中构造 优化拷贝构造
2.2参数值传递
void dis_play(people p_) //形参值传递 拷贝p的临时对象传给形参p_
{
people->display();
}
int main()
{
people p;
dis_play(p);
}
我们一般需要保持对原始数据的修改或者当对象比较大的时候传指针或者引用来减少拷贝开销
2.3中间算术表达式
#include<iostream>
using namespace std;
class people{
public:
people(){age_=0;name_="无名小卒";dollars_=0;};
people(string name):name_(name){age_=0,dollars_=0;}
people(int dollars):dollars_(dollars){age_=0,name_="无名小卒";}
people(int age,string name,int dollars):age_(age),name_(name),dollars_(dollars){};
people operator+(const people& other)
{
return people(this->dollars_+other.dollars_);
}
~people(){};
int age_;
string name_;
int dollars_;
};
int main()
{
people a(18,"lidiao",100);
people b(18,"lihua",200);
people c=a+b; //a+b的临时对象 被c接收
people d=a+b+c // b+c临时对象和a生成a+(b+c)的临时对象 被d接收
}
2.4隐式类型转换
#include<iostream>
using namespace std;
class people{
public:
people(){age_=0;name_="无名小卒";dollars_=0;};
people(string name):name_(name){age_=0,dollars_=0;}
people(int dollars):dollars_(dollars){age_=0,name_="无名小卒";}
people(int age,string name,int dollars):age_(age),name_(name),dollars_(dollars){};
people operator+(const people& other)
{
return people(this->dollars_+other.dollars_);
}
~people(){cout<<"析构函数的调用"<<endl;};
int age_;
string name_;
int dollars_;
};
int main()
{
people c = 10; //隐式转换 找到相应的构造函数 构造一个临时对象 给c接收
cout << c.name_ << " " << c.age_ << " " << c.dollars_ << endl;
}
这里属于单参构造函数的隐式调用,我们想禁用时可以用explicit关键字防止类型安全问题
2.5匿名对象
people p=people()
这里的people为临时对象
3.临时对象带来的问题
主要还是拷贝开销问题
4.怎么解决临时对象的拷贝开销问题
4.1形参使用指针或者引用传递
4.2移动语义
使用移动构造或者move转移资源所有权,减少拷贝构造带来的临时对象
移动语义可以看这里:左右值,移动语义,完美转发
4.3系统RVO优化
编译器可以直接在返回值所在的内存区域构造对象,而不是拷贝局部对象给返回值
RVO场景
只要返回的对象是唯一的、局部的、栈分配的临时对象且无条件控制和动态处理等
void get_people()
{
people p;
return p; //按照常理我们会拷贝p给返回值 这里无条件控制和动态处理 编译器优化直接在返回值函数栈内存上构造
}
void get_people()
{
return people();
}
RVO失效的场景
存在条件控制和动态处理
返回的对象不唯一(if条件控制),或者存在其他动态处理我们无法直接优化
返回的是指针或者引用
对象是局部的脱离函数后失效所以如果是指针和引用则会创建临时对象
返回的是堆分配的对象或者指针
函数返回的是基类型,而构造的是派生类型。由于涉及类型切片,编译器无法直接在调用方栈空间中构造派生类对象。