如果资源获取失败,如何实施RAII

发布于 2024-12-22 08:39:52 字数 667 浏览 2 评论 0原文

我想在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

千秋岁 2024-12-29 08:39:52

构造函数报告失败的唯一方法是抛出异常。

相反,析构函数不得抛出异常(如果析构函数在堆栈展开期间抛出异常,则调用 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 函数可能会失败,并且该失败可能与关闭相关,也可能无关(例如,某些写入错误仅在刷新时报告 < 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

  • Swallow errors silently
  • Abort the program
  • Log the error and do either of the above.

If you use RAII correctly, exceptions can traverse your code without damage.

Example here:

#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_);
}

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 the close system call on POSIX systems). Please use iostreams for file I/O in C++, since they provide convenient abstractions over those concerns.

北方。的韩爷 2024-12-29 08:39:52

尽管有这个名称,RAII 并不一定涉及资源获取;而是涉及资源获取。
例如,std::shared_ptr 不会执行new。名字是
历史性的,并且该模式确实与清理有关。

当然,对于 File 来说,“正确”的答案是使用
std::ifstreamstd::ofstream,然后就完成了。 而且非常
重要(至少对于输出):RAII 只能用于特殊情况
清理,因为您必须验证 close 是否正确
在正常情况下,在离开街区之前完成。作为一名将军
规则,如果关闭失败,则要删除该文件(并返回
来自 mainEXIT_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 the new, for example. The name is
historical, and the pattern really concerns cleanup.

In the case of File, of course, the "correct" answer is to use
std::ifstream and std::ofstream, and be done with it. And very
important (at least for output): RAII can only be used for exceptional
clean-up, since you have to verify that the close has correctly
finished 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 from main, or do something that will make the error
apparent, 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 already
found 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 an OutputFile class, with a
commit() function which closes the file, and marks the class as
committed 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文