智能指针unique_ptr<>创建的过程
智能指针unique_ptr<>创建的过程
两种初始化方式的比较
std::unique_ptr 可以通过两种方式进行初始化:直接构造或者使用 std::make_unique()。它们之间的区别如下:
直接构造 std::unique_ptr:
你可以通过直接构造来创建一个 unique_ptr,如下:
std::unique_ptr<int> ptr(new int(42));
- 优点:
- 你可以在构造时精确控制对象的构造方式,比如分配自定义的内存管理器。
- 缺点:
- 这种方式需要手动调用
new,可能会导致意外的内存泄漏。如果new分配成功但在接下来分配std::unique_ptr之前抛出异常,内存可能会泄漏。 - 手动调用
new是一种旧的习惯用法,不符合现代 C++ 的推荐实践。
- 这种方式需要手动调用
使用 std::make_unique():
从 C++14 开始,标准库引入了 std::make_unique(),这是现代 C++ 推荐的创建 unique_ptr 的方式:
auto ptr = std::make_unique<int>(42);
- 优点:
- 安全性:
make_unique更加安全,因为它在单一表达式中创建对象并将其所有权交给unique_ptr,不会出现手动调用new时的内存泄漏问题。 - 简洁性:代码更简洁,不需要显式调用
new,减少手动管理内存的风险。 - 异常安全性:在
make_unique内部,内存分配和unique_ptr的创建是原子操作。如果在创建过程中发生异常,内存会被自动清理,避免了内存泄漏。
- 安全性:
- 缺点:
- 在需要自定义的删除器或内存管理策略时,
make_unique不如直接使用unique_ptr构造函数灵活。
- 在需要自定义的删除器或内存管理策略时,
第一种方式的内部细节
-
调用
new int(42):new int(42)会在堆上分配一个int对象,并初始化其值为42。这一步返回一个 原生指针,指向堆上的新对象。- 假设这个原生指针为
p_raw,此时p_raw是一个指向整数的裸指针。
int* p_raw = new int(42); // p_raw 是一个原生指针,指向堆上的整数对象第一步的结果:
- 产生了一个原生指针
p_raw,它指向堆上分配的int(42)对象。
-
创建
std::unique_ptr<int> ptr:- 接下来,
std::unique_ptr<int>的构造函数被调用,接受该原生指针作为参数,并将其托管给unique_ptr。 std::unique_ptr接管了该原生指针的所有权,此时 原生指针 已经不再被使用,所有权转移到了std::unique_ptr上。
std::unique_ptr<int> ptr(p_raw); // ptr 接管了 p_raw 的所有权第二步的结果:
std::unique_ptr接管了该指针的所有权,并在其生命周期内管理这个堆上的int对象。该指针变成了unique_ptr内部持有的指针。
- 接下来,
-
销毁原生指针:
- 在执行完
std::unique_ptr<int> ptr(new int(42));这行代码后,堆上对象的所有权只归unique_ptr,裸指针p_raw不再存在(除非你显式声明了它)。
- 在执行完
过程总结:
-
两个指针:
- 第一步:当调用
new int(42)时,产生了一个 原生指针,它指向新分配的int对象。 - 第二步:
std::unique_ptr的构造函数接受这个原生指针,并接管其所有权,之后该原生指针不再使用。
- 第一步:当调用
-
最终结果:
- 最终,只有一个指针(即
std::unique_ptr内部持有的指针)管理这个堆上的对象。原生指针在unique_ptr接管后不再需要使用。
- 最终,只有一个指针(即

浙公网安备 33010602011771号