std::unique_ptr
定義於標頭檔案 <memory> |
||
template< class T, |
(1) | (C++11 起) |
template < class T, |
(2) | (C++11 起) |
std::unique_ptr
是一個智慧指標,它透過指標擁有(負責)並管理另一個物件,當 unique_ptr
超出作用域時,會銷燬該物件。
當發生以下任一情況時,將使用關聯的刪除器銷燬物件:
物件由潛在使用者提供的刪除器透過呼叫 get_deleter()(ptr) 來銷燬。預設刪除器 (std::default_delete
) 使用 delete 運算子,它銷燬物件並釋放記憶體。
unique_ptr
也可以不擁有任何物件,在這種情況下它被描述為 空。
unique_ptr
有兩個版本:
- 管理單個物件(例如,使用 new 分配)。
- 管理動態分配的物件陣列(例如,使用 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> 的預設刪除器將使用 B
的 operator delete,除非 B
的解構函式是 虛擬函式,否則會導致 未定義行為。請注意 std::shared_ptr 的行為不同:std::shared_ptr<B> 將使用型別 T
的 operator 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) | |
單物件版本,
| |
解引用指向託管物件的指標 (public member function) | |
陣列版本,
| |
提供對託管陣列的索引訪問 (public member function) |
[編輯] 非成員函式
(C++14)(C++20) |
建立一個管理新物件的唯一指標 (function template) |
(在 C++20 中刪除)(C++20) |
與另一個 unique_ptr 或與 nullptr 比較(function template) |
(C++20) |
將託管指標的值輸出到輸出流 (function template) |
(C++11) |
特化 std::swap 演算法 (function template) |
[編輯] 輔助類
(C++11) |
對 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* 不需要形成有效型別 |
需要 |
[編輯] 另請參閱
(C++11) |
具有共享物件所有權語義的智慧指標 (class template) |
(C++11) |
對由 std::shared_ptr 管理的物件的弱引用 (class template) |