std::unique_ptr
ヘッダ <memory> で定義
|
||
template< class T, |
(1) | (C++11以上) |
template < class T, |
(2) | (C++11以上) |
std::unique_ptr
はポインタを通して他のオブジェクトを所有、管理し、スコープから外れたときにそのオブジェクトを処分するスマートポインタです。
以下のいずれかが発生したとき、紐付けられたデリータを使用してオブジェクトが処分されます。
オブジェクトは、 get_deleter()(ptr) を呼ぶことによって、デフォルトのまたはユーザ提供のデリータを使用して処分されます。 デフォルトのデリータは delete 演算子を使用してオブジェクトを破棄し、メモリを解放します。
unique_ptr
は何のオブジェクトも所有しないこともあります。 この場合は空であるといいます。
std::unique_ptr
には2つのバージョンがあります。
このクラスは MoveConstructible および MoveAssignable の要件を満たしますが、 CopyConstructible および CopyAssignable はいずれも満たしません。
型の要件 | ||
-Deleter は unique_ptr<T, Deleter>::pointer 型の引数で呼び出せる FunctionObject または FunctionObject への左辺値参照または関数への左辺値参照でなければなりません。
|
目次 |
[編集] ノート
非 const な unique_ptr
だけが管理対象オブジェクトの所有権を他の unique_ptr
に転送できます。 const std::unique_ptr によって管理されている場合、オブジェクトの生存期間はそのポインタが作成されたスコープに限定されます。
std::unique_ptr
はオブジェクトの生存期間を管理するためによく使われます。 例えば、
- 正常終了および例外による終了の両方で削除されることを保証することによって、動的生存期間を持つオブジェクトを扱うクラスおよび関数に例外安全性を提供する場合。
- 唯一的に所有された動的生存期間を持つオブジェクトの所有権を関数に渡す場合。
- 唯一的に所有された動的生存期間を持つオブジェクトの所有権を関数から取得する場合。
- ムーブ対応コンテナ、例えば std::vector の要素型として、動的確保されたオブジェクトへのポインタを保持する場合 (多相的な動作が求められる場合など)。
std::unique_ptr
は不完全型 T
に対して構築できます。 例えば pImpl イディオムでハンドルとしての使用を容易にします。 デフォルトのデリータを使用する場合、 T
はコード中のデリータが呼び出される時点で完全でなければなりません。 これは std::unique_ptr
のデストラクタ、ムーブ代入演算子、 reset
メンバ関数で発生します (それに対して、 std::shared_ptr は不完全型に対する生のポインタから構築することはできませんが、 T
が不完全な場所でも破棄できます)。 ちなみに、 T
がクラステンプレートの特殊化である場合、 unique_ptr
を被演算子として使用するにあたっては (例えば !p)、 ADL のため、 T
の引数が完全であることが要求されます。
T
が何らかの基底クラス B
の派生クラスである場合、 std::unique_ptr<T> は std::unique_ptr<B> に暗黙に変換できます。 結果の std::unique_ptr<B> のデフォルトのデリータは B
に対する operator delete を使用するので、 B
のデストラクタが virtual でなければ、未定義動作に繋がります。 ちなみに、 std::shared_ptr は異なる動作をします。 std::shared_ptr<B> は、 B
のデストラクタが virtual でなくとも、型 T
に対する operator delete を使用し、所有オブジェクトを正しく削除します。
std::shared_ptr と異なり、 std::unique_ptr
は NullablePointer を満たす任意のカスタムハンドル型を通してオブジェクトを管理することができます。 これにより、例えば typedef boost::offset_ptr pointer;
または他のファンシーポインタを定義する Deleter
を提供することによって、共有メモリに配置されているオブジェクトを管理したりできます。
[編集] メンバ型
メンバ型 | 定義 |
pointer | std::remove_reference<Deleter>::type::pointer が存在すればその型、そうでなければ T* 。 NullablePointer を満たさなければなりません
|
element_type | この unique_ptr によって管理されるオブジェクトの型 T
|
deleter_type | デストラクタから呼ばれるための関数オブジェクトまたは関数への左辺値参照または関数オブジェクトへの左辺値参照 Deleter
|
[編集] メンバ関数
新しい unique_ptr を構築します (パブリックメンバ関数) | |
管理対象オブジェクトが存在すれば、それを破棄します (パブリックメンバ関数) | |
unique_ptr を代入します (パブリックメンバ関数) | |
変更 | |
管理対象オブジェクトへのポインタを返し、所有権を解放します (パブリックメンバ関数) | |
管理対象オブジェクトを置き換えます (パブリックメンバ関数) | |
管理対象オブジェクトを入れ替えます (パブリックメンバ関数) | |
観察 | |
管理対象オブジェクトへのポインタを返します (パブリックメンバ関数) | |
管理対象オブジェクトを破棄するために使用されるデリータを返します (パブリックメンバ関数) | |
紐付けられた管理対象オブジェクトが存在するかどうか確認します (パブリックメンバ関数) | |
単一オブジェクト版の
| |
管理対象オブジェクトへのポインタを逆参照します (パブリックメンバ関数) | |
配列版の
| |
管理対象配列へのインデックスアクセスを提供します (パブリックメンバ関数) |
[編集] 非メンバ関数
(C++14)(C++20) |
新しいオブジェクトを管理する unique_ptr を作成します (関数テンプレート) |
別の unique_ptr または nullptr と比較します (関数テンプレート) | |
(C++20) |
管理対象オブジェクトの値を出力ストリームに出力します (関数テンプレート) |
(C++11) |
std::swap アルゴリズムの特殊化 (関数テンプレート) |
[編集] ヘルパークラス
(C++11) |
std::unique_ptr に対するハッシュサポート (クラステンプレートの特殊化) |
[編集] 例
#include <iostream> #include <vector> #include <memory> #include <cstdio> #include <fstream> #include <cassert> #include <functional> struct B { virtual void bar() { std::cout << "B::bar\n"; } virtual ~B() = default; }; struct D : B { D() { std::cout << "D::D\n"; } ~D() { std::cout << "D::~D\n"; } void bar() override { std::cout << "D::bar\n"; } }; // unique_ptr を消費する関数は、それを値によってまたは右辺値参照によって受け取ることができます。 std::unique_ptr<D> pass_through(std::unique_ptr<D> p) { p->bar(); return p; } void close_file(std::FILE* fp) { std::fclose(fp); } int main() { std::cout << "unique ownership semantics demo\n"; { auto p = std::make_unique<D>(); // p は D を所有する unique_ptr です。 auto q = pass_through(std::move(p)); assert(!p); // もはや p は何も所有しておらず、ヌルポインタを保持します。 q->bar(); // そして q がその D オブジェクトを所有します。 } // ここで ~D が呼ばれます。 std::cout << "Runtime polymorphism demo\n"; { std::unique_ptr<B> p = std::make_unique<D>(); // p は基底へのポインタとして D を保持する // unique_ptr です。 p->bar(); // 仮想ディスパッチ。 std::vector<std::unique_ptr<B>> v; // unique_ptr はコンテナ内に格納できます。 v.push_back(std::make_unique<D>()); v.push_back(std::move(p)); v.emplace_back(new D); for(auto& p: v) p->bar(); // 仮想ディスパッチ。 } // ~D が3回呼ばれます。 std::cout << "Custom deleter demo\n"; std::ofstream("demo.txt") << 'x'; // 読み込むファイルの準備。 { std::unique_ptr<std::FILE, decltype(&close_file)> fp(std::fopen("demo.txt", "r"), &close_file); if(fp) // fopen は失敗する可能性があり、その場合 fp はヌルポインタを保持します。 std::cout << (char)std::fgetc(fp.get()) << '\n'; } // FILE* がヌルポインタでない場合 (つまり fopen が成功した場合) は、 // ここで fclose() が呼ばれます。 std::cout << "Custom lambda-expression deleter demo\n"; { std::unique_ptr<D, std::function<void(D*)>> p(new D, [](D* ptr) { std::cout << "destroying from a custom deleter...\n"; delete ptr; }); // p は D を所有します。 p->bar(); } // 上のラムダが呼ばれ、 D が破棄されます。 std::cout << "Array form of unique_ptr demo\n"; { std::unique_ptr<D[]> p{new D[3]}; } // ~D が3回呼ばれます。 }
出力:
unique ownership semantics demo D::D D::bar D::bar D::~D Runtime polymorphism demo D::D D::bar D::D D::D D::bar D::bar D::bar D::~D D::~D D::~D Custom deleter demo x Custom lambda-expression deleter demo D::D D::bar destroying from a custom deleter... D::~D Array form of unique_ptr demo D::D D::D D::D D::~D D::~D D::~D