C++左值、右值、左值引用、右值引用

 左值(lvalue)和右值(rvalue)

左值(lvalue):locator value,存储在内存中、有明确的的地址(可寻址)的数据

能够取地址,有名字的值就是左值

//左值引用
int a=10;
int &a1=a;//正确,a为左值类型,因为可以我们找到他的地址
int &a=10;//错误,左值引用不能引用一个右值类型的常量,(10是常量,常量为右值)

 编译器允许我们对左值建立引用,但不允许对右值建立引用。除非使用常量左值引用操作右值:

int num = 10;
const int &b = num;
const int &c = 10;

 也就是说常量左值引用即可以操作左值,也可以操作右值。

右值(rvalue):read value, 不能取地址,无名字的值就是右值。

举个例子:

int a = b+c, a 就是左值,其有变量名为a,通过&a可以获取该变量的地址;

表达式b+c、函数int fun()的返回值是右值,在其被赋值给某一变量前,我们不能通过变量名找到它,&(b+c)这样的操作则不会通过编译。

//右值引用
int &&a=10;//正确,右值引用一个右值类型
a = 100;
cout << a << endl;//100

int var = 10;
int&& var1 = var;//错误,因为无法将右值引用绑定到左值上
//右值引用绝对不能引用左值类型的,加上const也不行,这点是和左值引用不同的地方
const int&& var1 = var//照样报错,加上const也不行

//非引用返回的函数返回的都是右值,引用返回的函数返回的是左值
int fun1() { }
int &fun2() { }
int main()
{
	int& z = fun1();//左值引用,报错
	const int& z1 = fun1();//正确
	int&& z2 = fun1();//正确

    int& z = fun2();//左值引用,正确
	const int& z1 = fun2();//延长生命期的左值引用,正确
	int&& z2 = fun2();//报错,右值引用不能绑定左值
	const int&& z2 = fun2();//报错
}

常量右值引用:

const int&& a = 10;//编译器不会报错

 但这种定义出来的右值引用并无实际用处:

一方面,右值引用主要用于移动语义和完美转发,其中需要有修改右值的权限;

另一方面,常量右值引用的作用就是引用一个不可修改的右值,这些工作完全可以交给常量左值引用。

C++左值引用和右值引用
引用类型 可以引用的值类型 使用场景
非常量左值 常量左值 非常量右值 常量右值
非常量左值引用 Y N N N
常量左值引用 Y Y Y Y 常用于类中构建拷贝构造函数
非常量右值引用 N N Y N 移动语义、完美转发
常量右值引用 N N Y Y 无实际用途

 纯右值和将亡值

C++98中的右值就是纯右值,纯右值指的是临时变量值,不跟对象关联的字面量值

  • 临时变量值:非引用返回的函数返回值、表达式等。
  • 不跟对象关联的字面量值:例如true、2、"Hello World"等。

C++11对C++98中的右值进行了扩充。在C++11中右值又分为纯右值(prvalue,Pure Rvalue)将亡值(xvalue,eXpiring Value)。其中纯右值的概念等同于我们在C++98标准中右值的概念,指的是临时变量和不跟对象关联的字面量值;

将亡值则是C++11新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的对象(移为他用),比如:

  • 返回右值引用T&&的函数返回值
  • std::move的返回值
  • 转换为T&&的类型转换函数的返回值

将亡值可以理解为通过“盗取”其他变量内存空间的方式获取到的值。在确保其他变量不再被使用、或即将被销毁时,通过“盗取”的方式可以避免内存空间的释放和分配,能够延长变量值的生命期

一般情况下,右值引用是不能引用左值的,但是可以使用std::move()函数将左值强制转换为右值

int a;
int &&r1 = c;             // 编译失败
int &&r2 = std::move(a);  // 编译通过
#include 
#include 
#include 
#include
using namespace std;

class A
{
public:
	A() {
		a = 666;
		cout << "构造A" << endl;
	}
	A(const A&a) {
		cout << "拷贝构造A" << endl;
	}
	~A() {
		cout << "析构A" << endl;
	}

private:
	int a;
};

int main(void) {
	
	int a = 6;
	int &&b = std::move(a);
	cout << "b = "<<++b << endl;//b=7
	cout << "a = "< vec;
	//调用常规的拷贝构造函数,新建字符串,拷贝数据
	vec.push_back(s);
	cout << "在拷贝之后:"<< s << endl;
	
	//调用移动构造函数,掏空了s
	vec.push_back(std::move(s));
	cout << "在移动之后:" << s << endl;

	A aa;
	vector vec_A;
	vec_A.emplace_back(aa);

	//cout << "移动构造:" << endl;
	//调用移动构造函数,掏空了s
	//vec.emplace_back(std::move(aa));


	return 0;
}

C++11 左值、右值、右值引用详解

c++ 之 std::move 原理实现与用法总结

C++11右值引用(一看即懂)

你可能感兴趣的:(CPP,c++,内存管理)