经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
现代C++(Modern C++)基本用法实践:三、移动语义
来源:cnblogs  作者:寡人正在Coding  时间:2023/7/17 9:39:13  对本文有异议

概述

移动

移动(move)语义C++引入了一种新的内存优化,以避免不必要的拷贝。在构造或者赋值的时候,如果实参是右值(或者左值由std::move转换成右值),便会匹配移动语义的函数调用如下述举例的Str(Str&& obj)

移动语义的本质是将资源(内存/句柄)转移给另一个对象,被转移资源的对象不应再被使用。(这个概念有点像仙侠小说中的夺舍,夺舍成功的人获取被夺舍的人的身体(资源)),如下面伪代码:

  1. class Obj
  2. {
  3. data
  4. Obj(){
  5. data = malloc(100)
  6. }
  7. // 移动 (夺舍)
  8. Obj(Obj&& other){
  9. data = other.data
  10. other.data = nullptr
  11. }
  12. }

右值

右值直观理解是等号右边的值(大概如此,并不准确),右值的概念指代的东西比较多,大概是指不可寻址的值(也有例外)。我觉得这个不必太过纠结,记住几个常见的即可:

  • 临时对象:如函数返回的临时对象(下面有举例)
  • 字面量
  • 显式std::move()转换的值
  • 没有捕获参数的lambda

C++ 值类别表

在 C++11之后,C++根据

  • 被标识:可通过不同标识符指代同一实体。(对象/内存)
  • 可移动:可作为移动语义函数的参数,例如移动构造,移动赋值。

将值分为以下类别:

  • 泛左值:被标识
    • 左值:被标识且不可移动
    • 将亡值:被标识可移动
  • 右值:可移动
    • 将亡值:被标识可移动
    • 纯右值:不被标识且可移动

用法举例

参考测试项目代码ModernCppTest/modrenc_rvalueref_stdmove.cpp
主要内容:

  • 移动语义下的构造和赋值
  • 移动还是拷贝的重载匹配
  • C++ 优化临时对象(连加产生的中间临时对象)尝试调用移动语义
  1. #include "ModernCppTestHeader.h"
  2. #include <string>
  3. using std::string;
  4. namespace n_rvalueref {
  5. class Str {
  6. public:
  7. Str() {
  8. LOG("无参构造");
  9. this->str = new string();
  10. }
  11. Str(const string& str) {
  12. LOG("有参构造 str = " << str);
  13. this->str = new string(str);
  14. }
  15. Str(const Str& obj) {
  16. LOG("拷贝构造 obj.str = " << *obj.str);
  17. this->str = new string(*obj.str);
  18. }
  19. Str(Str&& obj) noexcept {
  20. LOG("移动构造 obj.str = " << *obj.str);
  21. this->str = std::move(obj.str);
  22. // 被移动的对象不应该再被使用了
  23. obj.str = nullptr;
  24. }
  25. Str& operator=(Str&& v) noexcept {
  26. LOG("移动语义 operator = ");
  27. if (this != &v) {
  28. this->str = std::move(v.str);
  29. }
  30. return *this;
  31. }
  32. Str operator+(const Str& v)
  33. {
  34. string s = *this->str + *v.str;
  35. return Str(s);
  36. }
  37. void Log()
  38. {
  39. LOG(str);
  40. }
  41. string* str;
  42. };
  43. }
  44. using n_rvalueref::Str;
  45. // 右值引用&移动语义
  46. void rvalueref_stdmove_test()
  47. {
  48. LOG_FUNC();
  49. LOG_TAG("拷贝构造");
  50. {
  51. Str t1("A");
  52. Str t2 = t1;
  53. LOG_VAR(*t2.str);
  54. }
  55. LOG_TAG("移动构造, 注意被移动的对象t1不应再被使用");
  56. {
  57. // t1是左值,使用std::move强制转换成右值
  58. Str t1("A");
  59. Str t2 = std::move(t1);
  60. LOG_VAR(*t2.str);
  61. }
  62. LOG_TAG("移动语义的运算符重载,注意运算符重载发生赋值运算(这个例子),而不是构造运算(上个例子)");
  63. {
  64. Str t1("A");
  65. Str t2;
  66. t2 = std::move(t1);
  67. }
  68. LOG_TAG("除了上述显示使用std::move转换,常见的容易忽视的发生移动构造场合列举");
  69. {
  70. LOG("---1 连续加法产生的临时对象,c++会尝试使用移动语义进行优化");
  71. Str t1("A");
  72. Str t2("B");
  73. Str t3("C");
  74. Str t4;
  75. t4 = t1 + t2 + t3;
  76. LOG("---2 函数返回的临时对象,c++会尝试使用移动语义进行优化");
  77. auto f = []() {
  78. auto s = Str("Hi");
  79. return s;
  80. };
  81. Str t5 = f();
  82. /*
  83. - 在容器中插入或删除元素:比如 std::vector::push_back,如果传递给它的是右值,它就会使用移动语义。
  84. - 在标准库算法中:许多标准库算法,比如 std::sort,std::partition 等,在进行元素交换时会使用移动语义。
  85. - 在 std::swap 中:std::swap 会使用移动语义来交换两个对象。
  86. */
  87. }
  88. }

原文链接:https://www.cnblogs.com/hggzhang/p/17523881.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号