exception in the constructor invoked by "new" operation

J

Junchen WANG

If an exception is thrown, will the memory allocated in the heap be
released? And what's the underlying mechanism ? Thanks in advance.


Best Regards,
Junchen
 
R

Ron Natalie

Junchen said:
If an exception is thrown, will the memory allocated in the heap be
released? And what's the underlying mechanism ? Thanks in advance.
Let me try to refine your question.

If an exception gets thrown during a construction of an object being
created with new, then yes, the memory for the object itself is
deallocated. Just as it was allocated with the appropriate operator
new function for that object, so will it be deleted with the
matching operator delete. This is the only time that the
so-called "placement delete" operator (one with args other
than just the pointer) is called.

It is however incumbent for the class designer to make sure that
any other subobjects or things allocated in the course of construction
are safe in the light of the exception. Of course if you design
your subobjects to be well behaved (like standard vector and
string classes or well designed user classes) this is taken
care for you. If you do your own dynamic allocation, you
need to clean up after youself.

For example

class MyClass {
char* internal;
public:
MyClass() {
internal = new char[some_non_constant];
throw "something";
}
};

MyClass* = new MyClass;

Here the memory for MyClass will be deallocated. However
the chars I new in the constructor will not be. So I
could handle the exception:

MyClass() {
try {
internal = new char[some_non_constant];
} catch(...) {
//yech
delete [] internal;
throw; // rethrow exception
}
}

But that's ugly. Of course you'll note that the rule of three
implies you also need to implement copy constructors, copy assignment
operators and destructors to handle the "internal" pointer.

Now if internal is a string, I could use std::string.
If internal was just some byte array, I could use vector.
Since vector has proper semantics for copying and destruction
I do not need to worry about it.

MyClass {
std::vector<char> internal;

public:
MyClass() : internal(some_non_constant) {
throw "something"
}
};

Voila.
 
D

Daniel Dearlove

Now if internal is a string, I could use std::string.
If internal was just some byte array, I could use vector.
Since vector has proper semantics for copying and destruction
I do not need to worry about it.

MyClass {
std::vector<char> internal;

public:
MyClass() : internal(some_non_constant) {
throw "something"
}
};

Voila.

To put the same thing in slightly different words and to slightly refine
what has just been said:

If an exception is thrown inside a constructor then the object never
really existed as you never completed the constructor. As the object
never really existed then destructor will not be called to delete any
dynamically allocated objects.

If you reach the body of the constructor then all the subobjects in the
class definition will be constructed and these will have their destructors
called when the exception is thrown.

Typically, a nice way to handle dynamically allocated objects in an
exception-safe way is by using one of std::auto_ptr, boost::share_ptr or
boost::scoped_ptr (or their array equivalents). These classes
automatically delete whatever they are pointing to when they go out of
scope. To use the parent's example:

class MyClass {
char* internal;
public:
MyClass() {
internal = new char[some_non_constant];
throw "something";
}
};

would become:

class MyClass {
boost::scoped_array<char> internal;
public:
MyClass()
: internal( new char[some_non_constant] )
{
throw "something";
}
};

and that would also be exception-safe as it MyClass::internal would be
fully constructed if it made it into the body of the constructor. When
'throw "something"' is called, the destructor of MyClass::internal will be
called (which will delete the char-array) then the memory for MyClass will
be deallocated. If creating the char-array caused the exception then the
char-array never existed and MyClass::internal never existed so there is
nothing to destruct there. Only the memory already allocated for MyClass
would need to be deleted.
 
J

James Kanze

<snip>
Typically, a nice way to handle dynamically allocated objects
in an exception-safe way is by using one of std::auto_ptr,
boost::share_ptr or boost::scoped_ptr (or their array
equivalents).

I think that's overstating it. The actual rule is to limit
(artificially, if necessary) each class to one resource that it
manages itself, and guarantee that anything that happens in the
constructor after acquisition of this resource is no throw.
Smart pointers can help, by delegating management of a resource
to a second class (the smart pointer), but they're far from the
only, or even the most frequent solution---I'd say that in well
designed code, most classes naturally won't manage more than one
resource, and the problem doesn't exist, and that using an
artificial base class to manage the resources is perhaps the
most widely used solution in the other cases. (In particular,
cases where you cannot easily encapsulate each resource in a
separate class, e.g. things like dynamic data structures.)
These classes automatically delete whatever they are pointing
to when they go out of scope.

More importantly, they do whatever is necessary to release the
resource and restore program coherence when their destructor is
called. boost::shared_ptr doesn't necessarily delete; it does
what you tell it to do.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,176
Messages
2,570,947
Members
47,499
Latest member
DewittK739

Latest Threads

Top