ぴぐノート

Good code is its own best documentation.

C/C++ プログラミング言語

【C++11】後処理が必要なリソースはスマートポインタを使おう

投稿日:

オープンソースのライブラリのソースコードを呼んでいるとC++11で追加されたunique_ptrを使用しているところもちらほらと見られるようになってきています。
MFCなどを利用していれば動的確保したメモリ以外にもHINSTANCE型やHBITMAP型など後処理が必要なリソースを扱うことが多いはずです。

Effective C++などでも解説されているように「リソースの確保が初期化」(RAII)をしっかりと守るためにスマートポインタは早いうちから使い方に慣れていくことが大事になります。

今回はLoadLibrary関数から取得したHINSTANCE(HMODULE)型をunique_ptrとして管理してみましょう。

unique_ptr?

名前からも推測できるように、リソースの所有権を一意(unique)にもっているように振る舞うスマートポインタ(賢いポインタ)です。

スマートポインタとは?

任意のリソースを指し示すポインタです。
スマートポインタが削除される際(スコープから抜けるなど)にそのスマートポインタが指すリソースも正しく削除されるため、リソースを管理する場合はこういったスマートポインタを利用するべきです。
同様に振る舞うスマートポインタ、auto_ptrを置き換えるものとして追加されました。

所有権を一意に持つ?

auto_ptrはコピーをする際に、コピー元をNULLにし同じリソースを指すスマートポインタを複数持つような動作はできない仕組みになっていました。

同じリソースを指すスマートポインタが複数あれば、複数回deleteが実行され未定義の動作が実行されてしまうため、この方法でそういった実装ができないようになっていました。

unique_ptrも同様に同じリソースを指すスマートポインタを複数持つことはできないように実装されています。

ただし、4行目のような初期化はできなくなっています
所有権を移動させるにはstd::move関数(5行目)を使用します。

解放処理

auto_ptrでは解放の処理は必ずdeleteが実行されるため、動的確保した配列には使用することはできませんでした。

unique_ptrは配列に対しても正しくdelete[]が呼ばれるようにテンプレートとして特殊化されているので安心して使用することができます

リンク:std::unique_ptr | cpprefarence.com

また専用のデリータ関数(リソースを解放するための関数)を自分で定義することもできるようになっています。
これはサンプルプログラムを見てもらった方がいいかもしれません。

HINSTANCE型のリソースを解放する

紹介するサンプルプログラムではDLLファイルを読み取り、そのまま解放するだけのプログラムです。
DLLファイルの読み取りはLoadLibrary関数を呼び、後処理にはFreeLibrary関数を呼ばなければいけません。

実はHMODULE型やHINSTANCE型は単なるvoid *型ではなく、HINSTANCE__構造体のポインタとして定義されています。
またHMODULE型は、

として定義されているので、HINSTANCE型とHMODULE型は全く同じ型です。

HINSTANCEについてはリンク先のページに詳しいです。
リンク:HINSTANCE ‐ 通信用語の基礎知識

また、解放にはdeleteやdelete[]ではなく、FreeLibrary関数を呼ぶ必要があるためデリータを定義する必要があります。
さて、2行目の「typedef HMODULE pointer;」はとても不思議に見えます。
デリータが指定された場合、unipue_ptrの実装ではpointer型(テンプレート引数のtypedef)の存在をチェックします。
pointer型が存在している場合は、デリータの引数にpointer型を指定し、デリータを呼び出すようになっており、存在しない場合は第一引数の型を使用するようになっています。

実際にプログラムを打ち込んで、2行目をコメントアウトすると代入できないといったエラーメッセージが表示されるはずです。

リンク:c++ – Using std::unique_ptr for Windows HANDLEs – Stack Overflow

実行コストは?

さて、リソース管理に大変便利なunique_ptrですが、実行コストはどれくらいなのでしょうか。
実は生のポインタを使用する場合とほぼ違いがありません
そのため、特に明確な理由がなければリソース管理でunique_ptrを使わない手はないのです。

試しに、上記のサンプルプログラムをunique_ptrを使用した場合と、直接LoadLibrary/FreeLibraryを使用した場合でそれぞれ10,000回呼び出した際の実行時間を確認したところ、以下のような結果でした。

unique_ptr<HMODULE, Library_deleter>
最速 19200ms
最低 21414ms
平均 20404.8ms
LoadLibrary / FreeLibrary(raw)
最速 20163ms
最低 21442ms
平均 20698.7ms

最適化の影響なのか、unique_ptrを使った方が早いくらいでした。
この結果から実行時コストを気にしてunique_ptrを使わないといった判断はできないことが分かります。

まとめ

少し記事が長くなってしまいましたが、C++11で実装されたunique_ptrの有用性を伝えることができたでしょうか?

オープンソースなどでは新しい技術なども使われたりするのでとても勉強になります。(意味が分からずに困惑することもありますが…。)

これからリソースの管理を行う場合にはunique_ptrをまず考えてみてください。
多くの場合はunique_ptrを使うことが正解になると思いますヨ。

スポンサーリンク

スポンサーリンク

-C/C++, プログラミング言語

執筆者:

関連記事

【Boost】boost::property_treeでXMLファイルを書き込もう!

前回の記事でXMLファイルの書き込みは難しくなくできそうでしたネ。 今回はXMLファイルの書き込みを行い、出力してみましょう。 読み込みと同じくライブラリファイルは不要で、ヘッダーファイルのみで利用で …

【OpenCV】OpenCVをビルドして画像処理をしてみよう!

今日はOpenCVを使って画像表示をしてみたいと思います。 ツールやアプリケーションを作る場合、多かれ少なかれ画像処理は必要になってくるのではないでしょうか。 また画像処理関係ではまると泥沼化する場合 …

【OpenCV】MFCのピクチャーコントロールに描画しよう!

前回、OpenCVを使って画像の回転や二値化などを行って動作確認をしました。 今回はMFCでダイアログベースのアプリケーションを作成し、ピクチャーコントロールにOpenCVで読み込んだ画像を表示させて …

【MFC】マルチスレッドでプログレスバーを実装しよう その3

プログレスバーをマルチスレッドを用いた実装について最後の記事です。 引き続き作成の目的は、 スレッド実行中はモーダルダイアログを表示させること。 モーダルダイアログではプログレスバーを表示し進捗が分か …

【MFC入門】メッセージボックスにエラーコードを表示させよう

ライブラリなどを使用していると、戻り値にエラーコードを返す関数は比較的多いです。 GetLastError関数(最新のエラーコードを取得する関数)がライブラリに含まれていることもありますが、そうでない …