C++のRAIの基本的な理解と使用

4409 ワード

発生原因:
C++では、このセグメントの終了時にリソース解放作業を完了する必要がある場合は、通常は問題ありませんが、例外が投げ出されると、リソースを解放する文は実行されません.そこでBjarne Stroustrupは、stack windingがそれらの構造関数が実行されることを保証するため、このプログラムセグメント(スタックフレーム)に配置されたオブジェクトの構造関数であることを確認したいと考えています.
初期化とリソース解放をパッケージクラスに移動するメリット:
-リソースの正常な解放を保証
-例外処理で冗長で重複し、必ずしも実行されていないクリーンアップロジックを省き、コードの異常なセキュリティを確保します.
-コードボリュームを簡略化します.
概要:
RAIIは、「リソース取得は初期化」とも呼ばれ、c++などのプログラミング言語でよく使われる管理リソースであり、メモリの漏洩を避ける方法です.いずれの場合も、オブジェクトを使用するときにオブジェクトを構築し、最後にオブジェクトを解析することを保証します.
理解:RAIIのやり方は1つのオブジェクトを使用して、その構造の時に資源を獲得して、オブジェクトのライフタイムで資源に対するアクセスを制御して終始有効に維持して、最後にオブジェクトが構造する時に資源を解放します.
RAIIのリソースの所有権によって、boost:shared_を表す定数タイプと変性タイプに分けられます.ptr<>とstd::auto_ptr<>;
管理リソースの初期化位置から、外部初期化タイプと内部初期化タイプに分けられます.
常態タイプとは、リソースを取得する場所がコンストラクション関数であり、リリースポイントがコンストラクション関数であり、この2つのポイントの間の期間、RAIタイプインスタンスの操作は、リソースの所有権を奪うべきではありません.
変性タイプとは、途中で別のリソースを引き継ぐように設定したり、いっそリソースを持たないように設定したりすることができます.
外部初期化タイプとは、リソースが外部で作成され、RAIIインスタンスに渡されるコンストラクション関数であり、後者は所有権を引き継ぐ.boost:shared_ptr<>とstd::auto_ptr<>はいずれもこのタイプです.これに対して内部初期化タイプです.
常性かつ内部初期化のタイプは最も純粋なRAII形式であり、最も理解しやすく、符号化しやすい.
RAII使用
ペアリングが必要な取得/解放関数呼び出しのリソースを処理するたびに、リソースをオブジェクトにカプセル化し、自動リソース解放を実現する必要があります.
使用例:
void Func()  
{  
  FILE *fp;  
  char* filename = "test.txt";  
  if((fp=fopen(filename,"r"))==NULL)  
  {  
      printf("not open");  
      exit(0);  
  }  
  ... //       fp             
       //    fp           
      
  fclose(fp);  
}  

リソースの取得から解放の間には、リソースを使用する必要がありますが、使用中に発生する予期せぬ異常がしばしば発生し、リソースの解放の一環が実行されません.この時、RAIIの慣用法を活躍させることができます.
RAIIの実現原理は簡単で、stack上の一時オブジェクトのライフサイクルを利用するのはプログラムの自動管理のこの特徴で、私たちの資源解放操作を一時オブジェクトにカプセル化します.
コードは次のとおりです.
class Resource{};  
class RAII{  
public:  
    RAII(Resource* aResource):r_(aResource){} //      
    ~RAII() {delete r_;} //      
    Resource* get()    {return r_ ;} //      
private:  
    Resource* r_;  
}; 

上記のファイル操作の例について、私たちのRAII一時オブジェクトクラスは以下のように書くことができます.
class FileRAII{  
public:  
    FileRAII(FILE* aFile):file_(aFile){}  
    ~FileRAII() { fclose(file_); }//              
    FILE* get() {return file_;}  
private:  
    FILE* file_;  
};  

このファイルを開く例は、RAIIで次のように書き換えることができます.
void Func()  
{  
  FILE *fp;  
  char* filename = "test.txt";  
  if((fp=fopen(filename,"r"))==NULL)  
  {  
      printf("not open");  
      exit(0);  
  }  
  FileRAII fileRAII(fp);  
  ... //       fp             
       //    fileRAII             ,           fp    
    
  //              ,       fp,fileRAII          ,           !      
 }  

独自のRAIクラスの作成
一般的に、RAIテンポラリ・オブジェクトではコピーや割り当ては許可されません.もちろん、heapでの作成は許可されません.そのため、次のRAIのbaseクラスを書き、サブクラスにBaseクラスをプライベートで継承させて無効にします.
class RAIIBase  
{  
public:  
    RAIIBase(){}  
    ~RAIIBase(){}//           ,               
      
    RAIIBase (const RAIIBase &);  
    RAIIBase & operator = (const RAIIBase &);  
    void * operator new(size_t size);   
    //          
};  

自分のRAIクラスを書くときは、そのクラスの実装を直接継承することができます.
template  
class ResourceHandle: private RAIIBase //       Base         
{  
public:  
    explicit ResourceHandle(T * aResource):r_(aResource){}//      
    ~ResourceHandle() {delete r_;} //      
    T *get()    {return r_ ;} //      
private:  
    T * r_;  
}; 

Handleクラスをテンプレートクラスにすることで、classタイプを入れることができます.さらに、ResourceHandleは、異なるリソースタイプの解放形態に従って異なる構造関数を定義することができる.このクラスのポインタは使用できないため,虚関数を使用することは意味がない.
まとめ1:
RAIIの核心思想は対象管理資源を使用し、対象「消滅」は自動的に資源を解放することである.RAIIの理解と使用は、ソフトウェアの設計をより明確にし、コードをより丈夫にすることができます.
有名なごみ収集(GC)とは異なり、RAIIは広義の資源を管理することができ、ごみ収集は「メモリ漏洩」だけに注目し、ファイルハンドル、同期オブジェクトなどのシステム資源の漏洩問題には関心がない.RAIIはプログラマにリソース解放のタイミングを決定させることができ、これもC++/CLIが決定的なリソースクリーンアップを導入した原因である.
まとめ2:
RAIIの本質的な内容は対象で資源を代表して、資源を管理する任務を管理対象の任務に転化して、資源の獲得と釈放を対象の構造と構造に対応して、それによって対象の生存期間内に資源が終始有効であることを確保して、対象が廃棄する時資源は必ず釈放される.すなわち,オブジェクトを持つことはリソースを持つことに等しく,オブジェクトが存在するとリソースは必ず存在する.
参照先:
http://developer.51cto.com/art/201106/267946.htm
http://www.cnblogs.com/gnuhpc/archive/2012/12/04/2802307.html
http://www.cnblogs.com/hsinwang/articles/214663.html
http://blog.csdn.net/hunter8777/article/details/6327704