c++之说_14|左值引用与右值引用

提起左右值引用我就头疼

左值:

1、在内存中开辟了空间的便叫左值

2、左值不一定可以赋值  如字符串常量

3、左值可以取地址

右值:

1、在内存中没有开辟空间的

2、右值无法取地址

如:

立即数(1,2,3)

函数返回值 直接返回非类型引用

Lvalue getT() { return Lvalue(); };
int get() { return c;  };

返回的这个int值或Lvalue类型对象就是一个右值 因为没有开辟空间

3、将亡值

什么叫将亡值  我的理解是一种标志 告诉说这个对象我要销毁

Lvalue&& getR();  函数返回值是右值引用 返回的这个对象 就叫将亡对象  即将销毁


Lvalue c;
static_cast<Lvalue&&>(c);  转换返回的操作数也叫将亡值

我的理解就是告诉我们 这个值是要销毁的  推荐使用一个新对象去存储

  什么叫左值引用? 其实就是 这个对象所代表的内存空间的另一个名字  在编译器内部

  本质上是指针实现的

   对左值引用取地址  所取的是引用对象的地址值

Lvalue d;//左值

const Lvalue& b2 = d;//绑定左值
Lvalue& b3 = d;//绑定左值

&b2 == &d;
&b3 == &d;

而右值引用 则引用的是右值

Rvalue r;//左值
Rvalue&& r1 = Rvalue();//绑定右值

//r1 是左值  有名的右值引用  为 左值
const Rvalue&& r2 = static_cast<Rvalue&&>(r1);//绑定右值(实际上是左值转换到右值)成将亡值

Rvalue&& r3 = static_cast<Rvalue&&>(r);

记住有名字的右值引用  是左值  所以可以取地址

std::cout << "r:" << &r << endl;
std::cout << "r1:" << &r1 << endl;
std::cout << "r2:" << &r2 << endl;
std::cout << "r3:" << &r3 << endl;

&r == &r3;
&r1 == &r2;

由于是引用  当然可以改变所引用的对象

struct Rvalue
{


};
struct Lvalue : public Rvalue
{
	int c;
	Lvalue() = default;
	Lvalue(int Inc) :c(Inc) {};

	Lvalue getT() { return Lvalue(); };
	Lvalue&& getR() { 
		auto c = new Lvalue(100);  
		return static_cast<Lvalue&&>(*c);
	};
	int get() { return c;  };
};
std::cout << "" << endl;
Lvalue op(50);
std::cout << "op.c:" << op.c << endl;
Lvalue& Lop = op;
Lop.c = 100;
std::cout << "左值引用修改"<< endl;
std::cout << "op.c:" << op.c << endl;
Lvalue& Rop = op;
Lop.c = 800;
std::cout << "右值引用修改" << endl;
std::cout << "op.c:" << op.c << endl;

这么一看左右值引用都是一样的感觉

他们主要体现在函数上

struct Lvalue : public Rvalue
{
	int c;
	int* p;
	Lvalue() = default;
	Lvalue(int Inc) :c(Inc), p(new int()) {};


	Lvalue& operator=(Lvalue& lc) 
	{ 
		c = lc.c;
		p = new int;
		*p = * lc.p;
		return *this; 
	};
	Lvalue& operator=(const Lvalue& lc) 
	{
		c = lc.c;
		p = new int;
		*p = *lc.p;
		return *this; 
	};
	Lvalue& operator=(Lvalue&& lc) 
	{
		c = lc.c;
		p = lc.p;
		lc.p = nullptr;
		return *this;
	};
	Lvalue& operator=(const Lvalue&& lc) 
	{	
		Lvalue&& Rc = const_cast<Lvalue&&> (lc);
		c = Rc.c;
		p = Rc.p;
		Rc.p = nullptr;

		return *this; 
	};


	Lvalue getT() { return Lvalue(); };
	Lvalue&& getR() { 
		auto c = new Lvalue(100);  
		return static_cast<Lvalue&&>(*c);
	};
	int get() { return c;  };
};

注意那几个 operator= 重载赋值运算符的函数

这其实就是所谓的 深拷贝  浅拷贝

//深拷贝
Lvalue& operator=(Lvalue& lc) 
{ 
	c = lc.c;
	p = new int;    //重新开辟了堆空间 
	*p = * lc.p;    //将 lc.p 中的int值 存入新开的堆空间中
	return *this; 
};

//浅拷贝
Lvalue& operator=(Lvalue&& lc) 
{
	c = lc.c;
	p = lc.p;        
//没有开辟新的堆空间  直接拿到 将亡值 lc 的 p所存储的内存地址  达到复用的目的
// 这也是他们所说的窃取资源
	lc.p = nullptr;
	return *this;
};
Lvalue& operator=(Lvalue& lc) 
{ 
	std::cout << "Lvalue& operator=(Lvalue& lc)" << endl;
	c = lc.c;
	delete p;
	p = new int;
	*p = * lc.p;
	return *this; 
};
Lvalue& operator=(const Lvalue& lc) 
{
	std::cout << "Lvalue& operator=(const Lvalue& lc)" << endl;
	c = lc.c;
	delete p;
	p = new int;
	*p = *lc.p;
	return *this; 
};
Lvalue& operator=(Lvalue&& lc) 
{
	std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;
	c = lc.c;
	delete p;
	p = lc.p;
	lc.p = nullptr;
	return *this;
};
Lvalue& operator=(const Lvalue&& lc) 
{	
	std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;
	Lvalue&& Rc = const_cast<Lvalue&&> (lc);
	c = Rc.c;
	delete p;
	p = Rc.p;
	Rc.p = nullptr;

	return *this; 
};

调用处

Lvalue t1(100);
*t1.p = 80;
Lvalue t2(200);
*t2.p = 800;
std::cout << "" << endl;
std::cout << "初始化时" << endl;
std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
std::cout << "*t2.p:" << *t2.p << endl;

std::cout << "" << endl;
std::cout << "t1 = t2" << endl;

t1 = t2;//Lvalue& operator=(Lvalue& lc) ;

std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
std::cout << "*t2.p:" << *t2.p << endl;


std::cout << "" << endl;
std::cout << "t1 = static_cast<Lvalue&&>(t2)" << endl;

t1 = static_cast<Lvalue&&>(t2); 
//Lvalue& operator=(Lvalue&& lc)  如果没有  就考虑 Lvalue& operator=(const Lvalue&& lc)  
如果还没有 就调用Lvalue& operator=(const Lvalue& lc) 

std::cout << "t1.c:" << t1.c << endl;
std::cout << "t1.p:" << t1.p << endl;
std::cout << "*t1.p:" << *t1.p << endl;
std::cout << "" << endl;
std::cout << "t2.c:" << t2.c << endl;
std::cout << "t12.p:" << t2.p << endl;
//std::cout << "*t2.p:" << *t2.p << endl; //访问空指针是错误的

我们注释掉

/* Lvalue& operator=(Lvalue&& lc) 
{
	std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;
	c = lc.c;
	delete p;
	p = lc.p;
	lc.p = nullptr;
	return *this;
}; 
  */

Lvalue& operator=(const Lvalue&& lc) 
{	
	std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;
	Lvalue&& Rc = const_cast<Lvalue&&> (lc);
	c = Rc.c;
	delete p;
	p = Rc.p;
	Rc.p = nullptr;

	return *this; 
};

看调用 Lvalue& operator=(const Lvalue&& lc) 

我们继续注释

/* Lvalue& operator=(Lvalue&& lc) 
{
	std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;
	c = lc.c;
	delete p;
	p = lc.p;
	lc.p = nullptr;
	return *this;
}; 
  */

/*
Lvalue& operator=(const Lvalue&& lc) 
{	
	std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;
	Lvalue&& Rc = const_cast<Lvalue&&> (lc);
	c = Rc.c;
	delete p;
	p = Rc.p;
	Rc.p = nullptr;

	return *this; 
};
*/

看调用与结果

这就是右值引用存在的价值

告诉大家这个对象需要销毁  你怎么处理看你的了  

如果只用左值引用 无法很好的区分  因为cosnt lvalue&&  即可左值也可右值

你不知道传进来的这个对象是 const 修饰的对象  还是  要表示的销毁对象

我们也看到了引用是可以直接修改对象的  所以也是会有点问题  有些人不按照规则来

这样的函数 是有隐患的  我的本意是  这个返回值是即将销毁的值  然而外部可修改

Lvalue&& getR() { 
	auto c = new Lvalue(100);  
	return static_cast<Lvalue&&>(*c);
};

折叠引用  使用类型别名  using  typedef  即可达到折叠引用的目的

using ob = Lvalue&;
typedef Lvalue& ob1;
using ou = Lvalue&&;
typedef Lvalue&& ou1;

Lvalue k;
ob& o = k;//左值引用
ob && o1= k;//左值引用
ou& o2 = k;//左值引用
ou&& o3 = (Lvalue&&)k;//右值引用

折叠引用只有一个规则:

右值引用的右值引用 才是右值引用  其余都为左值引用

&& &&  右值引用

& && 左值引用

&& & 左值引用

& & 左值引用

而我们所知晓的 std::forward 完美转发  返回值就是  &&

用了点折叠引用

好了  到最后了 

我们说一下

空类 和 继承 一个空类 内存大小是什么样的

Lvalue d;//左值
Rvalue r;//左值
std::cout<<	sizeof(d)<<endl;//4   继承一个空类  空类所占的1字节被优化掉
std::cout << sizeof(r) << endl;//1

  写完了

对了知乎大佬的参考  左右值引用的要点  

(3 条消息) c++为什么要搞个引用岀来,特别是右值引用,感觉破坏了语法的简洁和条理,拷贝一个指针不是很好吗? - 知乎 (zhihu.com)

最后参考代码


struct Rvalue
{


};
struct Lvalue : public Rvalue
{
	int c;
	int* p;
	Lvalue() = default;
	Lvalue(int Inc) :c(Inc), p(new int()) {};


	Lvalue& operator=(Lvalue& lc) 
	{ 
		std::cout << "Lvalue& operator=(Lvalue& lc)" << endl;
		c = lc.c;
		delete p;
		p = new int;
		*p = * lc.p;
		return *this; 
	};
	Lvalue& operator=(const Lvalue& lc) 
	{
		std::cout << "Lvalue& operator=(const Lvalue& lc)" << endl;
		c = lc.c;
		delete p;
		p = new int;
		*p = *lc.p;
		return *this; 
	};
	/*Lvalue& operator=(Lvalue&& lc) 
	{
		std::cout << "Lvalue& operator=(Lvalue&& lc) " << endl;
		c = lc.c;
		delete p;
		p = lc.p;
		lc.p = nullptr;
		return *this;
	};*/
	/*Lvalue& operator=(const Lvalue&& lc) 
	{	
		std::cout << "Lvalue& operator=(const Lvalue&& lc) " << endl;
		Lvalue&& Rc = const_cast<Lvalue&&> (lc);
		c = Rc.c;
		delete p;
		p = Rc.p;
		Rc.p = nullptr;

		return *this; 
	};*/


	Lvalue getT() { return Lvalue(); };
	Lvalue&& getR() { 
		auto c = new Lvalue(100);  
		return static_cast<Lvalue&&>(*c);
	};
	int get() { return c;  };
};

int main(int line,   const char* arg[])
{
	Lvalue d;//左值
	Rvalue r;//左值
	std::cout<<	sizeof(d)<<endl;//4   继承一个空类  空类所占的1字节被优化掉
	std::cout << sizeof(r) << endl;//1

	//const 修饰的左值引用 既可以绑定左值又可以绑定右值     Lvalue() 右值  无名的
	const Lvalue& b = Lvalue();//绑定右值
	const Lvalue& b2 = d;//绑定左值
	Lvalue& b3 = d;//绑定左值
	
	Rvalue&& r1 = Rvalue();//绑定右值
	//r1 是左值  有名的右值引用  为 左值
	const Rvalue&& r2 = static_cast<Rvalue&&>(r1);//绑定右值(实际上是左值转换到右值)
	Rvalue&& r3 = static_cast<Rvalue&&>(r);
	
	//std::move(r);
	std::cout << "左值"<< endl;

	std::cout << "d:"<< & d << endl;
	std::cout << "b2:"<< &b2 << endl;
	std::cout << "b:" << &b << endl;
	std::cout << "b3:" << &b3 << endl;

	std::cout << "右值"<< endl;

	std::cout << "r:" << &r << endl;
	std::cout << "r1:" << &r1 << endl;
	std::cout << "r2:" << &r2 << endl;
	std::cout << "r3:" << &r3 << endl;
	
	//td::cout << "纯右值" << &Rvalue() << endl; //error
	//&d.getT();
	decltype(auto) v = d.getR();
	v.c = 50;
	std::cout << "v:" << &v << endl;
	
	auto str = "dadad";
	std::cout << "字符串:" << &"dadad" << endl;
	std::cout << "str:" << &str << endl;

	std::cout << "" << endl;
	Lvalue op(50);
	std::cout << "op.c:" << op.c << endl;
	Lvalue& Lop = op;
	Lop.c = 100;
	std::cout << "左值引用修改"<< endl;
	std::cout << "op.c:" << op.c << endl;
	Lvalue& Rop = op;
	Lop.c = 800;
	std::cout << "右值引用修改" << endl;
	std::cout << "op.c:" << op.c << endl;

	int&& i = 10;
	i = 80;

	
	
	Lvalue t1(100);
	*t1.p = 80;
	Lvalue t2(200);
	*t2.p = 800;
	std::cout << "" << endl;
	std::cout << "初始化时" << endl;
	std::cout << "t1.c:" << t1.c << endl;
	std::cout << "t1.p:" << t1.p << endl;
	std::cout << "*t1.p:" << *t1.p << endl;
	std::cout << "" << endl;
	std::cout << "t2.c:" << t2.c << endl;
	std::cout << "t12.p:" << t2.p << endl;
	std::cout << "*t2.p:" << *t2.p << endl;
	
	std::cout << "" << endl;
	std::cout << "t1 = t2" << endl;

	t1 = t2;//Lvalue& operator=(Lvalue& lc) ;
	
	std::cout << "t1.c:" << t1.c << endl;
	std::cout << "t1.p:" << t1.p << endl;
	std::cout << "*t1.p:" << *t1.p << endl;
	std::cout << "" << endl;
	std::cout << "t2.c:" << t2.c << endl;
	std::cout << "t12.p:" << t2.p << endl;
	std::cout << "*t2.p:" << *t2.p << endl;


	std::cout << "" << endl;
	std::cout << "t1 = static_cast<Lvalue&&>(t2)" << endl;
	
	t1 = static_cast<Lvalue&&>(t2); //Lvalue& operator=(Lvalue&& lc)  如果没有  就考虑 Lvalue& operator=(const Lvalue&& lc)  如果还没有 就调用Lvalue& operator=(const Lvalue& lc) 
	
	std::cout << "t1.c:" << t1.c << endl;
	std::cout << "t1.p:" << t1.p << endl;
	std::cout << "*t1.p:" << *t1.p << endl;
	std::cout << "" << endl;
	std::cout << "t2.c:" << t2.c << endl;
	std::cout << "t12.p:" << t2.p << endl;
	//std::cout << "*t2.p:" << *t2.p << endl; //访问空指针是错误的
	using ob = Lvalue&;
	typedef Lvalue& ob1;
	using ou = Lvalue&&;
	typedef Lvalue&& ou1;

	Lvalue k;
	ob& o = k;//左值引用
	ob && o1= k;//左值引用
	ou& o2 = k;//左值引用
	ou&& o3 = (Lvalue&&)k;//右值引用
	
	std::forward<Lvalue&&>(k);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值