c++11中引入了右值引用。
左值与右值
先区分左值与右值,这里参考c++ 右值引用中对左值和右值区分方法:
左值和右值都是针对表达式,左值是指表达式结束后依然存在的持久对象,右值是指表达式结束时就不再存在的临时对象。
一个区分左值和右值的简便方法是:看能不能对表达式进行取地址。如果能,则为左值;如果不能,则为右值。
int a, b;
int* pFlag = &a;
vector<int> vcTemp;
string("hello");
const int &m=1;
a 、b 是持久对象(可以对它取地址),为左值
a+b 返回一个临时对象,非持久对象(不可取地址),为右值
a++ 先拷贝a变量,然后对a自增,接着返回拷贝的临时变量,为右值
++a 对a自增,然后返回a本身,为左值
pFlag、*pFlag 都是持久对象,为左值
vcTemp[0] 重载[] 操作符,返回int& ,为持久对象,为左值。
100、string(“hello”) 都是临时对象,为右值
m 是常量引用,引用到一个右值,但引用本身是一个持久对象,故为左值。
一般的情况开销
在不使用右值引用时,程序在运行过程中产生和销毁临时变量会浪费资源,而右值引用,就相当于“窃取”了临时变量的存储地址为己用。语法是 &&
#include<iostream>
#include<utility>
using namespace std;
class A
{
int i;
public:
void play()
{
++i;
}
~A()
{
cout<<"I am out"<<endl;
}
};
A fun()
{
A a;
a.play();
//return move(a);
return a;
}
int main()
{
A b = fun();
}
每个编译器都有自己的优化,MinGW中,输出:
I am out
这只有输出一次,说明只建立了一次A对象,是最好的情况。
但是在VC14++中:
I am out
I am out
输出了两次,说明,即使有些编译器有优化,但是临时变量的开销依然是存在的。
移动构造函数和移动赋值运算
用一张图解释(来源:【C\C++学习】之十八、C++11六大函数(构造函数,移动构造函数,移动赋值操作符,复制构造函数,赋值操作符,析构函数) )
Talk is cheap. Let me show the code. : )
#include<iostream>
#include<utility>
#include<cstring>
using namespace std;
class A
{
public:
char* pData;
A() {}
//复制构造函数
A(const A& a)
{
pData = new char[strlen(a.pData)+1];
strcpy(pData, a.pData);
}
//移动构造函数
A(A&& a)
{
pData = a.pData;
a.pData = nullptr;
}
~A()
{
if(pData!=nullptr)
{
delete[] pData;
pData = nullptr; //这是个好习惯
}
}
//赋值运算
A& operator= (const A& a)
{
pData = new char[strlen(a.pData)+1];
strcpy(pData, a.pData);
return *this;
}
//移动赋值运算
A& operator= (A&& a)
{
pData = a.pData;
a.pData = nullptr;
return *this;
}
void play()
{
if(pData!=nullptr)
cout<<pData;
cout<<endl;
}
};
int main()
{
A a;
a.pData = new char[10]
{
'1','2','3','4','5','6','7','8','9','\0'
};
A b = move(a);
cout<<"a: ";
a.play();
cout<<"b: ";
b.play();
}
输出:
a:
b: 123456789
参考:
c++ 右值引用
move 函数
【C\C++学习】之十八、C++11六大函数(构造函数,移动构造函数,移动赋值操作符,复制构造函数,赋值操作符,析构函数)
移动构造函数和移动赋值运算符 (C++)