C++11右值引用

第二章的内容:主讲右值引用

1、左值和右值区分的判据是看是否可对表达式用&取址,左值可以,右值不可以

//1、左值引用相当于给变量起个别名,故必须是变量,而不能是常量
int aa = 1;
int &b = aa;//ok
int &k = 2;//error 必须为左值
//2、右值引用
int&& a = 1; //ok,实质上就是给不具名(匿名)变量起个别名
int b = 1;
int && c = b; //error! 不能将一个左值赋值给一个右值引用

2、移动构造和移动赋值

传统的左值操作会开销很大的内存,比如在深拷贝构造函数中,对某变量进行拷贝赋值时,需重新申请一块内存对其进行赋值

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

class MyString
{
public:
	static size_t CCtor; //统计调用拷贝构造函数的次数
	static size_t MCtor; //统计调用移动构造函数的次数
	static size_t CAsgn; //统计调用拷贝赋值函数的次数
	static size_t MAsgn; //统计调用移动赋值函数的次数

public:
	// 构造函数
	MyString(const char* cstr = 0) 
	{
		if (cstr) {
			m_data = new char[strlen(cstr) + 1];
			strcpy(m_data, cstr);
		}
		else {
			m_data = new char[1];
			*m_data = '\0';
		}
	}

	// 拷贝构造函数
	MyString(const MyString& str) 
	{
		//MyString("hello")只是临时对象,拷贝完就没什么用了
		//下面的new是重新申请一块内存来存放它
		//造成了没有意义的资源申请和释放操作
		CCtor++;
		m_data = new char[strlen(str.m_data) + 1];
		strcpy(m_data, str.m_data);
		cout << "拷贝" << endl;
	}
	// 移动构造函数
	MyString(MyString&& str) 
		:m_data(str.m_data) 
	{
		MCtor++;
		str.m_data = nullptr; //不再指向之前的资源了
		cout << "移动拷贝" << endl;
	}

	// 拷贝赋值函数 =号重载
	MyString& operator=(const MyString& str) 
	{
		CAsgn++;
		if (this == &str) // 避免自我赋值!!
			return *this;

		delete[] m_data;
		m_data = new char[strlen(str.m_data) + 1];
		strcpy(m_data, str.m_data);
		return *this;
	}

	// 移动赋值函数 =号重载
	MyString& operator=(MyString&& str)  
	{
		MAsgn++;
		if (this == &str) // 避免自我赋值!!
			return *this;

		delete[] m_data;
		m_data = str.m_data;
		str.m_data = nullptr; //不再指向之前的资源了
		return *this;
	}

	~MyString() 
	{
		delete[] m_data;
	}

	char* get_c_str() const { return m_data; }
private:
	char* m_data;
};
size_t MyString::CCtor = 0;
size_t MyString::MCtor = 0;
size_t MyString::CAsgn = 0;
size_t MyString::MAsgn = 0;
int main()
{
	vector<MyString> vecStr;
	vecStr.reserve(10); //先分配好10个空间
	for (int i = 0; i<10; i++) 
	{
		//移动构造函数与拷贝构造函数的区别是,拷贝构造的参数是const MyString& str,是常量左值引用,
		//而移动构造的参数是MyString&& str,是右值引用,而MyString("hello")是个临时对象,是个右值,
		//优先进入移动构造函数而不是拷贝构造函数。
		vecStr.push_back(MyString("hello"));
	}
	cout << "CCtor = " << MyString::CCtor << endl;
	cout << "MCtor = " << MyString::MCtor << endl;
	cout << "CAsgn = " << MyString::CAsgn << endl;
	cout << "MAsgn = " << MyString::MAsgn << endl;
}

移动构造函数与拷贝构造不同,它并不是重新分配一块新的空间,将要拷贝的对象复制过来,而是"偷"了过来,将自己的指针指向别人的资源,这就节省了内存开销,然后将别人的指针修改为nullptr,这一步很重要,如果不将别人的指针修改为空,那么临时对象析构的时候就会释放掉这个资源,"偷"也白偷了。下面这张图可以解释copy和move的区别:

2、move的作用是将一个左值强制转换为一个右值引用,我们可以通过右值引用来使用该值,以用于移动构造函数,消减内存开销

MyString str1("hello"); //调用构造函数
MyString str2(str1); //调用拷贝构造函数
MyString str3(std::move(str1)); // 调用移动构造函数

3、emplace_back取代push_back

emplace_back没有内存的移动,可以节省中间变量的内存开销,建议多使用emplace_back

struct A
{
	int x;
	double y;
	//A(int a, double b):x(a), y(b)
	//{}
};

int main()
{
	vector<A> vec;
	
	A a{ 1, 2 };
	vec.emplace_back(a);
	//vec.push_back(a);
	
	vec.emplace(vec.begin(), a);
	cout << vec.size() << endl;
	cout << vec.at(1).y << endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值