Sdílet prostřednictvím


finally + destructors = best of both worlds

On comp.lang.c++.moderated, "Howard" <alicebt@hotmail.com> wrote:

You know how we use try { } catch() {} in C++?  Well, both Delphi and C# (which was developed with the aid of one of Delphi's main original developers) have another construct, which is "try {} finally {}".  The idea is that no matter what else takes place, no matter whether it's a return statement or an exception or simply dropping on out of the try block normally, the finally clause will ALWAYS be executed.  (Short of an abnormal program termination, I would suspect.)  It might be used like this, for example (if it were available in C++):

Resource* pRes = new Resource;
try
{
  // use the allocated resource somehow
}
finally
{
  delete pRes;
}

Actually, that's valid C++/CLI code -- C++/CLI also supports finally with exactly that syntax. This is described in the current draft spec available via: https://www.msdn.microsoft.com/visualc/homepageheadlines/ecma/default.aspx . See section 16.4, and search for "finally" throughout (there are other spots that specify how it interacts with other language features like goto).

This is in addition to destructors -- as another responder, Mike Wahler, noted, you can also get a similar effect with destructors:

C++ already has this, via destructors.  When 'throw' causes a scope to be exited, all automatic objects are destroyed, and any destructors will run. ALWAYS.  So just wrap that 'new' and 'delete' in a class, create an automatic object of that type, and you have the same mechanism.

One advantage of finally is visual locality of reference -- the code is right there in the function instead of off in a different place (a destructor definition). One advantage of destructors is that RAII objects encapsulate and automatically manage ownership. In your example above, I would prefer to use a destructor (by writing auto_ptr<Resource> instead of Resource*, you don't need a finally at all). You can use either facility, of course.

To me, this is yet another case where C++ gives you the best of both worlds. Having both is better than having either alone (in languages that have only finally or only destructors). Note this is very closely related to the Dispose pattern I mentioned in a recent post, because the Dispose pattern relies on finally for things that destructors do better, so it's good to have destructors. For other things, finally is better, so it's good to have finally.

Herb

Comments

  • Anonymous
    September 03, 2004
    Does the C++/CLI try/finally have any usage restrictions like the old __try/__finally?
  • Anonymous
    September 03, 2004
    Some, but fewer; and post-Whidbey we intend to remove those restrictions.

    Briefly, with try/catch you can catch anything. If your catch blocks explicitly catch exceptions only of managed types, then you can also use finally and exception filters.

    Post-Whidbey, we intend to allow finally and exception filters on all try/catch blocks seamlessly, including try/catch blocks that catch native exceptions. Enabling that just unfortunately didn't make the bar for the Whidbey release, is all.
  • Anonymous
    September 06, 2004
    I think this is close enough to try/finally in C++:

    Resource* pRes = new Resource;
    try
    {
    // use the allocated resource somehow
    }
    catch (...)
    {
    delete pRes;
    throw;
    }