我想在RAII的帮助下实现一个课程。资源应该在构造函数中获取,但是acquistition可能会失败。我将在下面使用FILE给出一个例子:
class file {
public:
file(const char* filename) {
file_ = fopen(filename, "w+");
if(!file_) {
// Okay
}
else {
// ERROR
}
}
~file() {
if (fclose(file_)) {
// ERROR
}
}
void write(const char* str) {
if (EOF == fputs(str, file_)) {
throw runtime_error("file write failure");
}
}
private:
FILE* file_;
};
那么,如果fopen返回NULL,那么处理错误的最佳方法是什么?因为它是构造函数,所以我也无法返回NULL。
我希望有人能给我一个如何处理这些错误的提示!
谢谢,最好的问候,
闪光
答案 0 :(得分:6)
构造函数报告失败的唯一方法是抛出异常。
相反,析构函数不能抛出异常(如果在堆栈展开期间抛出析构函数,则会调用std::terminate
,默认情况下会结束程序。)
如果破坏失败,你可以
如果您正确使用RAII,异常可以在不损坏的情况下遍历您的代码。
此处示例:
#include <cerrno>
#include <cstring>
#include <sstream>
file::file(const char* filename)
{
file_ = fopen(filename, "w+");
if (!file_)
{
std::ostringstream os;
os << "Cannot open " << filename << ": "
<< std::strerror(errno);
throw std::runtime_error(os.str());
}
}
file::~file()
{
fclose(file_);
}
请注意,此代码存在许多错误:fclose
函数可能会失败,并且失败可能与关闭有关,也可能与关闭无关(例如,某些写错误仅在{{1}时刷新时报告系统调用POSIX系统)。请使用iostreams在C ++中进行文件I / O,因为它们提供了对这些问题的方便抽象。
答案 1 :(得分:1)
尽管有这个名字,RAII并不一定涉及资源收购;
例如,std::shared_ptr
不会执行new
。名字是
历史,这种模式确实涉及清理。
在File
的情况下,当然,“正确”的答案是使用
std::ifstream
和std::ofstream
,并完成它。 和非常
重要的(至少对于输出):RAII只能用于例外
清理,因为您必须验证close
是否正确
在正常情况下离开街区前完成。作为一般
规则,如果关闭失败,您要删除该文件(并返回
来自EXIT_FAILURE
的{{1}},或做出会导致错误的内容
显而易见,并防止进一步的代码在假设下执行
数据是写的)。所以唯一可以接受的时间
将main
推迟到析构函数是在你已经存在的情况下
发现错误,并将作为清理的一部分删除该文件
反正。
通常,输出服从交易模型,而不是RAII;一世
倾向于将我的close
包裹在std::ofstream
课程中
OutputFile
函数关闭文件,并将类标记为
如果(并且仅当)结束成功,则承诺;如果析构函数是
调用并且文件尚未提交,析构函数关闭了
文件,然后删除它。 (据推测,一些更高级别的人会抓住
异常并将其转换为main中的commit()
。)
此外,IO总的来说有点特殊。通常,如果你不能 创建一个对象,无论出于什么原因,构造函数应该引发一个 例外;你不希望“僵尸”物体漂浮在周围,这不可能 使用。但是,对于IO,您必须处理这个事实 即使是在施工后物体也会变得无效和无法使用 施工成功的时候。因为你必须经常检查他们的 无论如何状态(每次读取输入后,输出结束后), 通常在对象中设置一些内部标志是合适的, 你测试的。