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 には2つのバージョンがあります。
- 単一のオブジェクトを管理します(例: 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 は、pImpl イディオムでハンドルとして使用しやすくするために、不完全型 T に対して構築することができます。デフォルトのデリータが使用される場合、デリータが呼び出されるコードのポイント(デストラクタ、ムーブ代入演算子、および unique_ptr の reset メンバ関数で発生します)で T は完全でなければなりません。(対照的に、std::shared_ptr は不完全型への生ポインタから構築できませんが、T が不完全な場所で破棄できます)。T がクラステンプレートの特殊化である場合、!p のような unique_ptr をオペランドとして使用するには、ADL のため、T のパラメータが完全である必要があることに注意してください。
T がある基底クラス B の派生クラスである場合、unique_ptr<T> は unique_ptr<B> に暗黙的に変換可能です。結果として得られる unique_ptr<B> のデフォルトデリータは、B のデストラクタが仮想でない限り、operator delete for B を使用し、未定義の動作につながります。std::shared_ptr は異なる振る舞いをすることに注意してください。std::shared_ptr<B> は、型 T の operator delete を使用し、B のデストラクタが仮想でなくても、所有されるオブジェクトは正しく削除されます。
std::shared_ptr とは異なり、unique_ptr は NullablePointer を満たす任意のカスタムハンドル型を介してオブジェクトを管理できます。これにより、例えば、typedef boost::offset_ptr 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 メンバ関数) | |
| 管理対象オブジェクトが存在する場合、それを破棄する (public メンバ関数) | |
unique_ptr を代入する(public メンバ関数) | |
変更 | |
| 管理対象オブジェクトへのポインタを返し、所有権を解放する (public メンバ関数) | |
| 管理対象オブジェクトを置き換える (public メンバ関数) | |
| 管理対象オブジェクトを交換する (public メンバ関数) | |
監視 | |
| 管理対象オブジェクトへのポインタを返す (public メンバ関数) | |
| 管理対象オブジェクトの破棄に使用されるデリータを返す (public メンバ関数) | |
| 関連付けられた管理対象オブジェクトがあるかどうかをチェックする (public メンバ関数) | |
単一オブジェクトバージョン,
| |
| 管理対象オブジェクトへのポインタを間接参照する (public メンバ関数) | |
配列バージョン,
| |
| 管理対象配列へのインデックス付きアクセスを提供する (public メンバ関数) | |
[編集] 非メンバ関数
| (C++14)(C++20) |
新しいオブジェクトを管理する一意のポインタを作成する (関数テンプレート) |
別の unique_ptr または nullptr と比較する(関数テンプレート) | |
| (C++20) |
管理対象ポインタの値を出力ストリームに出力する (関数テンプレート) |
| (C++11) |
std::swap アルゴリズムを特殊化する (関数テンプレート) |
[編集] ヘルパークラス
| (C++11) |
std::unique_ptr のハッシュサポート (クラステンプレートの特殊化) |
[編集] 例
#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++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| LWG 4144 | C++11 | T* は有効な型を形成する必要がなかった |
必要 |
[編集] 関連項目
| (C++11) |
オブジェクトの所有権を共有するセマンティクスを持つスマートポインタ (クラステンプレート) |
| (C++11) |
std::shared_ptr によって管理されるオブジェクトへの弱い参照 (クラステンプレート) |