question on freestore management

S

spipyeah

In the C++ FAQ Lite, it is said in question 16.9:

If an exception occurs during the Fred constructor of p = new Fred(),
the C++ language guarantees that the memory sizeof(Fred) bytes that
were allocated will automagically be released back to the heap.

Naturally, any locally declared objects in the class are destroyed as
the stack is unwound. But what about objects allocated in the
constructor using new?

I totally expect the answer to be that you have to delete them
yourself. I just want to be 100% sure.

So given class Fred, the following would be necessary in Fred's
constructor, right?

class Fred
{
public:
Fred();

Joe *p;
}

Fred::Fred()
{
try
{
joe = new Joe();
// do some more stuff
}
catch( ... )
{
// must manually delete joe
delete joe;
throw;
}
}


Thank you.
 
T

tom_usenet

In the C++ FAQ Lite, it is said in question 16.9:

If an exception occurs during the Fred constructor of p = new Fred(),
the C++ language guarantees that the memory sizeof(Fred) bytes that
were allocated will automagically be released back to the heap.

Naturally, any locally declared objects in the class are destroyed as
the stack is unwound. But what about objects allocated in the
constructor using new?

I totally expect the answer to be that you have to delete them
yourself. I just want to be 100% sure.

Yup, this is the case. This is why a single class should typically
only manage one concept.
So given class Fred, the following would be necessary in Fred's
constructor, right?

class Fred
{
public:
Fred();

Joe *p;

Joe* joe;
}

Fred::Fred()
{
try
{
joe = new Joe();
// do some more stuff
}
catch( ... )
{
// must manually delete joe
delete joe;
throw;
}
}

That code is incorrect, since it might try to delete an uninitialized
value of joe (if the call to new Joe throws). Instead:

Fred::Fred()
:joe(0)
{
try
{
joe = new Joe();
// do some more stuff that might throw

}
catch( ... )
{
// must manually delete joe
delete joe;
throw;
}
}

Note that you can put try catch blocks around the initializer list.
The following complicated example demonstrates why it isn't generally
a good idea to manage more than one pointer in a class. e.g.

#include <stdexcept>
#include <iostream>

class Joe
{
public:
static int count;
Joe()
{
if ((++count % 2) == 0)
throw std::runtime_error("Joe threw");
}
};

int Joe::count = 0;

class Fred
{
public:
Joe* joe;
Joe* schmoe;
Fred(Joe* newJoe);
~Fred();
};

Fred::Fred(Joe* newJoe)
try : joe(newJoe), schmoe(new Joe)
{
try
{
throw std::runtime_error("Fred threw");
}
catch(...)
{
std::cout << "In constructor body catch\n";
delete schmoe;
throw;
}
}
catch(...)
{
std::cout << "In constructor initializer catch\n";
//schmoe initialization threw
delete joe;
throw;
}

Fred::~Fred()
{
delete joe;
delete schmoe;
}

int main()
{
try
{
Fred fred(new Joe);
}
catch(std::exception const& e)
{
std::cout << "in main " << e.what() << "\n\n";
}

try
{
Fred fred(0);
}
catch(std::exception const& e)
{
std::cout << "in main " << e.what() << "\n\n";
}
}


The smart pointer version of Fred is much simpler:

class Fred
{
public:
shared_ptr<Joe> joe;
shared_ptr<Joe> schmoe;
Fred(Joe* newJoe);
};

Fred::Fred(Joe* newJoe)
:joe(newJoe), schmoe(new Joe)
{
throw std::runtime_error("Fred threw");
}

Tom
 
S

spipyeah

Thank you for your answers Tom.
Yup, this is the case. This is why a single class should typically
only manage one concept.

Would you mind expanding a bit on this?
 

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
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top