名稱空間
變體
操作

std::unique_ptr

來自 cppreference.com
< 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) [編輯]