C++11左值、右值

知识回顾,详解引用

简单概括,引用就是给已存在对象取别名,引用变量与其引用实体共用同一块内存空间

左右值区分

注意:不一定=左边的都是左值,=右边的都是右值

=左边的也可能是右值,等号右边的也可能是左值

int main()
{
	int a = 10;
	int b = 20;

	// a和b都是左值,b既可以在=的左侧,也可在右侧,
	// 说明:左值既可放在=的左侧,也可放在=的右侧
	a = b;
	b = a;

	const int c = 30;
	// 编译失败,c为const常量,只读不允许被修改
	//c = a;
	// 因为可以对c取地址,因此c严格来说不算是左值
	cout << &c << endl;

	// 编译失败:因为b+1返回的是一个临时变量,没有具体名称,也不能取地址,因此为右值
	//b + 1 = 20;
	//b+1会调用编译器自己的operator+这个函数,然后返回结果会返回一个临时对象
	return 0;
}

C++11对右值进行了严格的区分:

1.C语言中的纯右值,比如:a+b, 100,a+b返回的是一个临时对象,不能对该对象取地址,所以为右值

2.匿名对象也是右值,不能取地址

3.将亡值。比如:表达式的中间结果、函数按照值的方式进行返回。

4.const修饰的常量,不可修改,只读类型的,理论应该按照右值对待,但因为其可以取地址(如果只是 const类型常量的定义,编译器不给其开辟空间,如果对该常量取地址时,编译器才为其开辟空间), C++11认为其是左值

右值引用

int main()
{
	//reference引用ra=reference_a
	int a = 1;
	int& ra1 = a;//=不表达赋值,仅表示将a变量所代表的空间重新起了一个ra的别名

	//cout << &1 << endl;//错误--提示表达式必须为左值
	//cout << &a << endl;//正确--区分&1,a是将1的值赋值给a,a已经开辟了空间,a为左值

	//1为右值(纯右值),不能对1取地址,简单理解为。
	//int& ra2 = 1;//没有地址就没有可操作空间没有空间就不能对该空间起别名
	
	//右值引用--&&
	//由于右值都没有具体空间,根据引用特性,对同一块空间取别名
	//我们必须创造一个空间,并将右值传递到该空间,才能使用右值引用
	int&& r1 = 1;//1赋值给一个临时空间,r1实际是对该临时空间的引用
	r1 = 2;//有了空间后就能修改
	cout << r1 << endl;
	
	//int&& r2 = a;//报错--无法将右值引用绑定到左值(右值引用不能引用左值)

	return 0;
}

const引用

int main()
{
	int a = 1;
	//const左值引用
	const int& ra1 = a;
	//const右值引用
	const int&& ra2 = 1;
	return 0;
}

const可以对右值引用也可以对左值引用

右值引用作用

C++11中右值引用主要有以下作用:

1. 实现移动语义(移动构造与移动赋值)

移动构造:

string& operator=(string&& s)
{
	swap(s);//对象里的变量和s(临时对象)内容交换后,将老版本内容随着s的生命周期的消失而delete
	return *this//返回对象
}

移动赋值:

String(String&& s)
	: _str(s._str)
{
	s._str = nullptr;
}

2. 给中间临时变量取别名(提高效率节省资源,对于开空间的自定义类)

左右值引用后修改值

int main()
{

	int a = 1;
	cout << "引用前a的值" << a << endl;
	//左值引用
	int& ra1 = a;
	ra1 = 2;
	cout << "引用修改后a的值" << ra1 << endl;

	//右值引用
	int&& ra2 = 1;
	cout << "引用前a的值" << ra2 << endl;
	ra2 = 3;
	cout << "引用修改后a的值" << ra2 << endl;
    //这里表明:右值(1)被右值引用(ra2)后的属性是左值ra2 = 3
	return 0;
}

为什么右值被右值引用后引用变量的属性是左值?

答:在被右值引用后会为右值创建一个临时变量≈创建了一个实际的存储空间。所以最后引用变量的属性是左值。

右值引用后引用变量的属性是左值

class String
{
public:
	//有参构造
	String(const char* str = "")
	{
		if (nullptr == str)
			str = "";
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	//拷贝构造
	String(const String& s)
		: _str(new char[strlen(s._str) + 1])
	{
		strcpy(_str, s._str);
	}

	//嵌套多个函数模拟实验右值在传递后的属性
	String copy(const String& s)
	{
		_str = new char[strlen(s._str) + 1];
		strcpy(_str, s._str);
		cout << "传递来的str_left_value为左值" << endl;
		return *this;
	}

	void class_copy(String&& str_left_value)//str_left_value记为str1
	{
		copy(str_left_value);//str_left_value记为str2
	}
	//移动语义-移动构造
	String(String&& s)
		: _str(s._str)
	{
		s._str = nullptr;
	}

	~String()
	{
		if (_str) delete[] _str;
	}
private:
	char* _str;
};

int main()
{
	String str;
	str.class_copy("abc");

	return 0;
}

图解:

因为此时还是对数组类型进行操作,不断的复制已有内容会造成资源的浪费,我们依旧还想用右值引用来减少浪费

class String
{
public:
	//有参构造
	String(const char* str = "")
	{
		if (nullptr == str)
			str = "";
		_str = new char[strlen(str) + 1];
		strcpy(_str, str);
	}
	//拷贝构造
	String(const String& s)
		: _str(new char[strlen(s._str) + 1])
	{
		strcpy(_str, s._str);
	}

	//仅测试

	void copy(String& s)
	{
		cout << "传递来的str_left_value为左值" << endl;
	}
	void copy(String&& s)
	{
		cout << "传递来的str_left_value为右值" << endl;
	}

	//右值引用后继续使用右值
	void class_copy(String&& str_left_value)
	{
		copy(move(str_left_value));
	}

	//移动语义-移动构造
	String(String&& s)
		: _str(s._str)
	{
		s._str = nullptr;
	}

	~String()
	{
		if (_str) delete[] _str;
	}
private:
	char* _str;
};

int main()
{
	String str;
	str.class_copy("abc");

	return 0;
}

此时只要我们将右值引用后引用变量的属性是左值让其move后转变成右值就可以继续使用右值引用

注意:为了避免const修饰右值引用导致资源无法转移,在以后使用右值引用转移资源时应避免或禁止使用const

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值