C++11新特性之右值引用

一、左值、右值是什么?

左值是指存储在内存中、有明确存储地址(可取地址)的数据;

右值是指可以提供数据值的数据(不可取地址);

#include<iostream>
using namespace std;
int main()
{
	int a = 110;
	int b = 1100;
	a = b;
}

一般情况下,位于=前的表达式为左值,位于=后边的表达式为右值。也就是说例子中的a, b为左值,110,1100为右值。a=b是一种特殊情况,在这个表达式中a, b都是左值,因为变量b是可以被取地址的,不能视为右值。

#include<iostream>
using namespace std;
int main()
{
	//lvalue为左值 110为右值
	int lvalue = 110;
	//左值引用
	int &a = lvalue;
	//右值引用
	int && rvalue = 7;
	//常量右值引用
	const int &&b = 9;
	//常量左值引用(可通过同类型各种引用初始化)
	const int& c = lvalue;
	const int& d = rvalue;
	const int& e = b;
	const int& f = a;
}

   C++11中右值分为纯右值、将亡值。

  • 纯右值非引用返回的临时变量、运算表达式产生的临时变量、原始字面量和 lambda 表达式等
  • 将亡值与右值引用相关的表达式,比如,T&&类型函数的返回值、 std::move 的返回值等。

二、右值引用

       右值引用就是对一个右值进行引用的类型。因为右值是匿名的,所以我们只能通过引用的方式找到它。无论声明左值引用还是右值引用都必须立即进行初始化,因为引用类型本身并不拥有所绑定对象的内存,只是该对象的一个别名。通过右值引用的声明,该右值又“重获新生”,其生命周期与右值引用类型变量的生命周期一样,只要该变量还活着,该右值临时量将会一直存活下去。

右值引用用法:

#include <iostream>
using namespace std;

int&& value = 520;
class Test
{
public:
	Test()
	{
		cout << "我是构造函数" << endl;
	}
	Test(const Test& a)
	{
		cout << "我是拷贝构造函数" << endl;
	}
};

Test getTest()
{
	return Test();
}

int main()
{
	int a1;
	int &&a2 = a1;        // error
	Test& t = getTest();   // error
	Test && t = getTest();
	const Test& t = getTest();
	return 0;
}
  • 在上面的例子中int&& a = 520;里面520是纯右值,a是对字面量520这个右值的引用。

  • int &&a2 = a1;a1虽然写在了=右边,但是它仍然是一个左值,使用左值初始化一个右值引用类型是不合法的。

  • Test& t = getTest()这句代码中语法是错误的,右值不能给普通的左值引用赋值。

  • Test && t = getTest();getTest()返回的临时对象被称之为将亡值t是这个将亡值的右值引用。

  • const Test& t = getTest()这句代码的语法是正确的,常量左值引用是一个万能引用类型,它可以接受左值、右值、常量左值和常量右值。

三、右值引用在性能方面的优化

       在C++中在进行对象赋值操作的时候,很多情况下会发生对象之间的深拷贝,如果堆内存很大,这个拷贝的代价也就非常大,在某些情况下,如果想要避免对象的深拷贝,就可以使用右值引用进行性能的优化。

优化之前:

#include <iostream>
using namespace std;

class Test
{
public:
	Test() : num(new int(520))
	{
		cout << "我是构造函数" << endl;
	}

	Test(const Test& a) : num(new int(*a.num))
	{
		cout << "我是拷贝构造函数" << endl;
	}

	~Test()
	{
		delete num;
	}

	int* num;//指针类型
};

Test getTest()
{
	Test t;
	return t;
}

int main()
{
	Test t = getTest();
	cout << "t.num: " << *t.num << endl;
	return 0;
};

        通过输出的结果可以看到调用Test t = getTest();的时候调用拷贝构造函数对返回的临时对象进行了深拷贝得到了对象t,在getTest()函数中创建的对象虽然进行了内存的申请操作,但是没有使用就释放掉了。如果能够使用临时对象已经申请的资源,既能节省资源,还能节省资源申请和释放的时间,如果要执行这样的操作就需要使用右值引用了,右值引用具有移动语义,移动语义可以将资源(堆、系统对象等)通过浅拷贝从一个对象转移到另一个对象这样就能减少不必要的临时对象的创建、拷贝以及销毁,可以大幅提高C++应用程序的性能。

优化之后:

#include <iostream>
using namespace std;

class Test
{
public:
	Test() : num(new int(520))
	{
		cout << "我是构造函数" << endl;
	}

	Test(const Test& a) : num(new int(*a.num))
	{
		cout << "我是拷贝构造函数" << endl;
	}

	// 添加移动构造函数
	Test(Test&& a) : num(a.num)
	{
		a.num = nullptr;
		cout << "我是移动构造函数" << endl;
	}

	~Test()
	{
		delete num;
		cout << "我是析构函数" << endl;
	}

	int* num;
};

Test getTest()
{
	Test t;
	return t;
}

int main()
{
	Test t = getTest();
	cout << "t.num: " << *t.num << endl;
	return 0;
};

      通过修改,给Test类添加了移动构造函数(参数为右值引用类型),这样在进行Test t = getTest();操作的时候并没有调用拷贝构造函数进行深拷贝,而是调用了移动构造函数,在这个函数中只是进行了浅拷贝,没有对临时对象进行深拷贝,提高了性能。

       在测试程序中getTest()的返回值就是一个将亡值,也就是说是一个右值,在进行赋值操作的时候如果=右边是一个右值,那么移动构造函数就会被调用。移动构造中使用了右值引用,会将临时对象中的堆内存地址的所有权转移给对象t,这块内存被成功续命,因此在t对象中还可以继续使用这块内存。

  • 18
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
右值引用和move语义是C++ 11中重要的特性之一,可以提高程序的效率和性能。右值引用是一种新的引用类型,其绑定到临时对象或将要销毁的对象上,而不是左值对象。move语义则是利用右值引用,将一个对象的资源所有权从一个对象转移到另一个对象,避免了不必要的内存拷贝,提高了程序的效率。 下面是一个使用右值引用和move语义的例子: ```c++ #include <iostream> #include <vector> using namespace std; vector<int> getVector() { vector<int> v = {1, 2, 3, 4}; return v; } int main() { vector<int> v1 = getVector(); // 拷贝构造函数 vector<int> v2 = move(v1); // 移动构造函数 cout << "v1 size: " << v1.size() << endl; // 输出 0 cout << "v2 size: " << v2.size() << endl; // 输出 4 return 0; } ``` 在上面的例子中,getVector函数返回一个临时对象vector<int>,该临时对象是一个右值。在主函数中,我们使用拷贝构造函数将临时对象的值拷贝到v1中,然后使用move函数将v1中的值移动到v2中。由于move函数使用了右值引用,将v1中的资源所有权转移到了v2中,避免了不必要的内存拷贝,提高了程序的效率。最后,我们输出v1和v2的大小,可以看到v1的大小为0,v2的大小为4,说明资源已经成功转移。 需要注意的是,使用move语义之后,原对象的值会被移动到新对象中,并且原对象的值会被置为默认值(例如,对于vector而言,原对象的大小为0)。如果需要保留原对象的值,则需要在移动之前先进行一次拷贝操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值