如果资源获取失败,如何实施RAII
我想在 RAII 的帮助下实现一个课程。资源应该在构造函数中获取,但有可能获取失败。下面我将使用 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。
我希望有人能给我提示如何处理此类错误!
谢谢你,最诚挚的问候,
闪光者
I would like to implement a class with the help of RAII. The resources should be acquired in the constructor, but it's possible that the acquistition failed. I'll give an example in the following using 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_;
};
So, what's the best way to handle an error which is occurred if fopen returns NULL? Because it's the constructor I can't return also NULL.
I hope somebody can give me a hint how to handle such errors!
Thank you, best regards,
Flasher
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
构造函数报告失败的唯一方法是抛出异常。
相反,析构函数不得抛出异常(如果析构函数在堆栈展开期间抛出异常,则调用 std::terminate,这将默认结束程序)。
如果销毁失败,您可以
如果正确使用 RAII,异常可以遍历您的代码而不会造成损坏。
此处的示例:
请注意,此代码有许多错误:
fclose
函数可能会失败,并且该失败可能与关闭相关,也可能无关(例如,某些写入错误仅在刷新时报告 < code>close POSIX 系统上的系统调用)。请在 C++ 中使用 iostream 进行文件 I/O,因为它们为这些问题提供了方便的抽象。The only way a constructor can report failure is by throwing an exception.
Destructors, to the contrary, must not throw exceptions (if a destructor throws during stack unwinding,
std::terminate
is called, which ends the program by default).If destruction fails, you can
If you use RAII correctly, exceptions can traverse your code without damage.
Example here:
Note that this code has many bugs: the
fclose
function can fail, and the failure may or may not be related to the closing (eg. some write errors are only reported when flushing at theclose
system call on POSIX systems). Please use iostreams for file I/O in C++, since they provide convenient abstractions over those concerns.尽管有这个名称,RAII 并不一定涉及资源获取;而是涉及资源获取。
例如,
std::shared_ptr
不会执行new
。名字是历史性的,并且该模式确实与清理有关。
当然,对于
File
来说,“正确”的答案是使用std::ifstream
和std::ofstream
,然后就完成了。 而且非常重要(至少对于输出):RAII 只能用于特殊情况
清理,因为您必须验证
close
是否正确在正常情况下,在离开街区之前完成。作为一名将军
规则,如果关闭失败,则要删除该文件(并返回
来自
main
的EXIT_FAILURE
,或者执行一些会导致错误的操作显而易见,并阻止进一步的代码执行,假设
数据已写入)。所以唯一一次可以接受的是
将
close
推迟到析构函数是在您已经这样做的情况下发现错误,并将删除该文件作为清理的一部分
反正。
一般来说,输出遵循交易模型,而不是 RAII 模型;我
倾向于将我的
std::ofstream
包装在OutputFile
类中,并带有commit()
函数关闭文件,并将类标记为当(且仅当)关闭成功时才提交;如果析构函数是
调用并且文件尚未提交,析构函数关闭
文件,然后将其删除。 (大概更高的水平会捕捉到
异常并将其转换为 main 中的
return EXIT_FAILURE
。)此外,IO 通常有点异常。通常情况下,如果你不能
创建一个对象,无论出于何种原因,构造函数都应该引发一个
例外;你不希望“僵尸”物体漂浮在周围,这不能
被使用。然而,对于 IO,您必须面对这样一个事实:
该对象在构造后可能会变得无效且无法使用,甚至
当构建成功时。因为你必须不断地检查他们的
无论如何状态(每次读取输入后以及关闭输出后),
在对象中设置一些内部标志通常是合适的,
你测试的。
Despite the name, RAII doesn't necessarily involve resource aquisition;
std::shared_ptr
doesn't do thenew
, for example. The name ishistorical, and the pattern really concerns cleanup.
In the case of
File
, of course, the "correct" answer is to usestd::ifstream
andstd::ofstream
, and be done with it. And veryimportant (at least for output): RAII can only be used for exceptional
clean-up, since you have to verify that the
close
has correctlyfinished before leaving the block in the normal case. As a general
rule, if the close fails, you want to delete the file (and return
EXIT_FAILURE
frommain
, or do something that will make the errorapparent, and prevent further code from executing on the assumption that
the data were written). So the only time it would be acceptable to
defer the
close
to a destructor is in a case where you've alreadyfound an error, and are going to delete the file as part of cleanup
anyway.
Generally, output obeys a transaction model, more than an RAII one; I
tend to wrap my
std::ofstream
in anOutputFile
class, with acommit()
function which closes the file, and marks the class ascommitted if (and only if) the close succeeds; if the destructor is
called and the file hasn't been committed, the destructor closes the
file, then deletes it. (Presumably some higher level will catch the
exception and convert it into a
return EXIT_FAILURE
in main.)Also, IO is a bit exceptional in general. Normally, if you cannot
create an object, for whatever reason, the constructor should raise an
exception; you don't want "zombie" objects floating around, which can't
be used. In the case of IO, however, you have to deal with the fact
that the object can become invalid and unusable after construction, even
when construction succeeds. Since you have to constantly check their
status anyway (after each read on input, and after close on output),
it's often appropriate to just set some internal flag in the object,
which you test.