c++, placement delete upon exception
Hi All!
I'm having this really nasty problem regarding placement new and delete; more specific: with cleaning up the memory when an exception is thrown in the constructor of a class.
Here's a test class declaring it's own placement new and delete versions:
Code:
class memtest
{
public:
//non-placement overloads omitted
static void* operator new( size_t, void* p )
{ return p; }
static void operator delete( void* p, void* )
{ ( (memtest*) p )->~memtest(); free( p ); }
static void* operator new [] ( size_t, void* p )
{ return p; }
static void operator delete [] ( void* p, void* )
{ /* and now ? */ }
memtest() :
a( new int )
{
throw( 0 );
}
~memtest()
{
delete a;
}
};
Now, working with single objects is no big problem, as the compiler generates code to call the corresponding placement delete when the exception is thrown:
Code:
int main()
{
try
{
memtest* pMem = (memtest*) malloc( sizeof( memtest ) ); //allocate mem
new( pMem ) memtest; //calls placement new and new handler
delete pMem; //won't be reached due to constructor failure
}
catch(...)
{
//no mem leak, compiler generated code called memtest::operator delete( void* p, void* )
}
}
The problem arises when using placement new [] and delete []: the compiler will also correctly generate code to call operator delete [] ( void*, void* ), but what am I supposed to put in there?
Code:
int main()
{
try
{
memtest* pMem = (memtest*) malloc( sizeof( memtest ) * 2 + sizeof( size_t ) ); //allocate mem
pMem = new( pMem ) memtest[ 2 ]; //calls placement new[] and iterates it with new handler
delete [] pMem; //won't be reached
}
catch(...)
{
//memleak or crash ??
}
}
If I would use
Code:
operator delete [] ( void* p, void* )
{
free( p );
}
it will free the allocated memory, but memtest's destructor won't get called, resulting in memtest::a leaking.
If I'd put something like this:
Code:
operator delete [] ( void* p, void* )
{
size_t* pSecretSize = ((size_t*) p) - 1; //new [] stores size in front of array
for( size_t i = 0 ; i < *pSecretSize ; ++i )
(&(mem)[i])->~memtest(); //call destructor
free( pSecretSize ) ; //free
}
then it will call the destructor for all elements in the array, and afterwards nicely free the memory.
However, when this gets called from within the constructor, there are two problems:
- at first, p will still point to the beginning of the array memory, not to the beginning of the array, so the pSecretSize stuff is completely wrong
- second, even if the pointer passed pointed to the beginning of the array, the code crashes the program since it calls destructors of objects that are not constructed.
Am I missing something, or is it just impossible to not have a leak when using the vector new and delete ?
Re: c++, placement delete upon exception
- Why are you mixing malloc/free and new/delete?
- You should never explicitly call the destructor of a class..
- Is there any specific reason for writing your own complex new/delete operators?
Am I missing something here? Why don't you simply cleanup the members before throwing an exception? Or use auto/smart pointer or reference counting techniques.
How should I handle resources if my constructors may throw exceptions?
It should be as simple as this to handle the cleanup upon exception:
Code:
Something* some = 0;
try {
some = new Something[n];
}
catch (...)
{
delete[] some;
some = 0;
}
Re: c++, placement delete upon exception
Quote:
Originally Posted by
stinos
it will free the allocated memory, but memtest's destructor won't get called, resulting in memtest::a leaking.
Are you sure?
Code:
#include <cstddef>
#include <iostream>
class Test
{
public:
Test()
{
static int next_id = 0;
id = ++next_id;
std::cerr << __PRETTY_FUNCTION__ << '(' << id << ')' << std::endl;
if( id == 4 ) throw 1;
}
~Test()
{
std::cerr << __PRETTY_FUNCTION__ << '(' << id << ')' << std::endl;
}
static void * operator new[]( size_t, Test *pool )
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
return pool;
}
static void operator delete []( void *, Test * )
{
std::cerr << __PRETTY_FUNCTION__ << std::endl;
}
private:
int id;
};
int main()
{
size_t count = 5;
char buffer[ sizeof( size_t ) + count * sizeof( Test ) ];
try {
Test *t = new( (Test*)buffer ) Test[ count ];
}
catch(...)
{
}
std::cerr << "done" << std::endl;
return 0;
}
Output:
Quote:
$ ./a.out
static void* Test::operator new [](unsigned int, Test*)
Test::Test()(1)
Test::Test()(2)
Test::Test()(3)
Test::Test()(4)
Test::~Test()(3)
Test::~Test()(2)
Test::~Test()(1)
static void Test::operator delete [](void*, Test*)
done
It looks like it behaves just like ordinary new[] (note that the destructor won't be called for the object that threw the exception, as that object isn't fully constructed).
You can find some interesting information about placement new here: http://www.parashift.com/c++-faq-lit...html#faq-11.14 and of course in Lippman's C++ Primer.
Re: c++, placement delete upon exception
fisrt, thanks for the replies. Jacek, your example is quite interesting.
second, as I reread my post now I slapped myself in the face a few times, I was obviously not thinking clearly at the time of writing it, it's complete bollocks (very likely due to coding 18 hours in row combined with heavy drugs I have to take due to a recent accident).
Quote:
Originally Posted by
jpn
- Why are you mixing malloc/free and new/delete?
for the reason above I fear
Quote:
Originally Posted by
jpn
- You should never explicitly call the destructor of a class..
possibly, but it's the only way to destruct something when using placement new, I think.
Quote:
Originally Posted by
jpn
- Is there any specific reason for writing your own complex new/delete operators?
there are two: first, I'm quite addicted to learning all details, writing the operators myself learned me a lot about new/delete, before I didn't even know you can actually have an unlimited number of overloads of them, next to the standard eight. After this post everything became quite clear..
second, I'm using custom allocators all over the place so I have to overwrite them in order to let them use myallocator::malloc etc
btw I found http://msdn.microsoft.com/library/de...deep080599.asp to be very interesting and in-depth too.
Re: c++, placement delete upon exception
Jacek, just tested your code and it works as expected indeed; however, it didn't compile at first:
- line 38 generates "error: expression must have a constant value" with cl (microsoft) and cl6x (texas instruments); I didn't know it was ok for gcc?
- __PRETTY_FUNCTION__ is g++ specific
anyway, thanks again!
Re: c++, placement delete upon exception
Quote:
Originally Posted by
stinos
- line 38 generates "error: expression must have a constant value" with cl (microsoft) and cl6x (texas instruments); I didn't know it was ok for gcc?
Code:
size_t count = 5;
char buffer[ sizeof( size_t ) + count * sizeof( Test ) ];
That looks like a very constant expression though. Naughty ms/ti compilers.
*edit* ah no, count ain't const.
Re: c++, placement delete upon exception
The discussion about mixing new and malloc reminded me of when we recently had to port an existing application to a new platform with different compilers than used before. Building the app wasn't a problem, but running it was a nightmare because it crashed randomly. So after digging deep into the code, which was written in different languages and spread over several libraries, we found out that the problem indeed was a new/malloc mix. More details from a C++ expert with name Stroustrup can be found on his website:
http://www.research.att.com/~bs/bs_faq2.html#realloc