左值和左值引用
什么是左值?什么是左值引用?
左值是一个表示数据的表达式(如变量名或解引用的指针),可以获取它的地址、也可以对它赋值, 左值可以出现在赋值符号左边,可以出现在赋值符号右边(如左值引用)。右值不能出现在赋值符号左边。
左值引用就是给左值的引用,给左值起别名。
// 左值和左值引用
int main()
{
// a,b,c都是左值,*b也是左值
int a = 1;
int* b = new int;
const int c = 10;
// 左值引用
int& ra = a;
int*& rb = b;
const int& rc = c;
int& rp = *b;
return 0;
}
右值和右值引用
什么是左值?什么是左值引用?
右值也是一个数据的表达式,如:字面常量,表达式返回值,函数返回值(传值返回中间的临时变量)等。右值出现在赋值符号的右边,不能出现在赋值符号的左边,不能取地址。
右值引用就对右值的引用,给右值起别名。
// 右值和右值引用
int main()
{
int x, y;
// 下面就是常见的右值
1;
x + y;
fun(x, y); //返回时中间的临时变量
// 右值引用
int&& r1 = 1;
int&& r2 = x + y;
int&& r3 = func(x, y);
return 0;
}
两者比较
左值引用总结:
- 左值引用只能引用左值,不能引用右值。
const左值引用既可以引用左值,又可以引用右值。
int main()
{
int x, y;
// const左值引用可以引用右值
int& ret = (x + y); // 报错
const int& ret = (x + y);
}
右值引用总结:
- 右值引用只能引用右值,不能引用左值。
- 右值引用可以引用
move后的左值。
int main()
{
int a;
// 右值引用不可以给左值起别名
int&& ref1 = a;
//右值引用可以给move后的左值起别名
int&& ref2 = move(a);
return 0;
}
应用
C++提出左值引用是为了提高效率,比如传引用传参,传引用返回,目的就是为了减少拷贝。尤其对于自定义类型来说,拷贝的代价很大。。那C++11提出右值引用又是为了什么? 其实还是为了提高效率。来看下面代码:
class A
{
public:
A()
:_a(new char)
{}
A(const A& a)
{
cout << "A(const A& a) ---- 深拷贝" << endl;
_a = new char;
}
void swap(A& a)
{
::swap(_a, a._a);
::swap(_b, a._b);
}
A(A&& a)
:_a(nullptr)
{
cout << "A(A&& a) ---- 移动构造" << endl;
swap(a);
}
A& operator+=(int x)
{
_b += x;
return *this;
}
A operator+(int x)
{
A tmp(*this);
tmp += x;
return tmp;
}
private:
char* _a;
int _b = 1;
};
int main()
{
A a1;
A a2 = a1;
A a3 = (a2 + 1);
return 0;
}
无移动构造函数时:
可以看到,
return tmp的时候要调用拷贝构造,那必然会开辟额外的空间,对于深拷贝来说是很大的消耗。但是此时tmp是一个将亡值,如果我们可以将它的资源给转移走,就不用开辟额外空间。



添加移动构造:
可以看到,在
return tmp的时候在没有发生拷贝,只是将它的资源转移走。



~~接着更