有一个例子表明以这种方式使用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处理它?如果它不是我的类别,即
答案 0 :(得分:11)
RAII的重点是不要将任何资源(如int
- 数组)分配给悬空指针。相反,使用std::vector
或将数组指针指定为std::unique_ptr
之类的内容。这样,资源将在异常发生时被销毁。
不,你不必使用STL,但是为了使用RAII,必须使用RAII创建最低的基础资源(比如堆分配的数组),最简单的方法是使用STL而不是编写自己的智能指针或向量。
答案 1 :(得分:3)
如果在new
之后发生异常,则必须捕获异常并删除构造函数中的指针,然后在这种情况下重新抛出,因为永远不会构造对象,所以不会调用析构函数。
否则如果i
是std :: vector,它将自动清理
答案 2 :(得分: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);}
}
有关详细信息,请参阅Exception Safety。
答案 3 :(得分: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;
}
}
答案 4 :(得分:1)
File_ptr(const char* n, const char* s)
{
std::unique_ptr<int[]> sp(new int[100]);
p = fopen(n, s);
i = sp.release();
}
答案 5 :(得分:1)
将此视为您不想使用std::vector
的智力练习,您需要将课程分开,以便他们承担单一责任。这是我的&#34;整数数组&#34;类。它的职责是管理整数数组的内存。
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()
{}
};
注意缺少任何用户声明的析构函数,复制赋值运算符或复制构造函数。我可以交换成员的顺序,在任何一种情况下,哪个构造函数都不重要。