您现在的位置是:首页 >技术交流 >【C++11】右值引用和移动语义网站首页技术交流
【C++11】右值引用和移动语义
C++11——右值引用和移动语义
文章目录
一、基本概念
传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。
1.左值引用和右值引用
什么是左值?什么是左值引用?
左值是一个表示数据的表达式(如变量名或解引用的指针),有如下特性:
1、我们可以获取它的地址+可以对它赋值(不一定能赋值,但一定能取地址)
2、左值可以出现赋值符号的左边,右值不能出现在赋值符号左边
3、定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址
- 左值引用就是给左值的引用,给左值取别名。
void cpp11_test4() { //以下p、b、c、*p都是左值 int* p = new int(0); int b = 1; const int c = 2; //以下几个是对上面左值的左值引用 int*& rp = p; int& rb = b; const int& rc = c; int& pvalue = *p; cout << p << ":" << rp << endl; cout << b << ":" << rb << endl; cout << c << ":" << rc << endl; cout << *p << ":" << pvalue << endl; }
什么是右值?什么是右值引用?
右值也是一个表示数据的表达式,如临时变量:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回,要是传值返回)等等,有如下特性:
1、右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,
2、右值不能取地址。
综上左值和右值最大区别在于左值可以取地址,右值不可以取地址(因为右值是临时变量,没有实际被存储起来。)
补充:
C++里又把右值分为两类(纯右值和将亡值):
1.纯右值(内置类型的对象):10、x + y、‘a’
2.**将亡值(自定义类型的对象):**传值返回的拷贝对象:to_string(1234)、匿名对象:string(“xxxxx”)、s1 + “hello”
右值引用:
- 右值引用就是对右值的引用,给右值取别名。
int main() { double x = 1.1, y = 2.2; // 以下几个都是常见的右值 10;//字面常量 x + y;//表达式返回值 fmin(x, y);//函数返回值(传值返回) // 以下几个都是对右值的右值引用 int&& rr1 = 10; double&& rr2 = x + y; double&& rr3 = fmin(x, y); /* 这里编译会报错:error C2106: “=”: 左操作数必须为左值 10 = 1; x + y = 1; fmin(x, y) = 1; */ /* 这里编译会报错,右值不能取地址 cout << &10 << endl; cout << &(x + y) << endl; cout << &fmin(x, y) << endl; */ return 0; }
注意:
- 右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用,是不是感觉很神奇,这个了解一下实际中右值引用的使用场景并不在于此,这个特性也不重要。
int main() { double x = 1.1, y = 2.2; int&& rr1 = 10; const double&& rr2 = x + y; rr1 = 20; rr2 = 5.5; // 报错 return 0; }
2.左值引用与右值引用的比较
左值引用总结:
- 左值引用只能引用左值,不能引用右值
- 但是const左值引用既可以应用左值,也可以引用右值
int main() { // 左值引用只能引用左值,不能引用右值。 int a = 10; int& ra1 = a; // ra为a的别名 //int& ra2 = 10; // 编译失败,因为10是右值 // const左值引用既可引用左值,也可引用右值。 const int& ra3 = 10; const int& ra4 = a; return 0; }
右值引用总结:
- 右值引用只能引用右值,不能引用左值
- 但是右值引用可以引用move以后的左值
int main() { // 右值引用只能右值,不能引用左值。 int&& r1 = 10; int a = 10; /* error C2440: “初始化”: 无法从“int”转换为“int &&” message : 无法将左值绑定到右值引用 int&& r2 = a; */ // 右值引用可以引用move以后的左值 int&& r3 = move(a); return 0; }
再次强调这四句话:
- 左值引用只能引用左值,不能引用右值
- 但是const左值引用既可以引用左值,也可以引用右值
- 右值引用只能引用右值,不能引用左值
- 但是右值引用可以引用move以后的左值
右值引用是通过移动构造和移动赋值来极大提高深拷贝的效率,详情见下文:
二、右值引用使用场景和意义
前面我们可以看到和const左值引用既可以引用左值又可以引用右值,那为什么C++11还要提出右值引用呢?这不是画蛇添足呢?下面我们来看看左值引用的使用场景与短板(深拷贝的问题),以及右值引用是如何补齐这个短板的!
1.左值引用的使用场景
为了把整个过程说的通俗易懂,需要一个深拷贝的类,我们以先前模拟实现的string类来作为示例:简易版string类的模拟实现
类当中实现了一些基本的成员函数,并在string的拷贝构造函数和赋值运算符重载函数当中打印了一条提示语句,这样当调用这两个函数时我们就能够知道。
namespace cpp { class string { public: typedef char* iterator; iterator begin() { return _str; //返回字符串中第一个字符的地址 } iterator end() { return _str + _size; //返回字符串中最后一个字符的后一个字符的地址 } //构造函数 string(const char* str = "") { _size = strlen(str); //初始时,字符串大小设置为字符串长度 _capacity = _size; //初始时,字符串容量设置为字符串长度 _str = new char[_capacity + 1]; //为存储字符串开辟空间(多开一个用于存放'