Overloadable operators you gotta love 'em! They allow you to give your types the same syntax as C++'s built-in types, yet they let you put a measure of power into the functions behind the operators that's unheard of for the built-ins. Of course, the fact that you can make symbols like "+
" and "==
" do anything you want also means you can use overloaded operators to produce programs best described as impenetrable. Adept C++ programmers know how to harness the power of operator overloading without descending into the
Regrettably, it is easy to make the descent. Single-argument constructors and implicit type conversion operators are particularly troublesome, because they can be invoked without there being any source code showing the calls. This can lead to program behavior that is difficult to understand. A different problem arises when you overload operators like &&
and ||
, because the shift from built-in operator to user-defined function yields a subtle change in semantics that's easy to overlook. Finally, many operators are related to one another in standard ways, but the ability to overload operators makes it possible to violate the accepted
In the items that follow, I focus on explaining when and how overloaded operators are called, how they behave, how they should relate to one another, and how you can seize control of these aspects of overloaded operators. With the information in this chapter under your belt, you'll be overloading (or not overloading) operators like a
Item 5: Be wary of user-defined conversion functions.
C++ allows compilers to perform implicit conversions between types. In honor of its C heritage, for example, the language allows silent conversions from char
to int
and from short
to double
. This is why you can pass a short
to a function that expects a double
and still have the call succeed. The more frightening conversions in C those that may lose information are also present in C++, including conversion of int
to short
and double
to (of all things) char
.
You can't do anything about such conversions, because they're hard-coded into the language. When you add your own types, however, you have more control, because you can choose whether to provide the functions compilers are allowed to use for implicit type
Two kinds of functions allow compilers to perform such conversions: single-argument constructors and implicit type conversion operators. A single-argument constructor is a constructor that may be called with only one argument. Such a constructor may declare a single parameter or it may declare multiple parameters, with each parameter after the first having a default value. Here are two
class Name { // for names of things public: Name(const string& s); // converts string to // Name ... }; class Rational { // for rational numbers public: Rational(int numerator = 0, // converts int to int denominator = 1); // Rational ... };
An implicit type conversion operator is simply a member function with a strange-looking name: the word operator
followed by a type specification. You aren't allowed to specify a type for the function's return value, because the type of the return value is basically just the name of the function. For example, to allow Rational
objects to be implicitly converted to double
s (which might be useful for mixed-mode arithmetic involving Rational
objects), you might define class Rational
like
class Rational { public: ... operator double() const; // converts Rational to }; // double
This function would be automatically invoked in contexts like
Rational r(1, 2); // r has the value 1/2 double d = 0.5 * r; // converts r to a double, // then does multiplication
Perhaps all this is review. That's fine, because what I really want to explain is why you usually don't want to provide type conversion functions of any
The fundamental problem is that such functions often end up being called when you neither want nor expect them to be. The result can be incorrect and unintuitive program behavior that is maddeningly difficult to
Let us deal first with implicit type conversion operators, as they are the easiest case to handle. Suppose you have a class for rational numbers similar to the one above, and you'd like to print Rational
objects as if they were a built-in type. That is, you'd like to be able to do
Rational r(1, 2); cout << r; // should print "1/2"
Further suppose you forgot to write an operator<<
for Rational
objects. You would probably expect that the attempt to print r
would fail, because there is no appropriate operator<<
to call. You would be mistaken. Your compilers, faced with a call to a function called operator<<
that takes a Rational
, would find that no such function existed, but they would then try to find an acceptable sequence of implicit type conversions they could apply to make the call succeed. The rules defining which sequences of conversions are acceptable are complicated, but in this case your compilers would discover they could make the call succeed by implicitly converting r
to a double
by calling Rational
::operator
double
. The result of the code above would be to print r
as a floating point number, not as a rational number. This is hardly a disaster, but it demonstrates the disadvantage of implicit type conversion operators: their presence can lead to the wrong function being called (i.e., one other than the one
The solution is to replace the operators with equivalent functions that don't have the syntactically magic names. For example, to allow conversion of a Rational
object to a double
, replace operator
double
with a function called something like asDouble
:
class Rational { public: ... double asDouble() const; // converts Rational }; // to double
Such a member function must be called
Rational r(1, 2); cout << r; // error! No operator<< // for Rationals cout << r.asDouble(); // fine, prints r as a // double
In most cases, the inconvenience of having to call conversion functions explicitly is more than compensated for by the fact that unintended functions can no longer be silently invoked. In general, the more experience C++ programmers have, the more likely they are to eschew type conversion operators. The members of string
type they added to the library contains no implicit conversion from a string
object to a C-style char*
. Instead, there's an explicit member function, c_str
, that performs that conversion. Coincidence? I think
Implicit conversions via single-argument constructors are more difficult to eliminate. Furthermore, the problems these functions cause are in many cases worse than those arising from implicit type conversion
As an example, consider a class template for array objects. These arrays allow clients to specify upper and lower index
template<class T> class Array { public: Array(int lowBound, int highBound); Array(int size); T& operator[](int index); ... };
The first constructor in the class allows clients to specify a range of array indices, for example, from 10 to 20. As a two-argument constructor, this function is ineligible for use as a type-conversion function. The second constructor, which allows clients to define Array
objects by specifying only the number of elements in the array (in a manner similar to that used with built-in arrays), is different. It can be used as a type conversion function, and that can lead to endless
For example, consider a template specialization for comparing Array<int>
objects and some code that uses such
bool operator==(const Array<int>& lhs, const Array<int>& rhs); Array<int> a(10); Array<int> b(10); ... for (int i = 0; i < 10; ++i) if (a == b[i]) { // oops! "a" should be "a[i]" do something for when a[i] and b[i] are equal; } else { do something for when they're not; }
We intended to compare each element of a
to the corresponding element in b
, but we accidentally omitted the subscripting syntax when we typed a
. Certainly we expect this to elicit all manner of unpleasant commentary from our compilers, but they will complain not at all. That's because they see a call to operator==
with arguments of type Array<int>
(for a
) and int
(for b[i]
), and though there is no operator==
function taking those types, our compilers notice they can convert the int
into an Array<int>
object by calling the Array<int>
constructor that takes a single int
as an argument. This they proceed to do, thus generating code for a program we never meant to write, one that looks like
for (int i = 0; i < 10; ++i) if (a == static_cast< Array<int> >(b[i])) ...
Each iteration through the loop thus compares the contents of a
with the contents of a temporary array of size b[i]
(whose contents are presumably undefined). Not only is this unlikely to behave in a satisfactory manner, it is also tremendously inefficient, because each time through the loop we both create and destroy a temporary Array<int>
object (see Item 19).
The drawbacks to implicit type conversion operators can be avoided by simply failing to declare the operators, but single-argument constructors cannot be so easily waved away. After all, you may really want to offer single-argument constructors to your clients. At the same time, you may wish to prevent compilers from calling such constructors indiscriminately. Fortunately, there is a way to have it all. In fact, there are two ways: the easy way and the way you'll have to use if your compilers don't yet support the easy
The easy way is to avail yourself of one of the newest C++ features, the explicit
keyword. This feature was introduced specifically to address the problem of implicit type conversion, and its use is about as straightforward as can be. Constructors can be declared explicit
, and if they are, compilers are prohibited from invoking them for purposes of implicit type conversion. Explicit conversions are still legal,
template<class T> class Array { public: ... explicit Array(int size); // note use of "explicit" ... }; Array<int> a(10); // okay, explicit ctors can // be used as usual for // object construction Array<int> b(10); // also okay if (a == b[i]) ... // error! no way to // implicitly convert // int to Array<int> if (a == Array<int>(b[i])) ... // okay, the conversion // from int to Array<int> is // explicit (but the logic of // the code is suspect) if (a == static_cast< Array<int> >(b[i])) ... // equally okay, equally // suspect if (a == (Array<int>)b[i]) ... // C-style casts are also // okay, but the logic of // the code is still suspect
In the example using static_cast
(see Item 2), the space separating the two ">
" characters is no accident. If the statement were written like
if (a == static_cast<Array<int>>(b[i])) ...
it would have a different meaning. That's because C++ compilers parse ">>
" as a single token. Without a space between the ">
" characters, the statement would generate a syntax
If your compilers don't yet support explicit
, you'll have to fall back on home-grown methods for preventing the use of single-argument constructors as implicit type conversion functions. Such methods are obvious only after you've seen
I mentioned earlier that there are complicated rules governing which sequences of implicit type conversions are legitimate and which are not. One of those rules is that no sequence of conversions is allowed to contain more than one user-defined conversion (i.e., a call to a single-argument constructor or an implicit type conversion operator). By constructing your classes properly, you can take advantage of this rule so that the object constructions you want to allow are legal, but the implicit conversions you don't want to allow are
Consider the Array
template again. You need a way to allow an integer specifying the size of the array to be used as a constructor argument, but you must at the same time prevent the implicit conversion of an integer into a temporary Array
object. You accomplish this by first creating a new class, ArraySize
. Objects of this type have only one purpose: they represent the size of an array that's about to be created. You then modify Array
's single-argument constructor to take an ArraySize
object instead of an int
. The code looks like
template<class T> class Array { public: class ArraySize { // this class is new public: ArraySize(int numElements): theSize(numElements) {} int size() const { return theSize; } private: int theSize; }; Array(int lowBound, int highBound); Array(ArraySize size); // note new declaration ... };
Here you've nested ArraySize
inside Array
to emphasize the fact that it's always used in conjunction with that class. You've also made ArraySize
public in Array
so that anybody can use it.
Consider what happens when an Array
object is defined via the class's single-argument
Array<int> a(10);
Your compilers are asked to call a constructor in the Array<int>
class that takes an int
, but there is no such constructor. Compilers realize they can convert the int
argument into a temporary ArraySize
object, and that ArraySize
object is just what the Array<int>
constructor needs, so compilers perform the conversion with their usual gusto. This allows the function call (and the attendant object construction) to
The fact that you can still construct Array
objects with an int
argument is reassuring, but it does you little good unless the type conversions you want to avoid are prevented. They are. Consider this code
bool operator==(const Array<int>& lhs, const Array<int>& rhs);
Array<int> a(10); Array<int> b(10);
...
for (int i = 0; i < 10; ++i) if (a == b[i]) ... // oops! "a" should be "a[i]"; // this is now an error
Compilers need an object of type Array<int>
on the right-hand side of the "==
" in order to call operator==
for Array<int>
objects, but there is no single-argument constructor taking an int
argument. Furthermore, compilers cannot consider converting the int
into a temporary ArraySize
object and then creating the necessary Array<int>
object from this temporary, because that would call for two user-defined conversions, one from int
to ArraySize
and one from ArraySize
to Array<int>
. Such a conversion sequence is verboten, so compilers must issue an error for the code attempting to perform the
The use of the ArraySize
class in this example might look like a special-purpose hack, but it's actually a specific instance of a more general technique. Classes like ArraySize
are often called proxy classes, because each object of such a class stands for (is a proxy for) some other object. An ArraySize
object is really just a stand-in for the integer used to specify the size of the Array
being created. Proxy objects can give you control over aspects of your software's behavior in this case implicit type conversions that is otherwise beyond your grasp, so it's well worth your while to learn how to use them. How, you might wonder, can you acquire such learning? One way is to turn to Item 30; it's devoted to proxy
Before you turn to proxy classes, however, reflect a bit on the lessons of this Item. Granting compilers license to perform implicit type conversions usually leads to more harm than good, so don't provide conversion functions unless you're sure you want
Item 6: Distinguish between prefix and postfix forms of increment and decrement operators.
Long, long ago (the late '80s) in a language far, far away (C++ at that time), there was no way to distinguish between prefix and postfix invocations of the ++
and --
operators. Programmers being programmers, they kvetched about this omission, and C++ was extended to allow overloading both forms of increment and decrement
There was a syntactic problem, however, and that was that overloaded functions are differentiated on the basis of the parameter types they take, but neither prefix nor postfix increment or decrement takes an argument. To surmount this linguistic pothole, it was decreed that postfix forms take an int
argument, and compilers silently pass 0 as that int
when those functions are
class UPInt { // "unlimited precision int" public: UPInt& operator++(); // prefix ++ const UPInt operator++(int); // postfix ++ UPInt& operator--(); // prefix -- const UPInt operator--(int); // postfix -- UPInt& operator+=(int); // a += operator for UPInts // and ints ...
};
UPInt i;
++i; // calls i.operator++(); i++; // calls i.operator++(0);
--i; // calls i.operator--(); i--; // calls i.operator--(0);
This convention is a little on the odd side, but you'll get used to it. More important to get used to, however, is this: the prefix and postfix forms of these operators return different types. In particular, prefix forms return a reference, postfix forms return a const
object. We'll focus here on the prefix and postfix ++
operators, but the story for the --
operators is
From your days as a C programmer, you may recall that the prefix form of the increment operator is sometimes called "increment and fetch," while the postfix form is often known as "fetch and increment." These two phrases are important to remember, because they all but act as formal specifications for how prefix and postfix increment should be
// prefix form: increment and fetch UPInt& UPInt::operator++() { *this += 1; // increment return *this; // fetch }
// postfix form: fetch and increment const UPInt UPInt::operator++(int) { UPInt oldValue = *this; // fetch ++(*this); // increment
return oldValue; // return what was } // fetched
Note how the postfix operator makes no use of its parameter. This is typical. The only purpose of the parameter is to distinguish prefix from postfix function invocation. Many compilers issue warnings (see Item E48) if you fail to use named parameters in the body of the function to which they apply, and this can be annoying. To avoid such warnings, a common strategy is to omit names for parameters you don't plan to use; that's what's been done
It's clear why postfix increment must return an object (it's returning an old value), but why a const
object? Imagine that it did not. Then the following would be
UPInt i;
i++++; // apply postfix increment // twice
i.operator++(0).operator++(0);
and it should be clear that the second invocation of operator++
is being applied to the object returned from the first
There are two reasons to abhor this. First, it's inconsistent with the behavior of the built-in types. A good rule to follow when designing classes is when in doubt, do as the int
s do, and the int
s most certainly do not allow double application of postfix
int i;
i++++; // error!
The second reason is that double application of postfix increment almost never does what clients expect it to. As noted above, the second application of operator++
in a double increment changes the value of the object returned from the first invocation, not the value of the original object. Hence,
i++++;
were legal, i
would be incremented only once. This is counterintuitive and confusing (for both int
s and UPInt
s), so it's best
C++ prohibits it for int
s, but you must prohibit it yourself for classes you write. The easiest way to do this is to make the return type of postfix increment a const
object. Then when compilers
i++++; // same as i.operator++(0).operator++(0);
they recognize that the const
object returned from the first call to operator++
is being used to call operator++
again. operator++
, however, is a non-const
member function, so const
objects such as those returned from postfix operator++
can't call it.2 If you've ever wondered if it makes sense to have functions return const
objects, now you know: sometimes it does, and postfix increment and decrement are examples. (For another example, turn to Item E21.)
If you're the kind who worries about efficiency, you probably broke into a sweat when you first saw the postfix increment function. That function has to create a temporary object for its return value (see Item 19), and the implementation above also creates an explicit temporary object (oldValue
) that has to be constructed and destructed. The prefix increment function has no such temporaries. This leads to the possibly startling conclusion that, for efficiency reasons alone, clients of UPInt
should prefer prefix increment to postfix increment unless they really need the behavior of postfix increment. Let us be explicit about this. When dealing with user-defined types, prefix increment should be used whenever possible, because it's inherently more
Let us make one more observation about the prefix and postfix increment operators. Except for their return values, they do the same thing: they increment a value. That is, they're supposed to do the same thing. How can you be sure the behavior of postfix increment is consistent with that of prefix increment? What guarantee do you have that their implementations won't diverge over time, possibly as a result of different programmers maintaining and enhancing them? Unless you've followed the design principle embodied by the code above, you have no such guarantee. That principle is that postfix increment and decrement should be implemented in terms of their prefix counterparts. You then need only maintain the prefix versions, because the postfix versions will automatically behave in a consistent
As you can see, mastering prefix and postfix increment and decrement is easy. Once you know their proper return types and that the postfix operators should be implemented in terms of the prefix operators, there's very little more to
Item 7: Never overload &&
, ||
, or ,
.
Like C, C++ employs short-circuit evaluation of boolean expressions. This means that once the truth or falsehood of an expression has been determined, evaluation of the expression ceases, even if some parts of the expression haven't yet been examined. For example, in this
char *p;
...
if ((p != 0) && (strlen(p) > 10)) ...
there is no need to worry about invoking strlen
on p
if it's a null pointer, because if the test of p
against 0 fails, strlen
will never be called. Similarly,
int rangeCheck(int index) { if ((index < lowerBound) || (index > upperBound)) ... ... }
index
will never be compared to upperBound
if it's less than lowerBound
.
This is the behavior that has been drummed into C and C++ programmers since time immemorial, so this is what they expect. Furthermore, they write programs whose correct behavior depends on short-circuit evaluation. In the first code fragment above, for example, it is important that strlen
not be invoked if p
is a null pointer, because the strlen
on a null pointer is
C++ allows you to customize the behavior of the &&
and ||
operators for user-defined types. You do it by overloading the functions operator&&
and operator||
, and you can do this at the global scope or on a per-class basis. If you decide to take advantage of this opportunity, however, you must be aware that you are changing the rules of the game quite radically, because you are replacing short-circuit semantics with function call semantics. That is, if you overload operator&&
, what looks to you like
if (expression1 && expression2) ...
looks to compilers like one of
if (expression1.operator&&(expression2)) ... // when operator&& is a // member function
if (operator&&(expression1, expression2)) ... // when operator&& is a // global function
This may not seem like that big a deal, but function call semantics differ from short-circuit semantics in two crucial ways. First, when a function call is made, all parameters must be evaluated, so when calling the functions operator&&
and operator||
, both parameters are evaluated. There is, in other words, no short circuit. Second, the language specification leaves undefined the order of evaluation of parameters to a function call, so there is no way of knowing whether expression1
or expression2
will be evaluated first. This stands in stark contrast to short-circuit evaluation, which always evaluates its arguments in left-to-right
As a result, if you overload &&
or ||
, there is no way to offer programmers the behavior they both expect and have come to depend on. So don't overload &&
or ||
.
The situation with the comma operator is similar, but before we delve into that, I'll pause and let you catch the breath you lost when you gasped, "The comma operator? There's a comma operator?" There is
The comma operator is used to form expressions, and you're most likely to run across it in the update part of a for
loop. The following function, for example, is based on one in the second edition of Kernighan's and Ritchie's classic
// reverse string s in place void reverse(char s[]) { for (int i = 0, j = strlen(s)-1; i < j; ++i, --j) // aha! the comma operator! { int c = s[i]; s[i] = s[j]; s[j] = c; } }
Here, i
is incremented and j
is decremented in the final part of the for
loop. It is convenient to use the comma operator here, because only an expression is valid in the final part of a for
loop; separate statements to change the values of i
and j
would be
Just as there are rules in C++ defining how &&
and ||
behave for built-in types, there are rules defining how the comma operator behaves for such types. An expression containing a comma is evaluated by first evaluating the part of the expression to the left of the comma, then evaluating the expression to the right of the comma; the result of the overall comma expression is the value of the expression on the right. So in the final part of the loop above, compilers first evaluate ++i
, then --j
, and the result of the comma expression is the value returned from --j
.
Perhaps you're wondering why you need to know this. You need to know because you need to mimic this behavior if you're going to take it upon yourself to write your own comma operator. Unfortunately, you can't perform the requisite
If you write operator,
as a non-member function, you'll never be able to guarantee that the left-hand expression is evaluated before the right-hand expression, because both expressions will be passed as arguments in a function call (to operator,
). But you have no control over the order in which a function's arguments are evaluated. So the non-member approach is definitely
That leaves only the possibility of writing operator,
as a member function. Even here you can't rely on the left-hand operand to the comma operator being evaluated first, because compilers are not constrained to do things that way. Hence, you can't overload the comma operator and also guarantee it will behave the way it's supposed to. It therefore seems imprudent to overload it at
You may be wondering if there's an end to this overloading madness. After all, if you can overload the comma operator, what can't you overload? As it turns out, there are limits. You can't overload the following
. .* :: ?: new delete sizeof typeid static_cast dynamic_cast const_cast reinterpret_castYou can overload these:
operator new operator delete operatornew[] operatordelete[] + - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- , ->* -> () []
(For information on the new
and delete
operators, as well as operator
new
, operator
delete
, operator
, and operator
, see Item 8.)
Of course, just because you can overload these operators is no reason to run off and do it. The purpose of operator overloading is to make programs easier to read, write, and understand, not to dazzle others with your knowledge that comma is an operator. If you don't have a good reason for overloading an operator, don't overload it. In the case of &&
, ||
, and ,
, it's difficult to have a good reason, because no matter how hard you try, you can't make them behave the way they're supposed
Item 8: Understand the different meanings of new
and delete
.
It occasionally seems as if people went out of their way to make C++ terminology difficult to understand. Case in point: the difference between the new
operator and operator
new
.
When you write code like
string *ps = new string("Memory Management");
the new
you are using is the new
operator. This operator is built into the language and, like sizeof
, you can't change its meaning: it always does the same thing. What it does is twofold. First, it allocates enough memory to hold an object of the type requested. In the example above, it allocates enough memory to hold a string
object. Second, it calls a constructor to initialize an object in the memory that was allocated. The new
operator always does those two things; you can't change its behavior in any
What you can change is how the memory for an object is allocated. The new
operator calls a function to perform the requisite memory allocation, and you can rewrite or overload that function to change its behavior. The name of the function the new
operator calls to allocate memory is operator
new
.
The operator
new
function is usually declared like
void * operator new(size_t size);
The return type is void*
, because this function returns a pointer to raw, uninitialized memory. (If you like, you can write a version of operator
new
that initializes the memory to some value before returning a pointer to it, but this is not commonly done.) The size_t
parameter specifies how much memory to allocate. You can overload operator
new
by adding additional parameters, but the first parameter must always be of type size_t
. (For information on writing operator
new
, consult Items E8-E10.)
You'll probably never want to call operator
new
directly, but on the off chance you do, you'll call it just like any other
void *rawMemory = operator new(sizeof(string));
Here operator
new
will return a pointer to a chunk of memory large enough to hold a string
Like malloc
, operator
new
's only responsibility is to allocate memory. It knows nothing about constructors. All operator
new
understands is memory allocation. It is the job of the new
operator to take the raw memory that operator
new
returns and transform it into an object. When your compilers see a statement
string *ps = new string("Memory Management");
they must generate code that more or less corresponds to this (see Items E8 and E10, as well as the sidebar to my article on counting objects, for a more detailed treatment of this
void *memory = // get raw memory operator new(sizeof(string)); // for a string // object
call string::string("Memory Management") // initialize the on *memory; // object in the // memory
string *ps = // make ps point to static_cast<string*>(memory); // the new object
Notice that the second step above involves calling a constructor, something you, a mere programmer, are prohibited from doing. Your compilers are unconstrained by mortal limits, however, and they can do whatever they like. That's why you must use the new
operator if you want to conjure up a heap-based object: you can't directly call the constructor necessary to initialize the object (including such crucial components as its vtbl see Item 24).
There are times when you really want to call a constructor directly. Invoking a constructor on an existing object makes no sense, because constructors initialize objects, and an object can only be initialized given its first value once. But occasionally you have some raw memory that's already been allocated, and you need to construct an object in the memory you have. A special version of operator
new
called placement new
allows you to do
As an example of how placement new
might be used, consider
class Widget { public: Widget(int widgetSize); ... };
Widget * constructWidgetInBuffer(void *buffer, int widgetSize) { return new (buffer) Widget(widgetSize); }
This function returns a pointer to a Widget
object that's constructed within the buffer passed to the function. Such a function might be useful for applications using shared memory or memory-mapped I/O, because objects in such applications must be placed at specific addresses or in memory allocated by special routines. (For a different example of how placement new
can be used, see Item 4.)
Inside constructWidgetInBuffer
, the expression being returned
new (buffer) Widget(widgetSize)
This looks a little strange at first, but it's just a use of the new
operator in which an additional argument (buffer
) is being specified for the implicit call that the new
operator makes to operator
new
. The operator
new
thus called must, in addition to the mandatory size_t
argument, accept a void*
parameter that points to the memory the object being constructed is to occupy. That operator
new
is placement new
, and it looks like
void * operator new(size_t, void *location) { return location; }
This is probably simpler than you expected, but this is all placement new
needs to do. After all, the purpose of operator
new
is to find memory for an object and return a pointer to that memory. In the case of placement new
, the caller already knows what the pointer to the memory should be, because the caller knows where the object is supposed to be placed. All placement new
has to do, then, is return the pointer that's passed into it. (The unused (but mandatory) size_t
parameter has no name to keep compilers from complaining about its not being used; see Item 6.) Placement new
is part of the standard C++ library (see Item E49). To use placement new
, all you have to do is #include
<new>
(or, if your compilers don't yet support the new-style header names (again, see Item E49), <new.h>
).
If we step back from placement new
for a moment, we'll see that the relationship between the new
operator and operator
new
, though
you want to create an object on the heap, use the new
operator. It both allocates memory and calls a constructor for the object. If you only want to allocate memory, call operator
new
; no constructor will be called. If you want to customize the memory allocation that takes place when heap objects are created, write your own version of operator
new
and use the new
operator; it will automatically invoke your custom version of operator
new
. If you want to construct an object in memory you've already got a pointer to, use placement new
.
(For additional insights into variants of new
and delete
, see Item E7 and my article on counting objects.)
Deletion and Memory Deallocation
To avoid resource leaks, every dynamic allocation must be matched by an equal and opposite deallocation. The function operator
delete
is to the built-in delete
operator as operator
new
is to the new
operator. When you say something like
string *ps; ... delete ps; // use the delete operator
your compilers must generate code both to destruct the object ps
points to and to deallocate the memory occupied by that
The memory deallocation is performed by the operator
delete
function, which is usually declared like
void operator delete(void *memoryToBeDeallocated);
delete ps;
causes compilers to generate code that approximately corresponds to
ps->~string(); // call the object's dtor
operator delete(ps); // deallocate the memory // the object occupied
One implication of this is that if you want to deal only with raw, uninitialized memory, you should bypass the new
and delete
operators entirely. Instead, you should call operator
new
to get the memory and operator
delete
to return it to the
void *buffer = // allocate enough operator new(50*sizeof(char)); // memory to hold 50 // chars; call no ctors
...
operator delete(buffer); // deallocate the memory; // call no dtors
This is the C++ equivalent of calling malloc
and free
.
If you use placement new
to create an object in some memory, you should avoid using the delete
operator on that memory. That's because the delete
operator calls operator
delete
to deallocate the memory, but the memory containing the object wasn't allocated by operator
new
in the first place; placement new
just returned the pointer that was passed to it. Who knows where that pointer came from? Instead, you should undo the effect of the constructor by explicitly calling the object's
// functions for allocating and deallocating memory in // shared memory void * mallocShared(size_t size); void freeShared(void *memory);
void *sharedMemory = mallocShared(sizeof(Widget));
Widget *pw = // as above, constructWidgetInBuffer(sharedMemory, 10); // placement // new is used
...
delete pw; // undefined! sharedMemory came from // mallocShared, not operator new
pw->~Widget(); // fine, destructs the Widget pointed to // by pw, but doesn't deallocate the // memory containing the Widget
freeShared(pw); // fine, deallocates the memory pointed // to by pw, but calls no destructor
As this example demonstrates, if the raw memory passed to placement new
was itself dynamically allocated (through some unconventional means), you must still deallocate that memory if you wish to avoid a memory leak. (See the sidebar to my article on counting objects for information on "placement delete
".)
So far so good, but there's farther to go. Everything we've examined so far concerns itself with only one object at a time. What about array allocation? What happens
string *ps = new string[10]; // allocate an array of // objects
The new
being used is still the new
operator, but because an array is being created, the new
operator behaves slightly differently from the case of single-object creation. For one thing, memory is no longer allocated by operator
new
. Instead, it's allocated by the array-allocation equivalent, a function called operator
(often referred to as "array new
.") Like operator
new
, operator
can be overloaded. This allows you to seize control of memory allocation for arrays in the same way you can control memory allocation for single objects (but see Item E8 for some caveats on
(operator
is a relatively recent addition to C++, so your compilers may not support it yet. If they don't, the global version of operator
new
will be used to allocate memory for every array, regardless of the type of objects in the array. Customizing array-memory allocation under such compilers is daunting, because it requires that you rewrite the global operator
new
. This is not a task to be undertaken lightly. By default, the global operator
new
handles all dynamic memory allocation in a program, so any change in its behavior has a dramatic and pervasive effect. Furthermore, there is only one global operator
new
with the "normal" signature (i.e., taking the single size_t
parameter see Item E9), so if you decide to claim it as your own, you instantly render your software incompatible with any library that makes the same decision. (See also Item 27.) As a result of these considerations, custom memory management for arrays is not usually a reasonable design decision for compilers lacking support for operator
.)
The second way in which the new
operator behaves differently for arrays than for objects is in the number of constructor calls it makes. For arrays, a constructor must be called for each object in the
string *ps = // call operatornew[] to allocate new string[10]; // memory for 10 string objects, // then call the default string // ctor for each array element
Similarly, when the delete
operator is used on an array, it calls a destructor for each array element and then calls operator
to deallocate the
delete [] ps; // call the string dtor for each // array element, then call // operatordelete[] to // deallocate the array's memory
Just as you can replace or overload operator
delete
, you can replace or overload operator
. There are some restrictions on how they can be overloaded, however; consult a good C++ text for details. (For ideas on good C++ texts, see the recommendations beginning on page 285.)
So there you have it. The new
and delete
operators are built-in and beyond your control, but the memory allocation and deallocation functions they call are not. When you think about customizing the behavior of the new
and delete
operators, remember that you can't really do it. You can modify how they do what they do, but what they do is fixed by the