C++11右值引用


在这里插入图片描述

1.右值引用和移动语义

1.1左值引用和右值引用

之前我们对左右值比较浅显的概念是:左值可以修改 右值不可以修改
接下来我们会对左右值有进一步的理解:左值可以取地址

  1. 无论左值引用还是右值引用,都是给对象取别名
  2. 左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。
int main()
{
	//变量名
	int b = 0;
	int* pb = &b;         //左值可以取地址
	int& rb = b;          //左值引用

	//指针
	int* p = new int(1);  //*p也是一个左值
	int*& rp = p;
	int& pvalue = *p;     //左值引用

	//const后的左值
	const int c = 2;
	const int* pc = &c;
	//c = 20;             //不是所有的左值都可被修改 但是可以进行&操作
	const int& rc = c;    //左值引用

	//左值在=左右都可以出现
	int d = c;
	
	return 0;
}
  1. 右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名
double getmin(double a, double b)
{
	return a < b ? a : b;
}
double& Getmin(double a, double b)
{
	return a < b ? a : b;
}
int main()
{
	double x = 1.1, y = 2.2;
	// 以下几个都是常见的右值
	10;           //字面常量
	x + y;        //表达式返回值[此返回值有空间]
	getmin(x, y); //函数返回值[非左值引用返回]

	//右值引用
	int&& rr1 = 10;
	double&& rr2 = x + y;
	double&& rr3 = getmin(x, y);

	//编译报错 -- 右值不可修改 右值不可放在=左边 -- 表达式必须是可修改的左值
	10 = 1;
	x + y = 1;
	getmin(x, y) = 1; 
	Getmin(x, y) = 1; //左值引用返回不报错

	//表达式必须为左值或函数指示符 -- 右值不能取地址
	&10;
	&(x + y);
	&getmin(x, y);

	return 0;
}
  1. 右值引用特殊场景[不重要 了解即可]
int main()
{
	//不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1
	double x = 1.1, y = 2.2;
	int&& rr1 = 10;
	const double&& rr2 = x + y;
	
	rr1 = 20;
	rr2 = 5.5;  // 报错
	
	return 0;
}
  1. 左值引用和右值引用

左值引用只能引用左值,不能引用右值 非得引用右值 可以使用const左值引用
右值引用只能引用右值,不能引用左值 非得引用左值 可以引用move后的左值

int main()
{
	int a = 0;
	int b = 1;

	// 左值引用给左值取别名
	int& ref1 = a;

	// 左值引用给右值取别名需要加const 保持权限一致
	const int& ref2 = (a + b);

	// 右值引用给右值取别名
	int&& ref3 = (a + b);

	// 右值引用给move后左值取别名
	int&& ref4 = move(a);

	return 0;
}

1.2右值引用使用场景

区分传过来的参数是左值还是右值

1.string为例介绍

在这里插入图片描述
在这里插入图片描述

//void func(const int& a)
//{
//	cout << "void func(int& a) or void func(int&& a)" << endl;
//}

void func(int& a)
{
	cout << "void func(int& a)" << endl;
}

void func(int&& a)
{
	cout << "void func(int&& a)" << endl;
}

int main()
{
	int a = 0;
	int b = 1;

	func(a);     //传左值
	func(a + b); //传右值

	return 0;
}

在string类里面的应用

int main()
{
	string s1("hello"); 
	string s2("world");

	//左右值拷贝
	string s3 = s1;      //左值拷贝:不能修改s1的内容  s3和s1相互独立 这也是之前我们模拟实现为什么要搞深拷贝
	string s4 = s1 + s2; //右值拷贝:一个临时对象 具有常性 s1 + s2这个表达式的返回值所形成的临时对象 和s1\s2没有关系 
	                     //我们可以进行资源转移 而不再进行深拷贝

	return 0;
}

在模拟实现string类里进一步探索右值引用
普通拷贝参数const string& 既可引用左值又可引用右值
移动拷贝构造只可引用右值
右值传参调用移动拷贝构造而不调用普通拷贝构造:编译器调用最匹配的那个

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

代码示例

//模拟实现string类
namespace ape
{
	class string
	{
	public:
		//迭代器
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		//构造函数
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		//交换内容
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}

		//拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 左值深拷贝" << endl;

			string tmp(s._str);
			swap(tmp);
		}

		// 移动构造: 传过来的s[即 (s1 + '!')]是右值
		//ape::string ret2 = (s1 + '!'); 右值拷贝 ret2.string( (s1 + '!') );
		string(string&& s)
			:_str(nullptr)
		{
			cout << "  string(string&& s)    -- 右值移动拷贝" << endl;
			swap(s); //直接换过来
		}

		// 赋值重载
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 深拷贝" << endl;
			string tmp(s);
			swap(tmp);

			return *this;
		}

		//析构函数
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}

		//重载下标运算符[]
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		//扩容
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;

				_capacity = n;
			}
		}

		//尾插
		void push_back(char ch)
		{
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}

			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}

		//+=运算符重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//+号运算符重载 不改变this 返回新string
		string operator+(char ch)
		{
			cout << "operator+:";
			string tmp(*this);
			tmp += ch;
			return tmp;
		}

		//c_str函数
		const char* c_str() const
		{
			return _str;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity; 
	};

}

//整形变字符串
ape::string int_to_string(int value)
{
	bool flag = true;
	if (value < 0)
	{
		flag = false;
		value = 0 - value;
	}
	ape::string str;
	while (value > 0)
	{
		int x = value % 10;
		value /= 10;
		//数字变字符
		str += (x + '0' );
	}
	if (flag == false)
		str += '-';
	reverse(str.begin(), str.end());
	return move(str);
}

库里的移动构造

在这里插入图片描述

s1是个左值 move函数的返回值被识别为右值 – 调用移动构造 s3的内容[啥也没有]给了s1 s1的内容给了s3

2.其他容器在C++11后的构造函数

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.容器的插入函数

2.1list

在这里插入图片描述

2.2map

在这里插入图片描述

3.左值引用/右值引用的总结

左值引用通过引用直接操作 省去中间额外的拷贝
右值引用通过识别是左值还是右值 如果是右值 直接移动拷贝

4.完美转发

4.1首先看右值引用的特例

在这里插入图片描述

首先我们需要思考 C++11为什么要大费周折搞出一个右值引用? 右值引用的意义何在? 右值引用的初衷是啥?

显而易见,通过上述的讨论 我们知道 数据分为左值和右值 当涉及到容器的深层拷贝时 左值需要做深层拷贝[不动原数据 将原数据拷贝给新对象] 但是右值是一个临时变量 具有常性 当作用域完结 他的值也就消失 即所谓的"将亡值" 为了最大化提升C++语法的优势 大佬搞出了一个叫右值引用的东西 其目的就在于当数据为右值时 直接把该数据的资源转移到新对象上 而不管你这个临时对象被转移后会发生什么 因为你是个临时变量 程序结束自动销毁 但是上述代码又显示出了一个新的问题 当右值引用 引用了所传的参数 该引用的属性变为了左值

为什么要改成左值呢?

因为右值引用的初衷就是当参数时右值 可以直接进行资源转移 要想转移 此引用的属性就需要时左值

由此引发了新的问题 如下代码的输出违背了代码的本意

void Fun(int& x) 
{ 
	cout << "左值引用" << endl; 
}
void Fun(const int& x) 
{ 
	cout << "const 左值引用" << endl; 
}

void Fun(int&& x) 
{ 
	cout << "右值引用" << endl;
}
void Fun(const int&& x)
{ 
	cout << "const 右值引用" << endl;
}

//万能引用/引用折叠:既可以引用左值 又可以引用右值
template<typename T>
//这里是一个右值引用 当这个函数被调用 t是参数的右值引用 但是此时 t的属性变为了左值
void PerfectForward(T&& t)
{
	//Fun(forward<T>(t));
	Fun(t);
}

int main()
{
	PerfectForward(10);           // 右值

	int a;
	PerfectForward(a);            // 左值
	PerfectForward(move(a));      // 右值

	const int b = 8;
	PerfectForward(b);		      // const 左值
	PerfectForward(move(b));      // const 右值

	return 0;
}

由于t的属性成为左值 所以不管你原来是左值还是右值 都会调用左值的代码
在这里插入图片描述

怎么办?怎么解决?好不容易搞出来的右值引用难不成要抛弃?解决办法见下:

在这里插入图片描述
此时 代码的本意达到 提示:这里的forward<T>(t) 作用在于将t的属性置为原有属性 当原有属性为左值 你还是左值 是右值 你还是右值 当需要把原有属性是右值但是在该函数需要当成左值来用时 不加forward<T>(t) 即可

4.2接着看完美转发的应用场景

1.详细图解

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.完整代码

List.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <functional>
#include <assert.h>
using namespace std;

namespace ape
{
	//结点类---右值引用结点构造函数 
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prv;
		T _data;
		list_node(const T& x = T())
			:_next(nullptr)
			, _prv(nullptr)
			, _data(x)
		{
		
		}
		list_node(T&& x = T())
			:_next(nullptr)
			, _prv(nullptr)
			, _data(forward<T>(x))
		{
		
		}
	};

	//迭代器类
	template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> node;
		typedef __list_iterator<T, Ref, Ptr> self;
		node* _node;

		__list_iterator(node* n)
			:_node(n)
		{
		}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}

		self& operator++()
		{
			_node = _node->_next;

			return *this;
		}

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prv;

			return *this;
		}

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prv;

			return tmp;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

	//链表类
	template<class T>
	class list
	{
		typedef list_node<T> node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;
		iterator begin()
		{
			return iterator(_head->_next);
		}
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}

		//无参构造
		list()
		{
			_head = new node(T());
			_head->_next = _head;
			_head->_prv = _head;
		}

		//迭代器区间构造
		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			_head = new node(T());
			_head->_next = _head;
			_head->_prv = _head;

			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//交换内容
		void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}
		//拷贝构造
		list(const list<T>& lt)
		{
			_head = new node(T());
			_head->_next = _head;
			_head->_prv = _head;

			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

		//赋值运算符重载
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

		//析构函数
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		//清空函数
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				erase(it++);
			}
		}

		//尾插 --- 右值引用尾插
		void push_back(const T& x)
		{
			insert(end(), x);
		}
		void push_back(T&& x)
		{
			insert(end(), forward<T>(x));
		}

		//头插 -- 右值引用头插
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		void push_front(T&& x)
		{
			insert(begin(), forward<T>(x));
		}

		//尾删
		void pop_back()
		{
			erase(--end());
		}

		//头删
		void pop_front()
		{
			erase(begin());
		}

		//插入函数 --- 右值引用插入
		void insert(iterator pos, const T& x)
		{
			node* cur = pos._node;
			node* prev = cur->_prv;

			node* new_node = new node(x);

			prev->_next = new_node;
			new_node->_prv = prev;
			new_node->_next = cur;
			cur->_prv = new_node;
		}
		void insert(iterator pos, T&& x)
		{
			node* cur = pos._node;
			node* prev = cur->_prv;

			node* new_node = new node(forward<T>(x));

			prev->_next = new_node;
			new_node->_prv = prev;
			new_node->_next = cur;
			cur->_prv = new_node;
		}

		//删除函数
		iterator erase(iterator pos)
		{
			assert(pos != end());

			node* prev = pos._node->_prv;
			node* next = pos._node->_next;

			prev->_next = next;
			next->_prv = prev;
			delete pos._node;

			return iterator(next);
		}
	private:
		node* _head;
	};
}

String.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <functional>
#include <assert.h>
using namespace std;

namespace ape
{
	class string
	{
	public:
		//迭代器
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		//构造函数
		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		//交换内容
		void swap(string& s)
		{
			::swap(_str, s._str);
			::swap(_size, s._size);
			::swap(_capacity, s._capacity);
		}

		//拷贝构造
		string(const string& s)
			:_str(nullptr)
		{
			cout << "string(const string& s) -- 左值深拷贝" << endl;

			string tmp(s._str);
			swap(tmp);
		}

		// 移动构造: 传过来的s[即 (s1 + '!')]是右值
		//ape::string ret2 = (s1 + '!'); 右值拷贝 ret2.string( (s1 + '!') );
		string(string&& s)
			:_str(nullptr)
		{
			cout << "  string(string&& s)    -- 右值移动拷贝" << endl;
			swap(s); //直接换过来
		}

		// 赋值重载
		string& operator=(const string& s)
		{
			cout << "string& operator=(string s) -- 深拷贝" << endl;
			string tmp(s);
			swap(tmp);

			return *this;
		}

		//析构函数
		~string()
		{
			delete[] _str;
			_str = nullptr;
		}

		//重载下标运算符[]
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		//扩容
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;

				_capacity = n;
			}
		}

		//尾插
		void push_back(char ch)
		{
			if (_size >= _capacity)
			{
				size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newcapacity);
			}

			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}

		//+=运算符重载
		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		//+号运算符重载 不改变this 返回新string
		string operator+(char ch)
		{
			cout << "operator+:";
			string tmp(*this);
			tmp += ch;
			return tmp;
		}

		//c_str函数
		const char* c_str() const
		{
			return _str;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}
Test.cpp
#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <functional>
#include <assert.h>
using namespace std;



#include "List.h"
#include "String.h"

int main()
{
	ape::list<ape::string> lt;

	ape::string s1("hello world");
	lt.push_back(s1);

	lt.push_back(ape::string("hello world"));
	lt.push_back("hello world");

	return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿猿收手吧!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值