LZWcc's Blog

Back

移动语义底层逻辑拆解#

1. 左值(lvalue)与右值(rvalue)的本质#

在 C++ 内存模型中:

  • 左值:指的是拥有持久存储地址的对象(Identity)。例如变量 int_array,它在栈上有一个明确的内存起始位置,你可以通过 &int_array 获取它的地址。
  • 右值:通常是临时对象字面量(Cannot take address)。例如 std::vector<int>{1,2,3} 生成的匿名对象,它在表达式结束后就会被销毁。

2. std::move 的真实作用:右值强制转换#

std::move 并不移动数据。它的唯一功能是:将左值强制转换为“右值引用”类型

std::vector<int> stealing_ints = std::move(int_array);
c

这行代码告诉编译器:虽然 int_array 是个有名字的左值,但请把它当成一个“右值”来对待。这会触发 std::vector 的**移动赋值运算符(Move Assignment Operator)**而非拷贝赋值。


3. 所有权的转移#

vector 的结构可以简化为三个指针:start (指向数据首地址), finish, end_of_storage

  • 执行 std::moveint_array 拥有指向堆内存(存储 {1,2,3,4})的指针。
  • 执行 std::move
    1. stealing_ints 直接复制了 int_array 的三个指针地址。
    2. 关键步骤int_array 的三个指针被重置为 nullptr
    3. 结果:此时不能访问到 int_array 里的数据。

4. 右值引用 && 的深意#

std::vector<int> &&rvalue_stealing_ints = std::move(stealing_ints);
c

这里的 && 是一种类型声明。它声明 rvalue_stealing_ints 是对 stealing_ints 数据的引用,且暗示该引用所指向的对象是可以被“夺取”的。

  • 注意:这行代码并没有触发“移动构造”,因为它仅仅是绑定了一个引用,没有发生实际的所有权交接。所以 stealing_ints 此时仍然持有数据。

5. 函数调用中的所有权陷阱#

代码中两个函数的对比展示了移动语义的破坏性

场景 A:所有权被夺取#

move_add_three_and_print(std::move(int_array2));
c
  • 在函数内部执行了 std::vector<int> vec1 = std::move(vec);
  • 这导致 int_array2 在内存中的指针被置为空。
  • 后果:函数结束后,int_array2 变成了有效但内容为空的状态。

场景 B:仅操作引用#

add_three_and_print(std::move(int_array3));
c
  • 虽然传入的是右值引用,但函数内部直接操作了传入的 vec,没有再将其 move 给其他对象。
  • 后果:所有权从未离开过 int_array3,因此它依然持有数据。

附录#

move_semantics.cpp

CMU 15-445 必修:C++ 移动语义与所有权转移的深层逻辑
https://lzwcc.xyz/blog/cmu-15-445-post01/
Author LZWcc
Published at February 14, 2026
Comment seems to stuck. Try to refresh?✨