views:

371

answers:

9

Hi,

I have a simple question hopefully - how does one free memory which was allocated in the try block when the exception occurs? Consider the following code:

try
 {
  char *heap = new char [50];
        //let exception occur here
  delete[] heap;
 }
 catch (...)
 {
  cout << "Error, leaving function now";
  //delete[] heap; doesn't work of course, heap is unknown to compiler
  return 1;
 }

How can I free memory after the heap was allocated and exception occurred before calling delete[] heap? Is there a rule not to allocate memory on heap in these try .. catch blocks?

Thanks

+21  A: 

Study the RAII idiom (Resource Acquisition Is Initialization)! See e.g. the Wikipedia article on RAII.

RAII is just the general idea. It is employed e.g. in the C++ standard library's std::auto_ptr template class, or (if I remember correctly) in Boost's boost::shared_ptr template class, which is considered by some to be superior to auto_ptr.


Very brief explanation of the RAII idiom:

Basically, it is the C++ version of try..finally blocks found in some other languages. The RAII idiom is arguably more flexible.

It works like this:

  • You write a wrapper class around your resource (e.g. memory). The destructor is responsible for freeing the resource.

  • You create, as a local (automatic) variable, an instance of your wrapper class in a scope. Once program execution leaves that scope, the object's destructor will be called, thereby releasing the resource (e.g. memory).

The important point is that it doesn't matter how the scope is exited. Even if an exception is thrown, the scope is still exited and the wrapper object's destructor is still called.


Very crude example:

// BEWARE: this is NOT a good implementation at all, but is supposed to
// give you a general idea of how RAII is supposed to work:
template <typename T>
class wrapper_around
{
  public:
    wrapper_around(T value)
        : _value(value)
    { }
    T operator *()
    {
        return _value;
    }
    virtual ~wrapper_around()
    {
        delete _value;  // <-- NOTE: this is incorrect in this particular case;
                        // if T is an array type, delete[] ought to be used
    }
  private:
    T _value;
};
// ...

{
    wrapper_around<char*> heap( new char[50] );
    // ... do something ...

    // no matter how the { } scope in which heap is defined is exited,
    // if heap has a destructor, it will get called when the scope is left.
    // Therefore, delegate the responsibility of managing your allocated
    // memory to the 'wrapper_around' template class.
    // there are already existing implementations, e.g. 'boost::shared_ptr'!
}
stakx
+1 for pointing to the concept (RAII) first and the implementation (smart pointers, whatever) then.
Eduardo León
Thank you for the excellent though. Im pretty new to C++ and for the time being I'd rather use my own implementation instead of blindly using already-made solutions which I have no idea what they do in fact. I'll try to implement it myself firstly and then check what RAII is and how to use it properly. Thank you once again.
Kra
Fix your example so it calls delete []
Martin York
@Kra: You are welcome. If you implement your own version, you will also need to think about what happens when you copy-construct (or duplicate via `=` assignment) such a wrapper object. There will be two instances that manage the same resource, but the resource must only be freed once. -- It's not beginner's stuff, but may I suggest a good book on this topic (and others) anyway: *Exceptional C++* by Herb Sutter.
stakx
@Martin York: I added a note; Yes, it should call `delete[]` in this particular case, but not in the general case. (For example, `T` could also be just `int`.) As I wrote, my intent was *not* to give a perfect implementation, but merely to show in a basic fashion how RAII is supposed to work.
stakx
@Kra: I would advise just the opposite. The earlier you get used to the available libraries the better. Note also that if you are not experienced you are prone to make mistakes in the implementation, and it will most probably be hard to debug where the problems are. If you used tested libraries you will be able to focus on your particular code problems. Later, once you get more experienced you can play implementing your own or try to understand the implementations in the libraries and the rationale for each decisions.
David Rodríguez - dribeas
@Martin York, @stakx: This is why Boost has different classes for smart pointers and smart arrays.
Eduardo León
+5  A: 

Either move the new before the try, so that the pointer is still in scope, or use a smart pointer like shared_ptr or unique_ptr (in a pinch, auto_ptr, but it has issues) that will clean up for you on exit. Exceptions are a huge reason why smart pointers are important.

Kate Gregory
In this situation auto_ptr<> is perfect (if smart pointers were the solution). But what about other methods like a dynamic container?
Martin York
Would have voted +1, if you had mentioned the general concept first: RAII. Still, nice answer.
Eduardo León
@Martin York: You can't use `auto_ptr` with an array, can you?
Fred Larson
Would you care to explain the issues that auto_ptr has? The auto_ptr seems well suited to this particular example.
A. Levy
Ah yes, that is correct. You aren't supposed to use auto_ptr because it calls `delete` instead of `delete[]`. Though some compilers will let you get away with it...not recommended practice though.
A. Levy
auto_ptr is perfect here. People just auto_hate on it because they heard once that it "has issues."
John Dibling
boost::scoped_arr or boost::shared_arr would work for this case.
Fred Larson
@John Dibling: auto_ptr would result in undefined behavior in this case since it uses `delete` instead of `delete[]`, as A. Levy said (and I implied earlier).
Fred Larson
@Fred Larson: Yes std::auto_ptr like the other smart pointers mentioned will not work (without help) on dynamic arrays. My comment is based on using it in this context of this answer and not the question as a whole. If you need an array like structure you should be using some form of container (like std::vector or the new array available in tr1).
Martin York
A: 

The easiest way would be to declare the variable before the try block, and then just do the initialization within the block.

tloach
A: 

Agreed with the answers on RAII and smart pointers.

However, if you insist, you can do this:

try { dangerous operations } 
catch { cleanup; throw; }
Pavel Radzivilovsky
How do you clean up something that is not in scope? Why are you re-throwing the exception?
John Dibling
(And you forget to clean up in case that all dangerous operations succeed.)
stakx
of course, it should be in scope. rethrowing exception is to enable us catch any exception, without killing the error information. The common pattern above is equivalent to 'finally' in SEH and in other programming languages.
Pavel Radzivilovsky
So the OP asked "how does one free memory which was allocated in the try block when the exception occurs?" and you responded basically by saying "by freeing memory which was allocated in the try block when the exception occurs." How useless.
John Dibling
+6  A: 

OK mister Java programmer:

try
{
    // Exception safe dynamic allocation of a block of memory.
    std::vector<char>  heap(50);

    // DO STUFF

    // Note in C++ we use stack based objects and their constructor/destructor
    // TO give a deterministic cleanup, even in the presence of exceptions.
    //
    // Look up RAII (bad name for a fantastic concept).
}
catch (...)
{
    cout << "Error, leaving function now";
    return 1;  // Though why you want to return when you have not fixed the exception is
               // slightly strange. Did you want to rethrow?
}
Martin York
Nope I didn't want to rethrow. I have functions which return 0 when everything is fine (contains also handled exceptions in try block) and when something goes terribly wrong (like unhandled exception) function returns 1. Its up to the caller to handle the situation afterwards. Thanks for the RAII point.
Kra
@Kra: Any reason why you want to translate from exception to return value for error handling?
David Rodríguez - dribeas
+1  A: 

The 'correct' answer is RAII and shared_ptr as mentioned above, but just to be complete: in your example, you could substitute

char *heap = new char [50];

with

char *stack = static_cast<char*>( alloca(50) );

alloca is almost identical to malloc, except that it alocs memory on the stack instead of the heap, so no matter how you function exits (throwing or now), the memory will be reclaimed, and no deletes or frees are necessary.

Gianni
If you're going down that road, just do `char heap[50];`. The only reason to use `alloca` is if you don't know the size at compile time, and you're not too bothered about portability.
Mike Seymour
+1  A: 

I have to agree with all those that said RAII, however, I'd use Boost's shared_array instead of an auto_ptr. Auto pointer calls delete and not 'delete []' which will cause leaks with an array.

wheaties
+3  A: 

The general answer is use RAII.

However, its possible to solve it by moving the variable out of the try{} scope:

char * heap = NULL;
try {
  heap = new char [50];
  ... stuff ...
} catch (...) {
  if (heap) {
    delete[] heap;
    heap = NULL;
  }
  ... however you want to handle the exception: rethrow, return, etc ...
}

Please note that I'm not recommending this as a good practice - but more of a down & dirty to be used only if you really know the risks and are still willing to take them. Personally, I'd use RAII.

Peace

Mordachai
RAII: Resource Aquisition Is Initialization. RIAA is a different more evil entity. :)
Zan Lynx
I think maybe you mean RAII. I don't think getting the music industry involved will help exception safety :)
Peter
Lolls - thanks guys - obviously when I think of evil, RIAA springs to mind! ;)
Mordachai
A: 

Hello,

Yes - if you are considering the simplicity - pointer that is outer to your try block is the solution.

Regards

opal