C++右值引用

汇小川积流水,坚持多学一点点,开心就会多一点点。

目录

  • 左值引用和右值引用有什么区别?
  • 汇编代码下,左值引用和右值引用一样吗?
  • 局部对象返回值的处理方法
  • &&一定是右值引用吗?


左值引用和右值引用有什么区别?

左值引用:
可以代表左值,变量的别名,可以直接访问数据的地址,类似于直接访问指针。
右值引用:
绑定的必须是一个右值,是在等号右边的值


汇编代码下,左值引用和右值引用一样吗?

提供一个网站,该网站能够将C++代码直接编译成汇编代码
https://godbolt.org/z/UHsDRj

引用在汇编层面上相当于指针,调用的时候相当于指针的解引用。


局部对象返回值的处理方法

此处的局部对象处理返回值,只针对于在堆上开辟内存空间的类。转换成右值处理。

讲解代码示例:

#include 


using namespace std;
class Stack
{
public:
	// size表示栈初始的内存大小
	Stack(int size = 1000)
		:msize(size), mtop(0)
	{
		cout << "Stack(int)" << endl;
		mpstack = new int[size];
	}
	// 栈的析构函数
	~Stack()
	{
		cout << "~Stack()" << endl;
		delete[]mpstack;
		mpstack = nullptr;
	}


	// 栈的拷贝构造函数
	Stack(const Stack &src)
		:msize(src.msize), mtop(src.mtop)
	{
		cout << "Stack(const Stack&)" << endl;
		mpstack = new int[src.msize];
		memcpy(mpstack, src.mpstack, sizeof(int)*mtop);
	}
	// 栈的赋值重载函数
	Stack& operator=(const Stack &src)
	{
		cout << "operator=" << endl;
		if (this == &src)
			return *this;

		delete[]mpstack;

		msize = src.msize;
		mtop = src.mtop;
		mpstack = new int[src.msize];
		memcpy(mpstack, src.mpstack, sizeof(int)*mtop);
		return *this;
	}

	// 带右值引用参数的拷贝构造函数
	Stack(Stack &&src)
		:msize(src.msize), mtop(src.mtop)
	{
		cout << "Stack(Stack&&)" << endl;
		/*此处没有重新开辟内存拷贝数据,把src的资源直接给当前对象,再把src置空*/
		mpstack = src.mpstack;
		src.mpstack = nullptr;
	}

	// 带右值引用参数的赋值重载函数
	Stack& operator=(Stack &&src)
	{
		cout << "operator=(Stack&&)" << endl;
		if (this == &src)
			return *this;

		delete[]mpstack;

		msize = src.msize;
		mtop = src.mtop;
		/*此处没有重新开辟内存拷贝数据,把src的资源直接给当前对象,再把src置空*/
		mpstack = src.mpstack;
		src.mpstack = nullptr;
		return *this;
	}

	// 返回栈的长度
	int getSize()const { return msize; }
private:
	int *mpstack;
	int mtop;
	int msize;
};
Stack GetStack(Stack &stack)
{
	// 这里构造新的局部对象tmp
	Stack tmp(stack.getSize());
	/*
	因为tmp是函数的局部对象,不能出函数作用域,
	所以这里tmp需要拷贝构造生成在main函数栈帧上
	的临时对象,因此这里会调用拷贝构造函数,完成
	后进行tmp局部对象的析构操作
	*/
	return tmp;
}
int testStack()
{//提问: 考虑,如果使用左值,多了哪些开销?如果使用右值,那优化了哪些步骤?
	Stack s;
	/*
	GetStack返回的临时对象给s赋值,该语句结束,临时对象
	析构,所以此处调用operator=赋值重载函数,然后调用
	析构函数
	*/
	s = GetStack(s);
	return 0;
}


template <typename T>
void func(T && val)
{
	std::cout << " val : " << val << std::endl;
	T temp = val;
	temp++;
	std::cout << "temp : " << temp << " , val " << val << std::endl;
}

&&一定是右值引用吗?

答:不一定。也有可能是引用折叠,折叠后可能变成左值引用。

引用折叠的概念主要用于函数模板类推导参数类型时会用到,观察该类型是否包含&

int main()
{
	//先预估一下下面的 值,如果全部预估正确,则对其知识点有一定的了解。
	int a = 10;
	int &b = a;
	
	int &&c = 10;
	func(10);//10-11-10
	func(a);//10-11-11
	b = 30;
	func(b);//11-12-12  30-31-31
	func(a);//31-32-32
	func(c);//10-11-11


	//int &d = 20; // 这样写无法通过编译,原因是因为20是一个立即数字,不会在内存上存储地址
	const int &d = 20;//可通过编译,常引用,如果通过汇编代码,即可发现,20会生成一个临时变量地址在栈上,然后通过临时变量地址加载到d,d是左值常引用,const修饰无法修改d值
	//d = 30;错误
	int &&e = 20;//汇编代码显示,和const int&d = 20; 是一样的。唯一不同的是,如果要改e值,汇编层面是支持修改的,可去查看汇编代码。
	e = 30;
	//所以,&& 可以读取临时量,也可以修改其值。 const T& 只可读,不可修改
	//结论: 有地址的用左值引用,没有地址的用右值引用;有变量名字的用左值引用,没有变量名字的(比如临时量没有名字)用右值引用。
    std::cout << "Hello World!\n";

	testStack();
}

结论:右值引用只涉及到资源的转移,没有资源的拷贝开销; 有地址的用左值引用,没有地址的用右值引用;有变量名字的用左值引用,没有变量名字的(比如临时量没有名字)用右值引用。


1、左值引用和右值引用有什么区别?
2、汇编代码下,观察这两种一样吗?
3、如果是局部对象直接当作返回值这样好吗?不好的话该怎么做呢?
4、如果出现&&一定就是右值引用吗?什么情况下会出现不是右值的情况呢?
5、引用折叠是什么?

你可能感兴趣的:(C++,c++)