Is there a favored idiom for mimicing Java's t

2019-03-15 07:39发布

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?

Is there a favored idiom that mimics Java's try/finally?

Am also bothered that C++ doesn't have an ultimate super type for all possible exceptions that could be thrown - like Java's Throwable class.

I can write:

try {
  // do something
} catch(...) {
  // alas, can't examine the exception
  // can only do cleanup code and perhaps rethrow, ala:
  throw;
}

ADDENDUM EDIT:

I ended up accepting the answer that had the most up votes, i.e., use destructors to do cleanup. Of course, from my own comments, it is clear I don't entirely agree with that. However, C++ is what it is and so in the application endeavor I have in mind, I'm going to more or less strive to adhere to common community practice. I'll use template classes to wrap resources that don't already have a class destructor (i.e., C library resources), thus bestowing on them destructor semantics.

NEW ADDENDUM EDIT:

Hmm, instead of finally then a closure feature perhaps? A closure combined with ScopeGuard approach (see one of the answers below) would be a way to accomplish cleanup with arbitrary actions and access to the cleanup code's outer scope context. Cleanup could be done in the idiom fashion that is seen in Ruby programming where they supply cleanup blocks when a resource is being opened. Isn't a closure feature being considered for C++?

15条回答
贪生不怕死
2楼-- · 2019-03-15 08:19

My $.02. I've been programming in managed languages like C# and Java for years, but was forced to make the switch to C++ for the purposes of speed. At first I couldn't believe how I had to write out the method signature twice in the header file and then the cpp file, and I didn't like how there was no finally block, and no garbage collection meant tracking memory leaks everywhere - gosh I didn't like it at all!

However, as I said I was forced to use C++. So I was forced to seriously learn it, and now I've finally understood all the programming idioms like RAII and I get all the subtleties of the language and such. It took me a while but now I see just how different of a language it is compared to C# or Java.

These days I think C++ is the best language there is! Yes, I can understand that there is a little more what I call 'chaff' sometimes (seemingly unnecessary stuff to write), but after actually using the language seriously, I've changed my mind about it completely.

I used to have memory leaks all the time. I used to write all my code into the .h file because I hated the separation of code, I couldn't understand why they would do that! And I used to always end up with stupid cyclic include dependencies, and heaps more. I was really hung up on C# or Java, to me C++ was a huge step down. These days I get it. I almost never have memory leaks, I enjoy the separation of interface and implementation, and I don't have problems with cycle dependencies anymore.

And I don't miss the finally block either. To be honest, my opinion is that these C++ programmers that you talk about writing repeated cleanup actions in catch blocks just sound to me like they're just bad C++ programmers. I mean, it doesn't look like any of the other C++ programmers in this thread are having any of the problems you mention. RAII really does make finally redundant, and if anything, it's less work. You write one destructor and then you never have to write another finally ever! Well at least for that type.

With respect, what I think is going on is you're just used to Java now, just like I had been.

查看更多
Anthone
3楼-- · 2019-03-15 08:20

Thought I'd add my own solution to this - a kind of smart pointer wrapper for when you have to deal with non-RAII types.

Used like this:

Finaliser< IMAPITable, Releaser > contentsTable;
// now contentsTable can be used as if it were of type IMAPITable*,
// but will be automatically released when it goes out of scope.

So here's the implementation of Finaliser:

/*  Finaliser
    Wrap an object and run some action on it when it runs out of scope.
    (A kind of 'finally.')
    * T: type of wrapped object.
    * R: type of a 'releaser' (class providing static void release( T* object )). */
template< class T, class R >
class Finaliser
{
private:
    T* object_;

public:
    explicit Finaliser( T* object = NULL )
    {
        object_ = object;
    }

    ~Finaliser() throw()
    {
        release();
    }

    Finaliser< T, R >& operator=( T* object )
    {
        if (object_ != object && object_ != NULL)
        {
            release();
        }
        object_ = object;

        return *this;
    }

    T* operator->() const
    {
        return object_;
    }

    T** operator&()
    {
        return &object_;
    }

    operator T*()
    {
        return object_;
    }

private:
    void release() throw()
    {
        R::release< T >( object_ );
    }
};

... and here's Releaser:

/*  Releaser
    Calls Release() on the object (for use with Finaliser). */
class Releaser
{
public:
    template< class T > static void release( T* object )
    {
        if (object != NULL)
        {
            object->Release();
        }
    }
};

I have a few different kinds of releaser like this, including one for free() and one for CloseHandle().

查看更多
做自己的国王
4楼-- · 2019-03-15 08:21

Using C++11 with its lambda expressions, I've recently started using the following code to mimic finally:

class FinallyGuard {
private:
  std::function<void()> f_;
public:
  FinallyGuard(std::function<void()> f) : f_(f) { }
  ~FinallyGuard() { f_(); }
};

void foo() {
  // Code before the try/finally goes here
  { // Open a new scope for the try/finally
    FinallyGuard signalEndGuard([&]{
      // Code for the finally block goes here
    });
    // Code for the try block goes here
  } // End scope, will call destructor of FinallyGuard
  // Code after the try/finally goes here
}

The FinallyGuard is an object which is constructed with a callable function-like argument, preferrably a lambda expression. It will simply remember that function until its destructor is called, which is the case when the object goes out of scope, either due to normal control flow or due to stack unwinding during exception handling. In both cases, the destructor will call the function, thus executing the code in question.

It is a bit strange that you have to write the code for the finally before the code for the try block, but apart from that it actually feels a lot like a genuine try/finally from Java. I guess one should not abuse this for situations where an object with its own proper destructor would be more appropriate, but there are cases where I consider this approach above more suitable. I discussed one such scenario in this question.

As far as I understand things, std::function<void()> will use some pointer indirection and at least one virtual function call to perform its type erasure, so there will be a performance overhead. Don't use this technique in a tight loop where performance is critical. In those cases, a specialized object whose destructor does one thing only would be more appropriate.

查看更多
我想做一个坏孩纸
5楼-- · 2019-03-15 08:22

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. ;)

查看更多
一纸荒年 Trace。
6楼-- · 2019-03-15 08:25

To avoid having to define a wrapper class for every releasable resource, you may be interested in ScopeGuard (http://www.ddj.com/cpp/184403758) which allows one to create "cleaners" on the fly.

For example:

FILE* fp = SomeExternalFunction();
// Will automatically call fclose(fp) when going out of scope
ScopeGuard file_guard = MakeGuard(fclose, fp);
查看更多
男人必须洒脱
7楼-- · 2019-03-15 08:25

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.

查看更多
登录 后发表回答