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

处理此问题的一种方法是将可能因异常而无效的所有内容放入本身使用 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
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 回答
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 回答