ぴぐノート

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】フォルダー選択ダイアログの表示【Visual C++】

出力先の指定などフォルダー選択のダイアログを表示させたいことがあります。 フォルダー選択ダイアログを表示させるためには、SHBrowseForFolder関数を使います。 SHBrowseForFol …

Eclipse NeonとCDT/MinGWでC++開発環境を作成しよう!(2)

前回の記事でEclipseにCDTを追加インストールすることができました。 今回はMinGWとMSYSをインストールし、Eclipse上でC++プロジェクトを実行してみましょう。 この際に必要なダウン …

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

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

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

以前はアプリケーションの設定ファイルに単純なテキストである、INIファイルを用いていたアプリケーションも多かったですが、最近はXML(Extensible Markup Language:拡張可能なマ …

【Boost】Boostライブラリの日付を使ってみよう!

先日の記事でBoostをインストールできたのでBoostのライブラリを使って遊んでみましょう。 今回は日付に関する情報をBoostから取得・表示したり、〇月〇日~□月□日が何日間あるか(期間)取得して …