【问题标题】:Is it possible to do RAII without exceptions?是否可以毫无例外地做 RAII?
【发布时间】:2015-10-23 11:10:50
【问题描述】:

如果在构建对象时需要获取的资源可能会失败,如果本地编码标准禁止异常,是否可以执行 RAII?

如果是,那么在这种情况下处理资源获取失败的规范方式是什么?

【问题讨论】:

  • 当我因为某种原因不能使用异常时,我最终会为可能无法构造的对象创建初始化方法。所以构造函数除了简单的设置外不会做任何事情。然后我可以有一个 Init 方法来执行实际的资源获取并相应地返回 true/false。
  • 你的指导方针没有提到类似的情况吗?毕竟它一个非常常见的C++习语。如果指南允许,您始终可以使用类似于standard I/O library 及其流的系统,例如流可以在条件中用于检查错误,或者类似于具有is_open 函数的文件流。
  • @JoachimPileborg 我认为指南中没有考虑到它,它们很可能需要更新(尽管它是嵌入式实时系统,并且不喜欢异常!)无论如何,我的感觉是'不可能,所以为了这个问题,我希望我的感觉得到证实! :-)
  • 当然,如果不允许异常,那么周围就没有人可以捕捉到异常。因此,析构函数不会运行并不重要,反正程序已经死了。
  • @Joe 你也许可以让你的同事相信异常并没有那么糟糕。大多数人将性能成本与它们联系在一起,但多年来并非如此。如今,异常在实际抛出之前基本上是免费的,而且这种情况应该发生——嗯——非常罕见。如果您搜索 c++ 零成本异常,您会找到可能对您有所帮助的参考资料。

标签: c++ raii


【解决方案1】:

我一般不会采用无效对象方法,因为我认为这是糟糕的设计。构造后,对象必须处于建立不变量的状态(这是构造函数应该服务的唯一目的)。考虑一个类strange_vector 实现类似std::vector 的东西,但是在调用strange_vector<int>(10, 0) 之后,由于分配失败,该对象将处于不可用状态。

相反,我将构造函数声明为私有并使用返回可选的工厂方法:

class file
{
public:
    ~file() {fclose(m_file);}

    static std::optional<file> open(std::string const& filename)
    {
        auto f = fopen(filename.c_str(), "r");
        if (f)
        {
            return std::make_optional<file>(f);
        }
        else
        {
            return std::nullopt;
        }
    }

private:
    file(FILE* file);
    FILE* m_file;
};

异常处理的最大好处之一是(除了解耦错误处理和正常代码路径)您不会意外地忽略它们。如果你愿意,你可以创建你自己的类似于optional 的类,当没有使用有效对象初始化时,会记录错误消息并终止你的程序(或任何其他合理的错误处理)。我认为有一个talk from A. Alexandrescu about systematic error handling,他在其中实现了一个类Expected&lt;T&gt;,其中包含T 类型的值或异常。您可以以此为基础,而不是异常添加您的错误处理。

std::optional 还不是标准的一部分,但您可以轻松地获得作为最新编译器的一部分、在 boost 中或在其他库中的实现。

【讨论】:

  • 你把本地 f 和成员 m_file 混在一起了,不是吗?
【解决方案2】:

您始终可以创建bool valid(void) 方法。然后构造函数可以设置适当的条件,在你的代码中,你可以在构造之后检查这是否有效。

class foo
{
public:
    foo(const char *Filename)
    {
        mValid = false;

        if(fopen(Filename) == NULL)
           return;

        mValid = true;
    }

    bool valid(void) { return mValid; }

    private:
        bool mValid;
};

void myfunc(void)
{
    foo fl("myfile");
    if(!fl.valid())
    {
        printf("Error\n");
        return;
    }
}

【讨论】:

  • 我真的不喜欢这种方法。如果我调用了一个使用文件的 foo 方法,但是初始化失败了怎么办?不建立对象不变量的构造函数是不行的。
  • 如果您调用使用 foo 的方法,它应该在内部验证文件确实有效。嗯,它很丑,但至少它保证了对象本身是正确的。
  • 这增加了对每个方法调用的检查。我不确定 OP 是否希望在他的嵌入式域中使用它,而且我认为我一般不会想要它,例如当使用这种方法检查每个访问的向量类...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多