什么是右值引用
右值引用是在c++11标准中提出的,在此之前我们讲的引用都是左值引用。
要想搞明白左值引用和右值引用,首先要区分左值和右值。
在c++11中,右值又分为纯右值和将亡值,纯右值指的是临时变量和不跟对象关联的字面量值,将亡值指的是和右值引用相关的表达式,这样的表达式通常表示将要被移动的对象。
简单一点讲:可以用&去地址的表达式或变量是左值,不能用&取地址的变量或表达式是右值。
比如:
#include<iostream>
using namespace std;
int main()
{
int a = 8; // a为左值;8为右值
int& b = a; // b为左值引用
int&& c = 8; // c为右值引用
const int& d = 8; // d是一种特殊的左值引用
cout << "a: " << a << " b: " << b << " c: " << c << " d: " << d << endl;
//cout << "&a++: " << &a++ << endl; // a++ 为右值 不能取址
cout << "&++a: " << &++a << endl;
return 0;
}
移动语义
移动语义是c++标准中新增的改动,它通过转移资源的方式避免了拷贝带来的资源开销,使内存管理更加细致,当然被转移的一方将不可在使用。代码如下:
#include<iostream>
using namespace std;
class A
{
public:
A(){}
~A()
{
delete _ptr;
_ptr = nullptr;
}
void alloc()
{
_ptr = new int;
memset(_ptr, 0, sizeof(int));
}
/// 拷贝构造函数
A(const A& a)
{
cout << "拷贝构造调用" << endl;
if (!_ptr)
alloc();
memcpy(_ptr, a._ptr, sizeof(int));
}
A& operator= (const A& a)
{
cout << "赋值函数调用" << endl;
if (this == &a)
return *this;
if (!_ptr)
alloc();
memcpy(_ptr, a._ptr, sizeof(int));
return *this;
}
/// 移动构造函数
A(A&& a)
{
cout << "移动构造函数调用" << endl;
if (!_ptr)
delete _ptr;
_ptr = a._ptr;
a._ptr = nullptr;
}
A& operator= (A&& a)
{
cout << "移动赋值函数调用" << endl;
if (this == &a)
return *this;
if (!_ptr)
delete _ptr;
_ptr = a._ptr;
a._ptr = nullptr;
return *this;
}
int* _ptr;
};
int main()
{
A a;
a.alloc();
*a._ptr = 88;
A a1 = a;
A a2;
a2 = a;
cout << "a2:" << *a2._ptr << endl;
auto fun = [] {
A aa;
aa.alloc();
*aa._ptr = 99;
return aa;
};
A a3 = fun();
cout << "a3:" << *a3._ptr << endl;
A a4;
a4 = fun();
cout << "a4:" << *a4._ptr << endl;
return 0;
}
在类中增加移动构造函数和移动赋值函数,当参数为右值时,会调用对应的函数,将资源转移至目标对象中,避免了拷贝构造函数和赋值函数中拷贝带来的额外开销。记得编译时要用命令 -fno-elide-constructors 把优化关掉,否则编译器会优化fun()的返回。
完美转发
当我们需要通过函数转发变量时,普通方法无法将变量的左值右值属性“完美”转移过去。
c++11中提供了模版函数std::forward<T>(参数),用于完美转发参数的左右值属性。代码:
#include<iostream>
using namespace std;
void fun(int& val)
{
cout << "左值调用 val:" << val << endl;
}
void fun(int&& val)
{
cout << "右值调用 val:" << val << endl;
}
template <typename T>
void f(T&& val)
{
fun(forward<T>(val));
}
int main()
{
int val = 8;
f(val);
f(9);
return 0;
}