day1 c++右值引用学习总结

1、左值右值区分:

左值有名字,是变量,有储存区域,可取地址;右值为临时量,比如函数返回值,lambula表达式

2、右值引用引出原因:

实现自定义的浅拷贝(移动赋值函数),节省new堆内存的消耗

我们知道如果没实现"="赋值操作符函数的重载的话,类会有默认的赋值函数,但是实现的是默认浅拷贝(默认复制赋值函数)

浅拷贝:如果是常类型,直接赋值;如果是类类型,会将类中存在的指针进行指针赋值指向,但并没有真正给指针开辟新的内存

深拷贝:常类型同上;类类型,为指针开辟新内存

为什么要实现自定义的浅拷贝:因为系统默认的浅拷贝,会造成二次析构造成崩溃

自定义的浅拷贝可以实现将新指针指向旧指针的内存地址,并将旧指针置空,这样析构的时候旧指针为空,判空返回不析构这块内存

那么为什么要实现自定义的浅拷贝:因为在我们赋值拷贝的时候,不需要每个变量都给他开辟内存,赋值相同的内容,将指针指向旧指针的内存地址,旧指针置空,那新指针是不是就获得这块内存地址的使用权了呢  举个例子

class A

{

A(){}

~A(){ if(b)delete b;}

A&operater=(A&otherA)  //默认浅拷贝(默认复制赋值函数,有些地方也叫默认拷贝赋值函数)

{

this->b = otherA.b;

return *this;

}

A&operater=(A&otherA)  //自定义深拷贝(复制赋值函数)(自定义"="赋值操作符后,优先重载使用自定义的深拷贝,上面的浅拷贝不使用)

{

this->b = new B(*otherA.b);

return *this;

}

A&operater=(A&&otherA)  //自定义浅拷贝(移动赋值函数)(那么还想用跟深拷贝不一样的浅拷贝怎办,利用右值引用(参数不同重载))

{

this->b = otherA.b;

otherA.b = nullpter;//这样就不会有两个指针指向同一份地址,析构两次地址

return *this;

}

B* b;

}

// 交换的例子

int main

{

A a1;//假设类中的b指针已经有堆内存地址

A a2;

A temp = a1; //这里的赋值都是走的第二个函数深拷贝,会发现好几次b指针的new的操作,挺浪费内存的

a1 = a2;

a2 = temp;

}

//我们优化下看看 ,其实只用交换a1和a2的b一直就可以了;我们来使用std::move可以将左值强制转换成右值

int main

{

A a1;//假设类中的b指针已经有堆内存地址

A a2;

A temp = std::move(a1); //这里的赋值都是走的第三个函数自定义的浅拷贝(重载右值引用参数),不会产生new申请内存,而是交换指针指向

a1 = std::move(a2);

a2 = std::move(temp );

}

// 由上看出,右值引用是为了节省重复申请内存空间,它是节约空间的好帮手

3、左值右值在函数中传递中的一致性(forward):

test(A&&a) //作为参数a为右值引用

{

A a2 = a;// 这里a为左值引用,因为有名字,是变量,所以还是会走到第二个深拷贝的地方,这里出现了右值->左值的变化

}

那我们怎么办:使用foward函数

优化后的写法

test(A&&a) //作为参数a为右值引用

{

A a2 = std::forward(<A>(a));//那么这里std::forward的结果还是右值引用,所以可以正确调用到我们自定义的浅拷贝第三个函数

}

4、叠加原则

叠加原则:

A&+A&& = A&

A&&+A& = A&

A&+A& = A&

A&&+A&& = A&&

简单来说,只有右值引用叠加右值引用才是右值引用,其他的叠加都是左值

右值引用的模板推到类型也是遵循次规则,不重复写

5、forward和move

forward和move其实都是利用引用叠加原则去实现自己的功能

forward:引用参数传递的过程中保持一致,不改变,函数是右值,用这个值的时候还是右值,保持不改变的特性

move:将引用参数转化为右值引用

forward原型

  根据参数左右值选择不同的调用格式

template<class _Ty> inline
	constexpr _Ty&& forward(
		typename remove_reference<_Ty>::type& _Arg) _NOEXCEPT
	{	// forward an lvalue as either an lvalue or an rvalue
	return (static_cast<_Ty&&>(_Arg));
	}

 

  调用格式:forward(<A>a) //返回右值引用:_Ty变成A,_Ty&&变成A&&,最后类型转换成A&&右值引用

  调用格式:forward(<A&>a) //返回左值引用:_Ty变成A&,_Ty&&变成A& &&,根据叠加原则,最后类型转换成A&&左值引用    

  这里的a是变量,所以是左值,所以调用上面左值引用参数的forward

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

template<class _Ty> inline
	constexpr _Ty&& forward(
		typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
	{	// forward an rvalue as an rvalue
	static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");
	return (static_cast<_Ty&&>(_Arg));
	}

  调用格式:forward(<A>A()) //返回右值引用 :_Ty变成A,_Ty&&变成A&&,最后类型转换成A&&右值引用

  调用格式:forward(<A&>A()) //返回左值引用:_Ty变成A&,_Ty&&变成A& &&,根据叠加原则,最后类型转换成A&&左值引用

  这里的A()是临时变量,所以是右值,所以调用上面右值引用参数的forward

 

特别说一句,为什么用引用移除remove_reference,笔者也不太清楚,笔者亲身试验过,将他们改成这样没有remove_reference的样纸,结果也是一样的效果,所以笔者觉得可能是为了美观...

constexpr _Ty&& forward1(_Ty& _Arg) {}
constexpr _Ty&& forward1(_Ty&& _Arg) {}

 

可能读者会有一个疑问:我们forward(<A&>a)调用,typename remove_reference<_Ty>::type& _Arg,_Ty变成A&,式子变成

typename remove_reference<A&>::type& _Arg,有两个&诶,那不是变成A&&,变成右值引用了么,其实笔者也有这样的疑问,不过最后笔者克制住了,强势把它理解成A&类型的一个左值引用,可能有些牵强,不过笔者也找不到合适的解释了

大家如果对上述两点有好的理解也可以告诉笔者我...以后我知道了也会更新...

move原型

  //返回右值引用

template<class _Ty> inline
	constexpr typename remove_reference<_Ty>::type&&
		move(_Ty&& _Arg) _NOEXCEPT
	{	// forward _Arg as movable
	return (static_cast<typename remove_reference<_Ty>::type&&>(_Arg));
	}

同样不知道为何写remove_reference...

 

总结:

右值引用是为了节约拷贝时候的内存地址开销,实现自定义的移动赋值函数,将指针交换进行浅拷贝

左值是变量有名字有地址,右值是临时变量,特别说明,右值引用是左值

forward的功能是保持引用类型前后一致,forward<A&>a是左值引用,forward<A>a是右值引用

move的功能是返回一个右值引用

还有些不明白的地方,比如remove_reference为何存在于forward和move中,比如typename remove_reference<A&>::type&到底是理解成A&的一个左值引用,还是有什么其他好的理解方式呢,反正理解成A&&右值引用肯定错的.....

大家如果对上述两点有好的理解也可以告诉笔者我...以后我知道了也会更新...

【参考文献】

http://www.cnblogs.com/catch/p/5019402.html

http://www.cnblogs.com/catch/p/3507883.html

http://thbecker.net/articles/rvalue_references/section_01.html

https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

https://blog.csdn.net/u011529563/article/details/77620681

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值