More Related Content Similar to boost::shared_ptr tutorial (20) boost::shared_ptr tutorial2. 生ポインタは便利
ポインタの偉大な点
他の変数を指し示せる変数である
演算できる
無効値であるNULL が存在する
非常に低コスト(実体は64bit or 32bit の符号なし整数ですから!)
例えば:線形探索
1 // 線形探索関数
2 template < typename T>
3 T* Search (T* begin , T* end , T value ){
4 for(T* i= begin ; i!= end ; ++i){
5 if (*i== value ){
6 return i;
7 }
8 }
9 return NULL ;
10 }
11
12 // こう使う
13 double Array [10] = {...};
14 double * Result = Search (Array , Array +10 , 810);
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 2 / 27
3. 生ポインタは危ない
1 // 画像を作る関数
2 uint8_t * CreateImage (int w, int h, int c){
3 uint8_t * Image = new uint8_t [w*h*c];
4
5 uint8_t * Pixel = Image ;
6 for(int i =0; i<w*h*c; ++i){ // <- バッファオーバーラン!
7 memset (Pixel , 127 , c);
8 Pixel += c;
9 }
10
11 return Image ;
12 }
13
14 // 画像に何かの処理をする
15 void SomeProcess ( uint8_t * image , int w, int h, int c){
16 uint8_t * Buffer = CreateImage (w, h, c);
17
18 // 何かの処理をいっぱい書く
19
20 }// <- メモリリーク!
確かに便利だが危険も伴う
できればこういうリスクは抱えたくない
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 3 / 27
4. 生ポインタの扱い方
基本的な方針
速度が大事なところではオーバーヘッドの少ない生ポインタ
速度よりも楽ができること" が大事なところではより安全な別の手段を
使う
実行速度と安全性のトレードオフを考えて使いましょうということ
例えば:引数をconst 参照で置き換え
1 // コピー出来ないクラス
2 class FileWriter {
3 private :
4 FileWriter ( const FileWriter &);
5 SomeClass & operator =( const FileWriter &);
6 public :
7 // いろいろ
8 };
9
10 // ポインタでもとれるが参照の方が安全
11 void SomeProcess ( FileWriter & writer , ...){
12 // なにか処理する
13 }
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 4 / 27
5. 生ポインタの扱い方
例えば:関数の戻り値をboost.optional にする
1 // 答えが出ないかも知れない小難しい計算をする
2 boost :: optional <int > SomeProcess (...){
3 .
4 .
5 .
6 // 途中で計算が破綻した!
7 if (...){
8 return boost :: optional <int >(); // 明確な無効値を返却
9 }
10 .
11 .
12 .
13 }
14
15 // こう使う
16 boost :: optional <int > Result = SomeProcess (...);
17 if( Result ){ // そのまま真理値として有効/ 無効を判別できる
18 int value = * Result ; // 値は* でとれる
19 } else {
20 // 失敗したのでエラー処理
21 }
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 5 / 27
7. リソース管理クラスis 何
管理" に何を求めているのか?
自分で確保したリソースの後始末をいい感じに" やってほしい
いい感じ" とは適切なタイミング" でリソースを解放してくれること
適切なタイミング" とは大抵の場合はリソースが不要になったタイミング
リソース管理クラスとはつまり
生のポインタを格納
ポインタの示すリソースが不要になったら自動で解放してくれる
そんな機能を持ったクラスのこと
そういうクラスのことをスマートポインタという
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 7 / 27
8. スマートポインタ
スマートポインタの種類
一口にスマートポインタといっても何種類かある
主にコピー/移動に関する動作が違う
それぞれ適用できるシチュエーションが違ってくる
スマートポインタ一覧
名前所有権の移動move コピー
std::auto ptr ○ × ×
std::unique ptr ○ ○ ×
boost::scoped ptr × × ×
boost::shared ptr × × ○
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 8 / 27
9. std::auto ptr
特徴
コピーの時に所有権"(=リソース解放の義務)が移譲される
1つのリソースを指す有効なポインタは1個だけ
使えるんならstd::unique ptr を使うべき
使用例:戻り値にする
1 // リソースを生成する関数
2 std :: auto_ptr < ResouceClass > CreateResouce (...){
3 return std :: auto_ptr < ResouceClass >( new ResouceClass (...));
4 }
5
6 // リソースに何かの処理をする
7 void SomeProcess (...){
8 // B u f f e r 1 へ所有権を移譲
9 std :: auto_ptr < ResouceClass > Buffer1 = CreateResouce (...);
10
11 // B u f f e r 2 へ所有権を移譲( 見た目はコピーなのにB u f f e r 1 は無効! )
12 std :: auto_ptr < ResouceClass > Buffer2 = Buffer1 ;
13
14 // いろいろ処理する
15
16 } // <- a u t o _ p t r が自動的に解放してくれる
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 9 / 27
10. std::unique ptr
特徴
std::auto ptr と大体同じだがmove" ができる
使えるのはC++11 から(キーワード:右辺値参照、move セマンティクス)
使用例:戻り値にする
1 // リソースを生成する関数
2 std :: unique_ptr < ResouceClass > CreateResouce (...){
3 return std :: unique_ptr < ResouceClass >( new ResouceClass (...));
4 }
5
6 // リソースに何かの処理をする
7 void SomeProcess (...){
8 // B u f f e r 1 へ所有権を移譲( m o v e する)
9 std :: unique_ptr < ResouceClass > Buffer1 = CreateResouce (...);
10
11 // コピー禁止なのでこれはできない-> 無効なB u f f e r 1 は発生しない
12 // std :: unique_ptr < ResouceClass > Buffer2 = Buffer1 ;
13
14 // いろいろ処理する
15
16 } // <- u n i q u e _ p t r が自動的に解放してくれる
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 10 / 27
11. boost::scoped ptr
特徴
コピーも移動も出来ない(禁止されている)
あるスコープでのみ有効なポインタとして使える
使用例:例外によるメモリリーク対策
1 class SomeClass {
2 private :
3 ...
4 public :
5 SomeClass (...){
6 boost :: scoped_ptr < ResouceClass > Buffer (new ResouceClass (...));
7
8 // 例外を投げるかも知れない危ない関数を呼ぶ
9 DangerFunction (...); // <- 例外が起きた!
10 }
11 };
12 /*!
13 1 . 例外がt h r o w される
14 2 . コンストラクタを抜けようとする
15 3 . コンストラクタを抜ける時に変数B u f f e r が削除される
16 4 . n e w したR e s o u c e C l a s s のオブジェクトが開放される
17 5 . リソース漏れを起こさずにコンストラクタを抜ける
18 */
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 11 / 27
12. boost::shared ptr
特徴
1つのリソースの所有権をみんなで共有しましょうというポインタ
最後に所有権を手放した人が責任を持ってリソース解放
shared ptr の存在=所有権
何がすごいのか?
shared ptr が存在する間はリソースの寿命が保証される
コピーができるのでSTL コンテナに入れることができる
とにかくいろんな問題が解決する
そのほかいろいろすごい(後述)
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 12 / 27
13. すごいぞつよいぞshared ptr
1 // ファクトリ関数
2 boost :: shared_ptr < CResouce > CreateResouce (...){
3 // そのまま戻せる!
4 return boost :: shared_ptr < CResouce >( new CResouce (...));
5 }
6
7 // リソースのポインタを持つクラス
8 class CHoge { // コピー禁止しなくて良い!
9 private :
10 boost :: shared_ptr < CResouce > _pResouce ;
11
12 public :
13 // コンストラクタでリソースのポインタを受け取る
14 CHoge ( const boost :: shared_ptr < CResouce >& p_resouce )
15 : _pResouce ( p_resouce ){
16 }
17 };
18
19 // リソース利用側
20 void SomeProcess (...){
21 // コピー可なのでコンテナに入れることができる!
22 std :: map < std :: string , boost :: shared_ptr < CResouce > > ResouceMap ;
23 ResouceMap [" hoge "] = CreateResouce (...);
24 }
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 13 / 27
14. shared ptr の使い方
1 // 適当なクラス
2 class CHoge {
3 public :
4 void Piyo ();
5 };
6
7 typedef boost :: shared_ptr <CHoge > sp_hoge ;
8 typedef boost :: shared_ptr < const CHoge > scp_hoge ;
9
10 void f (){
11 sp_int pa( new CHoge );
12
13 // 有効なポインタなら
14 if( pa ){
15 pa -> Piyo ();
16 CHoge b = *a;
17 scp_hoge c = a;
18 CHoge * raw_ptr = a. get ();
19 }
20 }
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 14 / 27
15. shared ptr の実装
参照カウンタ
今時分が所有しているリソースは何人に所有されているか?" を表すカウ
ンタを持っている(参照カウンタ)
正確には参照カウンタへのポインタを持っている
この参照カウンタを上下してリソースを解放すべきか判断する
参照カウンタの動作
最初は参照カウンタ=1
shared ptr がコピーされると参照カウンタを+1
shared ptr がデストラクトされると参照カウンタを-1
参照カウンタを-1した時に0になったらリソース解放
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 15 / 27
16. shared ptr の動作をトレースしてみる
手順
1 リソースを確保
2 リソースへのポインタをshred ptr に渡す
3 参照数カウント初期化
4 shared ptr をコピー
5 shared ptr を1つ削除
6 shared ptr を0個に
7 リソースの解放
8 参照カウントの後始末
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 16 / 27
17. shared ptr の弱点
便利だけど弱点もある
循環参照" が発生すると正しく解放されない
連結リストの前後の要素を指すポインタには使えないということ
参照数1のshared ptr が残ってしまう
循環参照によるメモリリークのトーレス
1 初期状態(循環参照が存在すると最終的にこうなる)
2 1つ削除
3 さらに1つ削除
4 リソースが残ってしまった・・・
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 17 / 27
18. 循環参照によるリソース漏れの解決
weak ptr
shared ptr の仲間
所有権を持たないのでリソースの寿命には関係しない
指し示すリソースが存在していればshared ptr に昇格できる
実は参照カウンタとは別に弱参照カウンタ" というのも存在している
shared ptr への昇格
weak ptr はリソースへのポインタとカウンタへのポインタを持っている
この2つの情報があればshared ptr を生成できる
弱参照カウンタ
weak ptr による参照数のカウンタ
この参照カウンタと弱参照カウンタが両方0になった時カウンタがdelete
される
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 18 / 27
19. weak ptr の使い方
1 class CResouce {
2 ...
3 };
4
5 void f (){
6 boost :: shared_ptr < CResouce > sp(new CResouce );
7 boost :: weak_ptr < CResouce > wp(sp );
8
9 // 参照カウント=1 , 弱参照カウント=1
10
11 boost :: shared_ptr < CResouce > p = wp. lock ();
12 if(p){
13 // 確保成功= リソースはまだ生きていた
14 // 参照カウント=2 , 弱参照カウント=1
15 } else {
16 // 確保失敗= リソースはすでに死んでいた
17 // 参照カウント=0 , 弱参照カウント=1
18 }
19 }
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 19 / 27
20. weak ptr の動作をトレースしてみる
1 初期状態
2 weak ptr 生成
3 shared ptr 削除
4 weak ptr 削除
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 20 / 27
21. すごいぞshared ptr:デリータの指定
デリータとは?
shared ptr によって呼び出される解放関数のこと
デフォルトでははdelete が呼び出される
このデリータはコンストラクタで指定できる
つまりdelete 以外の専用の解放関数が必要なリソースもshared ptr で管理で
きる(すごい!)
IplImage をshared ptr で管理してみる
1 // I p l I m a g e をs h a r e d _ p t r に包んで生成するファクトリ関数
2 boost :: shared_ptr < IplImage > CreateIplImage (int w, int h, int c){
3 IplImage * pImage = cvCreateImage ( cvSize (w, h), IPL_DEPTH_8U , c);
4 return boost :: shared_ptr < IplImage >( pImage , & cvReleaseImage );
5 }
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 21 / 27
22. つよいぞshared ptr:バイナリを超えても大丈夫
考える状況
ライブラリA とB をリンクしてプログラムをコンパイルした
ライブラリA 内でshared ptr を生成した
生成したshared ptr をライブラリB で実装されているクラスに渡した
ライブラリB でカウントが0になってデリータが呼び出された
もし生ポインタなら
ライブラリA のnew で生成
ライブラリB のdelete で削除
ライブラリA のnew とライブラリB のdelete がちゃんと対応しているとは
限らない!
▶ ライブラリA とB でリンクしてあるmalloc の実装が違う
▶ リリースビルドとデバッグビルドが混在している
この場合正常にdelete される保証はない・・・
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 22 / 27
23. つよいぞshared ptr:バイナリを超えても大丈夫
shared ptr なら!
ライブラリA でshared ptr を生成した時にどのdelete を使うかは決まる
ライブラリB でリソースを削除する時にライブラリA で設定したdelete が
呼び出される!
ちゃんと対応したnew/delete なのでOK!
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 23 / 27
24. ヤバいぞshared ptr:マルチスレッドでもOK
マルチスレッドでshared ptr
スレッドA とB に1つのshared ptr を渡す(ここのコピーはmutex で保護)
親スレッドでshared ptr を削除
スレッドA とB で時間的に同時にshared ptr を削除
正しくリソースが解放される?
atomic なカウンタ
shared ptr のカウンタはatomic なので時間的に同時にカウンタ操作されて
もOK!
ただし、一つのshared ptr に同時に読み書きをする場合はmutex で排他制
御する必要がある(これは生ポインタでも同じ)
atmic なカウンタの代償
カウント操作が重たい!
逐次一貫性の保持のためにCPU の最適化に制限がかかるので
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 24 / 27
25. スマート配列
配列用shared ptr
new []" されたポインタはdelete []" しなければならない
shared ptr ではdelete" が呼び出される
shared array ならdelete []" を呼び出してくれる!
添字演算子[]" も定義されている
scoped array も存在する
ちなみに、バッファがほしい時はstd::vector を使うべきなのでscoped array
の出番は少ない
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 25 / 27
26. intrusive ptr
クラス備え付けのカウント機構を使う
Microsoft のCOM みたいにクラスにそもそも参照カウント機構が備わって
いる場合がある
その場合はカウント機構を使いたい・・・
boost::intrusive ptr ならクラス備え付けのカウント機構を使ってくれる
アロケーションの効率はよい(カウンタをnew しなくていい)が、weak ptr
を取れない
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 26 / 27
27. まとめ
説明したこと
生ポインタは強力だけど危ない
生ポインタは別の手段で置き換える事を考えよう
スマートポインタの使用を検討しよう
shared ptr はとても便利!
大事なこと
リソースを管理しようと思ったらスマートポインタ
shared ptr はとにかく強力でいろんな状況に適用できる
気をつけること
スマートポインタの種類によって解放タイミングや挙動は違う
shared ptr は循環参照が起きないよう注意
@NU-Pan 勉強会資料:スマートポインタ入門2014/07/10(木) 27 / 27