views:

649

answers:

6
+1  Q: 

Finally in C++

Is this a good way to implement a Finally-like behavior in standard C++? (Without special pointers)

class Exception : public Exception
    { public: virtual bool isException() { return true; } };

class NoException : public Exception
    { public: bool isException() { return false; } };


Object *myObject = 0;

try
{
  // OBJECT CREATION AND PROCESSING
  try
  {
    myObject = new Object();

    // Do something with myObject.
  }

  // EXCEPTION HANDLING
  catch (Exception &e)
  {
    // When there is an excepion, handle or throw,
    // else NoException will be thrown.
  }

  throw NoException();
}

// CLEAN UP
catch (Exception &e)
{
  delete myObject;

  if (e.isException()) throw e;
}
  1. No exception thrown by object -> NoException -> Object cleaned up
  2. Exception thrown by object -> Handled -> NoException -> Object cleaned up
  3. Exception thrown by object -> Thrown -> Exception -> Object cleaned up -> Thrown
+18  A: 

The standard answer is to use some variant of resource-allocation-is-initialization abbreviated RAII. Basically you construct a variable that has the same scope as the block that would be inside the block before the finally, then do the work in the finally block inside the objects destructor.

try {
   // Some work
}
finally {
   // Cleanup code
}

becomes

class Cleanup
{
public:
    ~Cleanup()
    {
        // Cleanup code
    }
}

Cleanup cleanupObj;

// Some work.

This looks terribly inconvenient, but usually there's a pre-existing object that will do the clean up for you. In your case, it looks like you want to destruct the object in the finally block, which means a smart or auto pointer will do what you want:

std::auto_ptr<Object> obj(new Object());

No matter which exceptions are thrown, the object will be destructed. Getting back to RAII, in this case the resource allocation is allocating the memory for the Object and constructing it and the initialization is the initialization of the auto_ptr.

David Norman
With the auto_ptr the Object destructor will always be called. As part of processing the exception, all objects constructed on the stack are themselves destructed. In this case, that means that the auto_ptr destructor will be called, which will in turn destruct the object.
David Norman
The key point to note is that C++ destructors for stack allocated objects ALWAYS run when the object goes out of scope. This means that they can do anything a finally block can do.
Eclipse
+1 because David Norman is correct :)
Robert Gould
Yes, Josh's point is a crucial feature of C++. The usual way to think of this is that when an exception is thrown, the stack is "unwound". Effectively the implementation looks up the call stack one step at a time, calling destructors for stack variables and looking for catch clauses.
Steve Jessop
... in practice it's a little less straightforward than that, because the compiler may have fiendishly optimised the code, but that's the essential effect.
Steve Jessop
+2  A: 

Assuming you are looking to delete the pointer myObject and avoid memory leaks, your code can still fail to do this if there is a "return" statement in the code where you say // Do something with myObject. (I am assuming real code would be here)

RAII techniques have the relevant action that is equivalent to a "finally" block, in a particular object's destructor:

class ResourceNeedingCleanup
{
  private:
    void cleanup(); // action to run at end
  public:
    ResourceNeedingCleanup( /*args here*/) {}
    ~ResourceNeedingCleanup() { cleanup(); }  

    void MethodThatMightThrowException();
};

typedef boost::shared_ptr<ResourceNeedingCleanup> ResourceNeedingCleanupPtr;
// ref-counted smart pointer


class SomeObjectThatMightKeepReferencesToResources
{
   ResourceNeedingCleanupPtr pR;

   void maybeSaveACopy(ResourceNeedingCleanupPtr& p)
   {
      if ( /* some condition is met */ )
         pR = p;
   }
};

// somewhere else in the code:
void MyFunction(SomeObjectThatMightKeepReferencesToResources& O)
{
   ResourceNeedingCleanup R1( /*parameters*/) ;
   shared_ptr<ResourceNeedingCleanup> pR2 = 
        new ResourceNeedingCleanup( /*parameters*/ );
   try
   {
      R1.MethodThatMightThrowException();
      pR2->MethodThatMightThrowException();
      O->maybeSaveACopy(pR2);
   }
   catch ( /* something */ )
   {
      /* something */
   }

   // when we exit this block, R1 goes out of scope and executes its destructor
   // which calls cleanup() whether or not an exception is thrown.
   // pR2 goes out of scope. This is a shared reference-counted pointer. 
   // If O does not save a copy of pR2, then pR2 will be deleted automatically
   // at this point. Otherwise, pR2 will be deleted automatically whenever
   // O's destructor is called or O releases its ownership of pR2 and the
   // reference count goes to zero.
}

I think I have the semantics correct; I haven't used shared_ptr much myself, but I prefer it to auto_ptr<> -- a pointer to an object can only be "owned" by one auto_ptr<>. I've used COM's CComPtr and a variant of it that I've written myself for "regular" (non-COM) objects that is similar to shared_ptr<> but has Attach() and Detach() for transfer of pointers from one smart pointer to another.

Jason S
Please note that I'm using pointers...
TomWij
OK, now I get it... It would have helped if you explained what you were looking for in general, before just listing code.
Jason S
Edited accordingly.
Jason S
+7  A: 

No. The Standard way to build a finally like way is to separate the concerns (http://en.wikipedia.org/wiki/Separation_of_concerns) and make objects that are used within the try block automatically release resources in their destructor (called "Scope Bound Resource Management"). Since destructors run deterministically, unlike in Java, you can rely on them to clean up safely. This way the objects that aquired the resource will also clean up the resource.

One way that is special is dynamic memory allocation. Since you are the one aquiring the resource, you have to clean up again. Here, smart pointers can be used.

try {
    // auto_ptr will release the memory safely upon an exception or normal 
    // flow out of the block. Notice we use the "const auto_ptr idiom".
    // http://www.gotw.ca/publications/using_auto_ptr_effectively.htm
    std::auto_ptr<A> const aptr(new A);
} 
// catch...
Johannes Schaub - litb
Looking for a normal pointer solution.
TomWij
Don't look for a "normal pointer solution", if by that you mean you want to avoid RAII. If you're after a "good way" to do it, then in C++ that's RAII. So either use a smart pointer from the library, or if you can't do that then write your own.
Steve Jessop
+3  A: 

If for some strange reason you don't have access to the standard libraries, then it's very easy to implement as much as you need of a smart pointer type to handle the resource. It may look a little verbose, but it's less code than those nested try/catch blocks, and you only have to define this template once ever, instead of once per resource that needs management:

template<typename T>
struct MyDeletable {
    explicit MyDeletable(T *ptr) : ptr_(ptr) { }
    ~MyDeleteable() { delete ptr_; }
private:
    T *ptr_;
    MyDeletable(const MyDeletable &);
    MyDeletable &operator=(const MyDeletable &);
};

void myfunction() {
    // it's generally recommended that these two be done on one line.
    // But it's possible to overdo that, and accidentally write
    // exception-unsafe code if there are multiple parameters involved.
    // So by all means make it a one-liner, but never forget that there are
    // two distinct steps, and the second one must be nothrow.
    Object *myObject = new Object();
    MyDeletable<Object> deleter(myObject);

    // do something with my object

    return;
}

Of course, if you do this and then use RAII in the rest of your code, you'll eventually end up needing all the features of the standard and boost smart pointer types. But this is a start, and does what I think you want.

The try ... catch approach probably won't work well in the face of maintenance programming. The CLEAN UP block isn't guaranteed to be executed: for example if the "do something" code returns early, or somehow throws something which is not an Exception. On the other hand, the destructor of "deleter" in my code is guaranteed to be executed in both those cases (although not if the program terminates).

Steve Jessop
This was what I'm looking for, very effective.
TomWij
This is a terrible solution compared to auto_ptr (and auto_ptr isn't a good solution). By separating the deleter from the object it becomes easy to end up in situations where an object that should not get deleted ends up attached to a deleter by accident. This will cause more bugs than it prevents.
jmucchiello
+2  A: 

To directly answer your question, no.

It's a clever way to implement that functionality, but it is not reliable. One way that will fail you is if your "do something" code throws an exception that is not derived from Exception. In that case, you will never delete myObject.

There's a more important issue at hand here, and that's the methodologies adopted by programmers of any particular language. The reason you're hearing about RAII is because programmers with much more experience than you or I have found that in the domain of C++ programming, that methodology is reliable. You can rely on other programmers using it and other programmers will want to rely on you using it.

Shmoopty
"programmers with much more experience than you or I" - good way of putting it, although note that they have also convinced us with the power of reason that they're right, it's not purely an appeal to authority ;-)
Steve Jessop
A: 

My advice is: don't try to emulate the behaviour of a try-finally clause in C++. Just use RAII instead. You'll live happier.

Marc