OPERATOR NEW
OPERATOR NEW
§1 Presentation
The general ideas outlined so far ( 4.9.20 ) correspond to the general description given in most textbooks on
the new operator . However, this operator contains special peculiarities that deserve a more detailed explanation.
Especially because understanding all its ins and outs [ 1 ] requires talking about "the" new operators , because in
reality, more than an operator, it is an amalgamation of functions under a common wrapper.
Recall that most of the statements included here about new can be extended to its version new[] for arrays.
To understand the matter we should begin by remembering that, in the preamble to this section dedicated to C++
operators ( 4.9 ), we pointed out that these can be considered an alternative notation for certain functions, and
that precisely the overloading of operators is based on the overloading of certain functions called operator-
function ( 4.9.18 ). The new and delete operators are no exception; C++ compilers provide a default version
of the new (), new[] (), delete (), and delete[] () operator functions .
It happens that the compiler implicitly declares in the global space of each compilation unit the following functions
(their definitions are included in the Standard Library):
It is important to note that these functions are not in the std subspace like the rest of the entities in the Standard
Library ( 4.1.11c2 ) but in the global file space. Precisely for this reason, it is customary to refer to them as the
global version of the corresponding operator.
Note : This implicit declaration does not introduce the names std , std::bad_alloc or
std::size_t . So it is possible to invoke these functions ( new (), new[] (), delete () and delete[] () )
without having to include the header file < new >. However, if we use them explicitly, then it is necessary
to include the header file or an equivalent mechanism. The reason is that the terms size_t and
bad_alloc are defined in the aforementioned header.
From inspection of the prototypes it is clear that both new () and new[] () accept as their first argument a type
size_t , and that they can throw an exception of type bad_alloc , or derived from it (more on this in 4.9.20d ) .
Also that both versions of delete () accept a pointer of any type as an argument [ 4 ], and cannot throw any
exceptions.
An explicit invocation of these functions is always possible by simply using the :: scope specifier to refer to
the global space of the file ( 4.1.11c ). So we would have ::operator new () and ::operator new[] () to allocate
objects and arrays, and the corresponding ::operator delete () and ::operator delete[] () to deallocate the
reserved memory with the previous.
For example, it is permissible to reserve a size number of bytes in the heap by directly invoking the new ()
operator-function in the form:
1 of 9 14/08/2024, 20:28
Nightly https://www-zator-com.translate.goog/Cpp/E4_9_20a.htm?_x_tr_sl=e...
here we use explicit modeling to indicate the necessary memory size (bytes) in size_t- type units [ 2 ], since this is
the type expected by the function-operator. Space could also be allocated for an object of typeX in the form:
new typeX
It then calls the class's constructor to construct the object in the newly assigned location. If the use of new is as
above (no explicit launcher), then the default constructor is used (no arguments). On the other hand, if initiators
had been used, the constructor that matched the supplied arguments would be invoked ( ). In our case, the
compiler would include an invocation to the constructor in the form:
typeX::typeX();
If type X is an abstract type ( 2.2 ) that does not have its own overloaded version, then the invocation refers to
the global version:
The default constructor typeX::typeX() would then be invoked to start an object in the allocated zone [ 9 ].
When you want to create an array of objects of an abstract type, a process analogous to the previous one is
followed: In principle, the global version of new allocates space for the elements of the array ( 4.9.20c ); The
class's default constructor is then used to start each of the objects in the array. For example, the sentence:
new typeX[5];
would allocate 5 contiguous spaces for 5 typeX objects. The class's default constructor would then be invoked five
times to start an default to start the members of the created array.
Destroying objects with delete follows the reverse process. When the compiler encounters a statement like:
delete ptr;
ptr->Xtype::~Xtype();
Next, if the class has a particular version of the delete operator , that method is invoked:
typeX::operator delete(ptr);
::operator delete(ptr);
If it is the destruction of an object created with the new[] operator for arrays, then the statement
delete[] ptr;
2 of 9 14/08/2024, 20:28
Nightly https://www-zator-com.translate.goog/Cpp/E4_9_20a.htm?_x_tr_sl=e...
ptr->typeX::~typeX();
once for each member of the array. The particular or global version of the corresponding operator delete[] ()
function is then called , depending on whether or not the class has an overloaded version of this operator.
It is significant that global versions also serve to construct objects that are not classes, structures or unions, but
rather types prebuilt in the language. This is what happens when new is used to create simple types. For
example, in the expressions:
In reality, C++ compilers offer various (overloaded) versions of the global operator-functions noted above. For
each of the forms §2a, §2b reviewed , there are two other forms called respectively in-position ("In-place") and
without-exception ("No-throw").
As can be seen, these versions are distinguished from the previous ones in that they accept as a second
argument a pointer of any type, which serves to indicate the address from which the object will be created. Also in
that they do not throw an exception in case of failure.
"In-place" versions are invoked when using the new / new[] operators with the optional location specifier (
4.9.20b ). For example:
These versions cannot throw exceptions ( 1.6.4 ), and are invoked when you want to alter the standard
behavior of new / new[ ] when an error occurs. So instead of throwing a bad_alloc exception , a null pointer is
returned. This would be the case of an invocation of the type:
3 of 9 14/08/2024, 20:28
Nightly https://www-zator-com.translate.goog/Cpp/E4_9_20a.htm?_x_tr_sl=e...
As we will see when discussing error handling ( 4.9.20d ), these versions were designed so that the new new
operators could be used with a minimum of modification in older programs, designed so that error checking was
not done by receiving an exception , but by comparing the value of the returned pointer with NULL.
We have seen that in reality, beneath the various syntaxes for using the new and new[] operators lies a set
of operator functions provided by default by the compiler (which in many cases can be overloaded). To offer an
overview, we list below the forms of use (in the source), along with the function-operator actually used by the
compiler. Without forgetting that, as we have indicated , the latter can also be used directly.
Note : in the table we assume that class T does not have its own overloaded version of the operator, so
the global version is invoked.
It is possible to overload the global version of new provided by the compiler [ 3 ], so that different versions exist in
the same program, but each new instance must have a different signature. The new versions would look like this:
The definition of the new functions must comply with the following standards:
They must be declared in the global space. They cannot be declared in a distinct subspace, nor can they
be declared static ( 4.1.8c ) in global space.
They must return a generic void* pointer , and accept as the first argument a size_t type , which cannot
have a default value and will be interpreted as the size of the requested space.
Unless it is an "in-place" version , the function will return the starting address of a contiguous space of at
least the size requested in the first argument.
The request for a space of size 0 will return a non-NULL pointer, and different from that of any other object
(in the case of the Borland C++ compiler, successive requests in this sense return different "non-null"
4 of 9 14/08/2024, 20:28
Nightly https://www-zator-com.translate.goog/Cpp/E4_9_20a.htm?_x_tr_sl=e...
pointers).
In case of failure in the requested assignment, the new function can invoke the handler installed if
applicable using the set_new_handler function ( 4.9.20d ). If no handler has been indicated, then you
must throw a bad_alloc exception or a class derived from it.
Once the overloaded version is defined according to the guidelines above, invocations to new would use the new
version, although global versions could still be used using the :: scope access operator ( 4.9.19 ).
As with the rest of the operators, the overloading of the new operator for abstract types is done by defining
particular versions of the corresponding operator-functions. The following prototypes can be used as a basis:
Note : see ( 4.9.21 ) for how to overload the delete operator on abstract types.
Example:
struct E {
void* operator new(size_t sz) { ... }
void operator delete(void* ptr) { ... }
...
};
Typically, the global versions of new and new[] are invoked from these methods . But remember that they hide the
global versions themselves. Example:
struct E {
void* operator new(size_t sz, int val) { ... }
...
};
...
E* ePtr = new E; // Mistake!!
the last statement is transformed by the compiler into an invocation to E::operator new(sizeof(E)); . But
there is no E::operator new() method that responds to these arguments, because E::operator
new(size_t, int) has hidden ::operator new(size_t) .
In a definition like the previous one (§4 ), the E structure has its own version of the new and delete operators , so
that the expressions:
E*eptr = new E;
...
delete eptr;
They create an object of type E and subsequently destroy it using the E::operator new() and E::operator
delete() methods , before the global versions. Only those classes that do not have their own versions of new ()
and delete () invoke the versions supplied by default by the compiler. But remember that the versions of these
operators defined for a class are inherited by subclasses. It is very common that in class hierarchies the versions
of new () and delete () of the entire lineage are defined in the superclasses.
5 of 9 14/08/2024, 20:28
Nightly https://www-zator-com.translate.goog/Cpp/E4_9_20a.htm?_x_tr_sl=e...
Study the proposed example ( 4.9.20b ) carefully , and note that the E::operator new() method does
not itself do all the work involved in the new E statement . It actually just allocates the space and returns a pointer
to the reserved area. The second part (the correct initialization of the object) is entrusted to the E::E()
constructors . These steps are performed automatically by the new "operator" .
The limitations for the new definitions of typeX::operator new() are analogous to those declared for the
global version:
They must return a generic void* pointer , which will correspond to the address of the reserved space. And
accept as the first argument a size_t type , which will be interpreted as the size of the requested space (in
addition to this, any number of additional arguments can be used).
The function will return the starting address of a contiguous space of at least the size requested in the first
argument.
The request for a space of size 0 will return a non-NULL pointer, and different from that of any other object
(successive requests in this sense will return different "non-null" pointers).
In case of failure of the requested assignment, the new function can invoke the installed handler (if any)
using the set_new_handler function ( 4.9.20d ). If none has been indicated, then you must throw a
bad_alloc exception or a class derived from it.
Remember that these member functions typeX::operator new (); typeX::operator new[] ();
typeX::operator delete () and typeX::operator delete[] () are declared static by the compiler [ 7 ]. The reason is
that they are called before the constructor and after the destructor. In reality, these functions do not operate on
objects of the class, but on the memory manager . new is limited to reserving an area of memory on which the
constructor will later work to transform it into an object of the class. delete proceeds to free a memory area in
which the destructor has previously worked.
§5 Initial value
We noted ( 4.9.20 ) that the syntax of new allows the use of an optional <(launcher)> specifier , which is
used to start the created object. This starter can be a list of expressions separated by commas and enclosed in
parentheses. Example:
In fact, when the compiler encounters an expression like the above, it reserves storage space, and then creates
an object in this space by invoking the appropriate constructor. To do this, it uses the expressions of the initiator
as arguments of the constructor (of course the existence of a constructor that corresponds to these arguments is
required !! ). In the case of the example the invocation would be made:
ClassC::ClassC(arg1, arg2);
If the specifier is empty ( ) or non-existent, then the default constructor is invoked. For example, the sentence:
ClassC::ClassC();
Example:
#include <iostream>
using namespace std;
6 of 9 14/08/2024, 20:28
Nightly https://www-zator-com.translate.goog/Cpp/E4_9_20a.htm?_x_tr_sl=e...
class C {
public:
int x;
void* operator new(size_t size) {
return ::operator new(size);
}
C (int n=10): x(n) {} // constructor
};
Departures:
Value x = 10
Value x = 20
Comment:
The M1 statement is decomposed by the compiler into an invocation to the operator method C::operator
new() with one argument, and another to the constructor without arguments:
C::operator new(sizeof(C));
DC();
In turn, the M2 statement is decomposed into an invocation to the C::operator new() method with one
argument, and another to the constructor also with one argument:
C::operator new(sizeof(C));
C::C(20);
Pay special attention, the following expressions have totally different meanings ( 4.9.20b ):
§5.1 We have pointed out , the new operator can be used even with basic types (preconstructed in the
language), although it is certainly not a frequent situation. Significantly, the compiler also provides suitable
constructors for these types.
Example:
Example:
void func() {
int x = 13; // L1:
int* ip = new int (x); // L2:
cout << *ip; // -> 13
int** ipp = new (int*) (ip); // L3:
cout << **ipp << endl; // -> 13
7 of 9 14/08/2024, 20:28
Nightly https://www-zator-com.translate.goog/Cpp/E4_9_20a.htm?_x_tr_sl=e...
Comment
In L1. An automatic object in the pile. i1 starts with the value of x . In turn ip starts with the address of i1 .
At L3 two new objects are created: a pointer-to-pointer-to- int ipp on the stack, and a pointer-to- int (which we will
call ip1 ) on the heap. ip1 is started with the value of ip . In turn, ipp is started with a value that is the address of
ip1 .
§5.2 Example
#include <iostream>
using namespace std;
class A {
public:
int x, y;
A (int n = 0) { x = y = n; } // default constructor
A (int a, int b) { x = a; y = b; } // constructor-2
};
Exit:
a1 == 0.0
a2 == 10.10
a3 == 20.30
i1 == 4302004
i2 == 123
Comment
The first three statements of main show three ways of invoking the new operator to create three instances a1 , a2
and a3 , of a user-defined type (class A ). Forms M.2 and M.3 use an optional initiator. The created objects are
accessible by three appropriate pointers a1p , a2p and a3p , which receive the values returned by new .
In the M.1 and M.2 statements the default constructor is used. In M.3 the second constructor of the class is
invoked.
The M.7 and M.8 statements are a somewhat unorthodox method of creating pointers to a basic type ( 2.2.1 )
using the new operator . They serve as a demonstration that even the preconstructed types in the language have
a suitable constructor. In this regard, it should be noted that the default constructor of the int type creates the
object but does not initialize the space with any value. The result obtained in i1 is garbage (previous content of
this heap area).
8 of 9 14/08/2024, 20:28
Nightly https://www-zator-com.translate.goog/Cpp/E4_9_20a.htm?_x_tr_sl=e...
Obviously, there is a substantial difference between sentence M.8 and M.9. Although they both represent the
same value, the object pointed to in M8 is persistent and placed on the heap, while x is an automatic object
created on the stack.
Start.
[1] In my opinion it is, by far, the most difficult operator to understand in the C++ language.
[2] Remember that the result of the sizeof operator ( 4.9.13 ) is size_t , an unsigned integer whose definition
depends on the implementation, and which can be found in the header files MALLOC.H and MEMORY.H among
others. For MS Visual C++ 6.0 and Borland C++, the type of size_t is unsigned int .
[3] Although it is possible to overload the global version of the new operator , Stroustrup himself tells us that it is
not advisable for people of fragile spirit: "However, replacing the global operator new() and operator delete() is not
for the fainthearted" . [ TC++PL-00 ] §15.6. He further points out that if two programmers write their own versions
of these operators, their code cannot be integrated into more general projects without additional effort.
[4] With the exceptions noted in ( 4.2.1d ). For example, the address of a function cannot be passed to them.
[5] The fact that an invocation of new actually involves an invocation of the function-operator, colloquially makes
operator new and function-operator new () almost synonymous. The Standard calls them allocation and
deallocation functions ("allocation functions" and "deallocations functions").
[6] See the new[] operator for arrays ( 4.9.20c ) for an explanation of the value n .
[7] B. Stroustrup [ TC++PL-00 ] §15.6: "Member operator new() and operator delete() are implicitly static
members." Consequently, they cannot be declared virtual ( 4.11.7 ).
[9] In reality, this invocation of the constructor is somewhat special, and in some ways different from what we can
do manually, since it creates the object in a specific place. Something we can't do with a normal constructor
(unless of course we use new ).
9 of 9 14/08/2024, 20:28