views:

263

answers:

4

Hi,

I was debating with some colleges about what happens when you throw an exception in a dynamically allocated class. I know that malloc gets called, and then the constructor of the class. The constructor never returns, so what happens to the malloc?

Consider the following

class B
{
    public:
     B()
     {
      cout << "B::B()" << endl;
      throw "B::exception";
     }

     ~B()
     {
      cout << "B::~B()" << endl;   
     }
};

void main()
{
    B *o = 0;
    try
    {
     o = new B;
    }

    catch(const char *)
    {
    cout << "ouch!" << endl;
    }
}

What happens to the malloced memory 'o', does it leak? Does the CRT catch the exception of the constructor and deallocate the memory?

Cheers Rich

+6  A: 

When an exception is thrown from the constructor, the memory allocated by new is released, but the destructor of class B is not called.

Cătălin Pitiș
Yes. Just to complement your answer... There's a good reason for the destructor not being called: The object was never created. Destructors are called only for objects that existed in a particular period of time. And those are the cases where the constructor completed successfully.
ltcmelo
Yes, you're right. An object is considered created (therefore destructible) only after the constructor successfuly executes.
Cătălin Pitiș
Note: The destrucotr of any fully constructed members and base class(s) are called.
Martin York
+10  A: 

A call to

new B();

resolves in two things:

  • allocating with an operator new() (either the global one or a class specific one, potentially a placement one with the syntax new (xxx) B())
  • calling the constructor.

If the constructor throw, the corresponding operator delete is called. The case where the corresponding delete is a placement one is the only case where a placement delete operator is called without the syntax ::operator delete(). delete x; or delete[] x; don't call the placement delete operators and there is no similar syntax to placement new to call them.

Note that while the destructor of B will not be called, already constructed subobjects (members or B and base classes of B) will be destructed before the call to operator delete. The constructor which isn't called is the one for B.

AProgrammer
Important addendum: The constructor for B did not run, there never was an object of that type. If there was a new() being called in the constructor of B before the exception was triggered, it is *not* being released - and you no longer have a pointer to that memory, so you cannot delete() it. *KEEP AWAY* from new() in constructors.
DevSolar
If you use RAII techniques (for example, with std::auto_ptr), new() in the constructors is not a big deal.
ltcmelo
@DevSolar: All depends on where the result of new is stored. If it is in a smart pointer, ether local to the constructor or a member of the class B or of its parents, that object will be destroyed and the memory allocated within the constructor of B but before the throwing of the exception will be freed. Obviously if you do new Foo(); throw "bar", the memory will not be freed and the Foo will not be destroyed. In a constructor or elsewhere. I'll clarify that the destructor of B isn't called.
AProgrammer
If the constructor throws the destructor is NOT (I repeat NOT) called. Only fully constructed members and any fully constructed base classes will have their constructor called.
Martin York
@DevSolar: The memmory is released automatically (note the 'New Statement' [Which calls the constructor] never completes). If you were using a smart pointer it would not help as the constructor to the smart pointer would never be entered and thus would not help in the deallocation.
Martin York
+2  A: 

In this case, your object, o, does not actually get constructed, and the memory allocated by new is freed. As such, the destructor does not get called. So you do NOT need to call:

delete o;

An interesting design pattern is RAII -- Resource Acquisition Is Initialization. In this pattern, you use a constructor to encapsulate the acquisition of a resource, and release the resource in the destructor. If the resource can not be acquired, you throw in the constructor -- much like your example. Thus if you have a valid object, you have the resource.

If the object is constructed, then you have successfully acquired the resource. This means that for the life of the object, you own the resource. When the object is deleted, the resource is released. If the object is never constructed, then you never acquired the resource. See wikipedia:

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

gnavi
Consequently, if the constructor has done _some_ resource acquisition work before whatever triggers the exception, it is that constructor's job to clean up before it throws.
Stewart
+1  A: 

From the C++ 2003 Standard 5.3.4/17 - New:

If any part of the object initialization described above terminates by throwing an exception and a suitable deallocation function can be found, the deallocation function is called to free the memory in which the object was being constructed, after which the exception continues to propagate in the context of the new-expression. If no unambiguous matching deallocation function can be found, propagating the exception does not cause the object’s memory to be freed. [Note: This is appropriate when the called allocation function does not allocate memory; otherwise, it is likely to result in a memory leak. ]

So there may or may not be a leak - it depends on whether an appropriate deallocator can be found (which is normally the case, unless operator new/delete have been overridden).In the case where there's a suitable deallocator, the compiler is responsible for wiring in a call to it if the constructor throws.

Note that this is more or less unrelated to what happens to resources acquired in the constructor, which is what my first attempt at an answer discussed - and is a question that is discussed in many FAQs, articles, and postings.

Michael Burr
This was really intended to answer the (duplicate?) question here: http://stackoverflow.com/questions/1674980/who-deletes-the-memory-allocated-during-a-new-operation-which-has-exception-in
Michael Burr