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:
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;
}
};
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;
}
};
To copy to clipboard, switch view to plain text mode
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:
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* )
}
}
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* )
}
}
To copy to clipboard, switch view to plain text mode
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?
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 ??
}
}
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 ??
}
}
To copy to clipboard, switch view to plain text mode
If I would use
operator delete [] ( void* p, void* )
{
free( p );
}
operator delete [] ( void* p, void* )
{
free( p );
}
To copy to clipboard, switch view to plain text mode
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:
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
}
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
}
To copy to clipboard, switch view to plain text mode
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 ?
Bookmarks