右值引用与移动构造函数、移动赋值
来源:互联网 发布:暗黑团队毁魔兽 知乎 编辑:程序博客网 时间:2024/05/16 17:35
有一阵子没看C++了,翻开C++Primer又陌生了一些,想了想引用,于是乎来看了下右值引用。
int a=5;int &b=a;
这是左值引用,若我们这样修改:
int a=5;int &b=a+1;
编译器会报错:非常量引用的初始值必须为左值。也就是右边的a+1是常量,常量给非常量引用赋值就报错。
我们可以这样修改就不报错了:
int a=5;const int &b=a+1;
此时我们可以试一试:
int a = 5; const int &b = a+1; a++; cout << b;
发现b的结果为6,也就是a++并没有影响到b,也就是说b指向的地址并不是a,而是一个新的地址存放着a+1的值,但是由于b被const修饰,我们无法修改b的值。
C++11新增了右值引用,那什么是右值引用呢?右值引用用&&表示,右值可以是常量,或者一些表达式以及返回值的函数,如上面的左值引用,需要修改为const才能够把常量给赋值过来,而右值引用就如下:
int a=5; int &&b=a+1; a++; cout<<b;
该代码和上面的那串代码结果一样,其中变量b的地址是存放着a+1值的地址,不同点就是我们可以自由的修改b的值,而不像左值引用那样被const给限制。
移动语义和右值引用
那么问题就来了,右值引用有什么用呢?我们来看看在类中的使用吧:
class A {private: static int count; char *str; int len;public: A(int n = 0, char ch = ' ') :len(n) { str = new char[n]; for (int i = 0; i < n; i++) { str[i] = ch; } count++; showdata(); cout << "构造函数" << endl; } ~A() { cout << "析构函数 :" << (void*)str << endl; delete[]str; str = nullptr; } A(const A ©) { len = copy.len; str = new char[len]; for (int i = 0; i < len; i++) { str[i] = copy.str[i]; } count++; showdata(); cout << "拷贝构造函数" << endl; } A(A &©) { str = copy.str; len = copy.len; copy.str = nullptr; copy.len = 0; count++; showdata(); cout << "移动构造函数" << endl; } A operator+(const A &test) { A temp(len + test.len); for (int i = 0; i < len; i++) { temp.str[i] = str[i]; } for (int i = 0; i < test.len; i++) { temp.str[i + len] = test.str[i]; } return temp; } A operator=(const A &test) { if (this == &test) return *this; delete str; len = test.len; str = new char[len]; for (int i = 0; i < len; i++) str[i] = test.str[i]; return *this; } void showdata() { cout << count << endl; cout << "Len: " << len << endl; cout << "address: " << (void *)str << endl; } void Aprint() { if (len == 0) cout << "NULL" << endl; else for (int i = 0; i < len; i++) cout << str[i]; cout << endl; }};int A::count = 0;int main(){ A a(3, 'x'), b(2, 'o'); A c(a+b); A d = a; cout << "a:"; a.Aprint(); cout << "b:"; b.Aprint(); cout << "c"; c.Aprint(); cout << "d:"; d.Aprint(); return 0;}
结果:
代码、结果分析:
首先,数字1、2下面对应的是a与b对象生成,调用构造函数,然后到a+b调用operator+,然后返回一个临时对象temp(调用构造函数),然后就到c(temp),此时并不是调用拷贝构造函数,而是移动构造函数,然后我们把原来的a+b产生的临时对象的str成员置成nullptr,于是调用了析构函数,析构0000000的位置(因为我们置临时对象为空了),而我们的c就相当于窃取了temp的内容(改变了对象里面的str指针),所以最后c的str地址没变,还是01052598而内容变为temp的。
那么,这和拷贝构造有什么区别呢?区别就是,若我们使用拷贝构造函数,我们将会重新生成临时对象,然后对临时的数据进行批量的复制,然后又清除,很费力,而使用移动构造我们可以直接让数据指向临时数据所指向的数据,而不用进行批量的复制操作。就例如我们上面的代码,若不使用移动构造函数,a+b产生的临时对象让c调用拷贝构造,然后批量复制数据,若数据越大,就越影响计算机性能。
移动赋值
同样的,我们既然可以这样修改构造函数为移动构造函数,我们也可以修改operator=,通过判断是否是自我复制,若不是,则将我们类的指针成员指向另一个对象的指针成员,省去很多操作,代码如下:
A operator=(A &&test) //此时的参数不能为const,因为我们要修改test的成员 { if (this == &test) return *this; delete str; len = test.len; str = test.str; test.str = nullptr; test.len = 0; cout << "调用移动赋值" << endl; return *this; }
若我们要使用移动赋值,我们可以将要让赋的值不是左值,看起来像右值,然后调用移动赋值运算,我们可以使用static_cast<>将对象的类型强制转换为classtype &&,除此之外,C++11提供了一种方式,通过使用utility头文件,使用std::move()。
如下:
A a(3, 'x'), b(2, 'o'); //a=static_cast<A>(b); a=std::move(b); a.Aprint(); b.Aprint();
结果:
可以看见对象b的值被置为空,而a的str值为对象b的str值。
- 右值引用与移动构造函数、移动赋值
- C++:右值引用与移动构造
- C++新特性 右值引用 移动构造函数
- 新概念“右值引用”和 ”移动构造函数”是怎么回事?
- C++11学习笔记2---右值引用与移动构造函数
- c++11:右值引用与转义语义和移动构造函数的关系
- 右值引用和移动构造
- 移动构造、移动赋值函数
- 移动构造函数与移动赋值运算符
- c++11中的右值引用以及移动构造
- C++学习笔记之右值引用以及移动构造
- c++ 特性: 右值引用与移动语义
- C++11——对象移动与右值引用
- [C++] 右值引用:移动语义与完美转发
- 右值以及移动函数
- 移动构造函数和移动赋值运算符
- 移动构造函数和移动赋值运算符
- C++11移动构造函数,移动赋值运算符
- 解决Bootstrap模态窗口Modal中使用Kindeditor或UEditor编辑器 点击关闭弹窗 出现第二次无法加载的问题
- div边框css动画
- 聊天机器人——智能回复机器人研发
- Android---RecyclerView 刷新与加载更多
- 小程序
- 右值引用与移动构造函数、移动赋值
- #define
- UVA11549 计算机谜题(Floyd判圈算法)
- LwIP BUG之TCP连接丢失
- 获取范围内的随机数
- 关于<c:ForEach/>中属性varStatus的详解
- 关于React.js的一些知识点(实时更新)
- it感悟
- Tensorflow使用slim工具(vgg16模型)实现图像分类与分割