[comp.lang.c++] Overloaded operator new

horstman@sjsumcs.sjsu.edu (Cay Horstmann) (12/04/90)

One of my students just had the bright idea to abuse the placement syntax
of the new operator 
     new (p) X(...);
to overload X::operator new( size_t, void* ) and feed something entirely
different into p than the "placement address". 

I told her that was a subversive idea, but then I looked into the ARM and
saw that it is possible to overload X::operator new( size_t, int) and make
a call new(7) X(...). 

Is this stuff actually useful?

I guess someone must have thought it is. I grant that the placement operation
has its use, but I was always mystified by its syntax. I mean, if you call the
destructor explicitly with 
     p->~X(),
why not call the constructor explicitly with
     p->X(...)
instead of 
     new(p) X(...)
(provided nobody tinkered with the definition of X::operator new().)

Any info (preferably by e-mail; I'll summarize if there is interest) would
be greatly appreciated. Hate mail too...

Cay

cline@cheetah.ece.clarkson.edu (Marshall Cline) (12/05/90)

In article <1990Dec4.062015.6637@sjsumcs.sjsu.edu> horstman@sjsumcs.sjsu.edu (Cay Horstmann) writes:
>One of my students just had the bright idea to abuse the placement syntax
>of the new operator 
>  new (p) X(...);
>to overload X::operator new( size_t, void* ) and feed something entirely
>different into p than the "placement address". 
>I told her that was a subversive idea, but then I looked into the ARM and
>saw that it is possible to overload X::operator new( size_t, int) and make
>a call new(7) X(...). 
>Is this stuff actually useful?
>I guess someone must have thought it is. I grant that the placement operation
>has its use, but I was always mystified by its syntax. I mean, if you call the
>destructor explicitly with 
>  p->~X(),
>why not call the constructor explicitly with
>  p->X(...)
>instead of 
>  new(p) X(...)
>(provided nobody tinkered with the definition of X::operator new().)
>Any info (preferably by e-mail; I'll summarize if there is interest) would
>be greatly appreciated. Hate mail too...
>Cay

Cay always has such colorful expressions (`abuse the placement syntax',
`subversive idea', etc)!!

As usual in C++, generality rules.  Ie: it seems more general to allow
                        new(size_t, any other parameters at all);
than to restrict it to  new(size_t, void*);

Ie: if I want to allow people to allocate objects of class X by some
pseudo-index, why not allow  ``new(size_t,int)''

Or if I want them to allocate X's from some pool of memory, why not
``new(size_t,Pool&)''

etc.

The problem with all this is that the *client* must remember where the
object came from, and delete has only one syntax.  The ARM (p.65-6)
discusses the possibility of having a matching placement deletion
syntax, but argues that it requires the client to remember all this
stuff, so that the client has to choose the right deletion routine.

Page 66 discusses an alternative which dispatches based on the actual
type of an `Arena' or `Pool' of memory.  Unfortunately this still
requires the client to remember whence an object cometh.

I suppose a sufficiently concerned supplier could overload operator
new so that it remembers the actual Arena/Pool.  A corresponding
operator delete would then use that Arena/Pool.  But an object isn't
`born' until its ctor builds it from the raw bits returned from `new',
and it dies during the dtor, which is before `delete' tries to peek at
the cached `Pool*'.

Any better solutions?
Marshall Cline
--
PS: If your company is interested in on-site C++/OOD training, drop me a line!
PPS: Career search in progress; ECE faculty; research oriented; will send vita.
--
Marshall Cline / Asst.Prof / ECE Dept / Clarkson Univ / Potsdam, NY 13676
cline@sun.soe.clarkson.edu / Bitnet:BH0W@CLUTX / uunet!clutx.clarkson.edu!bh0w
Voice: 315-268-3868 / Secretary: 315-268-6511 / FAX: 315-268-7600

horstman@sjsumcs.sjsu.edu (Cay Horstmann) (12/05/90)

In article <CLINE.90Dec4111352@cheetah.ece.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
(in response to my query why (a) there is the very general scheme of overloading
operator new( size_t, any other parameters ) and (b) the corresponding delete
syntax is not symmetric.
>
>Cay always has such colorful expressions (`abuse the placement syntax',
>`subversive idea', etc)!!
>
>As usual in C++, generality rules.  Ie: it seems more general to allow
>                        new(size_t, any other parameters at all);
>than to restrict it to  new(size_t, void*);
>
>Ie: if I want to allow people to allocate objects of class X by some
>pseudo-index, why not allow  ``new(size_t,int)''
>
>Or if I want them to allocate X's from some pool of memory, why not
>``new(size_t,Pool&)''
>
>etc.
>
>The problem with all this is that the *client* must remember where the
>object came from, and delete has only one syntax.  The ARM (p.65-6)
>discusses the possibility of having a matching placement deletion
>syntax, but argues that it requires the client to remember all this
>stuff, so that the client has to choose the right deletion routine.
>
>Page 66 discusses an alternative which dispatches based on the actual
>type of an `Arena' or `Pool' of memory.  Unfortunately this still
>requires the client to remember whence an object cometh.
>
>I suppose a sufficiently concerned supplier could overload operator
>new so that it remembers the actual Arena/Pool.  A corresponding
>operator delete would then use that Arena/Pool.  But an object isn't
>`born' until its ctor builds it from the raw bits returned from `new',
>and it dies during the dtor, which is before `delete' tries to peek at
>the cached `Pool*'.
>
Yep, this is all very true. Indeed, operator new could place information
into a "header" area of the block which an overloaded operator delete could
find (by subtracting the header size from this...) 

My student was working on the idea of implementing several pools of memory,
and she passed the address of a pool descriptor into new. So there is a
possible use, I guess.

I do realize that placement is useful.

IF one agrees that new(p) X(...) is a good syntax for placement, then it is
indeed reasonable to implement the more general new(...) X(...). 

Personally, I don't think this is very elegant. I would much favor the symmetric
syntax p->X(...) to run X::X(...) on the preallocated p and p->~X(...) on the
destructor, and separately deal with the allocation and deallocation of p.

Orthogonality of language features. (You know, like in Algol 68 :-))

I do realize that it is against the spirit of constructors to run them on
preallocated memory (but so is an explicit call to a destructor), and there-
fore a yucky syntax may discourage some from doing so. Don't bet on it, though.

You'll begin to see plenty "new(this) X() // clear the state " in student's
code, I bet.

Has anyone used the flexibility of operator new( size_t, ... ) for anything
BUT placement?

Cay

dlw@odi.com (Dan Weinreb) (12/06/90)

In article <CLINE.90Dec4111352@cheetah.ece.clarkson.edu> cline@cheetah.ece.clarkson.edu (Marshall Cline) writes:


   The problem with all this is that the *client* must remember where the
   object came from, and delete has only one syntax.  The ARM (p.65-6)
   discusses the possibility of having a matching placement deletion
   syntax, but argues that it requires the client to remember all this
   stuff, so that the client has to choose the right deletion routine.

This is all true.  However, sometimes you have an object in which you
want to control how it is allocated (e.g. you want it to live in
special shared virtual memory or something like that), for all
instances of the class.  So all instances are always allocated the
same way, and so you can just write one operator delete that does the
appropriate deletion.  The parameterization of operator new is still
useful.  This is only a special case, of course; in general you'd like to
be able to allocate some objects one way and some another way, and not
have the problem with delete.  But it is a useful special case.

sking@nowhere.uucp (Steven King) (12/06/90)

In article <1990Dec5.155604.24463@sjsumcs.sjsu.edu> horstman@sjsumcs.sjsu.edu (Cay Horstmann) writes:

>Yep, this is all very true. Indeed, operator new could place information
>into a "header" area of the block which an overloaded operator delete could
>find (by subtracting the header size from this...) 
>
>My student was working on the idea of implementing several pools of memory,
>and she passed the address of a pool descriptor into new. So there is a
>possible use, I guess.
>
>I do realize that placement is useful.
>
>IF one agrees that new(p) X(...) is a good syntax for placement, then it is
>indeed reasonable to implement the more general new(...) X(...). 
>
>Personally, I don't think this is very elegant. I would much favor the symmetric
>syntax p->X(...) to run X::X(...) on the preallocated p and p->~X(...) on the
>destructor, and separately deal with the allocation and deallocation of p.
>
>Orthogonality of language features. (You know, like in Algol 68 :-))
>
>I do realize that it is against the spirit of constructors to run them on
>preallocated memory (but so is an explicit call to a destructor), and there-
>fore a yucky syntax may discourage some from doing so. Don't bet on it, though.
>
>You'll begin to see plenty "new(this) X() // clear the state " in student's
>code, I bet.
>
>Has anyone used the flexibility of operator new( size_t, ... ) for anything
>BUT placement?

   A real world ;-} usage;

   An embedded system consisting of a central host and multiple coprocessors.
 Communication between the host and the copros are via dualport ram. Messages
 are of variable length, but of a common base format. class Message defines
 the common header shared by all derived messages and an operator new ( but
 not delete ), which is inherited by all derived message classes.
   Message::operator new is passed a reference to a "comm" which is an object
 containing the desired dualport area which itself exists as a "heap" object
 which has two methods, alloc and free ( actually void * heap::operator ( )
 ( size_t ) and void heap::operator ( ) ( void * ) -- I have a lot of fun
 finding new uses of operator ( ) ( ) ;-).
  Operator message::new calls the heap alloc method which synchronizes access
 to the heap ( it is a shared resource in a multitasking system ) and
 actually allocates the space in the dualport.
   The delete operator is not defined because one of comm's methods that
 functions as an interrupt handler simply invokes the heap free method on
 the message -- by definition, if the comm interrupt handler services a
 message, it must belong to that comms heap. kinda nasty, but it gets
 around the limited delete syntax.

   And of course comm::operator new invokes the constructor for the
 dualport class which does a ram check on the dualport and initializes the
 coprocessor, returning the result code in the third arguement to comm::new,
 a reference to an int...

   I had lots of fun with this code.


    The placement form of new is also usefull if one wants to create an array
 of objects but doesnt want to use a default constructor.

class A {
public:
   A ( int ) ;
} ;

main()
{
   char buf [ 10 * sizeof ( A ) ] ;
   int i = 0 ;
   for ( char *b = buf ; b < buf + 10 * sizeof ( A ) ; b+= sizeof ( A ) ) 
	new ( b ) A ( i++ ) ;
}


   I'm sure there are other ways to abuse overloading new that I havent 
 thought of yet...


-- 
sking@nowhere				..!cs.utexas.edu!ut-emx!nowhere!sking

been dazed and confused for so long its true; wanted an OS, never bargined for
you. Lotsa people hacking, few of them know, kernal of unix was created below...