C++11右值引用

用一个简单的方式理解右值引用在内存的状态:

 从图中明白:左值引用是对变量的引用(const int &rint = 1这种方式例外)

右值引用就是对变量对应的值的引用,为了延长这块值所在内存的声明周期(如果一块内存的引用计数为0就释放这块内存)

 

另外从代码执行的结果中明白函数return语句执行了两个过程:

  第一步:给接收本返回值的变量赋值,如果没有变量接收就创建一个临时变量

  第二步:析构局部变量

#pragma once
/************************************************************************/
/* 如果返回class或者struct类型的局部变量,那么就需要添加移动构造和移动赋值                         */
/* 容器的元素是class或者struct类型的值类型,那么就需要添加移动构造和移动赋值                      */
/* 右值引用如果不是const就可以被赋值                                                                              */
/***************************************注意******************************/
/* 函数中不要返回std::move()形式                                                                                   */
/* 不使用T&& =func() 形式接收函数返回值                                                                         */
/* 没有必要std::move(func()); 直接用变量T tvar = func()形式接收函数返回值                         */
/************************************************************************/
class RValueRef
{
public:
	explicit RValueRef(int a, int b) :m_a(new int(a)), m_b(b), m_varName{""}
	{
		cout << "构造函数" << endl;
	}

	RValueRef(const RValueRef& other) : m_a(new int(*(other.m_a))), m_b(other.m_b)
	{
		this->setVarName("=" + other.getVarName());
		cout << "copy构造" << endl;
	}

	RValueRef& operator=(const RValueRef& other) & // 该函数只能用于左值
	{
		if (this != &other)
		{
			std::swap(*(this->m_a), *(other.m_a));
			this->m_b = other.m_b;
			
			cout << "赋值运算符" << endl;
		}
		return *this;
	}

	/************************************************************************/
	/* 将移动构造和移动赋值定义为noexcept是有用的。在向容器添加元素时如果不定义noexcept,容器*/
	/* 优先使用拷贝构造函数或者常规赋值运算符函数                                                                 */
	/* 编译器不会默认创建移动构造或者移动赋值(满足默认创建的条件非常复杂,开发中不会遇到默认生成的这两个函数)*/
	/************************************************************************/
	RValueRef(RValueRef&& other) noexcept : m_a(other.m_a), m_b(other.m_b)
	{
		this->m_varName.assign("=" + other.getVarName());
		other.m_a = nullptr;
		cout << "移动copy构造" << endl;
	}

	RValueRef& operator=(RValueRef&& other) noexcept
	{
		if (this != &other)
		{
			*(this->m_a) = *(other.m_a); 
			this->m_b = other.m_b;
			delete other.m_a;
			other.m_a = nullptr;
			cout << "移动赋值运算符" << endl;
		}
		return *this;
	}

	~RValueRef()
	{
		if (m_a)
		{
			delete m_a;
			m_a = nullptr;
			cout << ("真实析构函数:" + this->getVarName()) << endl;
		}
		else
		{
			cout << ("假象析构函数:"+this->getVarName()) << endl;
		}
	}

	// 右值函数 或者 const右值函数
	void printfFunc() const
	{
		cout << "右值函数" << endl;
	}

	// 左值函数 或者 const左值函数
	void printFunc() const &
	{
		cout << "左值函数" << endl;
	}
	void setVarName(const string& name)
	{
		this->m_varName.assign(name);
	}

	const string& getVarName() const
	{
		return m_varName;
	}
private:
	int *m_a;
	int m_b;
	string m_varName;
};

// 通过右值引用占用局部变量的内存,避免了局部变量的析构
inline RValueRef testTmpFunc()
{
	RValueRef refvar(12, 23);
	refvar.setVarName("refvar");
	return refvar;
}

inline RValueRef&& testRvalRefFunc()
{
	RValueRef refvar2(25, 26);
	refvar2.setVarName("refvar2");
	return std::move(refvar2); // 不要返回std::move(), 因为std::move()的返回值会优先释放掉,那么在return中的移动赋值构造中会使用野指针。
}

inline void valueArg(const RValueRef ref)
{
	cout << "值传递" << endl;
}

inline void testRVRef()
{
	RValueRef com(1,2);
	com.setVarName("com");

	RValueRef comVar(121, 23);
	comVar.setVarName("comVar");

	RValueRef comVar2 = std::move(comVar);
	comVar2.setVarName("comVar2");

	// 不涉及任何构造函数,这个步骤之后comVar就不再使用了,即使comVar还能拿到这块内存的值但是从语义上讲我们不能再使用它。
	RValueRef&& at = std::move(comVar2);	// 将左值变成右值

	RValueRef assignVar(33, 45);
	assignVar.setVarName("assignVar");

	assignVar = std::move(com);
	assignVar.setVarName("com+");
	at = assignVar; // 调用operator=(RValueRef&) 而不是operator=(RValueRef&&), 现在代表的是左值要改变其内存的值
	at.printfFunc();

	cout << "***************0***************" << endl;
	RValueRef retvar1 = std::move(testTmpFunc());
	retvar1.setVarName("retvar1");
	cout << "***************1***************" << endl;
	RValueRef&& retvar2 = std::move(testTmpFunc()); // retvar2 指针内存被释放
	cout << "***************2***************" << endl;
	RValueRef retvar3 = testTmpFunc();
	retvar3.setVarName("retVar3");
	cout << "***************3***************" << endl;
	RValueRef&& Rretvar1 = testRvalRefFunc(); // Rretvar1 完全废弃的内存
	cout << "***************4***************" << endl;
	vector<RValueRef> vecRVR;
	vecRVR.push_back(RValueRef(11, 22));
	vecRVR[0].setVarName("vecRVR[0]");
	cout << "***************5***************" << endl;
	RValueRef argValue(2, 34);
	argValue.setVarName("argValue");
	valueArg(argValue);
	cout << "***************6***************" << endl;
	RValueRef argValue1(2, 34);
	argValue1.setVarName("argValue1");
	valueArg(std::move(argValue1)); // 函数调用结束, argValue1被释放
	cout << "***************7***************" << endl;
}

    输出结果

构造函数		// RValueRef com;
构造函数		// RValueRef comVar(12, 23);
移动copy构造	// RValueRef comVar2 = std::move(comVar);
构造函数		// RValueRef assignVar(33, 45);
移动赋值运算符	// assignVar = std::move(comVar2);
赋值运算符		// at = assignVar;
右值函数		// at.printfFunc();
***************0***************
构造函数		// testTmpFunc::RValueRef refvar(12,23);
移动copy构造	// return 先返回refvar,作为std::move()的参数
假象析构函数:refvar	// return 再析构refvar
移动copy构造	// RValueRef retvar1 = std::move(testTmpFunc()); std::move()返回值初始化retvar1
假象析构函数:=refvar	// std::move()返回值被析构

***************1***************
构造函数		// testTmpFunc::RValueRef refvar(12,23);
移动copy构造	// return 先返回refvar,作为std::move()的参数
假象析构函数:refvar	// return 再析构refvar
真实析构函数:=refvar	// 在retvar2被赋值前std::move()返回值被释放,所以retvar2中的指针是野指针

***************2***************
构造函数		// testTmpFunc::RValueRef refvar(12,23);
移动copy构造	// return 先返回refvar,赋值给RValueRef retvar3
假象析构函数:refvar	// return 再析构refvar

***************3***************
构造函数		// testTmpFunc::RValueRef refvar(12,23);
真实析构函数:refvar2	// return 先把refvar返回给RValueRef&& Rretvar1,接着又释放了refvar的内存,造成Rretvar1引用一个完全废弃的内存。和部分被废弃还不一样。

***************4***************
构造函数		// RValueRef(11, 22) 构造
移动copy构造	// vecRVR.push_back() 赋值
假象析构函数:	// 离开vecRVR.push_back()方法析构RValueRef(11, 22)

***************5***************
构造函数		// RValueRef argValue(2, 34);
copy构造		// 使用argValue初始化void valueArg(RValueRef ref)函数参数
值传递
真实析构函数:=argValue	// valueArg()函数参数ref的释放

***************6***************
构造函数		// RValueRef argValue1(2, 34);
移动copy构造	// 使用std::move(argValue)初始化void valueArg(RValueRef ref)函数参数
值传递
真实析构函数:=argValue1	// ref释放内存,同时也标志着argValue1被是释放 。

***************7***************
假象析构函数:argValue1
真实析构函数:argValue
真实析构函数:vecRVR[0]
真实析构函数:retVar3
真实析构函数:retvar1
真实析构函数:com+
真实析构函数:comVar2
假象析构函数:comVar
假象析构函数:com

 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值