-
Placement new?
OK, so next step is to use placement new. I'm not sure on how to use it. I've never done it before.
I've done a very simple test to get the hang of it:
Code:
void* __cdecl operator new(std::size_t size, LPCSTR strLine, int nLine)
{
//::operator new(size, strLine, nLine);
New::CMemoryRange* p = (New::CMemoryRange*)VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
new (p) New::CMemoryRange;
return 0;
}
Initiated by:
Code:
new New::CMemoryRange;
Is this the right way to use placement new? Because all I get is:
Code:
inline void *__CRTDECL operator new(size_t, void *_Where)
{return (_Where); }
In essence, it does nothing but return the same argument I passed in!
The basic idea here is to allocate memory using VirtualAlloc, then use placement new to actually construct the object, but I'm not sure how to do it.
-
The default placement new does exactly that, yeah. But other placement new forms can do different things.
What you want is a pool allocator. That's different from placement new.
-
Then how do I go about constructing the object once it's allocated?
Using templates to deduce the correct type and call the constructor is obviously out of question since that would generate a different new...
-
I'm not sure I understand. If you have a pointer to some memory, you can use the normal placement new to create an object there.
-
But how do I do it?
How do I fix the above code sample to work? How do I make new construct the object and call the constructor?
As I mentioned, I don't really know how placement new works. Never used it before.
If it's placement new or whatever else method, I care not, but somehow the constructor must be called so that objects are properly constructed. Simply allocating memory won't do.
On a side note, there is only one delete, one that calls the destructor and frees the memory?
-
The function you override, operator new(), is responsible only for allocating memory. Actually calling the constructor when the new operator is used is the compiler's business.
I suggest you look into Stroustrup's C++ Programming Language or Meyers's Effective C++ series to really learn how the new operator and operator new work and interact.
-
Ah, books. Great learning resources they are... if you can find them somewhere locally, that is.
But yes, you're right of course... the compiler will call the constructor after the call to operator new.
Hmmm. It might just be possible to get this show on the road!
-
that isn't overriding placement new, it's overriding New::CMemoryRange::operator new. so for example:
Code:
#include <cstdlib>
#include <iostream>
using namespace std;
struct test
{
test( void )
{
cout << "test( ) at address " << ( int * )this << endl;
}
static
void *
allocate( size_t size )
{
void * address = malloc( size );
cout << "allocating " << size << " bytes at address " << ( int * )address << endl;
return address;
}
static
void *
operator new( size_t size )
{
return allocate( size );
}
static
void *
operator new[ ]( size_t size )
{
return allocate( size );
}
static
void
deallocate( void * address )
{
cout << "deallocating at address " << ( int * )address << endl;
free( address );
}
static
void
operator delete( void * address )
{
deallocate( address );
}
static
void
operator delete[ ]( void * address )
{
deallocate( address );
}
~test( void )
{
cout << "~test( ) at address " << ( int * )this << endl;
}
char buffer[ 1024 ];
};
int
main( void )
{
delete [ ] new test[ 4 ];
}
[edit]
added code to account for arrays. also, I may be wrong about whether this is in fact overloading placement new. I'll look into it...
[/edit]
-
I'm not sure if this is completely accurate, but it should be pretty close...
Code:
#include <cstdlib>
#include <iostream>
using namespace std;
/*
wrappers for invoking constructors and
destructors 'in place' on raw memory
*/
template < typename Type >
inline
Type &
constructor( Type & object )
{
return *new( &object ) Type( );
}
template < typename Type >
inline
Type &
constructor( Type & object, Type const & value )
{
return *new( &object ) Type( value );
}
template < typename Type >
inline
Type *
constructor( Type * array, size_t size )
{
return new( array ) Type[ size ];
}
template < typename Type >
inline
void
destructor( Type & object )
{
object.~Type( );
}
template < typename Type >
inline
void
destructor( Type * array, size_t size )
{
for( size_t index = 0; index < size; index++ )
destructor( array[ index ] );
}
/*
overriding class-specific new/delete
note: these *must* not throw exceptions
*/
struct special
{
special( void )
{
cout << "special::special( ) at address " << ( int * )this << endl;
}
static
void *
allocate( size_t size )
{
void * address = malloc( size );
cout << "special::allocate " << size << " bytes at address " << ( int * )address << endl;
return address;
}
static
void *
operator new( size_t size )
{
return allocate( size );
}
static
void *
operator new[ ]( size_t size )
{
return allocate( size );
}
/*
new/new[] for 'in place' construction. note: the second parameter
can be overloaded for special types (such as pool allocators)
*/
static
void *
operator new( size_t size, special * address )
{
cout << "(in place) special::operator new[ ] at address " << ( int * )address << endl;
return address;
}
static
void *
operator new[ ]( size_t size, special * address )
{
/*
the compiler seems to think we need extra bytes
for book keeping, as if we were allocating memory
(why??). is this a portable approach?
*/
size_t const array_book_keeping_bytes = 4;
cout << "(in place) special::operator new[ ] at address " << ( int * )address << endl;
return ( char * )address - array_book_keeping_bytes;
}
static
void
deallocate( void * address )
{
cout << "special::deallocate at address " << ( int * )address << endl;
free( address );
}
static
void
operator delete( void * address )
{
deallocate( address );
}
static
void
operator delete[ ]( void * address )
{
deallocate( address );
}
~special( void )
{
cout << "special::~special( ) at address " << ( int * )this << endl;
}
char buffer[ 1024 ];
};
struct standard
{
standard( void )
{
cout << "standard::standard( ) at address " << ( int * )this << endl;
}
~standard( void )
{
cout << "standard::~standard( ) at address " << ( int * )this << endl;
}
char buffer[ 1024 ];
};
/*
overriding global new/delete
*/
void *
allocate( size_t size ) throw( std::bad_alloc )
{
void * address = malloc( size );
if( address == 0 )
throw std::bad_alloc( );
cout << "global allocate " << size << " bytes at address " << ( int * )address << endl;
return address;
}
void *
operator new( size_t size ) throw( std::bad_alloc )
{
return allocate( size );
}
void *
operator new[ ]( size_t size ) throw( std::bad_alloc )
{
return allocate( size );
}
template < typename Type >
void *
operator new( size_t size, Type * address )
{
cout << "(in place) global operator new[ ] at address " << ( int * )address << endl;
return address;
}
template < typename Type >
void *
operator new[ ]( size_t size, Type * address )
{
size_t const array_book_keeping_bytes = 4;
cout << "(in place) global operator new[ ] at address " << ( int * )address << endl;
return ( char * )address - array_book_keeping_bytes;
}
void
deallocate( void * address )
{
cout << "global deallocate at address " << ( int * )address << endl;
free( address );
}
void
operator delete( void * address )
{
deallocate( address );
}
void
operator delete[ ]( void * address )
{
deallocate( address );
}
int
main( void )
{
size_t const elements = 4;
/*
use class-specific memory management
*/
delete [ ] new special[ elements ];
/*
use 'in place' constructor/destructor
*/
char special_buffer[ sizeof( special ) * elements ];
constructor( ( special * )special_buffer, elements );
destructor( ( special * )special_buffer, elements );
/*
use global memory management
*/
delete [ ] new standard[ elements ];
/*
use 'in place' constructor/destructor
*/
char standard_buffer[ sizeof( standard ) * elements ];
constructor( ( standard * )standard_buffer, elements );
destructor( ( standard * )standard_buffer, elements );
}
-
Thanks for the tip, but as CornedBee mentioned, the compiler will call the constructor/destructor for me, so I was wrong in that I needed to call placement new.
And I'm overriding global new and delete, as well, not inside a namespace.