当 RAII 对象构建失败时

发布于 2024-09-30 04:06:05 字数 427 浏览 1 评论 0原文

假设我构造一个 RAII 对象,该对象可能无法构造。我该如何处理这个问题?

try {
    std::vector<int> v(LOTS);
    // try scope ends here because that's what the catch is for
} catch( const std::bad_alloc& ) {
    // ...
}
// v? what v?

当然,std::vector 的默认构造函数不会抛出异常,这会有所帮助,但这不是一般情况。构造函数很可能会抛出异常。如果我想处理任何资源获取失败,我该如何做到这一点,同时在它不抛出时仍然能够继续?

编辑:澄清一下,我的问题是,如果资源无法获取,那么我可能想重试,依此类推。也许我可以尝试获取替代资源。

Suppose I construct a RAII object, and that object may fail to construct. How do I handle this?

try {
    std::vector<int> v(LOTS);
    // try scope ends here because that's what the catch is for
} catch( const std::bad_alloc& ) {
    // ...
}
// v? what v?

Granted, the default constructor of std::vector won't throw and that can help, but this is not the general case. A constructor may very well throw. If I want to handle any resource acquisition failure, how do I do that while still being able to proceed if it doesn't throw?

Edit: To clarify, my issue is that if a resource fails to acquire then I might want to try again, and so on. Maybe I can try acquiring an alternative resource.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(4

爱你不解释 2024-10-07 04:06:05

取决于你所说的“继续”是什么意思。无论什么操作需要资源都会失败:这就是“需要”的含义。因此,当您想在错误后继续时,您可能最终会编写如下代码:

void something_using_RAII(thingummy &t) {
    vector<int> v(t.size_required);
    // do something using v
}

...

for each thingummy {
    try {
         something_using_RAII(this_thingummy);
    } catch(const std::bad_alloc &) {
         std::cerr << "can't manage that one, sorry\n";
    }
}

这就是为什么您应该只在可以用它们做一些值得做的事情时捕获异常(在这种情况下,报告失败并继续进行下一个事情ummy) 。

如果您想在失败时重试,但前提是构造函数失败,而不是其他任何失败:

while(not bored of trying) {
    bool constructor_failed = true;
    try {
        vector<int> v(LOTS);
        constructor_failed = false;
        // use v
    } catch(...) {
        if (!constructor_failed) throw;
    }
}

这或多或少是 std::new_handler 的工作方式 - 在 catch 中调用处理程序类似循环的子句,尽管不需要标志。

如果您想在失败时尝试不同的资源:

try {
    vector<int> v(LOTS);
    // use v
} catch(...) try {
    otherthing<int> w(LOTS);
    // use w
} catch(...) {
    // failed
}

如果“use v”和“use w”基本上是相同的代码,则重构为函数并从两个地方调用它。此时你的函数正在做很多事情。

Depends what you mean by "proceed". Whatever operation requires the resource will fail: that's what "requires" means. So when you want to continue after an error, you might end up writing code like this:

void something_using_RAII(thingummy &t) {
    vector<int> v(t.size_required);
    // do something using v
}

...

for each thingummy {
    try {
         something_using_RAII(this_thingummy);
    } catch(const std::bad_alloc &) {
         std::cerr << "can't manage that one, sorry\n";
    }
}

That's why you should only catch exceptions when there's something worthwhile you can do with them (in this case, report failure and move on to the next thingummy).

If you want to try again on failure, but only if the constructor fails, not if anything else fails:

while(not bored of trying) {
    bool constructor_failed = true;
    try {
        vector<int> v(LOTS);
        constructor_failed = false;
        // use v
    } catch(...) {
        if (!constructor_failed) throw;
    }
}

This is more-or-less how std::new_handler works - the handler is called in the catch clause of a similar loop, although with no need for a flag.

If you want to try a different resource on failure:

try {
    vector<int> v(LOTS);
    // use v
} catch(...) try {
    otherthing<int> w(LOTS);
    // use w
} catch(...) {
    // failed
}

If "use v" and "use w" are basically the same code, then refactor into a function and call it from both places. Your function is doing quite a lot at this point.

无人问我粥可暖 2024-10-07 04:06:05

如果 RAII 构造函数抛出异常,则抛出点之前绑定到 RAII 对象的所有资源都将被正确清理。 C++ 规则的设计合理就是为了保证这一点。

如果您的 v 构造由于 bad_alloc 而抛出异常,则在 try 块中的 v 之前创建的任何 RAII 对象都将得到妥善清理。

因此,如果您随后使用 RAII,则不需要像这样手动 try / catch,因为 RAII 对象会为您处理清理工作。如果您出于某种原因确实需要它,在上面的情况下您可以像下面这样使用交换。

std::vector<int> v;
try {
    std::vector<int> vtry(LOTS);
    v.swap(vtry); // no-throw
} catch( const std::bad_alloc& ) {
    // ...
}
// v!

If an RAII constructor throws, all resources bound to RAII objects prior to the throwing point will be cleaned up properly. The C++ rules are sensibly designed to guarantee that.

If your v construction throws because of a bad_alloc then any RAII object created prior to v in the try block will be properly cleaned up.

So if you consequently use RAII, you don't need a manual try / catch like that, because the RAII objects handle cleanup for you. If you do need it for some reason, in the case above you could use swap like the following.

std::vector<int> v;
try {
    std::vector<int> vtry(LOTS);
    v.swap(vtry); // no-throw
} catch( const std::bad_alloc& ) {
    // ...
}
// v!
秋凉 2024-10-07 04:06:05

如果无法创建v,则所有尝试使用v的代码都无法执行。将 catch 移到 code 使用 v 的代码后面,放在如果没有 v 则可以合理继续执行的位置。

If v can't be created, all the code that tries to use v can't be executed. Move the catch after the code that code uses v, in a place where it is reasonable to continue execution if there is no v.

花开雨落又逢春i 2024-10-07 04:06:05

所有使用 v 的代码都需要位于 try 块中。如果问题是如何缩小抛出异常的代码范围,您可以使用某种标志来指示您在 try 块中的位置,如下所示:

string flag;
try
{
    flag = "creating vector<int> v";
    std::vector<int> v(LOTS);

    flag = "performing blaggity bloop";
    blaggity_bloop();

    flag = "doing some other stuff";
    some_other_stuff();
}
catch( const std::bad_alloc& )
{
    cerr << "Bad allocation while " << flag << endl;
}

All code that uses v needs to be in the try block. If the question is how to then narrow down the code which threw the exception, you can use some kind of flag to indicate where in the try block you are, like this:

string flag;
try
{
    flag = "creating vector<int> v";
    std::vector<int> v(LOTS);

    flag = "performing blaggity bloop";
    blaggity_bloop();

    flag = "doing some other stuff";
    some_other_stuff();
}
catch( const std::bad_alloc& )
{
    cerr << "Bad allocation while " << flag << endl;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文