我试图理解C++的移动语义、移动构造函数、移动赋值运算符、std::move()。 让我们考虑以下示例:
#include <iostream>
void swapWithMove(int& a, int& b) {
int temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}
int main() {
int x = 5;
int y = 10;
std::cout << "Before swap: x = " << x << ", y = " << y << std::endl;
swapWithMove(x, y);
std::cout << "After swap: x = " << x << ", y = " << y << std::endl;
return 0;
}
此示例通过 std::move() 交换两个整数值。我知道该示例更多地用于教育目的,并不是移动语义的典型用例。 但是,我仍然想正确理解当执行 swapWithMove 函数时,内存中 x、y、temp、a 和 b 发生了什么。
提前非常感谢您!
在这种情况下(交换
int
s),移动语义根本不会产生任何区别,对于我所知道的任何实现(例外会有些令人惊讶)。
当我们处理像 vector
这样的东西(主要)存储指向它所包含的数据的指针时,移动语义很大程度上会发挥作用。例如,让我们考虑一下
std::vector
的稍微简化的实现:
template <class T>
class vector {
T *data;
std::size_t size_allocated;
std::size_t size_in_use;
// ...
这里需要注意的一件大事是
vector
结构本身不包含任何真实数据。它只包含一个指向数据的指针,该数据在其 Allocator
对象获取它的任何地方进行分配(但通常使用标准分配器,它从空闲存储中获取内存。
无论如何,让我们考虑一下我们的
vector
的作业:
vector &operator=(vector const &other) {
if (size_allocated < other.size_allocated) {
// reallocate our storage so we have enough room
}
for (int i=0; i<other.size_in_use; i++)
data[i] = other.data[i];
return *this;
}
这简化了很多,但你得到了总体思路——逐步遍历所有元素,并将每个元素从旧向量复制到新向量。
但是,如果右边的是
rvalue
,则意味着我们不需要保留其内容。所以我们可以“窃取”它包含的内容:
vector &operator=(vector &&other) {
delete [] data; // simplified--really uses allocator object
data = other.data;
size_in_use = other.size_in_use;
size_allocated = other.size_allocted;
other.data = nullptr;
other.size_allocated = 0;
other.size_in_use = 0;
return *this;
}
因此,我们不是单独复制每个元素,而是从源中获取指针,并将源转换为空向量。速度非常快,无论其大小如何。不过,我们可以通过一种稍微棘手的方法来简化它:
vector &operator=(vector &&other) {
swap(data, other.data);
swap(size_in_use, other.size_in_use);
swap(size_allocated, other.size_allocated);
return *this;
}
只需将我们的内容与其他人的内容交换即可。然后它会被销毁,然后我们的内容就会被处理掉。
无论如何:移动
int
通常与普通作业没有什么不同。与大多数其他标量类型相同(char
、short
、long
、double
、float
、指针等),结构化类型会出现差异,尤其是那些主要包含指向实际数据的指针的类型他们储存。