ぴぐノート

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++, プログラミング言語

執筆者:

関連記事

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

昨日に続き、プログレスバーをマルチスレッドを用いて実装してみましょう。 作成の目的は、 スレッド実行中はモーダルダイアログを表示させること。 モーダルダイアログではプログレスバーを表示し進捗が分かるこ …

【Boost】オープンソースライブラリBoostをインストールしよう

普段からC++でのプログラムを作成していてBoost(ブースト)を知らないという人はあまり多くないかもしれませんが、「実はまだ使ったことない…」という人もいるのではないでしょうか? ライブ …

C++でインターフェイスを作成しよう。

私事ですが今後業務ではC++を使用することになりそうです。 今まではウェブ関連の業務であったり、ちょっとしたテスト程度だったのでJavaを中心に使っていたため、C++の文法など少し忘れ気味です&#82 …

【OpenCV】OpenCVを使ってみよう!表示・回転・拡縮・二値化

前回インストールを行ったOpenCVライブラリを使ってサンプルプログラムをいくつか作成してみましょう。 OpenCVはすごく有名な画像処理ライブラリなのでインターネットで探せばサンプルプログラムはたく …

【Boost】boost::property_treeの例外処理を考えよう!

ライブラリなどを使用していると、「使い方は大体分かった!使ってみよう!」とすると微妙にはまってしまうことがあります。 前回・前々回でお話しした文字コードもその一つですが今回は例外処理について考えてみま …