Topic: Overloading 'operator new': errors in MSVC 2.0 or not?


Author: mike <mike@redline.ru>
Date: 1996/01/12
Raw View
I think I've discovered some errors in Microsoft Visual C++ 2.0 compiler.

1) Suppose we want to overload global 'operator new':

void* operator new (size_t size, MemoryAllocator* palloc)
{
    // Allocate memory using special allocator
    return palloc->alloc (size);
}

How to delete object allocated by this function?
We can't overload 'operator delete' (according to ARM 5.3.4, 12.5).
We must write something like this:

void Delete_SomeClass (SomeClass* p, MemoryAllocator* palloc)
{
    p->~SomeClass ();    // direct destructor call
    palloc->free (p);    // free memory
}

It works, but it is too tiresome to write such function for every class.
A better way is to use template function:

template <class Type>
void Delete (Type* p, MemoryAllocator* palloc)
{
    p->~Type ();         // direct destructor call - is it allowed here?
    palloc->free (p);    // free memory
}

But if I try to write

    MemoryAllocator alloc;
    SomeClass* p = new (&alloc) SomeClass;
    Delete (p, &alloc);

then compiler writes something like
filename(line#) : error C2300: 'SomeClass' : class does
not have a destructor called '~Type'

Is it an error of compiler? Or direct call of destructor
in template is not allowed? I haven't found something
about it in ARM.


2) Again, let's overload global 'operator new' with default parameter:

void* operator new (size_t size, MemoryAllocator* palloc = NULL)
{
    return palloc ? palloc->alloc (size) : malloc (size);
}

If I write now

    char* p = new char [100];

then compiler (it seems) must invoke operator new (100, NULL). But when I've
inspected assembler listing, I discovered that compiler invokes standard
library version of 'operator new' rather than overloaded one.
To correct it, I have to write

void* operator new (size_t size, MemoryAllocator* palloc)
{
    return palloc ? palloc->alloc (size) : malloc (size);
}
void* operator new (size_t size)
{
    return operator new (size, NULL);
}

Now it works OK. Is it also a compiler error?

Alex Bobkov.
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/01/13
Raw View
mike <mike@redline.ru> writes:

>I think I've discovered some errors in Microsoft Visual C++ 2.0 compiler.
>
>1) Suppose we want to overload global 'operator new':
>
>void* operator new (size_t size, MemoryAllocator* palloc)
>{
>    // Allocate memory using special allocator
>    return palloc->alloc (size);
>}
>
>How to delete object allocated by this function?
>We can't overload 'operator delete' (according to ARM 5.3.4, 12.5).

The ARM is out-of-date on this one; the committee has changed things so
you are indeed allowed to overload operator delete.  This is basically
just for specifying cleanup if an exception is thrown, since there is
no nice syntax for invoking the destructor and the overloaded operator
delete.  To ensure that you do get correct cleanup in an expression such
as `new (palloc) Foo' if the `Foo::Foo()' constructor throws an exception,
you should define an appropriate overloaded operator delete:

 void* operator delete (void *p, MemoryAllocator* palloc)
 {
     palloc->free (p);    // free memory
 }

>We must write something like this:
>
>void Delete_SomeClass (SomeClass* p, MemoryAllocator* palloc)
>{
>    p->~SomeClass ();    // direct destructor call
>    palloc->free (p);    // free memory
>}

Yep, you will still need this, even if you overload operator delete
as above; the syntax `delete (palloc) p' will not work.

>It works, but it is too tiresome to write such function for every class.
>A better way is to use template function:
>
>template <class Type>
>void Delete (Type* p, MemoryAllocator* palloc)
>{
>    p->~Type ();         // direct destructor call - is it allowed here?
>    palloc->free (p);    // free memory
>}

Yes, the direct destructor call is allowed.

>But if I try to write
>
>    MemoryAllocator alloc;
>    SomeClass* p = new (&alloc) SomeClass;
>    Delete (p, &alloc);
>
>then compiler writes something like
>filename(line#) : error C2300: 'SomeClass' : class does
>not have a destructor called '~Type'
>
>Is it an error of compiler?

Yes, your code is fine - you need to get a better compiler.

>2) Again, let's overload global 'operator new' with default parameter:
>
>void* operator new (size_t size, MemoryAllocator* palloc = NULL)
>{
>    return palloc ? palloc->alloc (size) : malloc (size);
>}

That is not correct, since it won't do the right thing for zero-sized
allocations.  You should write

 void* operator new (size_t size, MemoryAllocator* palloc = NULL)
 {
   return palloc ? palloc->alloc (size) : malloc (size == 0 ? 1 : size);
 }       ^^^^^^^^^^^^^^^^^^^^

to make sure that you return a distinct address for each zero-sized
allocation.

Also, the use of a default parameter probably doesn't do what you want.
Rather than replacing the built-in global `operator new(size_t)', the
two versions will coexist.  The September 95 draft standard says
(see 5.3.4 [expr.new]) that in an expression like `new int', the
"appropriate" allocation function will be called, but in this case it
is not clear which allocation function is appropriate.  A compiler
might always call the global `operator new(size_t)' rather than your
version.  Or it might try to perform overload resolution, which would
mean that if you #include <new.h>, which declares the global `operator
new(size_t)', then any expression like `new int' will result in an
ambiguity error.  Or it might consider such expressions ambiguous even
if you don't #include <new.h>.

>If I write now
>
>    char* p = new char [100];
>
>then compiler (it seems) must invoke operator new (100, NULL). But when I've
>inspected assembler listing, I discovered that compiler invokes standard
>library version of 'operator new' rather than overloaded one.
>To correct it, I have to write
>
>void* operator new (size_t size, MemoryAllocator* palloc)
>{
>    return palloc ? palloc->alloc (size) : malloc (size);
>}
>void* operator new (size_t size)
>{
>    return operator new (size, NULL);
>}
>
>Now it works OK. Is it also a compiler error?

No.  For

 char* p = new char [100];

the compiler should invoke `operator new [] (size_t)' rather than
`operator new (size_t)'.  The default behaviour of `operator new [] (size_t)'
is to invoke `operator new (size_t)'.  This explains what you are seeing.

Anyhow, defining a (possibly inline) `operator new (size_t)' is a much
better idea than using a default operator, because it avoids the possible
problems I mentioned above.

--
Fergus Henderson              WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au               PGP: finger fjh@128.250.37.3
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]