7

この方法で RAII を使用することを示す例があります。

class File_ptr{
//...
   File* p;
   int* i; 
   public:
      File_ptr(const char* n, const char* s){
         i=new int[100];
         p=fopen(n,a); // imagine fopen might throws
      }
      ~File_ptr(){fclose(p);}
}

void use_file(const char* fn){
    File_ptr(fn,"r");
}

安全です。しかし、私の質問は次のとおりです。例外がスローされた場合p=fopen(n,a);、割り当てられたメモリiは返されません。RAII安全になりたいたびに、X取得したすべてのリソースXをスタックに割り当てる必要があると想定するのは正しいですか? が作成されている場合X.a、のリソースaもスタックに配置する必要がありますか? 何度も何度も言いますが、最後にヒープに配置されたリソースがある場合、RAII でどのように処理できるのでしょうか? それが私のクラスでない場合

4

6 に答える 6

11

RAII の要点は、リソース ( int-array など) をダングリング ポインターに割り当てないことです。代わりにstd::vector、配列ポインタを のようなものに使用または割り当てますstd::unique_ptr。このようにして、例外が発生するとリソースが破棄されます。

いいえ、STL を使用する必要はありませんが、RAII を使用するには、最も低い基本リソース (ヒープ割り当て配列など) も RAII を使用して作成する必要があります。これを行う最も簡単な方法は、むしろ STL を使用することです。独自のスマート ポインターやベクトルを記述するよりも。

于 2013-04-24T19:32:32.097 に答える
3

の後に例外が発生した場合newは、例外をキャッチし、コンストラクターでポインターを削除してから再スローする必要があります。この場合、オブジェクトが構築されないため、デストラクタは呼び出されません。

それ以外の場合iは std::vector であり、自動的にクリーンアップされます

于 2013-04-24T19:15:30.130 に答える
3

これを処理する 1 つの方法は、例外によって無効になる可能性のあるすべてのものを、それ自体が RAII を使用するローカル変数に入れ、安全になったら最後にメンバーに割り当てることです。

class File_ptr{
//...
   File* p;
   int* i; 
   public:
      File_ptr(const char* n, const char* s) i(NULL), p(NULL) {
         unique_ptr<int> temp_i=new int[100];  // might throw std::bad_alloc
         p=fopen(n,a); // imagine fopen might throws
         // possibility of throwing an exception is over, safe to set members now
         i = temp_i.release();
      }
      ~File_ptr(){fclose(p);}
}

詳細については、例外の安全性を参照してください。

于 2013-04-24T19:47:30.720 に答える
2

スローされることがわかっている場合は、try-catch.

File_ptr(const char* n, const char* s) {
    i=new int[100];
    try {
        p=fopen(n,a); // imagine fopen might throws
    } catch(...) {
         delete[] i;
         throw;
    }
}
于 2013-04-24T19:21:33.453 に答える
1

これを を使用したくない知的な演習として扱うにはstd::vector、クラスを分割して、単一の責任を持つようにする必要があります。これが私の「整数配列」クラスです。その責任は、整数配列のメモリを管理することです。

class IntArray {
public:
    IntArray() : ptr_(new int[100]) {}
    ~IntArray() { delete[] ptr_; }
    IntArray(const IntArray&) = delete; // making copyable == exercise for reader
    IntArray& operator=(const IntArray&) = delete;
    // TODO: accessor?
private:
    int* ptr_;
};

これが私のファイル処理クラスです。その責任は、を管理することFILE*です。

class FileHandle {
public:
    FileHandle(const char* name, const char* mode)
     : fp_(fopen(name, mode))
    {
        if (fp_ == 0)
            throw std::runtime_error("Failed to open file");
    }
    ~FileHandle() {
        fclose(fp_); // squelch errors
    }
    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;
    // TODO: accessor?
private:
    FILE* fp_;
};

構築エラーを例外に変換することに注意してください。fp_有効なファイル ポインターであることは維持したい不変条件であるため、この不変条件を設定できない場合は構築を中止します。

これで、File_ptr例外を安全にするのは簡単になり、クラスは複雑なリソース管理を必要としなくなりました。

class File_ptr {
private:
    FileHandle p;
    IntArray i; 
public:
    File_ptr(const char* n, const char* s)
     : p(n, s)
     , i()
    {}
};

ユーザー宣言のデストラクタ、コピー代入演算子、またはコピー コンストラクタがないことに注意してください。メンバーの順序を入れ替えることができます。どちらの場合でも、どのコンストラクターがスローするかは問題ではありません。

于 2013-04-25T11:11:15.830 に答える
1
File_ptr(const char* n, const char* s)
{
  std::unique_ptr<int[]> sp(new int[100]);
  p = fopen(n, s);
  i = sp.release();
}
于 2013-04-24T19:53:12.353 に答える