Topic: Constructor implementation and operator new()
Author: gjditchfield@watmsg.waterloo.edu (Glen Ditchfield)
Date: 29 Aug 91 15:34:51 GMT Raw View
A C++ compiler is allowed to insert calls to ::operator new(size_t) or to
T::operator new(size_t) in the implementation of a constructor for class T.
As far as I can make out, it is not allowed to insert calls to any other
functions, not even other versions of operator new().
This implementation flexibility complicates the language by adding a
narrow special case to the language's rules. It weakens the separation
between allocation and implementation. Finally, it makes it erroneous to
define multi-argument versions of operator new without also defining a
single-argument version.
Why are implementations allowed to do this? Is there some gain in
efficiency that makes the linguistic complications worthwhile?
Observe:
#include <stddef.h>
struct Foo {
Foo( );
};
struct Bar { // This is line 7.
Foo f;
void *operator new( size_t, int * );
void operator delete( void * );
};
Cfront 2.1.0 reports
line 7: error: argument 2 of type int * expected for Bar::operator new()
Note that new is never called!
Eventually, I found an explanation in the ARM, section 12.5, in
commentary near the bottom of page 282.
class Y {
// ...
public:
void *operator new (size_t s, int flag);
Y() {} // may be error
};
Here, an implementation is allowed to place a call of operator new()
in the body of the constructor to implement the constructor semantics
(see s. 12.1). If it does so, it will call an operator new() with a
single argument, but Y::operator new() requires two.
(Section 12.1, in commentary again, says that constructors may call
operator new() if they were invoked by the "new" operator.) So, in my
case, Cfront generates a default constructor for Bar, which tries to call
Bar::operator new() with the wrong number of arguments.
The solution suggested by the ARM is to define Bar::operator new(size_t).
However, in my case, the "new" operator must always use the "placement"
syntax when allocating Bars. (Bar was abstracted from code that implements
memory-mapped disk-based data structures.) I would have to declare
struct Bar {
Foo f;
void *operator new( size_t, int * );
void operator delete( void * );
private:
void *operator new( size_t) { abort(); }
};
It would be simpler (and statically checkable!) to not declare the second
operator new().
Glen Ditchfield gjditchfield@violet.uwaterloo.ca Office: DC 2517
Dept. of Computer Science, U of Waterloo, Waterloo, Ontario, Canada, N2L 3G1
Author: gintera@cpsc.ucalgary.ca (Andrew Ginter)
Date: 29 Aug 91 16:52:38 GMT Raw View
In article <1991Aug29.153451.26824@watmath.waterloo.edu> gjditchfield@watmsg.waterloo.edu (Glen Ditchfield) writes:
>
>A C++ compiler is allowed to insert calls to ::operator new(size_t) or to
>T::operator new(size_t) in the implementation of a constructor for class T.
As I understand it (and I may be wrong), this "feature" provides
backwards compatibility with old implementations and should disappear
at some point in the indefinite future. Section 18.3.3 of the ARM
identifies assignment to "this" as an anachronism. Before "new" could
be overloaded, one implemented custom memory managers by testing and
assigning to "this":
class foo {
foo () { // the constructor
if (this) // if object is not allocated through "new"
...
else // else object is allocated through "new"
this = my_alloc (); // do custom memory management
...
To support separate compilation of non-inline constructors, "this" had
to be 0 on entry to these constructors. If the compiler determined
that a constructor did NOT assign to "this", it would emit code
invisibly, to call "new" from within the constructor, before the first
use of "this".
Any compiler which does not allow assignment to "this" does not need
to call "new" from within constructors. Instead, programmers call
"new" explicitly, using whatever placement syntax is appropriate, and
the resulting pointer is passed as "this" to the constructor. It may
be a long time before such compilers are the norm. Until then, you
must supply "new" operators which use the default calling syntax in
addition to any you need to use the placement syntax.
You should be able to achieve a degree of static type checking though,
by making the default syntax "new" a "private" or "protected" member.
Andrew Ginter, 403-220-6320, gintera@cpsc.ucalgary.ca