MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
C++の基礎 - スマートポインタ(unique ptr)のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
C++の基礎 - スマートポインタ(unique ptr)
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == C++11では、unique_ptr<T>、shared_ptr<T>、weak_ptr<T>の3種のスマートポインタが追加された。<br> これらのスマートポインタは、メモリの動的確保の利用の際に生じる多くの危険性を低減する目的で使用されるが、<br> それぞれ独自の考え方と機能を持っている。<br> <br> 3種のスマートポインタを適切に使い分けることで、安全性と開発速度の向上が見込めるだけでなく、<br> プログラマの意図に合わせてポインタを記述し分けることができる、非常に強力なツールとなる。<br> <br> ここでは、スマートポインタについて初めて学ぶ人を対象に、<br> C++11で追加された3種のスマートポインタの機能と使い方、および3種をどのように考えて使うかについて、初歩的な解説を行う。<br> <br><br> == unique_ptrとは == unique_ptr<T>は、あるメモリに対する所有権を持つポインタが、ただ一つであることを保証するようなスマートポインタである。<br> テンプレート引数で保持するポインタ型を指定し、スマートポインタが破棄される際にディストラクタにおいて自動的にメモリを開放する。<br> <br> unique_ptr<T>は、以下の様な特徴を持っている。<br> ・あるメモリの所有権を持つ unique_ptr<T>は、 ただ一つのみである。<br> ・コピーが出来ない。代わりに、C++11で新たに追加されたムーブによって、所有権を移動することができる。<br> ・通常のポインタに匹敵する処理速度。<br> ・配列を扱うことができる。<br> ・deleter(後述)を指定することができる。<br> <br> <syntaxhighlight lang="c++"> #include<iostream> #include<memory> // スマートポインタを使用する時に指定する class hoge { private: std::unique_ptr<int> ptr; public: hoge(int val_) : ptr(new int(val_)){} int getValue()const{return *ptr;} }; int main() { // hogeのコンストラクタでint型を動的に確保しunique_ptrに委ねる hoge Hoge(10); // コンストラクタの引数として、動的確保したメモリのアドレスを指定 std::unique_ptr<int> ptr(new int(10)); // reset関数を使用して後から代入できる std::unique_ptr<int> ptr2; ptr2.reset(new int(10)); // C++14以降では、make_unique関数が使用できる std::unique_ptr<int> ptr3 = std::make_unique<int>(10); // unique_ptrはコピーコンストラクタで作成しようとすると、コンパイルエラーになる //hoge Hoge2(Hoge); // 明示的にmoveするならOK hoge Hoge2(std::move(Hoge)); return 0; } </syntaxhighlight> <br><br> == unique_ptrの使い方 == 詳しい使い方を下記に示す。<br> 尚、使用の際には#include <memory>を指定する必要がある。<br> <br> <syntaxhighlight lang="c++"> // まず、unique_ptr<T>にメモリの所有権を委ねるには、コンストラクタで指定するかreset(pointer)を使う。 // C++14以降では、make_unique<T>関数を使って作成することができる。 // コンストラクタの引数として、動的確保したメモリのアドレスを指定 std::unique_ptr<int> ptr(new int(10)); // reset関数を使って、後から代入することもできる std::unique_ptr<int> ptr2; ptr2.reset(new int(10)); // C++14以降であれば、make_unique関数を使うこともできる std::unique_ptr<int> ptr3=std::make_unique<int>(10); </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // unique_ptr<T>は、コピーは禁止されているが、move関数は使用することができる。 std::unique_ptr<int> ptr(new int(10)); // コピーコンストラクタや、コピー代入演算子はエラー //std::unique_ptr<int> ptr2(ptr); //===ERROR=== //std::unique_ptr<int> ptr2; //ptr2 = ptr; // ムーブコンストラクタや、ムーブ代入演算子はOK // この時、所有権が移動する std::unique_ptr<int> ptr3(std::move(ptr)); // ptrの所有権がptr3に移動する std::unique_ptr<int> ptr4; ptr4 = std::move(ptr3); // ptr4の所有権がptr5に移動する </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // メモリの解放は、ディストラクタや reset(pointer)を使う。 // 引数なしやnullptrを引数としてreset関数を呼んでも、明示的に解放できる std::unique_ptr<int> ptr(new int(10)); ptr.reset(); </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // 生のポインタが欲しい時は、get()かrelease()を使う // get()は、生ポインタを得るだけで、ポインタの所有権はunique_ptr<T>が保持し続ける int *pint; pint = ptr.get(); // release()は、ポインタの所有権自体も放棄するため、メモリの開放は自分で行う必要がある pint = ptr.release(); delete pint; </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // unique_ptr<T[]>のように指定すれば、配列を扱うこともできる // 配列型の場合、operator[](size_t)を使用することができる // int型配列の要素数10を宣言 std::unique_ptr<int[]> ptrArray(new int[10]); // または std::unique_ptr<int[]> ptrArray = std::make_unique<int[]>(10); for(int i = 0; i < 10; i++) { ptrArray[i] = i; } </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // ポインタの保持するメモリにアクセスするには、通常のポインタ同様に、operator*()やoperator->()が使用できる std::unique_ptr<std::string> pStr(new std::string("test")); // operator*()でstring型を参照(testと表示される) std::cout << *pStr << std::endl; // operator->()で、string型のsize関数を呼び出せる unsigned int StrSize = pStr->size(); </syntaxhighlight> <br><br> == スマートポインタの使いどころ == ==== 関数の引数 ==== 関数を通して所有権を渡す時は、<code>std::unique_ptr</code>または<code>std::shared_ptr</code>を使用する。<br> <code>std::unique_ptr</code>は完全に所有権を譲渡、<code>std::shared_ptr</code>は共有する。<br> それ以外は、生ポインタ(または参照)を使用する。<br> <br> ==== 関数の戻り値 ==== ファクトリ関数(オブジェクトを生成する関数)の戻り値は、原則、<code>std::unique_ptr</code>を使用する。<br> <br> <code>std::shared_ptr</code>で扱いたい場合、受け取った<code>std::unique_ptr</code>を<code>std::shared_ptr</code>に代入するとよい。<br> (<code>std::shared_ptr</code>のコンストラクタにおいて、<code>std::unique_ptr&&</code>を取るものが存在する)<br> <syntaxhighlight lang="c++"> // factory methodで作成したオブジェクト std::unique_ptr<Foo> CreateFoo(); // std::unique_ptrをstd::shared_ptrに代入する std::shared_ptr<foo> sp = CreateFoo(); </syntaxhighlight> <br> オブジェクト内のメンバへのポインタを返す場合等は、生ポインタまたは参照でよい。<br> 変更が不要な場合、constキーワードを付加すること。<br> また、オブジェクトの生存期間には注意が必要である。<br> <syntaxhighlight lang="c++"> class Foo { private: Bar bar; public: const Bar& GetBar() const { return bar; } }; </syntaxhighlight> <br> ==== クラスのメンバ変数 ==== 所有権を持っている場合、<code>std::unique_ptr</code>または<code>std::shared_ptr</code>を使用する。<br> 所有権を持っていない場合、生ポインタまたは参照を使用する。<br> この時、参照しているオブジェクトの生存期間に注意が必要である。<br> <br> 生存期間の制御が難しい場合、<code>std::weak_ptr</code>を使用することもある。<br> <br> <code>std::shared_ptr</code>と<code>std::weak_ptr</code>は必要な場合のみ使用する。<br> <code>std::shared_ptr</code>を使用することで、オブジェクトの生存期間の制御が難しくなる。<br> <br> 同様に、<code>std::weak_ptr</code>も無闇に使用しない。<br> 複数の場所で使用されるオブジェクトの生存期間を制御することが難しい場合や、循環参照の解決には便利である。<br> また、<u>所有権を持たないオブジェクトへの参照は全て<code>std::weak_ptr</code>にする</u>、といったことは行わないこと。<br> <br> ==== スマートポインタのコスト ==== <code>std::unique_ptr</code>は、オーバーヘッドはほぼ無い。<br> メモリ使用量、コードサイズ、実行速度の全てにおいて、生ポインタを<code>new</code>および<code>delete</code>する場合に負けることはない。<br> <br> <code>std::shared_ptr</code>および<code>std::weak_ptr</code>は、オーバーヘッドがある。<br> コントロールブロックと呼ばれる別のメモリ領域がヒープから取られることで、格納するオブジェクトと合わせて2回のメモリ確保が行われる。<br> また、コピー時には、リファレンスカウントの操作が行われる。<br> これは、オブジェクトの生成・破棄に比べれば遥かに軽い操作であるが、スレッドセーフにするために排他されていたりもするので0ではない。<br> <br><br> __FORCETOC__ [[カテゴリ:C++]]
C++の基礎 - スマートポインタ(unique ptr)
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse