Cpp学习25 移动语义与智能指针

铁名_IronName Lv5

设想这样一个类,它的唯一作用是持有并“拥有”传递给它的指针,然后在类对象超出作用域时释放该指针。只要该类的对象仅作为局部变量创建,我们就能保证该类能够正确地超出作用域(无论函数何时或如何终止),并且所拥有的指针能够被销毁。
这样的类被称为智能指针。 智能指针是一个组合类,其设计目的是管理动态分配的内存,并确保当智能指针对象超出作用域时,内存会被释放。

Move semantics  移动语义

如果,我们不让复制构造函数和赋值运算符复制指针(“复制语义”),而是将指针的所有权从源对象转移到目标对象,会怎么样?这就是移动语义的核心思想。 移动语义意味着类会转移对象的所有权,而不是创建一个副本。

在 C++11 中,“移动”的概念被正式定义,并且语言中加入了“移动语义”,以便正确区分复制和移动操作。

R-value references

C++11 新增了一种称为右值引用的引用类型。右值引用只能用右值初始化。左值引用使用单个 & 符号创建,而右值引用则使用两个 & 符号创建:

1
2
3
int x{ 5 };
int& lref{ x }; // l-value reference initialized with l-value x
int&& rref{ 5 }; // r-value reference initialized with r-value 5

右值引用不能用左值初始化。

右值引用有两个非常有用的特性。首先,右值引用会将初始化对象的生命周期延长至右值引用本身的生命周期(指向常量对象的左值引用也能做到这一点)。其次,非常量右值引用允许你修改右值!

R 值引用作为函数参数

右值引用更常用作函数参数。

几乎永远不要返回右值引用,原因与几乎永远不要返回左值引用相同。大多数情况下,当被引用的对象在函数结束时超出作用域时,你最终会返回一个悬空引用。

关于左值和右值:Cpp学习16 第 12 章 复合类型:引用与指针

移动构造函数和移动赋值

C++11 定义了两个用于移动语义的新函数:移动构造函数和移动赋值运算符。复制构造函数和复制赋值运算符的目标是将一个对象复制到另一个对象,而移动构造函数和移动赋值运算符的目标是将资源的所有权从一个对象转移到另一个对象(这通常比复制操作开销小得多)。

移动构造函数和移动赋值何时被调用?

当移动构造函数和移动赋值函数定义完毕,且构造或赋值的参数为右值时,就会调用这些函数。通常情况下,这个右值是字面值或临时值。
否则复制。

不要指望编译器创建的隐式移动构造函数or移动赋值语句。

如果我们有一个表达式“a = b”(其中 b 是一个左值),我们不会合理地预期 b 会被以任何方式改变。
如果我们构造一个对象或进行赋值操作,而参数是右值,那么我们就知道这个右值只是一个临时对象。与其复制它(这样做开销很大),我们可以直接将它的资源(这样做开销很小)转移到我们正在构造或赋值的对象上。这样做是安全的,因为无论如何,这个临时对象都会在表达式执行完毕后被销毁,所以我们知道它不会再被使用!

C++11 通过右值引用,使我们能够在参数为右值和左值时提供不同的行为,从而使我们能够更智能、更高效地决定对象的行为方式。

不能使用 std::swap() 来实现移动构造函数和移动赋值。

std::move

在 C++11 中,std::move 是一个标准库函数,它使用 static_cast 将其参数强制转换为右值引用,从而可以调用移动语义。因此,我们可以使用 std::move 将左值强制转换为更倾向于移动而非复制的类型。std::move 定义在 <utility> 头文件中。

1
2
3
4
5
6
7
template <typename T>
void mySwapMove(T& a, T& b)
{
T tmp { std::move(a) }; // invokes move constructor
a = std::move(b); // invokes move assignment
b = std::move(tmp); // invokes move assignment
}

移动后的对象将处于有效但可能不确定的状态。

在对数组元素进行排序时,std::move ,使用移动语义,更加高效。

std::unique_ptr

std::unique_ptr 是 C++11 中 std::auto_ptr 的替代函数。它应该用于管理任何动态分配且不被多个对象共享的对象。也就是说,std::unique_ptr 应该完全拥有它所管理的对象,而不能与其他类共享所有权。属于<memory>头文件。

与 std::auto_ptr 不同,std::unique_ptr 正确地实现了移动语义。

请使用 std::make_unique() 而不是创建 std::unique_ptr 并自行使用 new。

std::shared_ptr

与旨在单独拥有和管理资源的 std::unique_ptr 不同,std::shared_ptr 旨在解决需要多个智能指针共同拥有资源的情况。

std::shared_ptr 内部会跟踪有多少个 std::shared_ptr 指向同一个资源。只要至少有一个 std::shared_ptr 指向该资源,即使个别 std::shared_ptr 被销毁,该资源也不会被释放。

可以使用(也应该使用)std::make_shared() 创建 std::shared_ptr。std::make_shared() 在 C++11 中可用。

std::shared_ptr 存在循环依赖问题 和 std::weak_ptr

循环引用 (也称为循环引用循环 )是指一系列引用,其中每个对象都引用下一个对象,而最后一个对象又引用回第一个对象,从而形成引用循环。导致互相阻止销毁。

std::weak_ptr

std::weak_ptr 的设计初衷是为了解决上述“循环所有权”问题。std::weak_ptr 是一个观察者——它可以像 std::shared_ptr(或其他 std::weak_ptr)一样观察和访问同一个对象,但它本身并不被视为所有者。

std::weak_ptr 有一个巧妙的机制——因为它能够访问对象的引用计数,所以它可以判断自己指向的对象是否有效!测试 std::weak_ptr 是否有效的最简单方法是使用 expired() 成员函数,如果 std::weak_ptr 指向无效对象,则返回 true ,否则返回 false 。

  • 标题: Cpp学习25 移动语义与智能指针
  • 作者: 铁名_IronName
  • 创建于 : 2026-08-14 13:22:45
  • 更新于 : 2026-08-14 16:03:32
  • 链接: https://blog.ironname.top/2026/Cpp/Cpp学习25/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论