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

目录

左值与右值

左值引用与右值引用 

右值引用的作用

移动构造和移动赋值

合成的移动操作

万能引用和完美转发


左值与右值

        左值和右值都是一个表示数据表达式,和它们一样,左值一般出现在赋值表达式的左边(右边也可以)右值出现在赋值表达式的右边(不能出现在左边)

        我们可以看一下几个表达式来明确左值和右值的区别:

//左值 a\b\*p 都是左值,可以长时间存在
const int a = 100;
double b = 10.0;
int* p = &a;

//右值 10\x+y\func(x) 都是右值,可以看作临时变量

10;
x + y;
func(x);

我们可以根据上方的表达式来总结左值和右值最大的区别:

1.左值有持久的状态,右值要么是字面常量、要么是求值过程中创建的临时变量

2.左值可以取地址,右值不可以


左值引用与右值引用 

        来看一段左值引用和右值引用的代码:

//左值引用
int& c = a;
int& d = *p;

//右值引用
int&& c2 = 10;
int&& d2 = a + b;

        可以看出,左值引用和右值引用在语法上最大的差别是&和&&的差别

但是左右值引用在定义上也有很大的差别:

1.左值引用可以看作平时我们使用的引用,作为一个对象的别名

2.右值引用只能绑定到一个将要销毁的变量(“将亡值”) 

那么左值引用可以引用右值,右值引用可以引用左值吗?

        答案是可以的,只是我们需要对语法进行一点改变:

//两个左值
int a = 1;
int b = 2;

//左值引用 引用 右值
const int& c = 20+b;

//右值引用 引用 左值
int&& d = move(a);
int&& e = move(b);

即:

左值引用 引用 右值时,需要加上const

右值引用 引用 左值时,需要使用move()函数将左值转为右值


右值引用的作用

        我们知道左值引用的一个作用是用别名来减少编译器的拷贝,提高效率。

        那么C++11右值引用的作用是什么呢?

        因为右值引用只能指向一个“将亡值”,我们可以在这个将亡值要销毁的时候把它内部的数据和需要用拷贝构造或拷贝赋值的对象进行交换,这样,这个对象的原数据一起被将亡值带走,这个对象也获得了它想要的数据。我们也可以减少拷贝,提高效率

        目的是为了实现更高的效率和减少更多的拷贝:实现移动赋值和移动构造

         让我们来看看代码:

移动构造和移动赋值

        首先我们先定义一个Student类,作为移动构造和移动赋值的基础,并在内部写一个移动构造,传的是一个右值引用

ps:因为右值引用的值在出了这个作用域后就会消亡,所以我们不需要另外开一个空间来把this指向的对象和拷贝进来的对象进行交换

//我们定义一个Student类
class Student
{
public:
	//普通构造
	Student(const string& n,const int& a) :name(n), age(a)
	{}
	
	//默认构造
	Student(){}

	//拷贝构造
	Student(const Student& s)
	{
		Student tmp(s.name, s.age);
		swap(tmp);
		cout << "拷贝构造" << endl;
	}

    //移动构造
	Student(Student&& s)
	{
		swap(s);
		cout << "移动构造" << endl;
	}

private:
    //重载了一下swap函数
	void swap(Student& s)
	{
		name.swap(s.name);
		std::swap(age,s.age);
	}

private:
	string name;
	int age;
};

再在main函数中创建Student:

//移动构造和拷贝构造
Student one("张三", 20);
	
//创建一个链表存储Student类
//简介调用拷贝构造和移动构造
list<Student> test1;
test1.push_back(one);
test1.push_back(Student("王五", 123));
cout << "--------------------------" << endl;
//直接调用拷贝构造和移动构造
Student two(one);
//用move把左值变成右值,使用移动构造后one就变为空
Student three(move(one));

运行结果如下:

        移动赋值和移动构造的区别在于:移动赋值通过重载=运算符来实现资源转移,本质和移动构造相似,不做赘述。


合成的移动操作

        和拷贝构造函数一样,C++11后,编译器也会为某些类默认生成移动构造函数和移动赋值函数,但前提是这个类没有显式定义自己的拷贝构造、拷贝赋值运算符和析构函数,如果一个类中显式定义了这些它们,那么编译器就不会默认生成合成的移动构造和合成的移动赋值

PS.C++11前,一个空类会默认生成的有构造函数、析构函数、拷贝构造函数、赋值运算符重载、两个&运算符重载,C++11后会默认生成构造函数、析构函数、拷贝构造函数、赋值运算符重载、两个&运算符重载和移动构造函数、移动赋值运算符重载


万能引用和完美转发

        如果我们想用一个函数同时引用左值和右值,同时又不使用const和move把原来的数据变成右值或左值时,我们应该怎么办呢?

        这里学习一个新概念:万能引用和完美转发

//这就是一个万能引用
template<class T>
void PerfectForward(T&& t)
{
//先写一个临时变量在里面,方便等会调用查看
	Student a(t);
}

        模板+右值引用,就是万能引用的一个格式

 此时,传进这个函数的值会被自动识别为左值或右值,然后再传给拷贝构造或移动构造函数

我们来传一传:

PerfectForward(one);  //传左值
PerfectForward(Student("李四", 24));  //传右值

最后运行结果却是两个拷贝构造函数

主要是因为形参t会变成左值,就算传进来的是右值,编译器也会把它看成左值(看它单传一个t是不是很像左值。。。) 

        我们想要解决这个问题就必须进行完美转发,在完美转发下,传进来的是谁,传给那个构造函数的就是谁:

template<class T>
void PerfectForward(T&& t)
{
    //forward<类型>()
    //完美转发
	Student a(std::forward<T>(t));
}

此时编译器就会好好调用应该调用的拷贝构造和移动构造了:


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
右值引用和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)。如果需要保留原对象的值,则需要在移动之前先进行一次拷贝操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值