Re: Possible memory leak when constructing an object from a new expression?
On Apr 19, 12:50 am, "Matthias Hofmann" <hofm...@anvil-soft.com>
wrote:
Anyway, the author starts out with the problem of writing the copy
assignment operator for a class and gradually develops the "ideal"
implementation for the following class:
class TFoo : public TSuperFoo {
TBar* fBar1;
TBar* fBar2;
// various method definitions go here...
}
He doesn't mention the copy-and-swap idiom, however, as the article seems to
be too old for that, but he finally comes up with the following
implementation:
TFoo&
TFoo::operator=(const TFoo& that)
{
if (this != &that) {
auto_ptr<TBar> bar1 = new TBar(*that.fBar1);
auto_ptr<TBar> bar2 = new TBar(*that.fBar2);
TSuperFoo::operator=(that);
fBar1 = bar1;
fBar2 = bar2;
These assignments don't compile as auto_ptr doesn't convert to a raw
pointer. They should probably read:
delete fBar1; fBar1 = bar1.release();
delete fBar2; fBar2 = bar2.release();
.... Unless, of course, the data members have been changed to auto_ptrs
too.
But what if, and that is my question,
std::auto_ptr throws an exception?
The constructor of std::auto_ptr is not permitted to throw an
exception. The standard is quite clear on that point. However,
supposing you'd used a shared_ptr instead (whose constructor can in
some circumstances throw exceptions), the standard then guarantees
that the constructor will temporarily catch that exception, delete the
raw pointer argument, and rethrow, thus ensuring that no memory leak
occurs.
template <class T> class X
{
T* m_ptr;
public:
X( T* ptr ) : m_ptr( ptr )
{
// Just for the fun of it.
throw 0;
}
If you do that, you will leak memory because nothing ever destroys
'ptr'. You should do something like:
if (error) {
delete ptr;
throw exception;
}
or:
try {
allocate_internal_structures(); // Might throw
} catch (...) {
delete ptr;
throw;
}
or even use a function try block:
X( T* ptr )
try : data( allocate_data() ), p(ptr) {
/* function body */
} catch (...) {
delete ptr;
}
The syntax for the latter is a bit peculiar, but if it's necessary to
handle an exception from a initialiser, this is sometimes the only way
of doing it. (This is because it's not always possible to rearrange
the function so the exception is raised in the main body of
constructor. For example, references and constant members must be
initialised in the initialiser list, and sometimes their
initialisation may throw exceptions.)
Richard Smith
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]