Topic: overloaded new/delete


Author: agray@ozemail.com.au (Alan Gray)
Date: 1995/08/09
Raw View
I pose the following problem.

Given a personalised allocator

    class Malloc {

    public:

      Malloc();

      void *allocate(size_t sz);
      void  deallocate(void *ptr);
    };

    void *operator new(size_t sz,Malloc *m=0)
    // ------------------------------------------------
    // here sz == sizeof(class/struct)
    // ------------------------------------------------
    {
      if(m) {
        return(m->allocate(sz));
      } else {
        return(::operator new(sz));
      }
    }
    void *operator new[](size_t sz,Malloc *m=0)
    {
      if(m) {
        return(m->allocate(sz));
      } else {
        return(::operator new(sz));
      }
    }

and then use Malloc to allocate a series of objects,
then finally delete the objects, how can this be achieved.
The draft standard does not cover this.

    class X {
    public:
      X();
      virtual ~X();
    };
    class Y : public X {
    public:
      Y();
     ~Y();
    };

    void fred(Malloc *m)
    {
      double *xarray = new(m) double[1024];
      double *yarray = new(m) double[1024];
      double *zvalue = new(m) double;
      X      *xone   = new(m) Y[1024];
      X      *xtwo   = new(m) Y;
      ....
      ....


      delete [] xarray;   // eek - gets the wrong version of ::operator
delete(size_t sz)
      delete [] yarray;   // dito
      delete    zvalue;   // dito
      delete [] xone;     // dito
      delete    xtwo;     // dito
    }

do we need to be able to overload as follows

    void operator delete(void *ptr,Malloc *m=0)
    {
      if(m) {
        m->deallocate(ptr);
      } else {
        ::operator delete(ptr));
      }
    }
    void operator delete[](void *ptr,Malloc *m=0)
    {
      if(m) {
        m->deallocate(ptr);
      } else {
        ::operator delete(ptr));
      }
    }

then fred will look like

    void fred(Malloc *m)
    {
      double *xarray = new(m) double[1024];
      double *yarray = new(m) double[1024];
      double *zvalue = new(m) double;
      X      *xone   = new(m) Y[1024];
      X      *xtwo   = new(m) Y;
      ....
      ....


      delete(m) [] xarray;
      delete(m) [] yarray;
      delete(m)    zvalue;
      delete(m) [] xone;
      delete(m)    xtwo;
    }


Alan Gray
4D Solutions
PO BOX 103 Narrabeen NSW 201
Australia






Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/08/09
Raw View
In article nta@oznet03.ozemail.com.au, agray@ozemail.com.au (Alan Gray) writes:
>I pose the following problem.
>
>Given a personalised allocator
>
>    class Malloc {
>
>    public:
>
>      Malloc();
>
>      void *allocate(size_t sz);
>      void  deallocate(void *ptr);
>    };
>
>    void *operator new(size_t sz,Malloc *m=0) <=== this will cause an ambiguity
>    // ------------------------------------------------
>    // here sz == sizeof(class/struct)
>    // ------------------------------------------------
>    {
>      if(m) {
>        return(m->allocate(sz));
>      } else {
>        return(::operator new(sz));  <=== ambiguous call
>      }
>    }


Both the predefined operator new and your placement version can be called
with a single size_t argument. You have to remove the default argument
value, which will give the effect you appear to want.
 void* operator new(size_t sz, Malloc* m)
 {
  if( m ) return m->allocate(sz);
  ... otherwise do something about the error
 }
Thus:
 ... new T ... // uses predefined operator new
 ... new (&allocator) T ... // uses your operator new

>
>do we need to be able to overload as follows
>
>    void operator delete(void *ptr,Malloc *m=0) ... <=== same ambiguity problem
>
> ... The draft standard does not cover this.

It is covered. There is no syntax for getting an overloaded operator delete
via a delete-expression. You can define a "placement delete", but it is
called only if
 - a constructor invoked from a placement-new expression exits via an exception, or
 - you call the operator delete explicitly.
That is:
 T* p ...
 delete p; // never calls a placement delete
 // but you can do this
 p->~T(); // call destructor
 operator delete(p, &allocator);

You can use class-specific operator new and delete in an Allocator base
class, and these will be inherited by any class derived from Allocator.
You can find examples of how to do this in "The C++ Programming
Language", 2nd edition and in other books.
---
Steve Clamage, stephen.clamage@eng.sun.com







Author: smeyers@netcom.com (Scott Meyers)
Date: 1995/08/09
Raw View
In article <40ak2i$9oa@engnews2.Eng.Sun.COM> clamage@Eng.Sun.COM writes:
| It is covered. There is no syntax for getting an overloaded operator delete
| via a delete-expression. You can define a "placement delete", but it is
| called only if
|  - a constructor invoked from a placement-new expression exits via an exception, or
|  - you call the operator delete explicitly.
| That is:
|  T* p ...
|  delete p; // never calls a placement delete
|  // but you can do this
|  p->~T(); // call destructor
|  operator delete(p, &allocator);

Okay, what am I missing?  DWP 3.7.3.2 says that if operator delete has a
second parameter, it must be of type size_t.  How can &allocator be of that
type?

Scott





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1995/08/09
Raw View
In article K1q@netcom.com, smeyers@netcom.com (Scott Meyers) writes:
>In article <40ak2i$9oa@engnews2.Eng.Sun.COM> clamage@Eng.Sun.COM writes:
>| It is covered. There is no syntax for getting an overloaded operator delete
>| via a delete-expression. You can define a "placement delete", but it is
>| called only if
>|  - a constructor invoked from a placement-new expression exits via an exception, or
>|  - you call the operator delete explicitly.
>| That is:
>|  T* p ...
>|  delete p; // never calls a placement delete
>|  // but you can do this
>|  p->~T(); // call destructor
>|  operator delete(p, &allocator);
>
>Okay, what am I missing?  DWP 3.7.3.2 says that if operator delete has a
>second parameter, it must be of type size_t.

My copy of the 28 April 1995 draft doesn't say that. It says a class
member operator delete (which the above example isn't) MAY have a
second parameter of type size_t. I don't see anything that says that
the second parameter if present must have type size_t. See also 5.3.4.

---
Steve Clamage, stephen.clamage@eng.sun.com







Author: smeyers@netcom.com (Scott Meyers)
Date: 1995/08/10
Raw View
Steve Clamage wrote:

>| It is covered. There is no syntax for getting an overloaded operator delete
>| via a delete-expression. You can define a "placement delete", but it is
>| called only if
>|  - a constructor invoked from a placement-new expression exits via an exception, or
>|  - you call the operator delete explicitly.
>| That is:
>|  T* p ...
>|  delete p; // never calls a placement delete
>|  // but you can do this
>|  p->~T(); // call destructor
>|  operator delete(p, &allocator);

I asked:

  Okay, what am I missing?  DWP 3.7.3.2 says that if operator delete has a
  second parameter, it must be of type size_t.

Steve replied:

  My copy of the 28 April 1995 draft doesn't say that. It says a class
  member operator delete (which the above example isn't) MAY have a
  second parameter of type size_t. I don't see anything that says that
  the second parameter if present must have type size_t. See also 5.3.4.

Here's the DWP paragraph in question (3.7.3.2, para 2):

  Each deallocation function shall return void and its  first  parameter
  shall  be  void*.   For  class member deallocation functions, a second
  parameter of type size_t may be added. If both versions  are  declared
  in  the  same  class, the one-parameter form is the usual deallocation
  function and the two-parameter  form  is  used  for  placement  delete
  (_expr.new_).  If the second version is declared but not the first, it
  is the usual deallocation function, not placement delete.

I agree that this paragraph can be interpreted as Steve has, though I
think it could be a lot clearer, especially in view of the fact that
this is a change from past behavior;  ARM 12.5 states,

  Only one operator delete() may be declared for a single class;  thus
  operator delete() cannot be overloaded.  The global operator delete()
  takes a single argument of type void*.

Is the following correct?

  - Non-class operator delete can take any number and types of
    arguments as long as the first argument is of type void*.

  - There may be at most two operator delete functions declared in a
    class, and they must have these signatures:

      void X::operator delete(void*);
      void X::operator delete(void*, size_t);

Finally, why is the obvious syntax for "placement delete" not allowed?
The committee must surely have considered and rejected it.  That is,
why is this not legal for the example above that started this thread?

  delete (&allocator) p;

The restriction against this seems even more glaring than usual in view
of the committee's deliberate introduction of "placement delete" for
handling deallocation when an exception is thrown during initialization
of a dynamic allocation.

Scott





Author: bgibbons@taligent.com (Bill Gibbons)
Date: 1995/08/10
Raw View
In article <smeyersDD2y1C.9nn@netcom.com>, smeyers@netcom.com (Scott
Meyers) wrote:

> Is the following correct?
>
>   - Non-class operator delete can take any number and types of
>     arguments as long as the first argument is of type void*.

Yes.

>   - There may be at most two operator delete functions declared in a
>     class, and they must have these signatures:
>
>       void X::operator delete(void*);
>       void X::operator delete(void*, size_t);

No.  There may be any number of delete functions in a class.  If present,
the first form above is always the usual delete.  Any which matches neither
of these forms is a placemeent delete.  If the second form appears, it's
the usual delete if the first form is missing; else it's a placement delete.
This is obviously more complex than one would like, but it's the simplest
scheme which does not require new syntax or keywords.

The wording about not overloading delete is obsolete.  When the "delete"
keyword is used, only the non-placement delete (and there is only one of
these) is ever called.  But when operator delete is invoked explicitly,
it is just an ordinary function and of course overloading rules apply.

> Finally, why is the obvious syntax for "placement delete" not allowed?
> The committee must surely have considered and rejected it.  That is,
> why is this not legal for the example above that started this thread?
>
>   delete (&allocator) p;

But what about:

    delete (&allocator) * p;

This is syntactically ambiguous (and it can be made semantically
ambiguous if "p" is an object of a class with both unary and binary
overloaded operator*).

A similar situation occurs with new-expressions:

    new int * * x;

The resolution for types is to use as many tokens as possible in the type.

But the situation with types (for "new") is fairly simple, since the
only ambiguities involve trailing "*" and "&".  The corresponding
situation with expressions is a little more complex, and the committee
did not consider it worth the added complexity, not to mention the
potential for breaking existing code.  Of course you're free to disagree.

--
Bill Gibbons
bgibbons@taligent.com