my question was really about GNU vs Visual C++ compiler but anyhow, I never back away from an intresting discussion.
My C++ may be rusty but I'm not braindead yet, so it's coming back to me reasonably fast.
First of all, just to make sure, I'm overloading
operator new[](...)
and not
operator new(...)
which would indead not make sense for my use case.
secondly, remember this thing compiles fine with the Visual C++ compiler and behaves as expected at runtime i.e. semantics are as expected and no
apparent memory leaks.
Now, concerning the behaviour of
operator new[](...)
. I don't know what it was originally meant for but I'm taking advantage of the features it offers.
This is how it works:
It requires an
size_t size
argument and optionally some extra arguments of your choice and you're expected to hand back a
void *
pointer to some block of memory.
It is invoked using something like
new(extraArgsIfAny) SomeClass[length]
not to be confused with the "placement new" operator
new(void *memory) SomeClass(arguments)
In the "placement new" variant of
operator new
the operator accepts a block of memory and initializes it using a constructor of choice. The block of memory should of course be large enough to contain the object, but apart from that no other requirements exist. I'm using this also in the body of my
operator new[]
implementation.
The size_t argument handed over to the method equals 4+sizeof(SomeClass)*length. So you need to hand back a
void *
pointer to a block of memory of that size.
Upon exit of this method, the runtime will fill up this memory block with:
* an unsigned int (the first four bytes) containing the length (# of elements) of the array.
* # of elements instances of SomeClass using the
default constructor
What I'm doing in the body of my
operator new[]
implementation is the following:
1 2 3 4 5 6 7 8
|
char *bufPtr=new char[size];
char *elPtr=bufPtr+4; //leave room for the unsigned int containing the length of the array
for(unsigned idx=0;idx<(size / sizeof(SomeClass));idx++,elPtr+=sizeof(SomeClass))
{
new(elPtr)SomeClass(...); // this is the "placement new" mentioned above using any constructor of SomeClass you want.
//possibly mutate the array element some more.
}
return bufPtr;
|
So, I'm filling up the buffer myself like I want it. Upon exit the runtime will try to fill it again using the default constructor SomeClass but if you see to it that it does not initiate any members, you're fine and the array will be filled the way you want it to contain.
Note that when you create an instance like
SomeClass *ar=new(extraArgsIfAny) SomeClass[length]
that ar itself will point to bufPtr+4.
Also note that I'm doing this in a recursive fashion like this:
1 2 3 4 5 6 7 8 9 10
|
void *Array<T,I>::operator new[](size_t size,vector<I> &dimensions)
{
char *bufPtr=new char[size];
char *elPtr=bufPtr+4;
for(unsigned idx=0;idx<(size / sizeof(Array<T,I>));idx++,elPtr+=sizeof(Array<T,I>))
{
new(elPtr)Array<T,I>(*dimensions.begin());
}
return bufPtr;
}
|
hence the need for a specialized template to end the recursion ... but that's another story.
PS. call me a purist but isn't this more a
kind of overriding
rather than
overloading
;-)