C++中的移动语义:初学者需要了解的内容

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

“移动语义”这个高深词汇背后是什么?

\n\n

想象一下,你要搬进新公寓。你有一个又大又重的沙发。哪种方式更快:自己扛着它爬五层楼梯,还是直接把钥匙交给搬运工,说:“伙计们,沙发归你们了,随便处理,我不需要了”?第二种方式,就是C++中的移动语义。

\n\n

在C++11标准出现之前,程序员们不得不痛苦地复制数据。每次从函数返回一个向量或字符串时,编译器都会创建一个精确的副本。如果向量包含一百万个元素——它会老老实实地复制每一个。这很慢、效率低下,就像用手抄写一本书,而不是直接把原稿给别人。

\n\n

移动语义是一种机制,它允许将资源(内存、文件描述符、套接字)从一个对象移动到另一个对象,而无需昂贵的复制操作。移动后,原始对象会处于“空”但有效的状态,可以安全地销毁或为其赋予新值。

\n\n

在本文中,我们将用通俗易懂的语言和代码示例,讲解右值引用、std::move函数、移动构造函数和移动赋值运算符。

\n\n

为什么没有移动语义代码会变慢?

\n\n

让我们看一个经典的例子,这个问题在2011年之前一直困扰着所有C++程序员:

\n\n
#include <iostream>\n#include <vector>\n#include <string>\n\nstd::vector<std::string> createBigVector() {\n    std::vector<std::string> vec(1000000, "Hello");\n    return vec;  // 糟糕!复制了一百万个字符串!\n}\n\nint main() {\n    std::vector<std::string> data = createBigVector();\n    std::cout << "Size: " << data.size() << std::endl;\n    return 0;\n}
\n\n

这里发生了什么?函数createBigVector创建了一个向量,填充数据并返回。编译器(未优化时)会执行以下操作:

\n
    \n
  1. 在函数内部创建向量。
  2. \n
  3. 将整个向量复制到返回值中。
  4. \n
  5. 销毁原始向量。
  6. \n
  7. 在main函数中,将该值复制到变量data中。
  8. \n
\n\n

结果:对一百万个字符串进行了两次完整复制。这不仅慢——对性能来说简直是灾难。

\n\n

右值和左值:C++的两个世界

\n\n

要理解移动语义,需要先了解两个基本概念:左值和右值。

\n\n
    \n
  • 左值 — 在内存中有地址和名称的对象。你可以获取它的地址(&obj)。例如:变量int x = 5; — x是左值。
  • \n
  • 右值 — 没有名称的临时值,不能出现在赋值运算符的左侧。例如:5、x + 3、函数getTemp()的返回值。你不能写&(x + 3)。
  • \n
\n\n

以前,右值引用(用&&表示)并不存在。编译器看到一个临时对象会说:“嗯,这是右值,我唯一能做的就是复制它或销毁它。”移动语义给了编译器第三种选择:窃取右值的资源,因为它反正很快就会被销毁。

\n\n

std::move和右值引用是如何工作的?

\n\n

std::move函数只是一个类型转换。它实际上并不移动数据,只是将左值转换为右值,从而允许编译器“掠夺”该对象。

\n\n

考虑一个管理动态内存的简单类:

\n\n
#include <iostream>\n#include <cstring>\n\nclass MyString {\nprivate:\n    cha                

博客

书籍推荐