Is there a favored idiom for mimicing Java's try/finally in C++?

前端 未结 15 806
无人及你
无人及你 2021-02-05 19:16

Been doing Java for number of years so haven\'t been tracking C++. Has finally clause been added to C++ exception handling in the language definition?<

相关标签:
15条回答
  • 2021-02-05 19:36

    I think that you are missing the point of what catch (...) can do.

    You say in your example "alas, can't examine the exception". Well, you have no information about the type of the exception. You don't even know if it's a polymorphic type so even if you had some sort of an untyped reference to it, you couldn't even safely attempt a dynamic_cast.

    If you know about certain exceptions or exception hierarchies that you can do something with then this is the place for catch blocks with explicity named types.

    catch (...) is not often useful in C++. It can be used in places which have to guarantee that they don't throw, or only throw certain contracted exceptions. If you are using catch (...) for cleanup then there is a very good chance that your code is not robustly exception safe in any case.

    As mentioned in other answers, if you are using local objects to manage resources (RAII) then it can be surprising and enlightening how few catch blocks you need, often - if you don't need to do anything locally with an exception - even the try block can be redundant as you let the exceptions flow out to the client code that can respond to them while still guaranteeing no resource issues.

    To answer your original question, if you need some piece of code to run at the end of a block, exception or no exception, then a recipe would be.

    class LocalFinallyReplacement {
        ~LocalFinallyReplacement() { /* Finally code goes here */ }
    };
    // ...
    { // some function...
        LocalFinallyReplacement lfr; // must be a named object
    
        // do something
    }
    

    Note how we can completely do away with try, catch and throw.

    If you had data in the function that was originally declared outside the try block that you needed access to in the "finally" block, then you may need to add that to the constructor of the helper class and store it until the destructor. However, at this point I would seriously reconsider whether the problem could be resolved by altering the design of the local resource handling objects as it would imply something awry in the design.

    0 讨论(0)
  • 2021-02-05 19:38

    Ok, I have to add in an answer to the points you made in a separate answer post: (It would be a lot more convenient if you'd edited this into the original question, so it doesn't end up at the bottom below the answers to it.

    If all cleanup always gets done in destructors then there wouldn't need to be any cleanup code in a catch block - yet C++ has catch blocks where cleanup actions get done. Indeed it has a block for catch(...) where it is only possible to do cleanup actions (well, certainly can't get at any exception information to do any logging).

    catch has a completely separate purpose, and as a Java programmer you should be aware of that. The finally clause is for "unconditional" cleanup actions. No matter how the block is exited, this must be done. Catch is for conditional cleanup. If this type of exception is thrown, we need to perform a few extra actions.

    The cleanup in a finally block will get done whether there was an exception thrown or not - which is what one always wants to happen when cleanup code does exist.

    Really? If we want it to always happen for this type (say, we always want to close a database connection when we're done with it), then why don't we define it once? In the type itself? Make the database connection close itself, rather than having to put a try/finally around every single use of it?

    That's the point in destructors. They guarantee that each type is able to take care of its own cleanup, every time it's used, without the caller having to think of it.

    C++ developers from day one have been plagued with having to repeat cleanup actions that appear in catch blocks in the code flow that occurs upon successful exit from the try block. Java and C# programmers just do it once in the finally block.

    No. C++ programmers have never been plagued by that. C programmers have. And C programmers who realized that c++ had classes, and then called themselves C++ programmers have.

    I program in C++ and C# daily, and I feel I'm plagued by C#'s ridiculous insistence that I must supply a finally clause (or a using block) EVERY SINGLE TIME I use a database connection or something else that must be cleaned up.

    C++ lets me specify once and for all that "whenever we're done with this type, it should perform these actions". I don't risk forgetting to release memory. I don't risk forgetting to close file handles, sockets or database connections. Because my memory, my handles, sockets and db connections do it themselves.

    How can it ever be preferable to have to write duplicate cleanup code every time you use a type? If you need to wrap the type because it doesn't have a destructor itself, you have two easy options:

    • Look for a proper C++ library which provides this destructor (hint: Boost)
    • Use boost::shared_ptr to wrap it, and supply it with a custom functor at runtime, specifying the cleanup to be done.

    When you write application server software like Java EE app servers Glassfish, JBoss, etc., you want to be able to catch and log exception information - as opposed to let it fall on the floor. Or worse fall into the runtime and cause a ungraceful abrupt exit of the application server. That's why it's very desirable to have an overarching base class for any possible exception. And C++ has just such a class. std::exception.

    Have done C++ since the CFront days and Java/C# most of this decade. Is clear to see there's just an enormous culture gap in how fundamentally similar things are approached.

    No, you've never done C++. You've done CFront, or C with classes. Not C++. There's a huge difference. Quit calling the answers lame, and you might learn something about the language you thought you knew. ;)

    0 讨论(0)
  • 2021-02-05 19:47

    C++'s answer is RAII: The object's destructor will be executed when they go out of scope. Whether by a return, by an exception or whatever. If you handle the exception somewhere else, you can be sure all objects from the called function down to your handler will be properly destructed by having their destructor called. They will clean up for you.

    Read http://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

    0 讨论(0)
  • 2021-02-05 19:48

    I've done plenty of class design and template wrapper design in C++ over those 15 years and done it all the C++ way in terms of destructors cleaning up. Every project, though, also invariably involved the use of C libraries that provided resources with the open it, use it, close it usage model. A try/finally would mean such a resource can just be consumed where it needs to be - in a completely robust manner - and be done with it. The least tedium approach to programming that situation. Could deal with all the other state going on during the logic of that cleanup without having to be scoped away in some wrapper destructor.

    I did most of my C++ coding on Windows so could always resort to using Microsoft's __try/__finally for such situations. (Their structured exception handling has some powerful abilities for interacting with exceptions.) Alas, doesn't look like C language has ever ratified any portable exception handling constructs.

    That wasn't ideal solution, though, because it was not straightforward to blend C and C++ code in a try block where either style of exception might get thrown. A finally block added to C++ would have been helpful for those situations and would enable portability.

    0 讨论(0)
  • 2021-02-05 19:50

    Cleanup functions, themselves, are thoroughly lame. They have low cohesion, in that they are expected to perform a series of activities only related in when they happen. They have high coupling, in that they need to have their internals modified when the functions that actually do something are changed. Because of this, they're error-prone.

    The try...finally construct is a framework for cleanup functions. It is a language-encouraged way to write lousy code. Moreover, since it encourages writing the same cleanup code over and over, it undermines the DRY principle.

    The C++ way is far preferable for these purposes. The cleanup code for a resource is written precisely once, in the destructor. It's in the same place as the rest of the code for that resource, and therefore has good cohesiveness. The cleanup code doesn't have to be put into unrelated modules, and therefore this cuts down on coupling. It is written precisely once, when well designed.

    Moreover, the C++ way is much more uniform. C++, with the smart pointer additions, handles all sorts of resources in the same way, while Java handles memory well and provides inadequate constructs to release other resources.

    There are plenty of problems with C++, but this isn't one of them. There are ways in which Java is better than C++, but this isn't one of them.

    Java would be much better off with a way to implement RAII instead of try...finally.

    0 讨论(0)
  • 2021-02-05 19:50

    Regarding your addendum-edit, yes closures are being considered for C++0x. They can be used with RAII scoped guards to provide an easy to use solution, check Pizer's weblog. They can also be used to mimic try-finally, see this answer ; but is this really a good idea ? .

    0 讨论(0)
提交回复
热议问题