开发者

When a RAII object fails to construct

开发者 https://www.devze.com 2023-01-22 14:26 出处:网络
Suppose I construct a RAII object, and that object may fail to construct. How do I handle this? try { std::v开发者_JAVA技巧ector<int> v(LOTS);

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

try {
    std::v开发者_JAVA技巧ector<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.


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.


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!


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.


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;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消