命名空间
变体
操作

std::unique_ptr

来自 cppreference.cn
< cpp‎ | 内存
 
 
内存管理库
(仅作说明*)
未初始化内存算法
(C++17)
(C++17)
(C++17)
受约束的未初始化
内存算法
C 库

分配器
内存资源
垃圾回收支持
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
未初始化存储
(直到 C++20*)
(直到 C++20*)
显式生命周期管理
 
 
定义于头文件 <memory>
template<

    class T,
    class Deleter = std::default_delete<T>

> class unique_ptr;
(1) (C++11 起)
template <

    class T,
    class Deleter

> class unique_ptr<T[], Deleter>;
(2) (C++11 起)

std::unique_ptr 是一个智能指针,它通过指针拥有(负责)并管理另一个对象,当 unique_ptr 超出作用域时,会销毁该对象。

当发生以下任一情况时,将使用关联的删除器销毁对象:

  • 管理 unique_ptr 对象被销毁。
  • 管理 unique_ptr 对象通过 operator=reset() 被赋予另一个指针。

对象由潜在用户提供的删除器通过调用 get_deleter()(ptr) 来销毁。默认删除器 (std::default_delete) 使用 delete 运算符,它销毁对象并释放内存。

unique_ptr 也可以不拥有任何对象,在这种情况下它被描述为

unique_ptr 有两个版本:

  1. 管理单个对象(例如,使用 new 分配)。
  2. 管理动态分配的对象数组(例如,使用 new[] 分配)。

该类满足 可移动构造 (MoveConstructible)可移动赋值 (MoveAssignable) 的要求,但不满足 可复制构造 (CopyConstructible)可复制赋值 (CopyAssignable) 的要求。

如果 T* 不是有效类型(例如,T 是引用类型),则实例化 std::unique_ptr<T, Deleter> 定义的程序是格式错误的。

类型要求
-
Deleter 必须是 函数对象 (FunctionObject) 或对 函数对象 (FunctionObject) 的左值引用,或对函数的左值引用,可使用类型为 unique_ptr<T, Deleter>::pointer 的参数调用。

目录

[编辑] 注意事项

只有非 const 的 unique_ptr 才能将托管对象的所有权转移给另一个 unique_ptr。如果对象的生命周期由 const std::unique_ptr 管理,则其生命周期仅限于指针创建的作用域。

unique_ptr 通常用于管理对象的生命周期,包括:

  • 通过确保在正常退出和异常退出时都进行删除,为处理动态生命周期对象的类和函数提供异常安全性。
  • 将唯一拥有的动态生命周期对象的所有权传递给函数。
  • 从函数获取唯一拥有的动态生命周期对象的所有权。
  • 作为支持移动的容器(如 std::vector)中的元素类型,这些容器持有指向动态分配对象的指针(例如,如果需要多态行为)。

unique_ptr 可以为 不完整类型 T 构造,例如为了方便在 pImpl 惯用法 中作为句柄使用。如果使用默认删除器,则在调用删除器时(发生在 unique_ptr 的析构函数、移动赋值运算符和 reset 成员函数中),T 必须是完整类型。(相比之下,std::shared_ptr 不能从指向不完整类型的裸指针构造,但可以在 T 不完整时销毁)。请注意,如果 T 是类模板特化,则由于 ADL,使用 unique_ptr 作为操作数,例如 !p,要求 T 的参数是完整类型。

如果 T 是基类 B派生类,则 unique_ptr<T> 可以 隐式转换unique_ptr<B>。生成的 unique_ptr<B> 的默认删除器将使用 Boperator delete,除非 B 的析构函数是 虚函数,否则会导致 未定义行为。请注意 std::shared_ptr 的行为不同:std::shared_ptr<B> 将使用类型 Toperator delete,即使 B 的析构函数不是 虚函数,拥有的对象也将正确删除。

std::shared_ptr 不同,unique_ptr 可以通过满足 可空指针 (NullablePointer) 的任何自定义句柄类型管理对象。这允许,例如,通过提供一个定义 typedef boost::offset_ptr pointer; 或另一个 花式指针 (fancy pointer)Deleter 来管理位于共享内存中的对象。

特性测试 标准 特性
__cpp_lib_constexpr_memory 202202L (C++23) constexpr std::unique_ptr

[编辑] 嵌套类型

类型 定义
pointer std::remove_reference<Deleter>::type::pointer 如果该类型存在,否则为 T*。必须满足 可空指针 (NullablePointer)
element_type T,此 unique_ptr 管理的对象的类型
deleter_type Deleter,要从析构函数中调用的函数对象或对函数或函数对象的左值引用

[编辑] 成员函数

构造一个新的 unique_ptr
(public member function) [编辑]
销毁托管对象(如果存在)
(public member function) [编辑]
赋值 unique_ptr
(public member function) [编辑]
修改器
返回指向托管对象的指针并释放所有权
(public member function) [编辑]
替换托管对象
(public member function) [编辑]
交换管理对象
(public member function) [编辑]
观察器
返回指向托管对象的指针
(public member function) [编辑]
返回用于销毁托管对象的删除器
(public member function) [编辑]
检查是否存在关联的托管对象
(public member function) [编辑]
单对象版本,unique_ptr<T>
解引用指向托管对象的指针
(public member function) [编辑]
数组版本,unique_ptr<T[]>
提供对托管数组的索引访问
(public member function) [编辑]

[编辑] 非成员函数

创建一个管理新对象的唯一指针
(function template) [编辑]
与另一个 unique_ptr 或与 nullptr 比较
(function template) [编辑]
将托管指针的值输出到输出流
(function template) [编辑]
特化 std::swap 算法
(function template) [编辑]

[编辑] 辅助类

std::unique_ptr 的哈希支持
(class template specialization) [编辑]

[编辑] 示例

#include <cassert>
#include <cstdio>
#include <fstream>
#include <iostream>
#include <locale>
#include <memory>
#include <stdexcept>
 
// helper class for runtime polymorphism demo below
struct B
{
    virtual ~B() = default;
 
    virtual void bar() { std::cout << "B::bar\n"; }
};
 
struct D : B
{
    D() { std::cout << "D::D\n"; }
    ~D() { std::cout << "D::~D\n"; }
 
    void bar() override { std::cout << "D::bar\n"; }
};
 
// a function consuming a unique_ptr can take it by value or by rvalue reference
std::unique_ptr<D> pass_through(std::unique_ptr<D> p)
{
    p->bar();
    return p;
}
 
// helper function for the custom deleter demo below
void close_file(std::FILE* fp)
{
    std::fclose(fp);
}
 
// unique_ptr-based linked list demo
struct List
{
    struct Node
    {
        int data;
        std::unique_ptr<Node> next;
    };
 
    std::unique_ptr<Node> head;
 
    ~List()
    {
        // destroy list nodes sequentially in a loop, the default destructor
        // would have invoked its “next”'s destructor recursively, which would
        // cause stack overflow for sufficiently large lists.
        while (head)
        {
            auto next = std::move(head->next);
            head = std::move(next);
        }
    }
 
    void push(int data)
    {
        head = std::unique_ptr<Node>(new Node{data, std::move(head)});
    }
};
 
int main()
{
    std::cout << "1) Unique ownership semantics demo\n";
    {
        // Create a (uniquely owned) resource
        std::unique_ptr<D> p = std::make_unique<D>();
 
        // Transfer ownership to “pass_through”,
        // which in turn transfers ownership back through the return value
        std::unique_ptr<D> q = pass_through(std::move(p));
 
        // “p” is now in a moved-from 'empty' state, equal to nullptr
        assert(!p);
    }
 
    std::cout << "\n" "2) Runtime polymorphism demo\n";
    {
        // Create a derived resource and point to it via base type
        std::unique_ptr<B> p = std::make_unique<D>();
 
        // Dynamic dispatch works as expected
        p->bar();
    }
 
    std::cout << "\n" "3) Custom deleter demo\n";
    std::ofstream("demo.txt") << 'x'; // prepare the file to read
    {
        using unique_file_t = std::unique_ptr<std::FILE, decltype(&close_file)>;
        unique_file_t fp(std::fopen("demo.txt", "r"), &close_file);
        if (fp)
            std::cout << char(std::fgetc(fp.get())) << '\n';
    } // “close_file()” called here (if “fp” is not null)
 
    std::cout << "\n" "4) Custom lambda expression deleter and exception safety demo\n";
    try
    {
        std::unique_ptr<D, void(*)(D*)> p(new D, [](D* ptr)
        {
            std::cout << "destroying from a custom deleter...\n";
            delete ptr;
        });
 
        throw std::runtime_error(""); // “p” would leak here if it were a plain pointer
    }
    catch (const std::exception&)
    {
        std::cout << "Caught exception\n";
    }
 
    std::cout << "\n" "5) Array form of unique_ptr demo\n";
    {
        std::unique_ptr<D[]> p(new D[3]);
    } // “D::~D()” is called 3 times
 
    std::cout << "\n" "6) Linked list demo\n";
    {
        List wall;
        const int enough{1'000'000};
        for (int beer = 0; beer != enough; ++beer)
            wall.push(beer);
 
        std::cout.imbue(std::locale("en_US.UTF-8"));
        std::cout << enough << " bottles of beer on the wall...\n";
    } // destroys all the beers
}

可能的输出

1) Unique ownership semantics demo
D::D
D::bar
D::~D
 
2) Runtime polymorphism demo
D::D
D::bar
D::~D
 
3) Custom deleter demo
x
 
4) Custom lambda-expression deleter and exception safety demo
D::D
destroying from a custom deleter...
D::~D
Caught exception
 
5) Array form of unique_ptr demo
D::D
D::D
D::D
D::~D
D::~D
D::~D
 
6) Linked list demo
1,000,000 bottles of beer on the wall...

[编辑] 缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 发布时的行为 正确的行为
LWG 4144 C++11 T* 不需要形成有效类型 需要

[编辑] 另请参阅

具有共享对象所有权语义的智能指针
(class template) [编辑]
(C++11)
对由 std::shared_ptr 管理的对象的弱引用
(class template) [编辑]