リソースの解放忘れを防ぐには? Effective C++ 14項を読んで
Scott Mayers著, 小林健一郎訳, Effective C++ 第3版, 丸善出版(2014) を1日1項目読んでいます。
今日は14項を読みました。
リソース(メモリ、ファイルディスクリプタ、ミューテックスロック、DB接続、ネットワークソケットなど)の解放忘れを防ぐにはどうしたらよいでしょうか? RAIIオブジェクトをリソースを渡すことで生成すればよい。そして、RAIIオブジェクトとしては、C++11のstd::shared_ptrを使うとよい、ということがわかりました。
どうして、リソースの解放忘れが起こるのでしょうか? それは、例えば、リソースとしてメモリを考えると、ある関数内でヒープ領域にメモリを確保すると、その関数を抜ける前に、メモリを解放する必要があります。
void f(){ A* a = new A(); ... // ここで、returnしたり、例外が発生したりするとdeleteが実行されない。 delete a; }
しかし、上のように、deleteする前にreturnしたり、例外が発生したりするとdeleteが実行されず、メモリリークになります。何らかの理由で関数を抜ける際に、必ずdeleteが実行されるような仕組みはないでしょうか? これを解決するアイディアはデストラクタです。
デストラクタは、そのオブジェクトの寿命が終わると実行されます。つまり、関数内で、あるオブジェクトをポインタなどのリソースを渡すことで生成すれば、関数を抜けるときに、オブジェクトのデストラクタが実行されるので、このデストラクタでポインタをdeleteしてもらうことができます。
このデストラクタをもつオブジェクトのことをRAIIオブジェクトといいます。既に用意されているRAIIオブジェクトとして、C++11のstd::shared_ptrがあります。また、std::shared_ptrをstaticでないメンバとしてもつオブジェクトもRAIIオブジェクトになります。なぜなら、デストラクタが呼ばれるとき、メンバのデストラクタも呼ばれるからです。
リソースがメモリのときには、リソースへのポインタをstd::shared_ptrのコンストラクタに渡します。すると、std::shared_ptrのデストラクタでこのポインタがdeleteされます。(delete[]でないことに注意)
リソースがメモリでないときには、リソースへのポインタをdeleteするとともに、何らかの解放処理(デリータ)が必要になります。リソースへのポインタを引数とする関数オブジェクト、すなわちデリータをstd::shared_ptrのコンストラクタに渡せます。
リソースがミューテックスロックの場合のサンプル
#include <memory> #include <iostream> class Mutex { public: void lock(){ std::cout << "locked\n"; } void unlock(){ std::cout << "unlocked\n"; } }; void lock(Mutex* pm){ pm->lock(); } void unlock(Mutex* pm){ pm->unlock(); } class Lock { public: Lock(Mutex* pm) : mutexPtr(pm, unlock) { lock(mutexPtr.get()); } ~Lock(){} private: std::shared_ptr<Mutex> mutexPtr; }; void test(const Lock& ml){ std::cout << "test start\n"; { Lock ml2(ml); std::cout << "in critical session in test...\n"; } std::cout << "test end\n"; } int main(int argc, char* argv[]){ Mutex m; std::cout << "start\n"; { Lock ml1(&m); std::cout << "in critical session...\n"; test(ml1); } std::cout << "end\n"; return 0; }
$ clang++ -std=c++11 Lock.cpp -o lock
実行
$ ./lock start locked in critical session... test start in critical session in test... test end unlocked end
Scott Mayersさんのブログhttp://scottmeyers.blogspot.jp/も読んでみようと思います。