ホームに戻る
出典 :
地蔵インスタンス - 組込屋 RAII - Wikipedia
関連 :
例外処理 ガード節 スマートポインタ コンテナ
目次 :

RAII(Resource Acquisition Is Initialization)とは

ヒープやセマフォなど、使用後に解放が必要となる資源の獲得・解放をクラス型変数の初期化・破棄に関連付け、
資源を安全に用いる(確実に解放する)ことを目論む技法。

C++においては、インスタンスの生成時にコンストラクタが呼ばれ、破棄される際にデストラクタが呼ばれる。
ローカルインスタンスであればスコープを抜ける際に必ず破棄される( = デストラクタが呼ばれる)ため、
セマフォの獲得・解放や割り込み禁止のオン・オフ専用のクラスインスタンスを「置いておく」だけで後処理の実装忘れを防止できる。
これは例外が発生した場合も同様であるため、例外安全を確保することができる。

ヒープの確保・解放に応用した場合は特に「(広義の)スマートポインタ」と呼ばれる。
(C++ STLのコンテナは明示的なヒープの獲得・解放の必要が無いスマートポインタとして実装されている。)
当然、デストラクタを持つ言語(C++、D言語など)でしか利用することができない。

地蔵インスタンス

RAIIの典型例。お地蔵様のように、「置いておくだけで効力を発揮する」インスタンスをクリティカルセクションの始点で生成する。

地蔵インスタンスを用いない場合

int Hoge() { // セマフォ獲得 sem_wait(SEM_ID_HOGE); int ret; // 何か処理を行う ret = DoSomething1(); // 成功時のみ次の処理を行う if (ret == 0) { ret = DoSomething2(); } : // セマフォ解放 sem_release(SEM_ID_HOGE); // 最後に行った処理の結果を返す return ret; }
Hoge() 関数ではセマフォの獲得・解放を行っている。
獲得したセマフォを必ず解放しないといけないため、処理( DoSomething#() )に失敗した場合でも、関数を途中で抜けることができない。
(関数内で例外が発生した場合はさらに煩雑となる。)

DoSomething#() 失敗時も条件比較を経ることになり、パフォーマンスに悪影響を及ぼすとともにコードの可読性も低下する。
(ネスト構造とすればパフォーマンスへの影響は最小化できるが、コードの可読性はさらに低下する。)
また、仮にセマフォの解放処理を実装し忘れた場合でもコンパイラは警告を発さないため、発見できないおそれがある。

地蔵インスタンスを用いた場合

// セマフォの獲得・解放専用クラス class C_SemaphoreManager { public: // コンストラクタ : セマフォ獲得 C_SemaphoreManager(int semId) : m_SemId(semId) { sem_wait(m_SemId); } private: int m_SemId; //< セマフォID // デストラクタ : セマフォ解放 ~C_SemaphoreManager() { sem_release(m_SemId); } } int Hoge() { // 地蔵インスタンス配置 ⇒ セマフォ獲得 C_SemaphoreManager semStatue(SEM_ID_HOGE); int ret; // 何か処理を行う ret = DoSomething1(); if (ret != 0) { return ret; // 関数脱出で地蔵インスタンス破棄 ⇒ セマフォ解放 } // 次の処理を行う ret = DoSomething2(); if (ret != 0) { return ret; // 関数脱出で地蔵インスタンス破棄 ⇒ セマフォ解放 } : return 0; // 関数脱出で地蔵インスタンス破棄 ⇒ セマフォ解放 }
このコードでは、セマフォの解放を明示的には実行していない
地蔵インスタンスを用いた場合、関数の脱出とともにローカルインスタンス( semStatue )が破棄されるため、デストラクタの処理としてセマフォの解放が行われる。
関数途中での return や例外発生時もスコープを抜けたタイミングで確実にデストラクタが走るため、セマフォの解放に注意する必要がない。
このようにガード節を使用できるため、プログラムの可視性を維持できる。

副次的効果

地蔵インスタンスを用いた場合、「クリティカルセクションの始点と終点が分かりやすくなる」効果がある。
int Hoge() { { //< クリティカルセクションここから // 地蔵インスタンス配置 ⇒ セマフォ獲得 C_SemaphoreManager semStatue(SEM_ID_HOGE); : // コードブロックを抜ける際に地蔵破棄 ⇒ セマフォ解放 } //< クリティカルセクションここまで : }
地蔵インスタンスの有効範囲(ここではセマフォを握っている期間)は、インスタンスが生成されたコードブロックの終端までなので、
コードブロックの先頭に地蔵を置けば、ブレースがクリティカルセクションの始点と終点に(ほぼ)一致する