1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
When ti(operator new)hi(new) is overloaded, it must define a ti(void *) return
type, and its first parameter must be of type ti(size_t). The default
tt(operator new) defines only one parameter, but overloaded versions may
define multiple parameters. The first one is not explicitly specified but is
deducted from the size of objects of the class for which tt(operator new)
is overloaded. In this section overloading tt(operator new) is discussed.
Overloading ti(new[]) is discussed in section ref(NEWDELETEARRAY).
It is possible to define multiple versions of the tt(operator new), as long as
each version defines its own unique set of arguments. When overloaded
tt(operator new) members must dynamically allocate memory they can do so using
the global tt(operator new), applying the scope resolution operator
tt(::). In the next example the overloaded tt(operator new) of the class
tt(String) initializes the substrate of dynamically allocated tt(String)
objects to 0-bytes:
verb(
#include <cstring>
#include <iosfwd>
class String
{
std::string *d_data;
public:
void *operator new(size_t size)
{
return memset(::operator new(size), 0, size);
}
bool empty() const
{
return d_data == 0;
}
};
)
The above tt(operator new) is used in the following program, illustrating
that even though tt(String)'s default constructor does nothing the object's
data are initialized to zeroes:
verb(
#include "string.h"
#include <iostream>
using namespace std;
int main()
{
String *sp = new String;
cout << boolalpha << sp->empty() << '\n'; // shows: true
}
)
At tt(new String) the following took place:
itemization(
it() First, tt(String::operator new) was called, allocating and
initializing a block of memory, the size of a tt(String) object.
it() Next, a pointer to this block of memory was passed to the
(default) tt(String) constructor. Since no constructor was defined,
the constructor itself didn't do anything at all.
)
As tt(String::operator new) initialized the allocated memory to zero bytes
the allocated tt(String) object's tt(d_data) member had already been
initialized to a 0-pointer by the time it started to exist.
All member functions (including constructors and destructors) we've
encountered so far define a (hidden) pointer to the object on which they
should operate. This hidden pointer becomes the function's ti(this) pointer.
In the next example of em(pseudo) bf(C++) em(code), the pointer is explicitly
shown to illustrate what's happening when tt(operator new) is used. In the
first part a tt(String) object tt(str) is directly defined, in the second
part of the example the (overloaded) tt(operator new) is used:
verb(
String::String(String *const this); // real prototype of the default
// constructor
String *sp = new String; // This statement is implemented
// as follows:
String *sp = static_cast<String *>( // allocation
String::operator new(sizeof(String))
);
String::String(sp); // initialization
)
In the above fragment the member functions were treated as
em(object-less) member functions of the class tt(String). Such members are
called em(static member functions) (cf. chapter ref(StaticDataFun)). Actually,
tt(operator new) em(is) such a static member function. Since it has no
tt(this) pointer it cannot reach data members of the object for which it is
expected to make memory available. It can only allocate and initialize the
allocated memory, but cannot reach the object's data members by name as there
is as yet no data object layout defined.
Following the allocation, the memory is passed (as the tt(this) pointer)
to the constructor for further processing.
tt(Operator new) can have multiple parameters. The first parameter is
initialized as an implicit argument and is always a tt(size_t)
parameter. Additional overloaded operators may define additional
parameters. An interesting additional tt(operator new) is the
hi(new: placement)emi(placement new) operator. With the placement new
operator a block of memory has already been set aside and one of the class's
constructors is used to initialize that memory. Overloading placement new
requires an tt(operator new) having two parameters: tt(size_t) and tt(char *),
pointing to the memory that was already available. The tt(size_t)
parameter is implicitly initialized, but the remaining parameters must
explicitly be initialized using arguments to tt(operator new). Hence we reach
the familiar syntactical form of the placement new operator in use:
verb(
char buffer[sizeof(String)]; // predefined memory
String *sp = new(buffer) String; // placement new call
)
The declaration of the placement new operator in our class tt(String)
looks like this:
verb(
void *operator new(size_t size, char *memory);
)
It could be implemented like this (also initializing the tt(String)'s
memory to 0-bytes):
verb(
void *String::operator new(size_t size, char *memory)
{
return memset(memory, 0, size);
}
)
Any other overloaded version of tt(operator new) could also be
defined. Here is an example showing the use and definition of an overloaded
tt(operator new) storing the object's address immediately in an existing array
of pointers to tt(String) objects (assuming the array is large enough):
verb(
// use:
String *next(String **pointers, size_t *idx)
{
return new(pointers, (*idx)++) String;
}
// implementation:
void *String::operator new(size_t size, String **pointers, size_t idx)
{
return pointers[idx] = ::operator new(size);
}
)
|