Вы находитесь на странице: 1из 171

Copyright 2005 Alf P.

Steinbach

1 Pointers.
Basically, a pointer is a value that identifies
where some object or function is located in
the computers memory.

Summary.
Basically, a pointer is a very small value, typically 4 or 8
bytes, that identifies where some object or function is
located in the computers memory. We also say that a
variable that can hold a pointer value is a pointer.
Sometimes we also say that a pointer type is a pointer.

We also say that a variable (or other object)


that can hold a pointer value, is a pointer.
Sometimes the type of a pointer value (or
pointer variable) is called a pointer, and
Pointers allow you to (1) avoid inefficient copying of
sometimes the word pointer is used for the
large objects, (2) write code that works with unknown
objects of partially unknown type, and (3) use objects
general concept of something that identifies
where something else is located. But the basic that refer to each other, e.g., in GUI programming.
meaning that all the other meanings derive
from, is a value that identifies where some object or function is located in the computers
memory.

C++ pointer values are very small, typically 4 or 8 bytes per value. This is just about the same
size as an int value. So instead of copying large objects to make them available wherever theyre
needed, you can just copy small pointer values that tell where the large objects are to be found.
Pointers are partly about that kind of optimization. But more importantly pointers allow you to
write code that can use and work on specified objects without knowing exactly which objects
they are, or even the exact type of objects they are. And by storing pointers in objects you can
have objects that refer to each other, which allows for very efficient data storage and retrieval,
and is essential to, for example, modern GUI (graphical user interface) programming.
Finally, due to the heritage from C, the C++ language is in many respects strongly tied to the use
of pointers, so that you simply cannot avoid pointers in professional programming. For example,
the second standard argument to main (main can also be declared without arguments) is a
pointer, and every non-static member function in a C++ class has an implicit pointer argument
called this. And if youve ever written Hello, world! in C++ you have already used pointers
theyre that omnipresent because its a pointer to the text thats passed to operator<<.

1.1 Introduction to the basics.


A pointer that tells where an object object is
located is said to point to object, and is said to
be a pointer to object.

Summary.
A pointer that tells where an object o is located is said
to point to o, and is said to be a pointer to o.

In the first subsection below I explain how


you can obtain a pointer to an object, such as a local variable, and how you can refer to that
object when you have the pointer in hand.
The following subsections then discuss the most obvious usage of pointers, namely how to use
pointers as function arguments. You should know about that because its (unfortunately) very
common and because its sometimes the right thing to do. However, I also discuss how C++
references are, in general, much better both safer and simpler for this particular usage.

Pointers chapter 1.

1.1.1

How to obtain a pointer to a given object, and how to use that pointer.

Summary.
Examples, dereferencing and address operator (a pointer value is an address):
*p

Referring to the object a pointer points to (dereferencing).

int object;
int* p;

Declaring a pointer.

p = &object;
*p = 42;

Obtaining a pointer to (also called address of) a given object.


Assigning to the object a pointer points to.

The address operator & yields an rvalue, a value-producing expression like 42 or a+b that can only form the right
hand side of an assignment, while the dereferencing operator * yields an lvalue that, disregarding constness, can
form the left hand side of an assignment.
The * in a declaration affects only that declarator, not subsequent ones:
int *p, x, *q, y;

p and q are pointers, x and y are not.

The type of p and q is int*, the type of x and y is int.


Pointer variables can be assigned to, and need not (generally) be initialized in the declaration.
If you have a pointer to an object with a member name, then with the basic * operator you must write, e.g.,
(*p).name to access that member. With the -> operator you can write p->name.

Given a pointer p that points to an object object, you can refer to object via the expression
*p

Referring to the object a pointer points to.

Thats called dereferencing the pointer.


For example, if object is an int variable, you can assign the value 42 to that int variable by
*p = 42;

Assigning to the object a pointer points to.

which doesnt change p, but the object that p points to. By applying the basic dereferencing
operator, *p, you have the object that p points to, and that object is then assigned to. And
much of the point (uh) is that you dont need to know exactly which object that is.
Of course for this to work you need to declare p, and you need to make p point to something.
Lets do that in order. To declare p you mimic the usage of p, e.g., the declaration
Declaring a pointer.

int *p;

says that if you have p, and apply the basic dereferencing operator, *p, you get an int object;
hence p is a pointer to int.
If you want you can declare several things at once in one declaration, like
int *p, x, *q, y;

p and q are pointers, x and y are not.

Copyright 2005 Alf P. Steinbach

Here p and q are pointers to int, while x and y are ordinary int variables. So, the * in a single
declarator like *p does not, language-wise, belong with the full declarations base type: it does
not affect subsequent declarators. It is a type modifier for (and only for) the thing declared.
Just to get into Good Habits at once you should never declare more than one pointer in one
declaration Ill move that asterisk one step to the left, so that its textually with the type:
Declaring a pointer, with formatting in the spirit of C++.

int* p;

This C++ way of writing the declaration makes it more clear that the type of p is int*, and that
you can write int* where a specification of that type is required. It can be misleading if you
declare several things at one go. But that can in itself be misleading, and you simply shouldnt.
Now we also need an int variable that p can point to, e.g.
int

object;

And to make p point to object you just apply the address operator, &, to object:
Obtaining a pointer to (also called address of) a given object.

p = &object;

The address operator & produces a pointer value also called an address1 that identifies the
location of the given object. The result of & is a pure rvalue, a value-producing expression like
42 or a+b that can only form the right hand side of an assignment. In contrast, * yields an
lvalue: disregarding constness2 it can form the left hand side of an assignment, e.g. *p = 42;.
And in a sense the address and dereferencing operators are inverses of each other, because
*&object (first, &, take the address of object, and then, *, dereference that address) is
identically the same as writing just object. On the other hand, with the C++98 standard its not
always formally the case that &*p is identically the same as writing just p, because the address
might not permit dereferencing. And when p is a variable, as above, p is an lvalue that can be
assigned to, whereas &*p yields an rvalue.
Putting it all together:
basics/simple_pointer.cpp
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
int main()
{
int
int*

o;
p;

p = &o;
// Set p to point to o.
*p = 42;
// Assign 42 to the object that p points to.
std::cout << o << std::endl;

You can of course initialize a pointer variable in its declaration, and usually that is a Good Idea,
as it is for any variable when you know what its first useful value will be. For pedagogical
purposes I havent done that here. The example shows that C++ doesnt force you to initialize a
pointer variable in its declaration, and also, that you can assign to pointer variables.
Although the terms are not very strictly defined, its common (but not universal) to regard address as a more lowlevel concept than pointer, with address as a machine code level concept, a typeless-bits-and-bytes level value.
2
const as it applies to pointers is discussed at the end of section 1.1.4, and in section 1.1.6.
1

Pointers chapter 1.

The first assignment, without dereferencing, just changes which object p points to; the second
assignment, with dereferencing, changes the value of the object that p points to.
Make sure you understand this example before reading on. E.g., what output do you expect,
from analyzing the source code above, and does the program produce that expected output?
And, for example, does the order of the two commented statements matter?
In addition to the basic * there are two almost-as-general dereferencing operators: -> and [].
Both of them are convenience operators, syntactic sugaring, expressing in a generally more readable
way common ways of using the * operator. Here Ill just discuss the -> operator. [] is used
with raw arrays, which I defer discussion of to a later chapter. Thats because youre generally
better served by standard library classes such as std::vector and std::string.
Since thats important, let me repeat it with emphasis, so that it really stands out:
Raw arrays are Very Dangerous and in C++ they are seldom actually needed.
You should instead use standard library classes such as std::vector and std::string.
And with std::vector and std::string, preferentially use the at member function
(which performs a safety check) rather than the [] operator (which has no safety).
Back to the -> operator
The -> operator is used to refer to members of an object you have a pointer to. The * operator
has lower precedence than the . member selection operator, so with the basic * operator you
must write, e.g., (*p).name. With the -> operator you can write p->name.
Using the basic * operator:

Using the -> operator:

(*p).name

p->name

The * operator has lower precedence than the . member


selection operator.

Much of the point is that when objects contain pointers to other objects, then the -> operator is
generally much more readable than the basic * operator. For example, if pAlbum points to an
object that represents a music album, that object might have a member artist that is a pointer
to an object representing the artist, which in turn might have a member name that is the name of
the artist. Then to access the artists name, with the basic * operator youd have to write
(*(*pAlbum).artist).name

which is neither obvious nor very readable, whereas with the -> operator you can write just
pAlbum->artist->name

So even though -> doesnt add more power to the language in terms of what you can do, it adds
more power in terms of what you can easily express, i.e. how easily you can do it.

Copyright 2005 Alf P. Steinbach

1.1.2

The nullpointer value, valid and invalid pointers.

Summary.
0, the nullpointer value, is a valid pointer value that points to nothing, and can be compared to other pointers via
== and !=, but in C++98, can generally not be dereferenced (Undefined Behavior).
NULL is macro defined as some integer 0 in <cstddef>, denoting the nullpointer value.

When a pointer is used where a bool is expected, the nullpointer value yields false, just as the nullvalue of any
built-in type yields false, and any other valid pointer value yields true.
A pointer variable that isnt initialized has an indeterminate value, which its Undefined Behavior to use in any way
whatsoever (including just copying it to some other pointer variable).
Doing anything with an invalid pointer value, one that isnt valid, can be Undefined Behavior. An indeterminate
pointer is an example of an invalid pointer value. A dangling pointer, one that earlier pointed to an object but
where that object has ceased to exist, is another example.
I call a pointer that points to an existing object, a RealGood pointer, and one that isnt RealGood, an URG (UnRealGood) pointer. A RealGood pointer, the nullpointer value, and a pointer that points to a hypothetical array
element one past the end of an array (not discussed here), are examples of valid pointer values. There are also other
valid pointer values.

Since C++ does not guarantee automatic initialization of local variables of built-in types or
pointer types, an uninitialized local pointer variable can hold an arbitrary, meaningless value, an
indeterminate value. Dereferencing such a pointer, especially assigning to what it points to,
is very likely to do something Really Bad, like crashing the program. For example,
basics/bad_pointer_01.cpp
int main()
{
int*
p;
*p = 43;
}

Compiled with MinGW g++ 3.4.2 under Windows XP:

Pointers chapter 1.

This can happen even if you just try to inspect what the pointer points to, although that is less
likely. And much less likely, on the order of the probability of being simultanously hit by a
wayward, crashing jetfighter and a hippopotamus on the loose in the near arctic city of Troms,
but still possible on platforms other than Windows and Linux, something like this can happen
even if you just try to assign the pointer value to another pointer variable, with no dereferencing1.
Its an invalid pointer value, and using an invalid pointer value in any way whatsoever, even just
looking at it, is or can be formally Undefined Behavior, where the program can do anything.
C++ novices, especially those coming from languages such as C# and Java, sometimes create
invalid pointer values by letting a function return a pointer to a local variable:
basics/bad_pointer_02.cpp
#include
<iostream>
#include
<ostream>

// std::cout
// <<, std::endl

int* wrongAnswer()
{
int x = 43;
return &x;
}
int main()
{
int*

= wrongAnswer();

std::cout << "The Answer Is..." << std::endl;


std::cout << *p << std::endl;

// Reuse x's memory location.


// Not necessarily 43...

After the wrongAnswer() function has returned to the caller the local variable x no longer exists,
and so the returned pointer to it is invalid. Since dereferencing that invalid pointer is Undefined
Behavior the result can be anything. For example, the result can be 43, and without the first
output statement it is, with some compilers, 43 (with the statement it can still be 43).
So, exactly what is an invalid pointer value, and what is a valid pointer value? While the C++98
standard does use these terms it doesnt provide a full definition, leaving it to the reader to
intuitively grasp whats meant by invalid and valid. However, invalid isnt difficult to define,
as long as we keep in mind that this is our own definition, not the standards:

An invalid pointer is one that isnt valid, and any use of it should be regarded as
Undefined Behavior.

That reduces the problem to defining what a valid pointer is, and now with a helpful constraint.
Intuitively a valid pointer value is one that points to an existing object. But the nullpointer value
of any particular pointer type can be safely copied and compared to other pointers via the built-in
== and != operators, and so is valid in that sense, but intentionally does not point to anything: its
a valid pointer value that doesnt point to anything. Id like to write that its the only valid pointer
that doesnt point to anything, which would make things much simpler conceptually2, but the

The standards 4.1/1 establishes formal Undefined Behavior for use of the value of any uninitalized variable, and
3.7.3.2/4 mentions that any use of an invalid pointer value is also Undefined Behavior, but unfortunately with only
a partial definition of what an invalid pointer value or valid pointer value is (the partial definition there is apparently
not the one assumed in e.g. 24.4.1/2, so were left with a half-formal-half-intuitive notion of validity).
2
In early Pascal, a language designed for learning programming, the nullpointer, there called nil, was the only valid
pointer that didnt point to anything. I guess it is that way in a number of current languages. But I havent checked.
1

Copyright 2005 Alf P. Steinbach

current standard, C++98, unfortunately allows an infinite number of valid pointer values that
dont point to anything (the number of possible such values only limited by the size of a pointer).
However, the nullpointer is the only such pointer that you can always and reliably detect.
Dereferencing a nullpointer or any other pointer that doesnt point to anything trying to
obtain the object it points to, which it doesnt generally1 invokes Undefined Behavior.
The nullpointer of any particular pointer type is extremely useful to denote that a given pointer
doesnt point to anything. E.g., you can use it to implement an optional function argument or
result (there are also other ways to do that). Thats because you can compare it safely to other
pointers, i.e., as opposed to an invalid pointer you can safely check whether it is a nullpointer.
To get yourself a nullpointer value you can use any compile-time integer expression that evaluates
to 0, and assign that or cast that to a pointer (integer 0 converts implicitly to any pointer type).
The NULL macro, in <cstddef>, is defined as a compile-time integer 0, for those who like names
better. By convention NULL is only used where a pointer is expected, so it indicates pointer.
int* p = 0;
if( p == 0 ) { std::cout << "Nullpointer!" << std::endl; }

Nullpointers can be checked for.

You can also write just


if( !p ) { std::cout << "Nullpointer!" << std::endl; }

A pointer decays to bool.

where the pointer p is implicitly converted to a bool value, true or false, where false
signifies nullpointer, in the same way that a value of any built-in type is implicitly converted to
bool with false signifying the nullvalue of that type.
The possibility of nullpointers in itself means that a valid pointer isnt necessarily one that points
to an existing object, and as mentioned there are other valid pointers that dont point to anything.
In addition, C++ supports pointers to functions, which arent regarded as objects. So the simple
intuitive definition of valid = pointing to an existing object just isnt good enough.
But we can turn it around and use a partial definition, which is good enough for novice level
programming:

A T* pointer that points to an existing T object, is a valid pointer.

A nullpointer is a valid pointer. It can be safely copied and compared with respect to
equality. Dereferencing a nullpointer is, however, Undefined Behavior in C++98.

A pointer to a hypothetical array element just past the end of an array (I wont discuss
arrays here) is a valid pointer that, like the nullpointer, doesnt point to anything and
cannot be dereferenced in C++98, and there are also other kinds of valid pointer.

With regard to the first bullet point the trick is knowing all the details about when objects cease
to exist, or may cease to exist. The earlier example of a local variable is one common case, where
In C++98 youre explicitly allowed to dereference a nullpointer in a typeid expression, although that exception to
the rules is inconsistent with other parts of the standard that dont allow such exceptions. In C99 youre allowed to
dereference a one-past-the-end-of-array pointer, if the only thing you do with the result is to apply the address
operator. Possibly the next revision of the C++ standard will allow that as well, which is just a syntactical level
transformation, and possibly (but in my opinion unfortunately) it will also allow dereferencing of nullpointers.
1

Pointers chapter 1.

the object definitely goes to the big bit bucket in the sky. Another example is an object within a
std::vector, when that vector is resized, e.g., when a new object is added via push_back; in
this case the original object may be copied and destroyed, leaving a pointer to it invalid, which is
known as a dangling pointer: a pointer that was valid, but has become invalid.
As far as I know theres no well-known, commonly used term for a pointer that points to an
existing object of a suitable type, e.g. as in the first bullet point. One could call it a pointer that
points to an existing object of a suitable type, but thats a bit long-winded, and the possible
short form, pointer that points, is, well, Ill refrain from commenting on that. One should not
call it a dereferencable pointer (although it is), since that is a more general term that in C99 and
possibly also in the next C++ standard includes pointers like in the last bullet point, pointers that
dont point to anything: whats needed here is a more restricted term.
So, Ill call it a RealGood pointer. A pointer that is RealGood points to an existing object of a
suitable type, and so is guaranteed valid. A pointer that is not RealGood, an Un-RealGood or
URG pointer, is a pointer that must be handled with care, e.g. it could be invalid, or it could be a
nullpointer, or in some other way restricted in what can be done with it, and thats often the case.
Note: RealGood implies valid, and by simple logic, that means invalid implies URG. In a scale of
goodness, going from bad to good, you have [invalid URG valid RealGood]. If this
makes sense to you, then thats fine, but if not, dont memorize it (and if youre a teacher, please
dont ask your students to reproduce a list like that, theres no value in memorizing lists!).
Summing up, C++ pointers have complex semantics, and theyre sort of dangerous.
1.1.3

How to implement out-arguments by using pointers.

Summary.
An out-argument passes data out of the function.
A function result value, a value returned from a function, is (conceptually) an out-argument.
As an optimization you can implement a conceptual out-argument as a pointer argument:
void getAnswer( int* pNumber )
{
*pNumber = 42;
}

and you can then use a wrapper function to provide more convenient client code notation:
int answer()
{
int
result;

getAnswer( &result );
return result;

Many compilers implement RVO (NRVO is a special case), return value optimization, where such wrappers and
expression-oriented functions in general have no copy overhead.
Andrei Alexandrescus Mojo, [http://www.cuj.com/documents/s=8246/cujcexp2102alexandr/], provides
guaranteed RVO but possibly not as efficient as a compiler could.

Copyright 2005 Alf P. Steinbach

When you implement an out-argument as a pointer-argument its a good idea to use swap to avoid changing the
callers actual argument before the function returns (for exception safety).

One usage of pointers is to pass arguments to functions. In C its very common to use pointer
arguments. In C++ there is a much better way for most cases where the C or C-oriented
programmer would use pointer arguments, but first, lets see how to do it with pointers.
The case corresponding closest to the example in section 1.1.1 is to use a pointer to implement a
pure out-argument. An out-argument is an argument that we think of as passing data just one
way: out of the function, back to the caller. C++ does not support that level of abstraction (Ada
does, and C# does to some extent), except that a function result in a sense is an out-argument.
But a C++ function result can involve very inefficient data copying. Therefore, if theres much
data to be transferred back to the caller, the raw efficiency of using a pointer argument might
outweight the very convenient client code notation and safety you get by returning the data.
As a first example, simplest possible but therefore not very realistic:
void getAnswer( int* pNumber )
{
*pNumber = 42;
}

which youd use like


int x;
getAnswer( &x );

which sets the value of x to 42.


Of course for a simple int value, which is almost nothing in terms of data size, a function result
would be preferable. Then you could write just int x = answer();. However, given an actionoriented function like getAnswer you can always write an expression-oriented wrapper
function answer that provides convenient usage possibly at the cost of lost efficiency like
int answer()
{
int
result;

getAnswer( &result );
return result;

Many modern compilers implement the so called return value optimization, RVO or (standing
for essentially the same, a special case, not the opposite) NRVO1, where the compiler optimizes
away the function result copying so that a wrapper like answer, especially if its inline, provides
convenient client code notation without incurring additional overhead: a free lunch.
However, the standard does not guarantee RVO, so depending on RVO is a judgement call.
The converse, implementing an efficient action-oriented function in terms of a convenient but
inefficient expression-oriented function, is in general not possible2. But that doesnt mean you
should write functions like getAnswer instead of functions like answer. You may have to write
functions like getAnswer when performance actually suffers, but even then, consider providing
1
2

Named Return Value Optimization, nowadays referring to the case where the return expression is a local variable.
And not just because its difficult to get rid of the inefficiency, but also type-wise; see section 1.6.1.

10

Pointers chapter 1.

both, as shown above. Just document the thing (a naming convention like getAnswer versus
answer can be enough). Then the client code programmer, who knows more about the client
code, the compiler and the acceptable efficiency, can make a more informed choice.
Now for a slightly more realistic example, a function that provides 10 000 int values to the
caller. Well store those values in a std::vector<int>. Heres a complete program:
basics/arguments/out_by_pointer.cpp
#include
<vector>
// std::vector
typedef std::vector<int>

IntVector;

void getData( IntVector* pResult )


{
static int const size
= 10000;
IntVector
data( size );

// Fill it with data, e.g., this could be a database query.


for( int i = 0; i < size; ++i )
{
data.at( i ) = i + 1;
}
data.swap( *pResult );
// swap does not copy, it just swaps content.

int main()
{
IntVector
v;
getData( &v );
// Do something with the data in v, here.
}

Regarding the swap at the end, it avoids copying huge amounts of data by swapping pointers to
the data instead of the actual data. A std::vector does not, in general, physically contain the
data it seems to hold. Instead it contains a pointer that tells where the data is located, elsewhere.
But why doesnt getData use *pResult directly, instead of declaring a local vector data, filling
that, and swapping the contents with *pResult at the end? Recall that the pointer argument and
the action-oriented nature of getData are an optimization, and optimizations should change as
little as possible: ideally they should change nothing at all except performance. Ideally wed have
a function data() that produced the data as its function result. And then, if the data()
function causes an exception, nothing would be changed at the callers site. But if getData used
*pResult directly it might change the callers actual argument before causing an exception.
The convenience of a data() function is now easy to achieve, in the same way as shown earlier
for getAnswer/answer, but here that might entail copying 10 000 int values:
IntVector data()
{
IntVector
result;

getData( &result );
return result;

Of course when writing code like this one hopes for RVO to kick in, and often RVO is a tacit
assumption. For as Hoare and Knuth noted (this quote is generally attributed to Knuth but he
got it from Hoare), premature optimization is the root of all evil. And coding for the case of
no RVO, like getData, which is an attempt at optimization, can cost a lot in the long run.

Copyright 2005 Alf P. Steinbach

11

Action-oriented functions like getData can cost a lot because they are generally more awkward,
yield more voluminous and unreadable client code, and allow more possible bugs and failure
modes than expression-oriented functions like data, and they do that every time theyre used.
Attempts have therefore been made stretching the language to its limits to implement
guaranteed return value optimization, possibly not on a par with what a compiler might achieve,
but guaranteed and portable, so that you can freely use the expression-oriented style; e.g., Andrei
Alexandrescus Mojo, a ground-breaking attempt discussed in CUJ February 2003,
[http://www.cuj.com/documents/s=8246/cujcexp2102alexandr/]. Unfortunately no facility so
good that its widely used, has emerged. That may change with the next C++ standard, where its
possible that some language support that makes this easier, will be added.
1.1.4

How to implement in-arguments by using pointers.

Summary.
An in-argument passes data in to the function.
An argument passed by value is an in-argument at the topmost level.
When you optimize an in-argument by representing it as a pointer argument, you should add a const in order to
keep the original no-modification guarantee.
Think of const as meaning read-only.
You can read the argument declaration IntVector const* pNumbers backwards as
pNumbers
pNumbers is a

const

IntVector

pointer to a
constant
IntVector
As a special case C++ allows you to put that particular const, one applying to the declarationss base type, in front
of the type name, like const IntVector* pNumbers.

Ideally an averaging function for our data would be like


double average( IntVector values )

where values is a pure in-argument: we think of it as passing data from the caller into the
function, and only that way. More generally an in-argument passes data one way at the top level.
But if that argument is a pointer, then it can point to something that the function modifies: the
function cant modify the pointer itself, the top-level, but it can modify the pointed to object.
Passing values by value, as above, guarantees that average has no way of changing the callers
actual argument. But unless the compiler is very good at optimization (or this compilers
std::vector does copy on write optimization to avoid unnecessary copying) this might cause
10 000 int values to be copied, so well replace the argument with a pointer. And when we do
that well have to add a const in order to keep the original no-modification guarantee.
To keep the focus on argument passing Ill use the simple-minded approach to averaging, finding
the average by computing the sum and dividing by the number of data items:
basics/arguments/in_by_pointer.cpp
#include
<iostream>
// std::cout
#include
<ostream>
// <<, std::endl
#include
<vector>
// std::vector

12

Pointers chapter 1.

typedef std::vector<int>

IntVector;

void getData( IntVector* pResult )

double average( IntVector const* pNumbers )


{
double sum = 0.0;
for( std::size_t i = 0; i < pNumbers->size();
{
sum += pNumbers->at( i );
}
return sum/pNumbers->size();
}

++i )

int main()
{
IntVector
v;
getData( &v );
std::cout << average( &v ) << std::endl;
}

You can read the argument declaration IntVector const* pNumbers backwards as
pNumbers
pNumbers is a

pointer to a

const

constant

IntVector
IntVector

Think of the const as meaning read-only.


That is, pNumbers is a pointer to a read-only IntVector. An IntVector that can be read from;
but not modified. That doesnt mean that the calling codes actual argument is necessarily located
in read-only memory. But it means that as far as average is concerned, the calling codes actual
argument could be located in read-only memory. And the compiler enforces this restriction, so
that average is probited from changing anything in the IntVector that pNumbers points to.
As a special case C++ allows you to put that particular const, one applying to the declarationss
base type, in front of the type name, like const IntVector* pNumbers, and that form is much
used but doesnt generalize, and isnt as easy to read in any direction.
1.1.5

How to use C++ style references instead of C style pointers for arguments.

Summary.
A reference is simply an alternate name for an object.
You declare a reference by placing an & in front of the reference name, and initializing it.
In contrast to an ordinary non-const pointer, a reference cannot be re-seated, i.e. it cannot be made to refer to
something else.
First, that means that a reference does not necessarily occupy any storage, and so you cannot have a reference to a
reference (or a pointer to a reference).
Second, it means that a valid reference corresponds to a RealGood pointer.
References serve to reduce three main problems where youd otherwise use pointers: (1) possible URG pointer
values, (2) ambiguous function signatures, and (3) cluttered, less than readable and less than clear code.
You should therefore preferentially use references for function arguments, rather than pointers.

Copyright 2005 Alf P. Steinbach

13

Its possible to call the functions Ive shown earlier with pointer values that dont identify real,
existing objects, URG pointers (discussed in section 1.1.1), in which case youll invoke Undefined
Behavior. And URG pointers are exceedingly easy to come by: one way is to simply declare a
pointer variable without initializing it, another is to return a pointer to a local variable from a
function, a third, to use the built-in nullpointer value 0, and so on, there are millions of ways,
lying patiently in wait for opportunities to trick you which they do. Some programmers
believe that ordinary RealGood pointers values mutate into URG ones when theyre not watched!
Another problem is that the function signature
double average( IntVector const* pNumbers )

doesnt tell someone who is looking at the declaration whether the argument should be a pointer
to a single IntVector, or perhaps whether it should be a pointer to the first of a number of
IntVectors laid end-to-end in memory, i.e., a raw array of IntVectors, and if so, how many?
And the function signature doesnt say whether average is happy with a nullpointer as
argument. I.e., whether the argument is effectively optional, or not.
A third problem is that with pointers you have to apply dereferencing and address-of and validity
checks as appropriate, and that clutters the code and makes the code less clear.
All three of these problems (1) possible URG pointer values, (2) ambiguous function
signatures, and (3) cluttered, less than readable and less than clear code can be significantly
reduced by using a C++ reference instead of a pointer. References were invented for C++
precisely to alleviate these problems, where the idea was to make the compiler do the necessary
dereferencing etc. Here are three possible explanations of what a C++ reference is:

The high level view:


A reference is simply an alternate name for an object, a kind of synonym or alias, and you
use it exactly as you would use any other name.

The technical view:


A reference is conceptually a constant pointer variable (one that like other constants must be
initialized and cannot be reassigned after initialization) that is automatically dereferenced,
but the compiler may optimize a reference away so that it doesnt occupy any storage.

The language lawyer view:


In a declaration T D where D has the form & D1 and the type of the identifier in the
declaration T D1 is derived-declarator-type-list T, then the type of the identifier of D is
derived-declarator-type-list reference to T. and so on, and on

You declare a reference by placing an & in front of the reference name, but that does not mimic
the usage since there is no explicit dereferencing of references:
Using references:

Doing (almost) the same with pointers:

int object;
int another;
int& r = object;

int object;
int another;
int* const p = &object;

r = 42;
// object = 42.
// No re-seating notation exists, so
// r can't be made to refer to another.

*p = 42;
p = &another;

// object = 42.
// !Not permitted.

14

Pointers chapter 1.

First, about the pointer declaration on the right: as before you can read it backwards,
int* const p = &object;

&object

The address of object

is the initial value for

p
p, which is a

const

constant

pointer to an

int
int

The crucial difference from the earlier use of const is that now it is the pointer p itself that is
declared constant, not the object that p points to. And what that means is that p cannot be made
to point to something else (the last statement in the example is not permitted). We say that this
pointer, like the reference, cannot be re-seated.
Compare that to the reference declaration,
int& r = object;

object

The object object

is the initial referent for

r
r, which is a

&

reference to an

int
int

where you can imagine that the same happens as for the pointer (i.e., you can imagine that the
compiler, behind the scenes, implements the reference as a pointer).
Since neither the const pointer nor the reference can be re-seated the word initial is a bit
misleading. The initialization provides not just the initial value/referent, but the one and only,
ever. E.g., with the reference
int& r = object;

you can subsequently write


r = another;

but that doesnt re-seat the reference: it is an ordinary assignment to the variable object that the
reference is an additional name for.
For references the impossibility of re-seating means that references are generally valid. You
generally have to do nasty Undefined Behavior things like letting a function return a reference to
a local variable, or dereferencing an URG pointer, in order to obtain an invalid reference (one
way that doesnt immediately involve Undefined Behavior is to destroy an object you already
have a reference to, which would yield a kind of dangling reference). For that reason its often
said that references are guaranteed to be valid, but its worth bearing in mind that that
guarantee is only for a correct program. And in a correct program there is no problem with
invalid pointer values, either, because even though they can exist they arent actually used in that
program. So instead of a guarantee you should think of an assumption: a reference should always
be assumed to be valid, and if it turns out to be invalid then you know that you have a serious bug.
In fact its much stronger than that might seem to be:

Copyright 2005 Alf P. Steinbach

15

A valid reference is RealGood, and a reference should always be assumed to be


RealGood.
I.e., you should always assume that a reference refers to an existing object, and there is no nullreference. Thats because if you create or end up with and use a reference to a non-existent
object, then youre off in Undefined Behavior land. For references valid means RealGood.
Oh, I forgot to define the terms. Well. Lets define: a reference r is valid if and only if &r is a
RealGood pointer, and r is RealGood if and only if &r is a RealGood pointer, which is the same.
Since a reference does not necessarily occupy any storage, it would not be meaningful to have a
reference to a reference1. Happily theres no way to obtain a reference to a reference (or a
pointer to a reference), e.g., you cannot2 declare T&& aReferenceRef, so theres no way to get it
wrong. If you try to create a reference to a reference by initializing a T& reference with another
you just end up with two references to the same T object; think of it as references that are names
for the same object, and then saying that one name refers to the same object as another name.
In addition to becoming a great deal more safe, the last program also becomes much more
readable and clear when its rewritten to use references instead of pointers:
basics/arguments/in_and_out_by_reference.cpp
#include
<iostream>
// std::cout
#include
<ostream>
// <<, std::endl
#include
<vector>
// std::vector
typedef std::vector<int>

IntVector;

void getData( IntVector& result )


{
static int const size
= 10000;
IntVector
data( size );
// Fill it with data, e.g., this could be a database query.
for( int i = 0; i < size; ++i )
{
data.at( i ) = i + 1;
}
data.swap( result );
// swap does not copy, it just swaps content.
}
double average( IntVector const& numbers )
{
double sum = 0.0;
for( std::size_t i = 0; i < numbers.size();
{
sum += numbers.at( i );
}
return sum/numbers.size();
}

++i )

A reference can be declared extern, and then it might seem as if it would have to occupy storage. But also that
references storage might be optimized away if the compiler does whole program optimization. As of 2005 few
compilers do whole program optimization (Microsofts Visual C++ compiler does), but that may change.
2
This limitation may be removed in the future, because it turned out to be very inconvenient for template
programming: Bjarne Stroustrup has suggested that U&, where U is T&, should be defined to mean T&, and this will
probably be part of the next standard, [http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#106].
On the other hand, John Spicer has suggested using the syntax T&& to denote an rvalue reference, see the ref. to
N1377 in [http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html]. And if both proposals are
adopted then well have yet another case where the definition of a type cant be substituted for use of that type
1

16

Pointers chapter 1.

int main()
{
IntVector
v;
getData( v );
std::cout << average( v ) << std::endl;
}

For all of the reasons discussed so far, plus a few more (e.g., the possibility of using an rvalue, a
value-producing expression, as actual argument for an in-argument like the one in average), you
should preferentially choose references over pointers for argument passing.
1.1.6

How to access main arguments.

Summary.
The information carried by the standard arguments to main, inherited from C, can be copied to a more safe and easy
to use std::vector of std::string values by one line of magic code,
int main( int argc, char* argv[] )
{
cppMain( StringVector( argv, argv + argc ) );
}

// Magic code to copy the info.

main was originally designed for early Unix, and so the arguments you get are not necessarily reasonable on other

platforms (in particular, Windows), and on non-Unix platforms they may depend on the compiler.

Unfortunately the second standard main argument is a pointer, not a reference, and the strings
passed into main from the operating system (typically those are strings specified in the command
to run the program) arent represented as std::strings, but as raw arrays of characters.
Thats because the main signature was inherited from C.
Lets deal with that. The second standard main argument is a pointer, and theres nothing that
can be done about that, at least by us. However, adding just a little bit of magic code (it isnt
really magic at all, but I dont want to discuss this yet) we can copy all the information to a
std::vector of std::strings, and from there on avoid dealing with the unsafe pointer:
basics/arguments/mainargs.cpp
#include
<iostream>
//
#include
<ostream>
//
#include
<string>
//
#include
<vector>
//

std::cout
<<, std::endl
std::string
std::vector

typedef std::vector<std::string>

StringVector;

void cppMain( StringVector const& arguments )


{
for( std::size_t i = 0; i < arguments.size(); ++i )
{
std::cout << arguments.at( i ) << std::endl;
}
}
int main( int argc, char* argv[] )
{
cppMain( StringVector( argv, argv + argc ) );
}

// Magic code to copy the info.

Copyright 2005 Alf P. Steinbach

17

The C++ standard does not specify what the main arguments will be for any given user
command line, which is natural since the C++ core language is platform-independent. That
means that the result of the above program may be different not only on different platforms, but
also for different C++ compilers and compiler options. Although, since main was originally
designed for Unix, you can probably count on compiler independence under Unix.
Compiled with MinGW g++ 3.4.4 and run from the Windows XP command interpreter:
K:\basics\arguments> mainargs Yet again, """Hello" "world!"""
mainargs
Yet
again,
"Hello world!"
K:\basics\arguments> _

If you dont understand the quoting rules: I dont understand them either, and the way I recall it
the details have changed in nearly every version of Windows, and are undocumented.
might appear to be portable, system-independent. But in spite of the system-independent
description in the standard main is the most platform-specific anachronism I know of in C++,
and so, although you can count on reasonable behavior in Unix, its not necessarily so on other
platforms. On other platforms, e.g. Windows, the behavior of main can depend on the compiler
and the compiler options, and tellingly, you cannot implement Windows versions of the Unix
utilities cat1 and echo2 in pure standard C++ which should be basic.
main

In particular, if you want to support Windows filenames with alphabetical characters outside the
English AZ range, or Unicode filenames, youll have to use system- and/or compiler-specific
means, not the main arguments.
I use main arguments for simple testing and personal tools, but not for anything more serious.

The main problem in implementing a Windows cat is that the standard iostreams cant be set to binary mode in a
portable way which is another area where pure standard C++ is strongly geared towards Unix programming. But
theres also the problem of filenames. And that is the problem relevant to main arguments.
2
The main purpose of echo is to see exactly what a process receives, so in Windows it should echo the received
command line, including spaces, as the Windows standard echo command does.
1

18

Pointers chapter 1.

1.1.7

Const-correctness for pointers and references.

Summary.
An rvalue, such as a temporary object or the number 42, can be bound to a reference to const, which is especially
useful for function arguments. For a local reference the binding introduces a hidden local variable, that may or may
not be optimized away. Since a reference cannot be re-seated T& const is meaningless and syntactically invalid; T
const& (or equivalently, const T&) is OK and prohibits modification via this reference.
const for pointers is more intricate:

Declaration:
int* p;
int const* p;
int* const p = &o;
int const* const p = &o;

Declaration read backwards:


p is a pointer to an int.
p is a pointer to a read-only int.
p is a read-only pointer to an int.
p is a read-only pointer to a read-only int.

p = 0;

*p = 0;

OK
OK

OK

OK

Rules for what youre allowed to do with various combinations of const or not, such that constness is honored
and preserved, are known as const correctness, and the basic rules for pointers are:
int o;
int const oConst = 7;
int* p
= &o;
int const* pConst
= &oConst;
pConst = p;
pConst = pConst;
p = p;
p = pConst;

//
//
//
//

OK.
OK.
OK.
!Not permitted.

A pointer to const guarantees that the pointed to object cant be changed if all you have is that pointer, and dont
use low-level language features to circumvent the restriction.
As shown above T* converts implicitly to T const*, but C++ has no implicit conversion from T** to T const**,
because allowing that latter implicit conversion would allow you to change a logically constant object (via some tricky
code, an example given).

The reason that a StringVector1 constructor call2, the typename used as an expression that
here produces a StringVector temporary object, can be used directly in the call to cppMain, is
that a reference to const can always be initialized with a temporary object, or with any rvalue,
such as the literal value 42:
basics/reference_to_const.cpp
#include
<iostream>
// std::cout
#include
<ostream>
// <<, std::endl
int main()
{
int const&

= 42;

// This is actually OK!

// The reference is still valid...


std::cout << r << std::endl;
}

See the previous subsections example program.


Syntactically a constructor call like this is a generalization of the C++ cast notation, a conversion, and although it
doesnt make sense to think of a constructor call with zero or more than one argument as a conversion, many prefer
to think of it that way, at the purely syntactical level, and (for obscure reasons) object to others using the word call.
1
2

Copyright 2005 Alf P. Steinbach

19

The bind-reference-to-const-to-an-rvalue feature is very useful for function arguments, as


illustrated in the previous subsection, but seemingly nobody knows a useful practical application
for the local reference variant, as shown above. Bjarne Stroustrup, the father of C++, put it this
way: The rules for references are simply the most general and uniform I could find1. Which
means the above possibility is just a side-effect of having conceptually very general rules, that you
can always bind a reference to const to an rvalue, no matter the context.
Conceptually it is allowed because the const means you cannot modify the rvalue (e.g.,
modifying the number 42 could have really disastrous consequences!). But in reality what goes
on for a local reference such as above is not necessarily that you get a reference to the rvalue.
Instead, although the compiler can optimize this, what happens is that a hidden local variable is
introduced, the rvalue is copied to the variable, and the reference is bound to the variable.
So, the above declaration, with an rvalue, can be regarded as a shorthand for
int const
int const&

__hiddenLocal
r

= 42;
= __hiddenLocal;

In spite of appearances initialization of a local reference to const can therefore involve copying,
and the type must support copying, which can be important to know when the type is not a
simple int. The internal translation to hidden local variable also means there is no obvious
technical reason why a reference to non-const can not also be initialized in this way. However,
A reference to non-const cannot be initialized with an rvalue.
Partly thats because its practically useful2 to disallow this; partly its because whether a copy was
made or not would matter and depend on the context; and partly its because the reference to
const is conceptually a reference to the rvalue, and a reference to non-const bound to an rvalue
would break that conceptual picture by seemingly allowing you to modify an rvalue like 42.
Syntax. In the declaration int const& r the no-modification guarantee is for the int, because in
the basic syntax each const applies to the entity on its left (we can read declarations backwards).
But what about int& const r? Well, if that was allowed then it would give a no-modification
guarantee for the reference itself. But since a reference cant be re-seated theres no need for
such a guarantee: its there already, and thats the reason that int& const simply isnt allowed.
To ensure that something that is const can not be modified by accident is called const
correctness.
The C++ language rules provide basic const correctness when you dont use low-level features
such as casts. Youre not allowed to assign to a const variable of built-in type, and youre not
allowed to obtain a pointer or reference that would enable you to assign to the variable. const
correctness for references is just as simple, because a reference is just an alternate name.
as it generally applies to pointer declarations is a bit more intricate, so lets start with what
such declarations mean (here o is the name of an int variable, while 0 is the number zero):
const

[http://groups.google.com/group/comp.lang.c++/msg/ae302ea9c7378287], or more generally, when or if an


archive supports standard NNTP access, [news:1130692896.529200.183670@g44g2000cwa.googlegroups.com].
2
An example is discussed in section 1.2.3.
1

20

Pointers chapter 1.

Declaration:

Declaration read backwards:

int* p;

p = 0;

*p = 0;

p is a pointer to an int.

OK

OK

int const* p;

p is a pointer to a read-only int.

OK

int* const p = &o;

p is a read-only pointer to an int.

OK

int const* const p = &o;

p is a read-only pointer to a read-only int.

The two rightmost columns says whether assignment to p and to *p is OK as far as the compiler
is concerned. When p is a pointer to a read-only int its not OK to assign to *p. In these two
cases (the second and the fourth) its OK to let p point to any int variable, because *p can only
be read from, and reading the contents of the variable is supported by any int variable.
In the first and third case you can assign to *p. Therefore, in these cases the compiler prohibits
you from pointing p towards a read-only int. For if you could point p to that read-only int,
then you could assign to a read-only int. Of course, C++ being based on the assumption that
the programmer is always right you can lift the prohibition by uttering various phrases from
the black book of C++ magic incantations. But if you do, youre on your own: no protection.
More generally, if you have a T const object and a T* pointer, say,
int const oConst = 7;
int* p;

then youre not allowed to directly assign the address of that object to the pointer,
p = &oConst;

// !Not permitted.

This is important.

because that would allow you to change a logically constant object, a read-only object, via *p. A
T const* pointer does not implicitly convert to T*: all implicit conversions preserve constness.
is generally a restriction on operations, and youre not allowed to remove a restriction on
operations without at least telling the compiler that you really mean it, the black book
incantations I hinted about. On the other hand, adding new restrictions is OK. For direct
pointers, considering only const and not volatile (which follows the same rules), this means:
const

int o;
int const oConst = 7;
int* p
= &o;
int const* pConst
= &oConst;
pConst = p;
pConst = pConst;
p = p;
p = pConst;

//
//
//
//

OK.
OK.
OK.
!Not permitted.

Understanding the above list can be difficult at first. But if thats the case, take some time to
study it and think about it. After a while it becomes obvious that this is how it Must Be.
The main stumbling block, if the list is not obvious, may be the first assignment,
pConst = p;

// OK.

Copyright 2005 Alf P. Steinbach

21

Here you obtain a pointer to const, where the pointed to object can actually be changing over
the lifetime of the pointer, no matter how much youre pointing at it with your pointer to const.
The pointed to object can be changing because its directly accessible as o, as well as accessible
via *p. C++ allows this because a pointer to const does not guarantee that the object is
necessarily unchanging,
A pointer to const only guarantees that the pointed to object cant be changed if all you
have is that pointer.
For example, if you pass that pointer to a function, and that pointer is all that the function has,
then the function cant change the pointed to object (without a proper black book incantation).
Is it obvious yet? If yes, read on. If no, study the list and the comments above some more.
When the above has become obvious to you, it may come as a shock to you that while T*
readily converts to T const* (because adding a restriction is OK), C++ has no implicit
conversion from T** to T const**. Seemingly also this conversion would just add a restriction,
const, and thats OK, isnt it? But no. The details are quite tricky, but allowing that implicit
conversion would allow you to change a logically constant object, it would actually be unrestricting adding in a const is not always the same as restricting operations. Heres an example:
int const o = 42;
int* p;
int** pp = &p;
int const** ppConst = pp;
*ppConst = &o;
*p = 1;

//
//
//
//
//

pp now points to p.
!Not permitted, because
it lies about the nature of p, so that
then we could set p to point to o, as shown here, and
change the value of the constant o, as shown here.

It doesnt really matter if you understand the above example or not. I understand it when I see it,
but I have to think and fiddle a bit to reproduce it on my own. Whats important is that when
the compiler protests about a conversion from T** to T const** you know theres a good
reason, that it would somehow, via some tricky code, allow constness to be circumvented.
So, pointers to pointers are effectively different.
The standard generalizes the above to arbitrary levels of pointers to pointers to pointers, but in
practice two levels, pointers to pointers, is the most youll see in practice, and even that is rare.

1.2 Run-time polymorphism.


If youre well acquainted with virtual member
functions and know almost all about them,
youre an expert, this section may be
explaining in detail things you already know.

Summary.
Understanding the details of C++ runtime
polymorphism (virtual member functions, function
pointers, vtables) helps you use technologies such as
Microsofts COM effectively, and generally helps you
choose appropriate C++ solutions: the ++ in C++
solves many C-level problems.

However, many (all right, some) students who


thought they understood virtual member
functions got a real aha!-experience from the
kind of explanation I offer here. To prepare
you: I get down to the nitty-gritty of how youd have to solve a problem in C, which lacks the

22

Pointers chapter 1.

++ such as virtual member functions, and then show how C++ can do all that automatically
for you, and much more safely (e.g., virtual member functions typically are implemented in terms
of function pointers and vtables). This yields a level of understanding necessary for using
technologies such as Microsofts COM (and similar, derived technologies, some of which are
platform independent1) effectively, and for choosing appropriate C++ solutions. Regarding the
latter, if you find yourself writing code or using ideas like in the C-style program examples, then
know that C++ probably solves that for you. All you have to do is use the ++.
This road I lead you on is long and arduous2. The scenery is interesting, even exciting, but not
always pretty! And at the end well arrive, not on another, more lush and beautiful continent, but
just a few meters from where we are now, where we could have just sauntered over to in a direct
line but hopefully the Long Tour will have made us wiser and more understanding.
As on any Long Tour, take breaks!
If you still find the following too longwinded & hard to grasp, I suggest you go back and forth
between the full text and the summaries, such as the one above.
1.2.1

Polymorphism.

Summary.
Polymorphism is to treat different kinds of objects uniformly, in the same way, usually with the same piece of code.
With compile-time polymorphism the compiler selects an overload or template instantiation for e.g.
stringFrom(x) based on the static type for x known at compile-time.
With run-time polymorphism, where the polymorphism is present all the way down to the resulting machine code,
the static type for x is just a constraint on what type x can be when this function call is executed. The actual type of x
at the time of the call is its dynamic type. Having a dynamic type different from the static type is possible by using
pointers and/or references, e.g., you can have static type Animal and dynamic (pointed to or referenced) type Dog,
which is a kind of Animal.

Polymorphism is to treat different kinds of objects uniformly, in the same way, usually with the
same piece of code. Thats desirable because otherwise youd have to write essentially the same
code, just with different types in the declarations, again and again for each type of object youd
like to support. Which the old-timers who were weaned on Pascal had to do
As an example of one kind of polymorphism, you might write stringFrom(x) without knowing
or caring about the exact type of x. That would most probably be an example of compile-time
polymorphism, where the compiler selects an overload or template instantiation of stringFrom
based on the declared type for x, the static type known at compile-time, or for a general
expression, a static type inferred from literal constants and declared types. For example,

COM: Component Object Model. Originally a Windows-only technology: a 32-bit binary level format for pluggable
external objects, plus infrastructure. The COM binary level format plus basic ideas have been widely adopted
elsewhere, e.g. in the platform independent Java JNI specification, in Linux Gnome Bonobo (Gnome::Unknown
corresponds directly to COM IUnknown), in Mozillas XPCOM (used e.g. in the Firefox browser), and so on.
2
When I read what I wrote here I have a sense of dj vu: possibly this sudden eloquence is someone elses!
1

Copyright 2005 Alf P. Steinbach

23

polymorphism/compile_time.cpp
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
std::string stringFrom( double ) { return "it's a 'double'!"; }
std::string stringFrom( bool ) { return "it's a 'bool'!"; }
template< typename T >
void doStuff( T x )
{
// Here we don't know the exact type T of x, but hey, no problemo!
// We just apply some compile-time polymorphism, and let T be whatever.
std::cout << stringFrom( x ) << std::endl;
}
int main()
{
doStuff( true );
}

For this kind of compile-time polymorphism the compiler generates one instantiation (concrete
function) of doStuff for each type T actually used. So although theres polymorphism at the
source code level, uniform treatment of x regardless of type, theres no such polymorphism in
the resulting machine code. Unless you have a very smart compiler and instruct it to optimize for
smallest machine code, and it so happens that some of the types T actually used can be handled
by the same machine code, and it further happens that the compiler takes advantage of that.
In contrast, what Im discussing in detail in the following is run-time polymorphism, where the
polymorphism is present all the way down to the resulting machine code. With run-time
polymorphism the declared type for x is just a constraint on what type x can be when this function
call is executed. The actual type of x, at the time of the call, is called its dynamic type. And
thats where this ties in to pointers and references. For without them the dynamic type would
necessarily always be the static type, but with them, we can let a pointer point to an object that is
of static type Animal (say), which is all the compiler knows, where the dynamic type is a specific
kind of Animal, maybe a Dog or perhaps an Elephant: both dogs and elephants are cool, and
when you have a RealGood Animal pointer pAnimal and say, letWind(pAnimal), you can
actually hear the difference depending on the dynamic type of Animal you have a pointer to.
As a more safe example,
polymorphism/run_time.cpp
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
#define override

virtual

struct Animal
{
virtual std::string species() const = 0;
};
struct Dog: Animal
{
override std::string species() const { return "a dog"; }
};
struct Elephant: Animal
{
override std::string species() const { return "an elephant"; }
};

24

Pointers chapter 1.

void doStuff( Animal const& anAnimal )


{
// Here we don't know the dynamic type of anAnimal, but hey, no problemo!
// We just apply some run-time polymorphism, and let Animal be whatever.
std::cout << "it's " + anAnimal.species() + "!" << std::endl;
}
int main()
{
doStuff( Dog() );
}

I think you get the general idea: doStuff will call the species function defined in Dog or
Elephant depending on the dynamic type of the actual argument, which in this case is a Dog.
However, I wont explain either of these two example programs further yet. Theyre just to
anchor the discussion in the following subsections, so that you can relate it back to C++ features
and have an inkling of Whats It All Good For. The discussion in the following subsections leads
to very detailed explanations of the C++ features used in the last program above.
1.2.2

The concepts of data representations and linked data structures.

Summary:
Some things (e.g., a boolean value, an arithmetic expression) can be represented both as code, a code
representation, and as data, a data representation.
A data representation can be somewhat less efficient but is much more flexible in what you can do with it e.g.,
generating a representation of an expression from a string the user types in.
In a linked data structure each object, called a node, contains pointers, called links, to other nodes.
A binary tree is a linked data structure where each parent node has (potential) links to two child nodes and there
are no cycles; the root node is the node you reach everything else from.
A binary tree can represent a simple arithmetic expression by letting each node represent one of (1) constant, (2)
identity function x, (3) addition operator, and (4) multiplication operator, here exemplified for 2x+5:
+

Newbies to C++ often ask how they can let the user type in an arithmetic expression and have it
evaluated, even [favorite script language] does that!. Unfortunately C++ does not support
expression string evaluation out of the box. It can be implemented, though to wit, [favorite
script language] is probably implemented in C and/or C++, or in a language that in turn is
implemented in C and/or C++, since C forms the software backbone on most computers.
Lets take some small steps toward that goal by implementing a structured data representation of
simple arithmetic expressions involving one free variable x.

Copyright 2005 Alf P. Steinbach

25

A data representation is to encode something as data that can be manipulated, i.e., were out to
manipulate expressions, not just evaluate them. For example, with a data representation of an
expression one might implement a parser that produces such a data representation from an
expression string typed in by the user, and then produce a graph, or allow general expressions in
numeric fields in forms, or whatever. An ordinary C++ function would be a more efficient code
represention of an expression, but that expression would be fixed, chosen by the programmer.
Here is an example of using a code representation of an expression, which is simple and best for
what this program does, but does not help in evaluating expressions typed in by the user:
polymorphism/expr_as_code.cpp
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
int main()
{
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << 2*x + 5 << std::endl;
}
}

0
1
2
3
4

5
7
9
11
13

At the cost of some efficiency a data representation of the expression supports all that the code
representation does, although at run-time, plus it supports things like

it can be generated from an arbitrary expression that the user types in,

it can be symbolically integrated by the program (yielding x 2 + 5x + C ), and it can be


symbolically differentiated by the program (yielding 2), and such mathematical things,

it can be simplified by the program (e.g. x + x + x can be simplified to 3x ),

it can be dynamically combined with other expressions,

it can be displayed, textually and graphically, in various ways, or even read out aloud,

and more, only limited by your imagination, as they say.


When you need things like the above, you need a data representation.
Well do this initially with C++ as only a kind of better C, because that will make the problems
that full C++ solves and hides for us, stand out, and it will illuminate what goes on inside C++.
To keep the example code from exploding in size only addition and multiplication will be
supported. And concerning useful operations, only evaluation and conversion to displayable
string form will be supported. But do keep in mind the list above: although the example
programs dont actually do more than build very simple expressions and display and evaluate
them, thats not what theyre about. Instead theyre directly about general techniques for building a
data representation that can be used for anything, such as in the list above. And at a higher level,
the outer context, theyre about learning more about pointers and polymorphism.

26

Pointers chapter 1.

The kinds of expressions supported will be of the form exemplified by 2 x + 5 . And as this little
example shows, the fundamental parts that an expression is formed from are
1. any constant, such as 2 and 5,
2. the identity function f( x ) = x , corresponding to an occurrence of x in a formula, where
if you evaluate the expression object for some value of x you get that value as result,
3. a sum of two sub-expressions, the +-operator, and
4. a product of two sub-expressions, the implicit -operator between 2 and x.
Well use one object for each such fundamental expression part in a given expression.
Sums or products of more than two sub-expressions can then be represented in various ways.
For example, one simple but limited option is to use a stack of operations, as on an RPN1
calculator. For more generality (easier to support operations like those in the list shown earlier)
we can instead build an explicit expression object hierarchy.
In an expression object hierarchy each sum or product object contains two argument pointers:
one to an object representing the sub-expression before the operator, and one to an object
representing the sub-expression after the operator, i.e. left and right argument pointers. To
evaluate a sum, say, one then simply evaluates the two arguments that the sum object has
pointers to, and then add the results for the arguments. For 2 x + 5 the hierarchy can look like
this, where a pointer in an object a, pointing to an object b, is shown as an arrow from a to b:
+

This is an example of a linked data structure. This particular linked data structure is called a
binary tree; the fundamental property of a tree is that it has no cycles when you follow the
pointers. The objects in a linked data structure are commonly called nodes, and the pointers,
links. For a tree one can talk about parent nodes and child nodes, with the uppermost parent
node the root of the tree. And the reason that operators are parent nodes and sub-expressions
are child nodes in this tree, is that this makes it easy to evaluate an expression, by starting at the
root and following the pointers down into the tree (and yes, a tree does have its root at the top!).

RPN: Reverse Polish Notation; see section 1.5.1.

Copyright 2005 Alf P. Steinbach

1.2.3

27

C-style polymorphism with simulated dynamic types (just one actual type).

Summary:
An enum type is an integer-based type that converts implicitly to integer and where some values have names; if you
just list the names then C++ will assign values for you, by default starting at 0.
In C enum values are useful for representing logical sub-types, such as logically different kinds of nodes (sum,
product, constant, identity). One can use one common actual type for all the node objects, with each logical subtype represented as an enum type tag value in each object. Each object then physically contains all data members
used by all possible logical sub-types.
A binary tree is self-referential, recursive, in that each nodes two pointers point to sub-trees of the same form.
This leads naturally to functions that use recursion, i.e. call themselves. For example, a binary tree representing an
expression can be evaluated by a recursive function.
The type-tag way of representing logical sub-types then leads to a big switch in each such function, and updating all
such switches can be a maintenance nightmare in a large system.
So the simple C-style way of representing and dealing with the structure has three main problems:

Possible invalid members in an object, for the objects logical sub-type.

Larger than necessary object size.

Maintenance nightmare for switch statements.

A factory function produces something whole out of parts, or perhaps apparently out of thin air, and is useful &
convenient for producing node values.
Its natural to let the factory function arguments be of reference to const type, but that allows the client code to use
temporary objects, which are not OK to store pointers to, so dont.
With whats been introduced so far the client code must declare a variable for each node, and that prevents us from
constructing an Expression that represents something the user types in (we dont know which or how many
variables that typed-in expression would require).

A direct translation of the previous subsections thoughts to C-style code for now we restrict
ourselves to pure data structs and freestanding functions can look like this:
polymorphism/expr_like_c_1.cpp
#include
<cassert>
// assert
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
#include
<sstream>
// std::ostringstream
#include
<string>
// std::string
enum ExpressionKind{ constantKind, identityKind, sumKind, productKind };
struct Expression
{
ExpressionKind
double
Expression const*
Expression const*
};

kind;
value;
pLeft;
pRight;

// For constantKind.
// For sumKind and productKind.
// For sumKind and productKind.

The ExpressionKind enum is an integer-based type (an enumeration) that implicitly converts
to integer, where four values have been named. Since I havent defined the values C++ chooses

28

Pointers chapter 1.

them, starting at 0 and going upwards. So constantKind is a name for 0, identityKind is a


name for 1, and so on, but the actual values dont matter as long as theyre different values.
The Expression type is a single struct type where the kind member is a type tag that
represents the logical sub-type (telling us whether an Expression object represents a constant,
a sum, or what). The single Expression type means that we can freely point our pointers to any
object no matter which logical sub-type it is. But there is a cost: an Expression object must
have all the members that exist in the various logical sub-types, and so depending on the actual
run-time logical sub-type some members will be invalid, which can cause nasty bugs. There is
also a cost in that an Expression object is somewhat larger than it ideally would need to be for
the particular logical sub-type it has. But that can be partially fixed by using a union for the subtype specific data members, which is called a tagged union a not uncommon solution in C.
In essence this provides effective run-time polymorphism by not representing logical types as
actual types, keeping the compiler happily uninformed about what were doing, so it cant protest.
For example, in valueOf (our evaluation function) we can use an Expression reference
argument because all our objects are Expression objects, no matter which logical sub-type:
double valueOf( Expression const& e, double x )
{
switch( e.kind )
{
case constantKind: return e.value;
case identityKind: return x;
case sumKind:
return valueOf( *e.pLeft, x ) + valueOf( *e.pRight, x );
case productKind:
return valueOf( *e.pLeft, x ) * valueOf( *e.pRight, x );
}
assert( false );
// Should never get here -- all cases covered above.
return 0;
// Just to satisfy the compiler (otherwise possible warning).
}

How does this work? Well, if the object passed as an argument is of constant logical sub-type,
constantKind, then valueOf just returns that objects value members value. And the identity
logical sub-type, identityKind, corresponding to an x, is just as simple: return the value of x.
Otherwise, sum or product logical sub-type, valueOf uses recursion.
If youre not familiar with recursion: a recursive thing has a naturally self-referential description,
and a recursive function is a function that calls itself, directly or indirectly, the recursion.
A binary tree can be described recursively as being either nothing or a node with two links where
each link goes to a subtree that itself is a binary tree (and where there are no cycles). And that
recursive description of the data structure is the basis of the valueOf implementation. valueOf
calls itself recursively in order to dive into the subtrees of the node its given a reference to.
Detail: e.pLeft is a pointer to the object representing the left argument, and *e.pLeft (recall
that * has lower precedence than the member selection operator .) yields that object.
The Expression object e is a node in an expression tree, and we can imagine that with the client
codes call to valueOf, valueOf is at that node, the root node of the tree. Essentially the
recursive call valueOf( *e.pLeft, x ) then sends valueOf delving into the left subtree of
node e. And when valueOf returns to itself it knows the left expression argument value.
Then (or perhaps first, we dont know) it delves into the right subtree in the same way, a
recursive call. And now it has both argument values at hand, and can compute the +, say.

Copyright 2005 Alf P. Steinbach

29

Theres very very much more to say about recursion, but thats not the point here, so: valueOf
delves into, recursively, and returns out of, and on the return trips its carrying results out of.
A function to produce a displayable string representing the expression, can use the same pattern:
std::string stringFrom( double aValue )
{
std::ostringstream stream;
stream << aValue;
return stream.str();
}
std::string stringFrom( Expression const& e )
{
switch( e.kind )
{
case constantKind: return stringFrom( e.value );
case identityKind: return "x";
case sumKind:
return "(" + stringFrom( *e.pLeft ) + "+" + stringFrom( *e.pRight ) + ")";
case productKind:
return "(" + stringFrom( *e.pLeft ) + "*" + stringFrom( *e.pRight ) + ")";
}
assert( false );
// Should never get here -- all cases covered above.
return 0;
// Just to satisfy the compiler (otherwise possible warning).
}

And if we had more operations then wed have more functions with switches. This is a third
problem with this solution: if a new logical sub-type is introduced then all such switch
statements have to be updated, adding in the correct action for the new logical sub-type. Finding
and updating all relevant switch statements can be a maintenance nightmare for a large system.
Weve identified three problems, so far:

Possible invalid members in an object, for the objects logical sub-type.

Larger than necessary object size.

Maintenance nightmare for switch statements (this is an example of the phenomenon


that code redundancy is generally UnGood, no matter which form it takes).

But before addressing those problems, lets complete this program so that it creates and evaluates
an example expression.
To create expressions we can use C-style functions that produce expression object values, object
value factory functions. A factory function produces something whole out of parts, or perhaps
apparently out of thin air. Its a factory for whatever kind of thing it produces, here values:
Expression expressionInstance(
ExpressionKind
aKind,
double
aNumber,
Expression const*
aLeftPointer,
Expression const*
aRightPointer
)
{
Expression const result = { aKind, aNumber, aLeftPointer, aRightPointer };
return result;
}
Expression constant( double value )
{ return expressionInstance( constantKind, value, 0, 0 ); }

30

Pointers chapter 1.

Expression identity()
{ return expressionInstance( identityKind, 0, 0, 0 ); }
Expression sum( Expression const& left, Expression const& right )
{ return expressionInstance( sumKind, 0, &left, &right ); }
Expression product( Expression const& left, Expression const& right )
{ return expressionInstance( productKind, 0, &left, &right ); }

In passing, note the technique of applying the address operator, &, to arguments passed by
reference: that produces the addresses of the calling codes actual arguments, with the use of
references gently inducing the calling code to supply existing objects that we can point to.
However, thats only a half-way measure, because the use of const means that the calling code is
free to supply temporary objects, which would not at all be right to keep pointers to For that
reason many prefer and even demand to have such arguments non-const, which is a trade-off in
that it restricts the calling code slightly the calling code cannot then benefit from using const.
If the logical sub-types were represented as actual types instead of as type-tag values these factory
functions would naturally be C++ constructors. And actually its possible to C++ify this
solution so that the functions above become constructors, but of the single Expression type.
However, theres no real point in doing that since well soon go on to better ways of doing this,
so I leave it as an exercise (there is a little problem to solve regarding the last two functions).
Constructing an object hierarchy corresponding to 2 x + 5 might now seem simple:
Expression const e = sum( product( constant( 2 ), identity() ), constant( 5 ) );

And the compiler will and must accept this. But its wrong: the lifetime of a temporary object is
not extended just because its being passed by reference somewhere, so after this e will contain
pointers to temporary objects that no longer exist, invalid pointers. So the trade-off I chose here
was not a very good one, and perhaps one should adopt the following guideline:
Dont pass arguments that the function stores pointers to, by reference to const
(unless you really know what youre doing).
With whats been discussed so far the only way to avoid the dangerous temporary objects is to
use local variables in the calling code, or perhaps static variables here I use local variables:
int main()
{
// 2x + 5
Expression
Expression
Expression
Expression
Expression

const
const
const
const
const

eX
e2
e2x
e5
e2xplus5

=
=
=
=
=

identity();
constant( 2 );
product( e2, eX );
constant( 5 );
sum( e2x, e5 );

std::cout << stringFrom( e2xplus5 ) << ":" << std::endl;


std::cout << std::endl;
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << valueOf( e2xplus5, x ) << std::endl;
}

Copyright 2005 Alf P. Steinbach

31

((2*x)+5):
0
1
2
3
4

5
7
9
11
13

And because having to name and declare each little sub-expression object, as above, is not only
extremely inconvenient, but prevents us from later constructing an Expression that represents
something the user types in (because we dont know what that will be, and so cant declare
suitable variables in advance), which is a showstopper problem, lets tackle this problem first of all.
1.2.4

Basic use of dynamic memory allocation & deallocation.

Summary:
Dynamic memory allocation and deallocation solves the showstopper problem of having to declare variables in
advance.
Using dynamic memory allocation can be extremely inefficient or no-cost, depending on your point of view
(compared to C++ local variables, compared to e.g. Java, C# and script languages).
C++ new (allocation) reserves a suitable chunk of memory from the heap, and delete (deallocation) returns the
chunk to the heap, cancels the reservation, so it can be reused:
int* p = new int;

delete p;

Its OK to call delete with a nullpointer as argument; in that case nothing happens.
During initialization an object must be non-const, even if it appears as const after initialization.
If dynamically allocated objects are not deallocated youll have a memory leak: that more and more memory
becomes reserved as time goes on. A memory leak is in practice for a running program. When the program
terminates, the operating system will (in general) clean up.
One way of dealing with deallocation is to use the concept of owned pointers: that a pointer within an object is
owned by that object, so that e.g. a common deallocation function can visit all objects and for each object
deallocate its owned objects before deallocating that object itself.

One solution to the temporary-object-problem could be to have a large pool of Expression


objects, e.g. a global std::vector, and let each factory function initialize and return a pointer to
the next so-far-unused object in the pool or fail if there were no unused objects in the pool.
But the computers raw memory is just such a pool of storage, and C++ provides the new
operator precisely to reserve a suitable chunk of memory, and get back a pointer to an object of
the right type, now occupying that chunk of memory. Theres also the delete operator to return
a chunk of memory to the pool again. The memory pool is called the heap (although in more
advanced programming one may have more than one heap, so the is a bit of a misnomer).
Using new (or any other means, such as C malloc) to allocate memory from the heap, and
delete to deallocate it, is very convenient but also in general extremely inefficient, from a hardcore
C++ point of view. Thats because new has to find a suitably large, unused chunk of memory,
i.e. it must do a search, and it must also do some memory management chores to prepare for the

32

Pointers chapter 1.

next search and for easy deallocation. For a typical implementation of new, and a variable
requiring little or no time-consuming initialization, were talking about hundreds or thousands of
times slower than creating a local variable, or more. So using new (or malloc, or whatever) is
not something you should do without a good reason, such as our temporary-object-problem.
But do bear in mind that the extreme inefficiency is relative: its only inefficient when compared
to the ultra-efficient C++ local variables. C# and Java programmers, not to mention script
language programmers, allocate almost all of their objects, including their local variables, via the
equivalent of new, and consequently they think of new as a cost-free operation. And in a sense it is,
because it only adds a (roughly) small fixed amount of time per object, and using such dynamic
allocation can support solutions algorithms and data structures that are Really Really Fast.
The new operator can use object constructors. That is, if the type in question has at least one
user-defined constructor, you not only reserve a suitable chunk of memory, but is also guaranteed
proper initialization of that memory chunk. But since were still in simple-C-mode except for
using new, where in real C youd have to use more primitive means! lets do the minimal to
turn the existing object value factory functions into object pointer factory functions, starting with
the basic expressionInstance, which is now accordingly renamed newExpression:
polymorphism/expr_like_c_2.cpp
#include
<cassert>
// assert
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
#include
<sstream>
// std::ostringstream
#include
<string>
// std::string
enum ExpressionKind{ constantKind, identityKind, sumKind, productKind };
struct Expression
{
ExpressionKind
double
Expression const*
Expression const*
};

kind;
value;
pLeft;
pRight;

// For constantKind.
// For sumKind and productKind.
// For sumKind and productKind.

typedef Expression const* ExpressionPtr;


ExpressionPtr newExpression(
ExpressionKind aKind,
double
aNumber,
ExpressionPtr
aLeftPointer,
ExpressionPtr
aRightPointer
)
{
Expression* pResult = new Expression;
pResult->kind = aKind;
pResult->value = aNumber;
pResult->pLeft = aLeftPointer;
pResult->pRight = aRightPointer;
return pResult;
}

Since ExpressionPtr is defined as pointer to const, once newExpression returns youre


dealing with a (pointer to a) const object, an object that cant be modified: that object turns to
stone as soon as it leaves newExpressions womb. But inside newExpression the object cant
very well be const, since the various members must be given appropriate values initialization is
a special phase of an objects life cycle, where the object is malleable even if the final result is constant. Hence the
use of Expression*, pointer to non-const, instead of ExpressionPtr, for the new result.

Copyright 2005 Alf P. Steinbach

33

ExpressionPtr constant( double value )


{ return newExpression( constantKind, value, 0, 0 ); }
ExpressionPtr identity()
{ return newExpression( identityKind, 0, 0, 0 ); }
ExpressionPtr sum( ExpressionPtr left, ExpressionPtr right )
{ return newExpression( sumKind, 0, left, right ); }
ExpressionPtr product( ExpressionPtr left, ExpressionPtr right )
{ return newExpression( productKind, 0, left, right ); }

For convenience I have now changed the argument types from references to pointers, so that the
client code can plug the result of product (say) directly into sum (say). With the previous design
the client code dealt with Expression objects, and so that design had reference arguments. But
with this new design the client code deals with pointers, and so it would not reduce the risk of
anything to require extra dereferencing of the pointers in the client code.
Now, with the functions above the client code can easily allocate expression objects and connect
them in a hierarchy. But if new objects were allocated all the time, without being returned to the
pool of free memory after having served their purpose, one would have a serious memory leak:
that more and more memory is reserved, until all available memory is exhausted (typically the
computer slows down to an unbearable snails pace before that limit is reached and some failure
occurs). Therefore there should also be a means to deallocate an expression object hierarhy.
Hence,
void destroy( ExpressionPtr e )
{
if( e != 0 )
{
destroy( e->pLeft );
destroy( e->pRight );
}
delete e;
}

For purely pedagogical purposes Ive placed the delete call after the if block instead of inside
it. I wanted to show that its entirely OK to call delete with a nullpointer. The effect is welldefined, namely that nothing happens.
On the other hand, the delete call couldnt have been placed before the if, because after a
delete the object doesnt exist anymore. The pointer would then be a dangling pointer. And it
would then be Undefined Behavior to access the now non-existent objects pLeft and pRight
members (accessing members of a deleted object is a very common error, and it can be
extremely difficult to spot this error: youre not guaranteed a crash).
Finally, for this design, the client code will be simpler if we change also the valueOf arguments
to pointers, so that e.g. the result of sum can be plugged directly into valueOf:
double valueOf( ExpressionPtr e, double x )
{
switch( e->kind )
{
case constantKind: return e->value;
case identityKind: return x;
case sumKind:
return valueOf( e->pLeft, x ) + valueOf( e->pRight, x );
case productKind:
return valueOf( e->pLeft, x ) * valueOf( e->pRight, x );
}
assert( false );
// Should never get here -- all cases covered above.
return 0;
// Just to satisfy the compiler (otherwise possible warning).
}

34

Pointers chapter 1.

And ditto for the stringFrom function.


Ditto: if youre copying and pasting the code to try it out with your favorite C++ compiler, just
copy the previous versions stringFrom code and modify it the same way as in valueOf above.
With the now totally uniform way of using expression pointers, the client code is very simple:
int main()
{
// 2x + 5
ExpressionPtr const e = sum( product( constant( 2 ), identity() ), constant( 5 ) );

std::cout << stringFrom( e ) << ":" << std::endl;


std::cout << std::endl;
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << valueOf( e, x ) << std::endl;
}
destroy( e );

Whats not so readily apparent from this code is that the client code now has several new
responsibilities. The client code must (1) eventually call our destroy on all Expression
pointers that arent owned by other objects (in the code above that means for the final e
pointer), which implies that (2) the client code must store such pointers in variables, not just use
them as temporary objects in calls to valueOf, say, and (3) the client code must not use pointers
after our destroy has been called on them. C++ has features that can help also with this.
To not leave you with a possibly incorrect impression: not calling our destroy (i.e., not calling
delete on every pointer obtained via new) doesnt necessarily imply a memory leak. The
operating system will, in general, clean up when the program terminates. A memory leak is in
practice only for a running program, but its a good idea to make a habit of always cleaning up.

Copyright 2005 Alf P. Steinbach

1.2.5

35

How to use function pointers to simulate dynamic types, C-style.

Summary:
The large-system maintenance nightmare caused by logical sub-type tag switch statements in common functions
can be solved by storing function pointers in each object.
That is the basis of object-orientation: instead of common functions that have knowledge of all relevant types of
objects, use objects that have knowledge of all relevant type-specific functions.
double (*pValueOf)( ExpressionPtr e, double x );
pValueOf = &valueOf;
result = (*pValueOf)( e, x );
result = pValueOf( e, x );

A declaration of a function pointer variable pValueOf.


Assigning a function pointer value.
Calling a function via a function pointer.
Calling a function via a function pointer, simpler syntax.

Since the declarations can become cumbersome its a good idea to use typedefs, and then you may encounter a
circular type dependency.
One way to resolve a circular type dependency is to use a forward declaration of a class (struct), which just
introduces its name to the compiler so that e.g. pointers (and references) can be defined.
When a function is called via a function pointer in an object, the object that the pointer to this function was retrieved
from for the function call is the current object for this call, and we say that the function is called on that current
object.
If we ensure (e.g. via factory functions) that only objects of the proper logical sub-type carry pointers to the sub-type
specific functions, then for a sub-type specific function the current object must be of the correct logical sub-type,
otherwise it wouldnt have that pointer.
result = e->valueOf( e, x );

C idiom for calling a logical member function.

In this common C-style calling pattern e->valueOf retrieves the function pointer from the object pointed to by e,
and then e is passed as first argument to the function: in this function call *e is the current object, presumably of the
correct logical sub-type for the function implementation.

Recall the three problems identified in section 1.2.3:

Possible invalid members in an object, for the objects logical sub-type.

Larger than necessary object size.

Maintenance nightmare for switch statements.

One way of fixing the last problem is to move knowledge of types from code to data. As it is we
have a function valueOf, to which we bring various data objects for evaluation, and valueOf
must inspect the object to determine what logical sub-type of object it is and thereby what should
be done, and to do the inspection valueOf must know of all kinds of logical sub-types; and the
same is the case for stringFrom, it must know of all kinds of logical sub-types. I.e., sub-type
specific knowledge of what to do resides with common functions, which is the problem, for when a new
logical sub-type is introduced all such functions must be updated: every such function must be
infused with knowledge about the new sub-type, and that is problematic in a large system.
Instead we can store function pointers in each object, that points to appropriate valueOf and
stringFrom functions for that kind of object only. Then, instead of common functions that

36

Pointers chapter 1.

knows what to do with various different kinds of objects, the object knows which sub-type
specific functions to call. I.e., sub-type specific knowledge of what to do resides with the sub-type object. And
the sub-type specific functions need only know about this specific sub-type and the general type.
New logical sub-types can therefore be introduced without affecting any of the existing code.
And this is the basis of object-orientation yes, thats where were headed:
Object-orientation (OO):
Instead of common functions that have knowledge of all relevant types of objects, use
objects that have knowledge of all relevant type-specific functions.
Of course to do this in primitive C-style we first of all need function pointers.
Given a function declaration such as
double valueOf( ExpressionPtr e, double x );

you can transform it to a pointer-to-function declaration by mimicking the way the pointer would
be used, just as we did earlier with pointer to int etc. I.e., the declaration
double (*pValueOf)( ExpressionPtr e, double x );

A declaration of a function pointer variable pValueOf.

means: if you dereference pValueOf you get something that you can add an argument list to and
call with an ExpressionPtr and a double as arguments, obtaining a double as result. Hence
pValueOf is a pointer to a function with the same signature as valueOf, and you can assign it
the address of the valueOf function, or any function with that signature, in the usual way:
pValueOf = &valueOf;

Assigning a function pointer value.

and then call the function it points to:


result = (*pValueOf)( e, x );

Calling a function via a function pointer.

Actually the address-of and dereferencing operators are superflous here in both C and C++1. We
say that a function name implicitly decays to a pointer to function wherever such a pointer is
expected; no & required, although for clarity its a good idea to supply an & anyway, and Ill do
that. Conversely, and much more practically useful2, a simple function pointer is treated as a
function in the context of a function call, no * required, so that you can write a call simply as
result = pValueOf( e, x );

Calling a function via a function pointer, simpler syntax.

Okay, enough talk, code:


polymorphism/expr_like_c_3.cpp
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
#include
<sstream>
// std::ostringstream
#include
<string>
// std::string

In C++ one can also omit the * in a declaration of a formal function argument that is a function pointer, because
C++ supports function types, and an argument of function type is adjusted to function pointer type.
2
Especially for template programming, where function pointers can be treated in the same way as functor objects.
1

Copyright 2005 Alf P. Steinbach

37

//------------------------------------- General expression:


struct Expression;
// Explains that Expression is a struct...
typedef Expression const* ExpressionPtr;
// ... so that this is accepted.
typedef double (*FuncValueOf)( ExpressionPtr e, double x );
// ... and this.
typedef std::string (*FuncStringFrom)( ExpressionPtr e );
// ... and this.
struct Expression
{
FuncValueOf
FuncStringFrom
double
ExpressionPtr
ExpressionPtr
};

valueOf;
stringFrom;
value;
// For constant.
pLeft;
// For sum and product.
pRight;
// For sum and product.

ExpressionPtr newExpression(
FuncValueOf
aFuncValueOf,
FuncStringFrom aFuncStringFrom,
double
aNumber,
ExpressionPtr
aLeftPointer,
ExpressionPtr
aRightPointer
)
{
Expression* pResult = new Expression;
pResult->valueOf = aFuncValueOf;
pResult->stringFrom = aFuncStringFrom;
pResult->value = aNumber;
pResult->pLeft = aLeftPointer;
pResult->pRight = aRightPointer;
return pResult;
}
void destroy( ExpressionPtr e )
{
if( e != 0 )
{
destroy( e->pLeft );
destroy( e->pRight );
}
delete e;
}

As you can see the kind member has been removed, because theres no longer any need to
discriminate on the logical sub-type (a kind member can be convenient for debugging, though).
I added a comment to the forward-declaration of Expression, at the top, in case youre not
familiar with forward-declarations. The forward-declaration introduces the name Expression so
that ExpressionPtr and in turn FuncValueOf, short for a valueOf function, can be defined
and then used in the definition of Expression. This is possible because all pointer values are
generally of the same size, say 4 or 8 bytes, no matter what they point to1, so to handle pointers
the compiler doesnt need to know more about Expression than that it is some class type.
There isnt much more that Expression can be used for at that point without being actually
defined2. But the ability to define pointer types (you can of course also define reference types) is
enough to resolve the circular type dependency, that ExpressionPtr is defined in terms of
Expression, which in turn is defined in terms of ExpressionPtr. Otherwise we would have to
tackle the circular dependency by writing the full definition of ExpressionPtr and the more
complicated FuncValueOf type inside the Expression definition, as we did in the previous
version for ExpressionPtr instead of using the simple typedefed names.

The standard doesnt guarantee that all pointer values are of the same size, and in particular pointers to member
functions can be of different size than other pointers; however, pointers to class type objects are all of the same size.
2
It can also be used as result or argument type in a function declaration, and for some template class typedefs.
1

38

Pointers chapter 1.

Now, as was our goal, a logical sub-type can be added with very little code and without updating
any of the previous (existing) code at least as long as the sub-type doesnt require more
ordinary data members or function pointers than is present in Expression:
//------------------------------------- Constant-expression:
double constant_valueOf( ExpressionPtr self, double /*x*/ )
{ return self->value; }
std::string constant_stringFrom( ExpressionPtr self )
{
std::ostringstream stream;
stream << self->value;
return stream.str();
}
ExpressionPtr constant( double value )
{ return newExpression( &constant_valueOf, &constant_stringFrom, value, 0, 0 ); }

In case you wonder: yes, this is all of the code for the constant expression sub-type, and the code
for the identity logical sub-type is similarly self-contained, just added code, not (as would be the
case in the previous version) requiring modification or special support in any of the other code.
//------------------------------------- Identity-expression:
double identity_valueOf( ExpressionPtr /*self*/, double x )
{ return x; }
std::string identity_stringFrom( ExpressionPtr self )
{ return "x"; }
ExpressionPtr identity()
{ return newExpression( &identity_valueOf, &identity_stringFrom, 0, 0, 0 ); }

The crucial point here is that any object that (e.g.) the constant factory function produces is
initialized with (e.g.) valueOf set to &constant_valueOf, so that this is the function that will be
called for any object of logical sub-type constant.
Because constant_valueOf is always called via the function pointer in some object that must
have been produced by constant, the specific such object for any given call gains a very special
status. We say that its the object that constant_valueOf is called on, this time. And thats
the object that the calling code should always supply a pointer to as the first argument: from
constant_valueOfs point of view its the current object for the call, the object of interest.
Current object in a call of a function:
The object that the pointer to this function was retrieved from for the function call (that
object must be of the correct logical sub-type, otherwise it wouldnt have that pointer).
If the calling code fails to observe this rule of always passing a pointer to the current object as
first argument (but instead passes a pointer to some other object) then all sorts of nasty things
can happen, because the object that constant_valueOf deals with need not then be of logical
sub-type constant. And then its value member might be anything. In the next version of the
program the object might not even have a value member, so that the code could possibly crash.
Therefore, any programmer who doesnt follow the rule, Will Be Fired.

Copyright 2005 Alf P. Steinbach

39

And correspondingly, Ive named constant_valueOfs formal argument self, to indicate its
special status and what it refers to. The name self for this argument stems from the Smalltalk
language. In Visual Basic its called me, in Eiffel its called current, and so on; in C++ its called
this, which is a keyword we cant use as the name of anything, and shouldnt redefine anyway.
But how do you then call valueOf, given a general Expression pointer e? Well, the valueOf
member is obtained by e->valueOf (for a constant-expression object this will be a pointer to
constant_valueOf). And you call it by supplying the arguments, as with any function:
result = e->valueOf( e, x );

C idiom for calling a logical member function.

This is a pattern that one finds in much C code: the object pointer, here e, is used both to
retrieve a function pointer, and as the first argument to that function.
Here it just turned out that way: we already had the argument, the second occurrence of e in the
call, and then added the function pointer thing in this version, the first occurrence of e. But that
was not a coincidence: in almost any reasonable design one ends up with that double tap pattern,
because with most1 functions the function needs to know which object it should do something
with, which object is the current object (assumed to be of the correct logical sub-type). Thats
why C++ has special support for this, so in full C++ you dont have to repeat e in the call.
The code for the sum logical sub-type has four examples of this calling pattern, namely for
evaluating the left and right sub-expressions:
//------------------------------------- Sum-expression:
double sum_valueOf( ExpressionPtr self, double x )
{
ExpressionPtr const left = self->pLeft;
ExpressionPtr const right = self->pRight;
return left->valueOf( left, x ) + right->valueOf( right, x );
}
std::string sum_stringFrom( ExpressionPtr self )
{
ExpressionPtr const left = self->pLeft;
ExpressionPtr const right = self->pRight;
return "(" + left->stringFrom( left ) + "+" + right->stringFrom( right ) + ")";
}
ExpressionPtr sum( ExpressionPtr left, ExpressionPtr right )
{ return newExpression( &sum_valueOf, &sum_stringFrom, 0, left, right ); }

And ditto for the code for the product logical sub-type (if youre copying and pasting code to try
it out, just copy the code for the sum logical sub-type, rename things, and change + to *).
When you see the code presented as above it might seem simple. But this was the first place I
had to debug any of the examples presented so far. For my original code,
double sum_valueOf( ExpressionPtr self, double x )
Original code, had a nasty BUG.
{
return self->pLeft->valueOf( self, x ) + self->pRight->valueOf( self, x );
}

went into an infinite recursive loop do you see why2?


The operatorResult and operatorSymbol functions in the next sub-section are examples of a type-specific
function that doesnt need to know anything about the object its called on, except the type. Such functions are rare,
but not unheard of. I dont know any language that supports them by allowing you to state, in some way, I dont
want a current object argument: probably the overhead of passing a current object argument is too marginal.
2
Hint: there is an absolute rule that isnt observed by this code.
1

40

Pointers chapter 1.

Anyway, for completeness, the main program:


//------------------------------------- Main:
int main()
{
// 2x + 5
ExpressionPtr const e = sum( product( constant( 2 ), identity() ), constant( 5 ) );

std::cout << e->stringFrom( e ) << ":" << std::endl;


std::cout << std::endl;
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << e->valueOf( e, x ) << std::endl;
}
destroy( e );

Copyright 2005 Alf P. Steinbach

1.2.6

41

Using real types and inheritance for logical sub-types, mostly C-style.

Summary:
In C a conceptual class extension can be implemented by having an A member at the start of B,
struct A { int x; };
struct B { A baseA; int y; };
A*

pA

C-style class extension.

= &pB->baseA;

C-style class extension requires a cast to go from a pointer to that A-member, to a pointer to the full B object. In C
you write pB = (B*)pA, and in C++ its pB = reinterpret_cast<B*>( pA ). Such casts are extremely
dangerous because they cast between unrelated types.
In C++ a class extension can be more safely expressed as inheritance,
struct A { int x; };
struct B: A { int y; };
A*

pA

C++-style class extension, inheritance.


Implicit conversion from derived class pointer to base class pointer.

= pB;

where A is a base class for the derived class (extended class) B, and the anonymous A part of a B object is called a
(base class) sub-object of that B object (ordinary data members are also sub-objects); as an explanation to be
adjusted later, inheritance makes the members of A directly available in B, it makes them members of B.
Now the types are related so that a B* pointer converts implicitly to A* (shown above), called an upcast, and you
can use a static_cast to perform a downcast from A* to B*:
A downcast.

pB = static_cast<B*>( pA );

For POD (Plain Old Data) types its Undefined Behavior to call delete with a pointer of type T* where T is not
the objects dynamic type, i.e. an upcasted pointer yields Undefined Behavior.
A* pA = new B;
delete pA;

// OK, B* is implicitly converted up to A*.


// !Undefined Behavior.

Dont do this at home!

You can think of this as if delete uses the static type to infer the size of the object to be deleted, and that an
incorrect inferred size can cause Serious Havoc.
Inheritance forms an IsA-hierarchy of types, which we often just call a class hierarchy:
IsA-hierarchy (class hierarchy),
static:

Parts hierarchy (object hierarchy),


dynamic:

Expression

+
Sum

Constant

Identity

BinaryOperator

2
Sum

Product

Product

Constant

Constant

x
Identity

To do sub-type specific things, such as calling delete with a properly downcasted pointer, a general solution is to
add function pointers in the objects (object orientation, OO), and in general, each type-specific function
implementation must downcast the current object pointer to the specific sub-type.

In the list of three problems identified in section 1.2.3,

Possible invalid members in an object, for the objects logical sub-type.

42

Pointers chapter 1.

Larger than necessary object size.

Maintenance nightmare for switch statements.

we have solved the last one.


We can now solve both of the two remaining problems by the idea of extending a class, adding in
whats needed for a logical sub-type. As youll see, at the C level of coding this will introduce
several new and even more serious maintenance nightmares. At the C level of coding its so
much code, so manifestly unsafe, and apparently so complex, that the cure seems much worse
than the illness! ;-) What that means is that in C or C-style you have to choose between one set
of problems (the original illness) and another (the cure). C++ of course helps with this, reducing
both the code size (about half) and the complexity, and removing the unsafety; well start looking
at the C++ support in section 1.2.8, but for now well do it mostly C-style.
The idea of class extension is to take an existing class, and define a new class that is otherwise
exactly like the original but extended with whatever extras you need in a logical sub-type.
In C a class extension is nothing new language-wise, just a new way of thinking about the old
struct. You let a class (in C, a struct) B contain a class A object as a data member at the very
start, a logical base class member. Then B extends A in the sense of adding more members
after the A base class member.
Extension means that if you have a pointer to a B-object, you also have a pointer to the A-object
that the B-object extends, the logical base class member of the B-object:
struct A { int x; };
struct B { A baseA;
void foo()
{
B
b;
B* pB
A* pA
}

int y; };

C-style class extension.

= &b;
= &pB->baseA;

Here pB and pA are the same address, since the baseA member is at the start of a B-object (starts
at the same address in memory), but the pointers are of different, seemingly unrelated types.
So, given a pointer pA which you know in reality points to a B-object, how do you obtain a pBpointer that can access the B-specific members? Since the types are unrelated you have to use a
cast. In C you would write pB = (B*)pA, and in C++ its pB = reinterpret_cast<B*>( pA ).
In general those are extremely dangerous constructions with Undefined Behavior, but C++ supports
this special usage, going from an objects first member to the full object, or vice versa, in 9.2/17.
However, the support is limited to C-style POD (Plain Old Data) objects, and its not a good
idea to get used to C-style casts or C++ reinterpret_cast, because in almost all other
situations they are so dangerous.
Therefore Ill now bump us up one level, that from now on well use a little bit of C++ language
support. In C++ the notion of a B-object that contains an A base class member at the start, can
be expressed as inheritance, that B inherits all members etc. from A. You simply write
struct A { int x; };
struct B: A { int y; };

C++-style class extension, inheritance.

Copyright 2005 Alf P. Steinbach

43

and voil, every B object contains the members defined in A, plus those defined in B.
Here A is a base class for B, and B, the extended class, is a derived class of A, and this C++
feature alone removes a number of sources of bugs; e.g., in the C-style version above a
maintenance programmer could inadvertently add a member before the baseA member.
The A base class member with inheritance we more properly call it a sub-object, reserving the
more specific term member for sub-objects that are ordinary data members now has no
name, which seemingly would make it difficult to obtain a pointer to it, but the compiler now
knows that the types are related so that a pointer to B can be implicitly converted to a pointer to A,
which is another C++ feature:
void foo()
{
B
b;
B* pB
A* pA
}

= &b;
= pB;

Implicit conversion from derived class pointer to base class pointer.

And going the opposite way its now no longer necessary or even supported to use the generally
extremely dangerous reinterpret_cast, but instead the merely somewhat dangerous
static_cast (a static_cast requires that the types are related) can be employed:
pB = static_cast<B*>( pA );

A downcast.

This, going from A* to B*, is known as a downcast, because inheritance can form a hierarchy of
classes, and with a downcast you go down in that hierarchy, towards a more specific type; if all
the code is in one file you also in a sense go downward in the program text, from the definition
of class A to the definition of the extended class B. You can also use a static_cast to explicitly
do the conversion from B* to A*, e.g. to make it very clear. And if you do then that is an
example of an upcast, going up in the class inheritance hierarchy (and in the program text). We
also refer to an implicit conversion from B* to A* as an upcast, even though it isnt a cast. Thats
because it could just as well be a cast, and it would be silly to say, e.g., up-conversion.
Well need a lot of downcasts, and thats what makes this version so manifestly unsafe. In reality
the previous versions were just as unsafe: you could risk e.g. dereferencing the pRight member
in an object of identity sub-type. But with the downcasts all that unsafety will be directly visible.
In contrast to downcasting upcasting is almost always safe (and thats why the implicit conversion
is allowed), with one important exception: applying the delete operator to the result.
For the classes were currently dealing with, the effect of the delete operator is Undefined
Behavior if the object is of dynamic type T, and the pointer is anything but T*. I.e., deleteing
such an object via an upcasted pointer is Undefined Behavior (C++ does support deleteing via
upcasted pointers, but to activate that support we need to use more of C++, namely those
features were heading towards). You can think of this as if delete uses the static type to infer
the size of the object to be deleted, and that an incorrect inferred size can cause Serious Havoc.
A* pA = new B;
delete pA;

// OK, B* is implicitly converted up to A*.


// !Undefined Behavior.

Dont do this at home!

With that out of the way, what we want is to not have any more data in an object than necessary
for that logical sub-type, and wed like to do that by decomposing the existing Expression type,

44

Pointers chapter 1.

struct Expression
{
FuncValueOf
FuncStringFrom
double
ExpressionPtr
ExpressionPtr
};

valueOf;
stringFrom;
value;
// For constant.
pLeft;
// For sum and product.
pRight;
// For sum and product.

into a hierarchy of types connected by inheritance, one real type per logical sub-type, like
struct Expression
struct
Constant: Expression
struct
Identity: Expression
struct
Sum: Expression
struct
Product: Expression

{ FuncValueOf valueOf;
{ double value; };
{};
{ ExpressionPtr pLeft;
{ ExpressionPtr pLeft;

FuncStringFrom stringFrom; };
ExpressionPtr pRight; };
ExpressionPtr pRight; };

But now we have objects of different actual sizes, handled via base class ExpressionPtr
pointers, which means were on a head-on collision course with delete, the Undefined Behavior
problem discussed above. One way to solve that is by delegating the responsibility for calling
delete to each type, yielding type-specific calls to delete, in the same way that valueOf
currently provides a type-specific computation. This means one extra function pointer, which Ill
call destroy, and that well now have self-destroying objects (that doesnt mean that an object
will automatically destroy itself, but that on command it will commit suicide, so to speak):
struct Expression
struct
struct
struct
struct

Constant: Expression
Identity: Expression
Sum: Expression
Product: Expression

{ FuncValueOf valueOf; FuncStringFrom stringFrom;


FuncDestroy destroy; };
{ double value; };
{};
{ ExpressionPtr pLeft; ExpressionPtr pRight; };
{ ExpressionPtr pLeft; ExpressionPtr pRight; };

And there is one more problem: redundancy. Sum and Product, and any other binary operators
that we might care to add, such as Difference, must currently all declare the same two pointer
members, and must all include their own almost identical code to handle them. We should get
rid of this evil redundancy, and we can do that by adding an intermediate BinaryOperator class
that Sum and Product inherit from (extend), and that can do the common things for them:
struct Expression
struct
struct
struct
struct
struct

{ FuncValueOf valueOf; FuncStringFrom stringFrom;


FuncDestroy destroy; };
Constant: Expression
{ double value; };
Identity: Expression
{};
BinaryOperator: Expression { ExpressionPtr pLeft; ExpressionPtr pRight; };
Sum: BinaryOperator
{};
Product: BinaryOperator {};

Before elaborating on the BinaryOperator definition, which needs some additional features to
really get rid of the code redundance, a more high level view of what we have so far.
In a sense a Sum object is a BinaryOperator object, which in turn is an Expression object. Its
the same as how a Dog, or e.g. an Elephant, is an Animal. So the use of inheritance has formed
an IsA-hierarchy of types, which we often just call a class hierarchy. But its different from the
hierarchy of objects that we have for a specific expression. That object hierarchy is more akin to
how a Dog consists of a body and a head, where the Dogs head consists, among other parts, of
eyes and ears and a snout, and so on down into more details: a parts hierarchy.

Copyright 2005 Alf P. Steinbach

45

IsA-hierarchy (class hierarchy),


static:

Parts hierarchy (object hierarchy),


dynamic:

Expression

+
Sum

Constant

Identity

BinaryOperator

2
Sum

Product

Product

Constant

Constant

x
Identity

Flashback: aha, the class hierarchy is what downcast and upcast refer to!
Back to BinaryOperator There are three cases where the code has to delve into the pLeft
and pRight sub-trees of the expression object hierarchy, namely for the valueOf, the
stringFrom and the destroy operations, and BinaryOperator can and should do this delvinginto on behalf of Sum and Product, so as to centralize this code: no redundancy. But to do this
the BinaryOperator code has to do Sum- and Product-specific things, namely, for Sum, (1)
computing + in valueOf, (2) knowing that the operator symbol is + in stringFrom, and (3)
calling delete on a Sum object in destroy; and correspondingly for Product.
As before the general solution to doing sub-type-specific things is to add function pointers. For
the operator symbol we have a choice of adding a std::string operator symbol data member,
or adding a function that produces the symbol, which adds a function pointer data member. The
std::string data member might seem more natural and requires less code, but the function
pointer member will be possible to remove with the technique I discuss in the next sub-section.
Ill call these function pointers respectively operatorResult, operatorSymbol and
callDelete where the latter is only responsible for calling delete, and nothing else:
struct BinaryOperator;
typedef
typedef
typedef
typedef

BinaryOperator const* BinaryOperatorPtr;


double (*FuncOperatorResult)( double a, double b );
std::string (*FuncOperatorSymbol)();
void (*FuncCallDelete)( BinaryOperatorPtr e ); // Call delete for specific type.

struct BinaryOperator: Expression


{
FuncOperatorResult operatorResult;
FuncOperatorSymbol operatorSymbol;
FuncCallDelete
callDelete;
ExpressionPtr
pLeft;
ExpressionPtr
pRight;
};

So, the price of removing code redundancy for Sum and Product is that every BinaryOperator
object, that is, every Sum and Product object, will contain three extra pointers. Well shortly see
how that trade-off can be traded further, getting back the lost memory + more, and paying
instead in potentially slower function calls. But first because this is already complicated! the
code, which for best results you should read & study in order, slowly, piece by small piece:
polymorphism/expr_like_c_4.cpp
#include
<cassert>
// assert
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
#include
<sstream>
// std::ostringstream
#include
<string>
// std::string

46

Pointers chapter 1.

//------------------------------------- class Expression:


struct Expression;
typedef
typedef
typedef
typedef

Expression const* ExpressionPtr;


double (*FuncValueOf)( ExpressionPtr e, double x );
std::string (*FuncStringFrom)( ExpressionPtr e );
void (*FuncDestroy)( ExpressionPtr e );
// Do recursive clean-up.

struct Expression
{
FuncValueOf
FuncStringFrom
FuncDestroy
};

valueOf;
stringFrom;
destroy;

void initExpression( Expression* expression )


{
// There will never exist objects of _dynamic_ type Expression, so:
expression->valueOf = 0;
// Is set appropriately by derived class.
expression->stringFrom = 0;
// Is set appropriately by derived class.
expression->destroy = 0;
// Is set appropriately by derived class.
}
//------------------------------------- class Constant:
struct Constant: Expression { double value; };
typedef Constant const* ConstantPtr;
double Constant_valueOf( ExpressionPtr expressionSelf, double /*x*/ )
{
ConstantPtr const self = static_cast<ConstantPtr>( expressionSelf );
return self->value;
}
std::string Constant_stringFrom( ExpressionPtr expressionSelf )
{
ConstantPtr const self = static_cast<ConstantPtr>( expressionSelf );
std::ostringstream stream;
stream << self->value;
return stream.str();
}
void Constant_destroy( ExpressionPtr expressionSelf )
{
ConstantPtr const self = static_cast<ConstantPtr>( expressionSelf );
delete self;
}
Constant* initConstant( Constant* constant, double aValue )
{
initExpression( constant );
constant->valueOf = &Constant_valueOf;
constant->stringFrom = &Constant_stringFrom;
constant->destroy = &Constant_destroy;
constant->value = aValue;
return constant;
}
ConstantPtr constant( double aValue )
{
return initConstant( new Constant, aValue );
}
//------------------------------------- class Identity:
struct Identity: Expression {};
typedef Identity const* IdentityPtr;
double Identity_valueOf( ExpressionPtr /*e*/, double x )
{ return x; }

Copyright 2005 Alf P. Steinbach

47

std::string Identity_stringFrom( ExpressionPtr /*e*/ )


{ return "x"; }
void Identity_destroy( ExpressionPtr expressionSelf )
{
IdentityPtr const self = static_cast<IdentityPtr>( expressionSelf );
delete self;
}
Identity* initIdentity( Identity* identity )
{
initExpression( identity );
identity->valueOf = &Identity_valueOf;
identity->stringFrom = &Identity_stringFrom;
identity->destroy = &Identity_destroy;
return identity;
}
IdentityPtr identity()
{
return initIdentity( new Identity );
}
//------------------------------------- class BinaryOperator:
struct BinaryOperator;
typedef
typedef
typedef
typedef

BinaryOperator const* BinaryOperatorPtr;


double (*FuncOperatorResult)( double a, double b );
std::string (*FuncOperatorSymbol)();
void (*FuncCallDelete)( BinaryOperatorPtr e ); // Call delete for specific type.

struct BinaryOperator: Expression


{
FuncOperatorResult operatorResult;
FuncOperatorSymbol operatorSymbol;
FuncCallDelete
callDelete;
ExpressionPtr
pLeft;
ExpressionPtr
pRight;
};
double BinaryOperator_valueOf( ExpressionPtr expressionSelf, double x )
{
BinaryOperatorPtr const self = static_cast<BinaryOperatorPtr>( expressionSelf );

ExpressionPtr const left


ExpressionPtr const right

= self->pLeft;
= self->pRight;

double const
double const

= left->valueOf( left, x );
= right->valueOf( right, x );

leftArg
rightArg

return self->operatorResult( leftArg, rightArg );

std::string BinaryOperator_stringFrom( ExpressionPtr expressionSelf )


{
BinaryOperatorPtr const self = static_cast<BinaryOperatorPtr>( expressionSelf );
ExpressionPtr const left
ExpressionPtr const right

= self->pLeft;
= self->pRight;

std::string const
std::string const

= left->stringFrom( left );
= right->stringFrom( right );

leftArgString
rightArgString

return "(" + leftArgString + self->operatorSymbol() + rightArgString + ")";


}

48

Pointers chapter 1.

void BinaryOperator_destroy( ExpressionPtr expressionSelf )


{
BinaryOperatorPtr const self = static_cast<BinaryOperatorPtr>( expressionSelf );
assert( self != 0 );
// No call here supplies nullpointer as argument.
ExpressionPtr const left
ExpressionPtr const right

= self->pLeft;
= self->pRight;

left->destroy( left );
right->destroy( right );
self->callDelete( self );
}
void initBinaryOperator(
BinaryOperator*
binaryOperator,
ExpressionPtr
left,
ExpressionPtr
right
)
{
initExpression( binaryOperator );
binaryOperator->valueOf = &BinaryOperator_valueOf;
binaryOperator->destroy = &BinaryOperator_destroy;
binaryOperator->stringFrom = &BinaryOperator_stringFrom;
binaryOperator->operatorResult = 0;
// Is set appropriately by derived class.
binaryOperator->operatorSymbol = 0;
// Is set appropriately by derived class.
binaryOperator->callDelete = 0;
// Is set appropriately by derived class.
binaryOperator->pLeft = left;
binaryOperator->pRight = right;
}
//------------------------------------- class Sum:
struct Sum: BinaryOperator {};
typedef Sum const* SumPtr;
double Sum_operatorResult( double a, double b )
{ return a + b; }
std::string Sum_operatorSymbol()
{ return "+"; }
void Sum_callDelete( BinaryOperatorPtr binaryOperatorSelf )
{
SumPtr const self = static_cast<SumPtr>( binaryOperatorSelf );
delete self;
}
Sum* initSum( Sum* sum, ExpressionPtr left, ExpressionPtr right )
{
initBinaryOperator( sum, left, right );
sum->operatorResult = &Sum_operatorResult;
sum->operatorSymbol = &Sum_operatorSymbol;
sum->callDelete = &Sum_callDelete;
return sum;
}
SumPtr sum( ExpressionPtr left, ExpressionPtr right )
{
return initSum( new Sum, left, right );
}
//------------------------------------- class Product:
struct Product: BinaryOperator {};
typedef Product const* ProductPtr;
double Product_operatorResult( double a, double b )
{ return a*b; }
std::string Product_operatorSymbol()
{ return "*"; }

Copyright 2005 Alf P. Steinbach

49

void Product_callDelete( BinaryOperatorPtr binaryOperatorSelf )


{
ProductPtr const self = static_cast<ProductPtr>( binaryOperatorSelf );
delete self;
}
Product* initProduct( Product* product, ExpressionPtr left, ExpressionPtr right )
{
initBinaryOperator( product, left, right );
product->operatorResult = &Product_operatorResult;
product->operatorSymbol = &Product_operatorSymbol;
product->callDelete = &Product_callDelete;
return product;
}
ProductPtr product( ExpressionPtr left, ExpressionPtr right )
{
return initProduct( new Product, left, right );
}
//------------------------------------- Main:
int main()
{
// 2x + 5
ExpressionPtr const e = sum( product( constant( 2 ), identity() ), constant( 5 ) );

std::cout << e->stringFrom( e ) << ":" << std::endl;


std::cout << std::endl;
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << e->valueOf( e, x ) << std::endl;
}
e->destroy( e );

I think youll agree that its not a very practical proposition to do object-oriented programming in
C. Or even, as here, C augmented with new and delete and simple data struct inheritance,
which do make things a good deal easier. The code overhead needed to carry a small payload
such as the Sum_operatorResult and Sum_operatorSymbol functions, the +, which
constitute the essence and externally useful part of that class, is simply incredible at the C-level!

50

Pointers chapter 1.

1.2.7

Using vtables, mostly C-style (also a few words about abstract classes, etc.).

Summary:
Since every object of a given class (in our example) will have the same function pointer values as every other object
of that class the collection of function pointers in each object can be replaced with just one pointer, which then
points to a common table of function pointers for that class.
The table is called a vtable, the single pointer in each object a vtable pointer, and the v stands for virtual, which
means that which function implementation is executed depends on the object.
+
Sum

2
Constant
Constant_vTable

&Constant_valueOf
&Constant_stringFrom
5
Constant

&Constant_destroy

Sum_vTable
valueOf
stringFrom
destroy

&BinaryOperator_valueOf
&BinaryOperator_stringFrom
&BinaryOperator_destroy

operatorResult

&Sum_operatorResult

operatorSymbol

&Sum_operatorSymbol

callDelete

+
Sum

&Sum_callDelete
+
Sum

We call the functions accessed via function pointers in objects or vtables methods (a language-independent term);
usually a method takes the current object pointer as its first argument.
Calling a method via a vtable, C-style, involves (1) fetching the vtable pointer from the current object, (2) fetching
the function pointer from that vtable, and (3) calling that function (usually) with a pointer to the current object as
first argument, e.g., e->vTable->foo(e).
If a class Ts vtable has entries for functions that are to be defined in derived classes, and those entries are 0 (or some
other invalid value) in Ts vtable, the functions typically not implemented in T, we say that these functions are
abstract methods in the Smalltalk language they were denoted as subclass responsibility, a Smalltalk subclass
being the same concept as a C++ derived class.
The class is then an abstract class, and should not be instantiated on its own (instantiate: create an object of a
class). Instantiating a concrete derived class, one where there are no longer any abstract methods, is of course OK
even though that instantiates the abstract class sub-object.
A class that provides a new implementation for a method introduced in a base class is said to override that method,
as opposed to compile time function overloading. With overriding, in the derived class vtable its then a pointer to
the derived class implementation in that methods entry. If the method was abstract in the base class we also say
that the derived class implements the method, implying that it wasnt implemented before.

With the so far latest version every Sum object contains six function pointers, namely valueOf,
stringFrom, destroy, operatorResult, operatorSymbol and callDelete. And every Sum
object has the same values for those six pointers as every other Sum object. Talk about redundancy!
Clearly that can damp ones enthusiasm for doing type-specific things: every little type-specific
thing one wants done incurs a cost of one pointer in every object of the type, and the cost is
proportional not to the number of classes, but to the number of objects at run-time

Copyright 2005 Alf P. Steinbach

51

One solution is to replace the collection of function pointers in each object with just one pointer
in each object, namely a pointer to a static collection of the function pointers for the objects
type. Such a per-type static collection of function pointers is commonly called a vtable, short for
virtual function table, and the single pointer in each object is a vtable pointer. The word
virtual, the v, just means that when you have a pointer to an Expression object and call
valueOf, for example, you dont know whether it will be Sums or Products or Constants or
Identitys valueOf implementation that will be executed, because that depends on the object.
+
Sum

2
Constant
Constant_vTable

&Constant_valueOf
&Constant_stringFrom
5
Constant

&Constant_destroy

Sum_vTable
valueOf
stringFrom
destroy

&BinaryOperator_valueOf
&BinaryOperator_stringFrom
&BinaryOperator_destroy

operatorResult

&Sum_operatorResult

operatorSymbol

&Sum_operatorSymbol

callDelete

+
Sum

&Sum_callDelete
+
Sum

As the above figure tries to illustrate, the vtables for related classes have the same placement of
the common operations. Here the three first entries in each vtable are determined by the
Expression class, which is a base class of both Constant and Sum. So given a pointer to an
Expression object, if you call destroy on that object, the function implementation to execute
will be the one (there is a pointer to) in the third entry of the objects associated vtable, no matter
whether the object really is a Constant or a Sum; and the function pointer there will be the one
appropriate for the dynamic type of the object, which is what polymorphism is all about.
The price for saving per-object memory, going down to just one pointer per object, is potentially
overall execution efficiency for method calls, which was an issue in the 1980s and early 1990s
and perhaps can still be an issue when doing embedded systems (microcontroller) programming.
Heres why: without any optimization, e.g. optimization at the processor level, every method call
involves first retrieving the vtable pointer from the object, then retrieving the function pointer
from the vtable, and finally calling the function via that pointer. This adds two memory accesses
compared to calling a freestanding function, and one memory access compared to our previous
version with function pointers in each object. However, argument transfer and other things
typically constitute the bulk of the time involved in a function call (apart from what the function
does), modern processors for desktop computers presumably optimize this calling pattern down
to near 0% overhead, and Bjarne Stroustrup describes the overhead for the C++ languagesupported implementation of this calling pattern as not measurable1.
Well implement vtables as simple structs instead of actual C or C++ tables, because with
structs we avoid Undefined Behavior casting and can use simple names for the entries. Since
the pointers are all of the same size the structs will end up as tables in the executing programs
memory, anyway. For the Expression class there isnt much to it, as you can see:

[http://www.research.att.com/~bs/bs_faq2.html#no-derivation], as of October 2005.

52

Pointers chapter 1.

polymorphism/expr_like_c_5.cpp
#include
<cassert>
// assert
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
#include
<sstream>
// std::ostringstream
#include
<string>
// std::string
//------------------------------------- class Expression:
struct Expression;
struct ExpressionVTable;
typedef
typedef
typedef
typedef

Expression const* ExpressionPtr;


double (*FuncValueOf)( ExpressionPtr e, double x );
std::string (*FuncStringFrom)( ExpressionPtr e );
void (*FuncDestroy)( ExpressionPtr e );
// Do recursive clean-up.

struct Expression
{
ExpressionVTable const* vTable;
};
struct ExpressionVTable
{
FuncValueOf
valueOf;
FuncStringFrom stringFrom;
FuncDestroy
destroy;
};
ExpressionVTable const& vTable( ExpressionPtr o )
{
return *(o->vTable);
}
void initExpression( Expression* expression )
{
static ExpressionVTable const theVTable =
{
0, 0, 0
};
expression->vTable = &theVTable;
}

The only trick, if it can be called that, is to declare the vtable as a static local constant within
the initExpression function. Thats only because its a tad cleaner than having the vtable as a
globally accessible constant. This way its more clear that the vtable isnt accessed globally.
Now, given an Expression pointer e, youd call e.g. destroy by e->vTable->destroy(e). So
whats the point of the vTable function above, which doesnt save that many keystrokes, writing
vTable(e).destroy(e) instead? Well, that will become clear when we get to the
implementation of BinaryOperator: Im just mentioning it so you wont think its arbitrary.
Here are the relatively speaking simpler Constant and Identity implementations. Or, on
second thoughts, just Constant. The Identity class is pretty much identical
//------------------------------------- class Constant:
struct Constant: Expression { double value; };
typedef Constant const* ConstantPtr;
typedef ExpressionVTable ConstantVTable;
double Constant_valueOf( ExpressionPtr expressionSelf, double /*x*/ )
{
ConstantPtr const self = static_cast<ConstantPtr>( expressionSelf );
return self->value;
}

Copyright 2005 Alf P. Steinbach

53

std::string Constant_stringFrom( ExpressionPtr expressionSelf )


{
ConstantPtr const self = static_cast<ConstantPtr>( expressionSelf );
std::ostringstream stream;

stream << self->value;


return stream.str();

void Constant_destroy( ExpressionPtr expressionSelf )


{
ConstantPtr const self = static_cast<ConstantPtr>( expressionSelf );
delete self;
}
Constant* initConstant( Constant* constant, double aValue )
{
static ConstantVTable const theVTable =
{
&Constant_valueOf, &Constant_stringFrom, &Constant_destroy
};
initExpression( constant );
constant->vTable = &theVTable;
constant->value = aValue;
return constant;
}
ConstantPtr constant( double aValue )
{
return initConstant( new Constant, aValue );
}

Theres no Constant-specific version of the vTable function because the one for Expression
is eminently usable also for Constant objects such is the power of polymorphism.
Since Constant does not add any new function pointers the ConstantVTable is physically
identical in layout to ExpressionVTable, and so is defined as the same simply via a typedef.
BinaryOperator, on the other hand, does add a few new function pointers. And since
BinaryOperatorVTable should be an extension of ExpressionVTable the natural way

to
define it would be via C++ inheritance. But with inheritance the class will no longer be a POD
(it wont even be a so-called aggregate), and then the convenient C-style initialization, the braceenclosed initializer list illustrated in initConstant above, isnt available. Wed have to use a
C++ constructor to do the initialization, far above the near-C-level were still at. So, instead well
use the C technique for class extension, just with C++ reinterpret_cast instead of a C cast:
//------------------------------------- class BinaryOperator:
struct BinaryOperator;
typedef
typedef
typedef
typedef

BinaryOperator const* BinaryOperatorPtr;


double (*FuncOperatorResult)( BinaryOperatorPtr e, double a, double b );
std::string ( *FuncOperatorSymbol)( BinaryOperatorPtr e );
void (*FuncCallDelete)( BinaryOperatorPtr e ); // Call delete for specific type.

struct BinaryOperator: Expression


{
ExpressionPtr
pLeft;
ExpressionPtr
pRight;
};
struct BinaryOperatorVTable
{
ExpressionVTable
basePart;
FuncOperatorResult operatorResult;
FuncOperatorSymbol operatorSymbol;
FuncCallDelete
callDelete;
};

54

Pointers chapter 1.

BinaryOperatorVTable const& vTable( BinaryOperatorPtr o )


{
ExpressionVTable const* evt = o->vTable;
return *reinterpret_cast<BinaryOperatorVTable const*>( evt );
}

// Standard's 9.2/17.

And here you see the reason for the existence of the vTable function. Without it one would
have to write an ugly, unsafe, long-winded cast expression like the one inside vTable above, in
order to call one of the functions introduced in BinaryOperator (those functions are only
present in BinaryOperators vtable, and in derived classes). But with the vTable function
defined for all classes, one can use a uniform and simple notation for calling functions on objects.
Of course, overloading the vTable function, as we have done here (two vTable functions with
different argument types), is a very non-C-ish C++ feature: its compile time polymorphism.
But why do the casting that the vTable function is said to be all about, at all? Why not just
declare the BinaryOperator vtable as a global constant, and use that directly, or, if the author
feels there is some religious-like ban on accessing global constants directly, let vTable return a
reference to a static constant inside vTable? Well, the reason for that is that the actual vtable
that vTable returns a reference to, depends on the object, which is the point of vtables.
Even though the argument of vTable is declared as BinaryOperatorPtr, we dont know that
the objects dynamic type is BinaryOperator. Or rather, we do know that it certainly isnt a
BinaryOperator. It will be either a Sum or a Product object, or some other as yet not
introduced derived class, and the vtable will be the Sum or Product or Whatever class vtable.
A bit more subtle, also in the interest of uniform function call notation Ive changed the signature
of operatorResult and operatorSymbol, so that they now take a BinaryOperatorPtr first
argument. With that change all functions to be called on objects, all methods, take a pointer to
the object as first argument. And that not only reduces the risk of wasting time on mistakes that
the compiler will catch, but also reduces the risk of the more likely and non-reported error of not
using the same pointer for vtable retrieval and as first argument, by facilitating use of macros like
#define ARG ,
#define NOARGS
#define CALL_METHOD( object, method, args ) vTable( object ).method( object args )
//
// E.g., the call
//
vTable( e ).foo( e, x, y );
// can be expressed as
//
CALL_METHOD( e, foo, ARG x ARG y );

Note: method is a language-independent concept term, and most programmers will understand
what you mean, but it does not have a technically precise meaning in C++ programming.
OK, that was a digression. And it was pure C territory: in C++ macros are no-no. Be ashamed
for even mentioning this, Alf. Be Ashamed! On with the rest of BinaryOperator, then:

Copyright 2005 Alf P. Steinbach

55

double BinaryOperator_valueOf( ExpressionPtr expressionSelf, double x )


{
BinaryOperatorPtr const self = static_cast<BinaryOperatorPtr>( expressionSelf );

ExpressionPtr const left


ExpressionPtr const right

= self->pLeft;
= self->pRight;

double const
double const

= vTable( left ).valueOf( left, x );


= vTable( right ).valueOf( right, x );

leftArg
rightArg

return vTable( self ).operatorResult( self, leftArg, rightArg );

std::string BinaryOperator_stringFrom( ExpressionPtr expressionSelf )


{
BinaryOperatorPtr const self = static_cast<BinaryOperatorPtr>( expressionSelf );

ExpressionPtr const left


ExpressionPtr const right

= self->pLeft;
= self->pRight;

std::string const
std::string const
std::string const

= vTable( left ).stringFrom( left );


= vTable( right ).stringFrom( right );
= vTable( self ).operatorSymbol( self );

leftArgString
rightArgString
symbol

return "(" + leftArgString + symbol + rightArgString + ")";

void BinaryOperator_destroy( ExpressionPtr expressionSelf )


{
BinaryOperatorPtr const self = static_cast<BinaryOperatorPtr>( expressionSelf );
assert( self != 0 );
// No call here supplies nullpointer as argument.
ExpressionPtr const left
ExpressionPtr const right

= self->pLeft;
= self->pRight;

vTable( left ).destroy( left );


vTable( right).destroy( right );
vTable( self ).callDelete( self );
}
BinaryOperator* initBinaryOperator(
BinaryOperator*
binaryOperator,
ExpressionPtr
left,
ExpressionPtr
right
)
{
static BinaryOperatorVTable const theVTable =
{
{
&BinaryOperator_valueOf, &BinaryOperator_stringFrom, &BinaryOperator_destroy
},
0,
// operatorResult
0
// callDelete
};
initExpression( binaryOperator );
binaryOperator->vTable = &theVTable.basePart;
binaryOperator->pLeft = left;
binaryOperator->pRight = right;
return binaryOperator;
}

As with Expression, the methods that are to be defined by derived classes, called abstract
methods, are just set to 0 in the vtable. In order to encounter one of those 0s in the programs
execution it would have to have erronously managed to create a BinaryOperator dynamic type
object, which would be as meaningless as a concrete Animal of no more particular kind; or the
BinaryOperator initialization code is calling one of those methods; or, the BinaryOperator
destruction code is calling one of those methods for an implementation that more formally
correct moves the objects vtable pointer up again in the class hierarchy on destruction, which
hasnt been an issue in this implementation. Anyway, it would indicate a serious bug to
encounter that 0, like requiring the hypothetical pure Animal to be real and do something.

56

Pointers chapter 1.

In the Smalltalk language, which popularized object-oriented programming in the late 1970s,
abstract methods were called subclass responsibility methods (a Smalltalk subclass is the same
concept as a C++ derived class), which I think is a good, descriptive name: it is the derived
classes, here Sum and Product, that are responsible for implementing the abstract methods.
By the way, a class with abstract methods, such as Expression and BinaryOperator, is called
an abstract class. Ideally it should not be possible to directly create an object of an abstract
class (because of those 0s in the vtable). And of course C++ helps with that, too.
A class that has no abstract methods, i.e. it implements all abstract methods from its base class, if
any, and doesnt introduce any new ones itself, is correspondingly called a concrete class.
and Product are concrete classes that both implement the BinaryOperator abstract
methods operatorResult, operatorSymbol and callDelete; here I show just the vtablebased version of Sum:
Sum

//------------------------------------- Sum-expression:
struct Sum: BinaryOperator {};
typedef Sum const* SumPtr;
typedef BinaryOperatorVTable SumVTable;
double Sum_operatorResult( BinaryOperatorPtr, double a, double b )
{ return a + b; }
std::string Sum_operatorSymbol( BinaryOperatorPtr )
{ return "+"; }
void Sum_callDelete( BinaryOperatorPtr binaryOperatorSelf )
{
SumPtr const self = static_cast<SumPtr>( binaryOperatorSelf );
delete self;
}
Sum* initSum( Sum* sum, ExpressionPtr left, ExpressionPtr right )
{
static SumVTable const theVTable =
{
{
&BinaryOperator_valueOf, &BinaryOperator_stringFrom, &BinaryOperator_destroy
},
&Sum_operatorResult,
&Sum_operatorSymbol,
&Sum_callDelete
};

initBinaryOperator( sum, left, right );


sum->vTable = &theVTable.basePart;
return sum;

SumPtr sum( ExpressionPtr left, ExpressionPtr right )


{
return initSum( new Sum, left, right );
}

Both Sum and Product must duplicate exactly the values in the parts of the BinaryOperation
vtable that these classes dont provide new function implementations for, the functions that they
dont override. Here thats only the three methods at the top. But you can imagine that for a
larger system it would be very tedious and error-prone to keep doing this duplication by hand:
another maintenance nightmare, and another reason that language support is critical and key to
object-oriented programming (so in the next subsection well start looking at C++ OO features).

Copyright 2005 Alf P. Steinbach

57

Newcomers to C++ often confuse the concepts of overloading and overriding, so, definitions:
Overload a function:
Compile-time effect.
Use the same name for more than one function, e.g. stringFrom, that have different
number of arguments and/or argument types, but conceptually do the same.

Override a function:
Run-time effect.
Provide a class-specific implementation of a function thats called e.g. via a vtable or
similar mechanism; if a class T overrides a function f, then for an object of dynamic type
T it will be Ts override thats executed when f is called on the object.
The three functions that Sum does override are all abstract in BinaryOperation, and for abstract
methods its common to indicate that by saying that Sum implements these methods. I.e., these
methods lacked implementations in the base class (they were abstract there, 0), but now, in this
class, they have implementations, and are no longer abstract; for an object of this class theyre
very real and callable. Some programming languages but unfortunately not C++ have
corresponding keywords, like override and implements, with compiler checking of validity.
For completeness, the updated main program:
//------------------------------------- Main:
int main()
{
// 2x + 5
ExpressionPtr const e = sum( product( constant( 2 ), identity() ), constant( 5 ) );
std::cout << vTable( e ).stringFrom( e ) << ":" << std::endl;
std::cout << std::endl;
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << vTable( e ).valueOf( e, x ) << std::endl;
}
vTable( e ).destroy( e );
}

Yes, the extremely awkward and error-prone method calling syntax is also a reason a very
important reason to move on to C++ support for OO!

58

Pointers chapter 1.

1.2.8

C++ virtual member functions.

Summary:
Most, if not all, C++ implementation use vtables for their OO support, but this is not required by the standard,
which doesnt mention vtables.
A C++ member function is a function that is declared or defined inside a class. If a member function isnt declared
as static it can and must be called on an object, e.g. o.foo(), and receives a pointer to that object as an implicit
argument (the current object pointer) called this. C++ takes care of the downcasting for this.
Theres no vtable involved in a call of a simple member function, but still you cannot call a member function on a
nullpointers object: that yields Undefined Behavior.
Within a member function this-> is assumed and can therefore usually be omitted, and we usually use a member
naming convention such as myValue or value_ instead of qualification with this.
To express that the current object should be const in a member function, use a const member function,
void foo() const { }

A const member function.

which corresponds to a C-style T const* current object pointer, and thereby allows the member function to be
called on a const object (which otherwise ordinary pointer const correctness would prevent).
You can express that the function should be called via the vtable by using the keyword virtual, which gives you a
virtual member function:
virtual void foo() const { }

A virtual member function.

Once a function is virtual in a class, its virtual in all derived classes.


For a class that has at least one virtual member function, a polymorphic class, every object contains (in practice)
something that identifies the dynamic type, and that something is usually/always a vtable pointer.
In the cases where you dont want a virtual call (a call via the vtable), but instead a non-virtual call (a direct call of
some given implementation), you can express that by qualifying the function name with the relevant class name, like
Base::foo(). Some languages provide a special pseudo-name for the base class for this purpose, e.g. Smalltalk
super, and some C++ implementations also do that, as a language extension. For the simplest cases you can
emulate having such a pseudo base class name by using a typedef of the base class name.
You can express that a function should be abstract (conceptually: have 0 in its vtable entry) by tacking = 0 on after
the function head, which is called a pure virtual member function:
virtual void foo() const = 0;

A pure virtual member function (in this case a const one).

A function declared as pure virtual in a class T is pure virtual in T even if an implementation is provided in class T.
Such an implementation (unusual) must then be provided outside the class definition. A class with a pure virtual
member function is abstract, i.e., cannot be instantiated except as a base class sub-object.
You can remove the pure-virtualness simply by implementing the function in a derived class (recall the previous
subsections comment about abstract methods being denoted as subclass responsibility in Smalltalk).

The C++ standard doesnt mention anything about vtables, so in principle a C++
implementation could exist that doesnt employ vtables in its internal support for OO. But all
implementations that I know and have heard of use vtables. So Ill frame the discussion here in
terms of vtables: an assumption that also your C++ implementation, and any other, uses vtables.
Thats to make it concrete. But do keep in mind that things can be implemented in other ways.

Copyright 2005 Alf P. Steinbach

59

One other matter before we start: there are many areas where C++ language features can greatly
simplify and/or improve our C-style program. But in this subsection I focus only on what C++
can do for our vtables and function pointers and function naming and downcasts and call syntax;
in short, for our method support: we want it all automated! Ill keep the C-style for the rest. And
equally important, Ill keep the way the new version works as close to the original as possible.
That means, were examining one little set of strongly related C++ features in isolation, and so
this version wont be very natural C++, nor very safe C++ compared to later versions.
One C-style function that then requires the least new C++ features is initConstant, which at
the end of our C-style coding efforts looked like this:
Constant* initConstant( Constant* constant, double aValue )
{
static ConstantVTable const theVTable =
{
&Constant_valueOf, &Constant_stringFrom, &Constant_destroy
};

initExpression( constant );
constant->vTable = &theVTable;
constant->value = aValue;
return constant;

Since C++ will take care of all the vtable stuff when we start using the relevant features, whats
to be translated to more full C++ is simply
Constant* initConstant( Constant* constant, double aValue )
{
constant->value = aValue;
return constant;
}

A C++ member function is a function that is declared or defined inside a class. If a member
function isnt declared as static it can and must be called on an object, and receives a pointer to
that object as an implicit argument. That implicit argument can then be referred to as this:
struct Constant: Expression
{
double value;

};

Constant* init( double aValue )


{
this->value = aValue;
return this;
}

Code for the other member functions goes here.


The C++ implementation of C-style initConstant.

Since the function is now a C++ member function the class name Constant takes care of our
Constant name suffix in the C-style code. Hence the simpler name init. Do note that even
though init can and must be called on an object, like in the C-style version init is not called
via a function pointer. Theres no vtable involved in a call of init, or in calls of similar simple
member functions. Thats the default for C++ member functions, and it is that way because
C++ is designed to not add any overhead that you dont explicitly ask for.
Whether to say that such a member function, one that isnt called via a function pointer, is a
method, is up to you but since method isnt a C++ technical term but a languageindependent concept term, it can cause confusion when used as a C++ technical term. For
example, in C# all member functions, including static ones, are called methods. Personally I
draw the line at static, because static member functions are just like freestanding functions.

60

Pointers chapter 1.

Even for a member function like init, where, from a practical point of view, the vtable isnt
used and hence a call does not peek into the object to find the vtable pointer,
You cannot call a C++ member function on a nullpointers object
(and you should always assume that this is a RealGood pointer, never a nullpointer).
Or, you can use a nullpointer, actually, but then youll be invoking the Undefined Behavior troll,
which has a tendency to stomp all over the place and do Really Nasty Things1.
The above implementation of init has one little detail that makes it unnatural to a seasoned
C++ programmer, namely qualification of members with this, like this->value. Inside a
(non-static) member function this is assumed when you refer to members. So instead of the
implementation on the left below, preferentially write code like on the right:
Explicit use of this:

Implicit use of this, + member naming convention:

struct Constant: Expression


{
double value;

struct Constant: Expression


{
double myValue;

Code for the other member functions goes here.

};

Constant* init( double aValue )


{
this->value = aValue;
return this;
}

};

Constant* init( double aValue )


{
myValue = aValue;
return this;
}

The my-prefix in myValue is one common data member naming convention, replacing the
use of this-> to indicate that one is dealing with a data member. An alternative common
convention is to append an underscore, _, to every data member name, like value_. Some
newbies add the underscore at the front instead, but thats generally not a good idea since certain
names and name forms starting with underscore are reserved for the C++ implementation.
Given a RealGood pointer e of type Constant*, where in C-style youd write
initConstant( e )

with a C++ member function you write


e->init()

Calling a (non-static) C++ member function.

to call the member function. In C-style code you indicate exactly which init function you want,
via the function name, initConstant. With the C++ member function you indicate exactly
which init function by calling the function on the object (even though theres no vtable lookup),
and the objects static type says that this should be the init member function in class Constant.

This means that unlike the C-style version, in C++ style you cant or really shouldnt call operatorResult or
without actually having an object. The C++ workaround if one wants to support object-less calls
from client code, is to implement a member function that doesnt need an object, as a pair: a virtual member
function that calls a static member function that does the real work, and that can be called without an object. To
be true to the letter of the C-style version I should do that here. But I dont (consider it an exercise ;-)).
1

operatorSymbol

Copyright 2005 Alf P. Steinbach

61

In passing: there isnt any real magic, so in the compiled code the various init member
functions do have different names, automatically generated by the compiler. Those names are
called mangled names, because the name parts that the compiler adds are generally unreadable.
Unfortunately name mangling is not standardized, and so it varies from compiler to compiler.
C++ supplies e as an implicit argument, automatically, and it ends up as this inside init.
And because of that automatic, implicit argument forwarding we can replace the named pointer
variable e with a new-expression that produces a pointer (compare this to the call shown above):
(new Constant)->init()

The C-style factory function


ConstantPtr constant( double aValue )
{
return initConstant( new Constant, aValue );
}

C-style.

can therefore now be written as the more C++-like style factory function
ConstantPtr constant( double aValue )
{
return (new Constant)->init( aValue );
}

More C++-like style


(full C++ style would use a C++ constructor).

This factory function could have been implemented as a static member function, but at the
level were at that would only complicate the client code (more to write), with no benefit.
Now on to the destroy implementation, which in C-style looked like this:
void Constant_destroy( ExpressionPtr expressionSelf )
{
ConstantPtr const self = static_cast<ConstantPtr>( expressionSelf );
delete self;
}

Here there is that Dangerous Construct, a downcast, blithely assuming that the pointer argument
actually points to an object of type Constant, but C++ removes this danger:
C++ takes care of the downcasting for this, automagically.
Theres no way to do it wrong, because you dont do it. And because the C++ type checking
guarantees (at least if you dont do any dangerous casts on your own) that a member function is
never called on an object of the wrong type. I.e., the type checking guarantees that its always a
pointer to the actual current object, of the right type, thats passed as this.
But there is a second issue in translating this function to C++. And to be more clear about that,
here is the C-style version without using the typedefed pointer type name, with the const:
void Constant_destroy( Expression const* expressionSelf )
{
ConstantPtr const self = static_cast<ConstantPtr>( expressionSelf );
delete self;
}

62

Pointers chapter 1.

How does one express that the current object should be accessed as const? The this argument
isnt explicitly there in a member function, is it? Well, you simply place the const that in C-style
is in the explicit formal argument (as above), after the C++ member function head, like this:
struct Constant: Expression
{

};

void destroy() const


{
delete this;
}

A const member function.

Its called a const member function, and as you see it corresponds to a T const* current
object argument in C. Here it changes the type of the this pointer to Constant const*.
Ordinary const correctness for pointers would otherwise prevent you from calling this member
function on a const object: a const-correct this object pointer could not then be provided.
Simple, huh?
Well, apart from the conceptual incongruence of destroying an unchanging object. Thats what
weve done through a number of versions, but now its more clear. The reason its necessary is
that otherwise the client code couldnt invoke destruction on const objects, and what it means is
that the object is unchanging, constant, right up till the moment its deleted, after which it
doesnt exist any more, and then its rather a moot point whether it has changed or not.
And theres a third issue with this function: it should be called via the vtable, i.e. different typespecific implementations should be executed depending on the dynamic type of the object.
Since calls via the vtable can incur a little cost (an extra table lookup and pointer indirection) you
have to indicate explicitly that the function has this requirement, by using the keyword virtual:
struct Constant: Expression
{

virtual void destroy() const


{
delete this;
}

A virtual member function.

};

So, this is at last an example of the long-sought-for C++ virtual member function.
Actually the keyword virtual could have been omitted in this case, because the function is
declared as virtual in the base class Expression. And once a function is virtual in a class, its virtual
in all derived classes, whether you specify it as virtual there or not. But if you do specify every
virtual member function as virtual, then its easier to see which functions are virtual.
The C-style call
vTable( e ).destroy( e );

C-style method call, using a vtable.

can now be expressed simply as


e->destroy();

C++ style virtual member function call.

Copyright 2005 Alf P. Steinbach

63

which, because destroy is virtual, the C++ compiler translates to something much like the Cstyle call: you can think of it as being exactly the same, because the effect is exactly the same.
Because of the vtable lookup the call (via an upcasted pointer)
static_cast<Expression const*>( e )->destroy();

also has the same effect, a virtual call: e.g., when the dynamic type is Constant, it calls the
Constant implementation of destroy the static type you have casted the pointer (or
reference) to doesnt matter when the function is virtual in that static type.
But what if, given a pointer to, say, a Constant object, you really want to call class Expressions
implementation of a virtual function? I.e., a guaranteed non-virtual call, no vtable lookup? In
that case you can qualify the member function name with the class name, like T::foo().
Within a Constant member function its simply
Expression::foo();

A guaranteed non-virtual call.

which, because this is assumed and implicit in member functions, is equivalent to


this->Expression::foo();

Some languages provide a special pseudo-name for the base class for this purpose, e.g. Smalltalk
super. And some C++ implementations also do that, e.g. __super. Unfortunately standard
C++ does not, but in most cases1 you can simply typedef the base class name as e.g. Base. A
typedef can be placed inside a class and referred to by its simple name in members of that class,
and otherwise via the qualified name. But in order to keep the C-style look Ill not do that here.
Having a virtual member function is the way to add in vtable support in a class:
A class that has at least one virtual member function uses a vtable, and every object of
that class has a vtable pointer.
Or more formally correct, since the C++ standard doesnt mention vtables, its a polymorphic
class. As I cautioned at the start of this subsection you should keep in mind that there are other
ways to implement things, but whichever scheme is employed every object of a class with at least
one virtual member function must contain something that identifies the dynamic type, or that
information must be stored somewhere else, per object, and associated with each object. A
vtable is just the most popular way in C++, used by circa 100% of current implementations.
If C++ didnt have support for abstract methods, the 0s we had in the C-style vtables, we could
have provided a dummy implementation of destroy in the Expression class. But C++ does
support also that: you just write = 0 after the function head, and use a semicolon instead of a
function body, i.e., you just declare the function. Thats called a pure virtual member function:

With multiple inheritance the functionality of a language-supported pseudo-name for base class cannot be emulated
by a single typedef. Also, a typedef is unsafe compared to language support, and with template programming a
typedef is sometimes awkward. The __super language extension is provided by Microsofts Visual C++ compiler.
1

64

Pointers chapter 1.

struct Expression
{

virtual void destroy() const = 0;

// Do recursive cleanup.

A pure virtual function.

};

Actually the C++-style complete Expression class is also just six lines! ;-)
A class that has at least one pure virtual member function is abstract: you cannot create
objects of that class (except as base class sub-objects of objects of derived classes).
Oddly enough C++ allows you to implement a pure virtual member function in the same class
its declared as pure virtual in. This implementation must then be provided textually outside the
class definition, and the only way to call it is to use a non-virtual call (a call that doesnt use the
vtable). I havent shown how to implement a member function textually outside the class
definition, but its not really something you need yet. Just note that giving a member function an
implementation in the same class its declared as pure virtual in, doesnt make it less abstract:
conceptually its vtable entry is still 0, because that implementation cant and will never be called
via the vtable. So the function and the class are still abstract.
To override a virtual member function in a derived class, C++ style, you just provide an
implementation there. That implementation will then be the one called when the function is
called on an object of that class, just like in our C-style versions of the program. C++ keeps
track of which functions are implemented in which classes, and updates the vtables accordingly.
And that means that to implement a pure virtual member function, to get rid of the abstractness,
you do the same: just implement the function. Which has already been shown for destroy, in
Constant. In order not to waste vertical space I refer you back to that example. Or better, to
the complete code below. You find the declaration of the abstract destroy function in class
Expression, and implementations of it in classes Constant, Identity and BinaryOperator.
The complete program text for this version is about half the size of the last C-style version, in
number of characters (and yes, all thats needed has been discussed now):
polymorphism/expr_cpp_1.cpp
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
#include
<sstream>
// std::ostringstream
#include
<string>
// std::string
//------------------------------------- class Expression:
struct Expression
{
virtual double valueOf( double x ) const = 0;
virtual std::string stringFrom() const = 0;
virtual void destroy() const = 0;
};

// Do recursive clean-up.

typedef Expression const* ExpressionPtr;


//------------------------------------- class Constant:
struct Constant: Expression
{
double myValue;

Copyright 2005 Alf P. Steinbach


virtual double valueOf( double /*x*/ ) const
{
return myValue;
}
virtual std::string stringFrom() const
{
std::ostringstream stream;

stream << myValue;


return stream.str();

virtual void destroy() const


{
delete this;
}

};

Constant* init( double aValue )


{
myValue = aValue;
return this;
}

typedef Constant const* ConstantPtr;


ConstantPtr constant( double aValue )
{
return (new Constant)->init( aValue );
}
//------------------------------------- class Identity:
struct Identity: Expression
{
virtual double valueOf( double x ) const
{
return x;
}
virtual std::string stringFrom() const
{
return "x";
}

};

virtual void destroy() const


{
delete this;
}

typedef Identity const* IdentityPtr;


IdentityPtr identity()
{
return new Identity;
}
//------------------------------------- class BinaryOperator:
struct BinaryOperator: Expression
{
ExpressionPtr
myPLeft;
ExpressionPtr
myPRight;
virtual double operatorResult( double a, double b ) const = 0;
virtual std::string operatorSymbol() const = 0;
virtual void callDelete() const = 0;
virtual double valueOf( double x ) const
{
return operatorResult( myPLeft->valueOf( x ), myPRight->valueOf( x ) );
}

65

66

Pointers chapter 1.

virtual std::string stringFrom() const


{
return
"(" +
myPLeft->stringFrom() + operatorSymbol() + myPRight->stringFrom() +
")";
}
virtual void destroy() const
{
myPLeft->destroy();
myPRight->destroy();
callDelete();
}

};

void init( ExpressionPtr left, ExpressionPtr right )


{
myPLeft = left;
myPRight = right;
}

typedef BinaryOperator const* BinaryOperatorPtr;

//------------------------------------- class Sum:


struct Sum: BinaryOperator
{
virtual double operatorResult( double a, double b ) const
{
return a + b;
}
virtual std::string operatorSymbol() const
{
return "+";
}
virtual void callDelete() const
{
delete this;
}

};

Sum* init( ExpressionPtr left, ExpressionPtr right )


{
BinaryOperator::init( left, right );
return this;
}

typedef Sum const* SumPtr;


SumPtr sum( ExpressionPtr left, ExpressionPtr right )
{
return (new Sum)->init( left, right );
}
//------------------------------------- class Product:
struct Product: BinaryOperator
{
virtual double operatorResult( double a, double b ) const
{
return a*b;
}
virtual std::string operatorSymbol() const
{
return "*";
}

Copyright 2005 Alf P. Steinbach

67

virtual void callDelete() const


{
delete this;
}
Product* init( ExpressionPtr left, ExpressionPtr right )
{
BinaryOperator::init( left, right );
return this;
}
};
typedef Product const* ProductPtr;
ProductPtr product( ExpressionPtr left, ExpressionPtr right )
{
return (new Product)->init( left, right );
}
//------------------------------------- Main:
int main()
{
// 2x + 5
ExpressionPtr const e = sum( product( constant( 2 ), identity() ), constant( 5 ) );
std::cout << e->stringFrom() << ":" << std::endl;
std::cout << std::endl;
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << e->valueOf( x ) << std::endl;
}
e->destroy();
}

As before, for best results it can be a good idea to read and study this code in textual order, from
top to bottom (because its written in define-before-use order) slowly.
All OK?
Well, then heres something I chose not to mention before now, because the explanation needed
to get to the code above was really long enough bordering on too long for a single subsection.
When we blindly translated the C-style method call
vTable(e).valueOf( e, x )

The valueOf expression e at a particular value of x.

to the simpler C++ member function call syntax, we got something not quite readable:
e->valueOf( x )

Expression es valueOf x. Uh Value of x?

For readability the name of that function should now be changed, e.g. to
e->valueAt( x )

Expression es valueAt a particular value of x.

The less cluttered and simpler C++ notation has changed our view of a function which does
exactly the same now as before to the degree that its name should be changed! This change of view
corresponds directly to the shift from a common valueOf function that knew about all logical
sub-types, to objects that know about their type-specific valueOf implementations. What it
reflects is that we no longer call a function that uses a pure data object (the valueOf expression
e), but rather were now asking an object to compute a value (expression es valueAt).

68

Pointers chapter 1.

Consequently Ill use the name valueAt in the next version, and correspondingly, Ill also use the
object-oriented name toString instead of the command-oriented C-style stringFrom.
1.2.9

C++ destructors and polymorphic delete.

Summary:
A C++ destructor is a special member function that is automatically called when an object or sub-object is about
to cease to exist:
struct FooBar
{
~FooBar() {}
};

Destructor.

A destructor has no arguments, and no result type, and the name of the destructor for a class T is ~T, which is
otherwise not a valid name form for a member function.
The destructor is a good place to put calls to delete for owned objects, which obviates the need for a special virtual
function for this purpose (i.e., you dont need a special, recursive destroy function).
However, without using more of C++ theres still a need for a virtual function to call delete on the object itself,
with the right type of pointer; a polymorphic delete operation.
Happily, when a class destructor is virtual, a virtual destructor, the delete operator is polymorphic for that
class and derived classes. I.e., you can then call delete with an upcasted pointer, and dont need a special
polymorphic delete function. One possible explanation for why it is this way is that the compiler can arrange that
the implementation of a destructor returns the size of the object, and that delete calls the destructor virtually to
find the size, and then calls some memory deallocation function with that size as argument.

When a class type object is definitely about to wink out of existence e.g. via delete, or
because its a local variable and the program execution leaves the scope its declared in C++
calls the objects destructor member function. This gives the object a guaranteed opportunity to
clean up, e.g., in our case, that a BinaryOperator destroys its left and right sub-expressions. If
you havent defined a destructor for the class, then a simple automatically generated one is called.
The destructor is a special member function. Special member functions have special syntax,
and special semantics. Some of them, like the destructor, are called automatically.
Heres a definition of a do-nothing destructor for a class FooBar:
struct FooBar
{
~FooBar() {}
};

Destructor.

As you can see a destructor has no arguments, and no result type. And that doesnt mean void
as result type: that means no result type (its a special member function). And the name1 of the
destructor for a class T is ~T, which is otherwise not a valid name form for a member function.
Heres an example of having a destructor called:

C++ constructors do not formally have names, but destructors do, e.g. as mentioned in 12.4/12 and 3.4.3.1/1.

Copyright 2005 Alf P. Steinbach

69

struct FooBar
{
~FooBar()
{
std::cout << "I'm a FooBar and I'm being destroyed, help!" << std::endl;
}
};
int main()
{
FooBar whatever;
std::cout << "In 'main'..." << std::endl;
}

which has the effect of calling ~FooBar when the program execution leaves the main function.
There is one destructor call for each class type sub-object. So with single inheritance, at most
one immediate base class per class, which is what weve used, you get one destructor call for each
class in the base class chain. You can look at this as if the compiler inserts calls to member and
base-class destructors at the end of every destructor, with member destructors called first, then
the base class destructor (and that base class destructor calling its member destructors and base
class destructor, and so on), as the following program illustrates for base class destructors:
polymorphism/destructor_call_chain.cpp
// For g++ compiler, specify -Wno-unused-variable to avoid warning on the 'e' variable.
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
struct Expression
{
~Expression() { std::cout << "~Expression" << std::endl; }
};
struct BinaryOperator: Expression
{
~BinaryOperator() { std::cout << "~BinaryOperator" << std::endl; }
};
struct Sum: BinaryOperator
{
~Sum() { std::cout << "~Sum" << std::endl; }
};
int main()
{
Sum e;
std::cout << "In 'main'..." << std::endl;
}

In 'main'...
~Sum
~BinaryOperator
~Expression

This means that no matter whether the object that is on its way to oblivion, is a Sum or a
Product object, well get a call of the BinaryOperator destructor, after the Sum or Product
destructor call.
And that further means that we have a function in BinaryOperator (namely, the destructor)
that is guaranteed to be called when an object of a BinaryOperator subclass, is destroyed.
And so we can put the left and right subtree cleanup in the BinaryOperator destructor instead
of in a virtual destroy function. However, since it is our destroy function that so far has
provided derived class specific calls to delete for the Constant and Identity classes, well
need to do those delete calls some other way. And in order to take one step at a time wrt. C++

70

Pointers chapter 1.

features, well just move the declaration of callDelete (which provides that service in Sum and
Product) all the way up to Expression, so that the main function can call callDelete:
polymorphism/expr_cpp_2.cpp
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
#include
<sstream>
// std::ostringstream
#include
<string>
// std::string
//------------------------------------- class Expression:
struct Expression
{
virtual double valueAt( double x ) const = 0;
virtual std::string toString() const = 0;
virtual void callDelete() const = 0;
};
typedef Expression const* ExpressionPtr;

For Constant and Identity this change means nothing more than that destroy, now nonexistent, is replaced with callDelete, e.g. (code not presented here is indicated by ):
//------------------------------------- class Identity:
struct Identity: Expression
{
virtual double valueAt( double x ) const

virtual std::string toString() const

virtual void callDelete() const


{
delete this;
}
};
typedef Identity const* IdentityPtr;
IdentityPtr identity()
{
return new Identity;
}

and Product, which already used callDelete to provide type specific calls to delete, are
completely unaffected, no change. Only BinaryOperator and main need to be updated. For
BinaryOperator the destroy implementation, recursive destruction of left and right sub-trees,
is moved to ~BinaryOperator, which is automagically called by the C++ OO support:
Sum

//------------------------------------- class BinaryOperator:


struct BinaryOperator: Expression
{
ExpressionPtr
myPLeft;
ExpressionPtr
myPRight;
~BinaryOperator()
{
myPLeft->callDelete();
myPRight->callDelete();
}
virtual double operatorResult( double a, double b ) const = 0;
virtual std::string operatorSymbol() const = 0;

Copyright 2005 Alf P. Steinbach

71

virtual double valueAt( double x ) const

virtual std::string toString() const

void init( ExpressionPtr left, ExpressionPtr right )

};
typedef BinaryOperator const* BinaryOperatorPtr;

It is perhaps not obvious, at a glance, that ~BinaryOperator is recursive, just like the old
destroy function was. Especially since ~BinaryOperator isnt virtual. But the point is that
~BinaryOperator is called (recursively) via a virtual function, namely, via our callDelete.
//------------------------------------- Main:
int main()
{
// 2x + 5
ExpressionPtr const e = sum( product( constant( 2 ), identity() ), constant( 5 ) );
std::cout << e->toString() << ":" << std::endl;
std::cout << std::endl;
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << e->valueAt( x ) << std::endl;
}
e->callDelete();
}

Now all that our destroy and callDelete did together is effected via the callDelete
function, which simply provides a polymorphic delete operation, a type-specific call of delete.
Wouldnt it be nice if C++ had some built-in polymorphic delete operation we could use
instead, thus getting rid of yet another virtual function (and its multiple implementations)?
And yes, there is!, but its not a separate operation, like, a special operator:
When a class destructor is virtual, the delete operator is polymorphic for that class
and derived classes.
From a language-lawyer point of view thats all there is to it (except a language lawyer would
probably go into much more detail and employ much more precise & incomprehensible, uh,
language). But as programmers we can speculate about why it is this way. And one possible
explanation is that the compiler can arrange that the C-level implementation of a destructor
returns the size of the object, and that delete calls the destructor virtually via the vtable
to find the size, and then calls some memory deallocation function with that size as argument.
So, we can now remove the callDelete declaration and all its four implementations, and as
replacement just define a virtual destructor for Expression, and we not just can, but absolutely
should, as the g++ compiler warns about when compiling the above program:
warning: `struct BinaryOperator' has virtual functions but non-virtual destructor
warning: `struct Sum' has virtual functions but non-virtual destructor
warning: `struct Product' has virtual functions but non-virtual destructor

72

Pointers chapter 1.

Of course the compiler is here just applying a blind, mechanical rule: it doesnt understand that
we have provided our own polymorphic delete. But neither may some maintenance
programmer understand that, or if that is understood, the programmer may wonder why on earth
that was done, and perhaps land on some invented reason that leads him (or her) astray. Theres
generally no reason to do yourself what C++ can do for you automatically, and much safer, and
so if you find yourself coding a polymorphic delete thyself operation, consider the ++!
Without further ado, then, lets remove 1 + 4 4 = 17 lines of callDelete code, and replace
them with just one line in Expression, defining a virtual destructor there:
polymorphism/expr_cpp_3.cpp
#include
<iostream> // std::cout
#include
<ostream>
// <<, std::endl
#include
<sstream>
// std::ostringstream
#include
<string>
// std::string
//------------------------------------- class Expression:
struct Expression
{
virtual ~Expression() {}
virtual double valueAt( double x ) const = 0;
virtual std::string toString() const = 0;
};
typedef Expression const* ExpressionPtr;

//------------------------------------- class BinaryOperator:


struct BinaryOperator: Expression
{
ExpressionPtr
myPLeft;
ExpressionPtr
myPRight;
virtual ~BinaryOperator()
{
delete myPLeft;
delete myPRight;
}

};
typedef BinaryOperator const* BinaryOperatorPtr;

//------------------------------------- Main:
int main()
{
// 2x + 5
ExpressionPtr const e = sum( product( constant( 2 ), identity() ), constant( 5 ) );
std::cout << e->toString() << ":" << std::endl;
std::cout << std::endl;
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << e->valueAt( x ) << std::endl;
}
delete e;
}

Copyright 2005 Alf P. Steinbach

At last weve started seriously reducing the size of the program text!

73

74

Pointers chapter 1.

1.3 Safety for dynamically allocated objects.


With dynamic allocation comes a whole slew
of safety issues related to lifetime
management, polymorphic behavior and
sharing.

Summary:
The problems of lifetime management, polymorphic
behavior and sharing are strongly interrelated.

Lifetime management essentially boils down to ensuring that for each new there is exactly one
delete, at the proper time. Otherwise one will have memory leaks and/or dangling pointers,
both of which are generally disastrous. What we have done so far has been to offer the client
code simple-to-use creation and destruction operations, much like offering modern climbing gear
to a person struggling to climb a mountain. The gear doesnt help the person in selecting a safe
route, nor does it help him or her to do things in the right order to avoid disaster (oops, forgot to
fasten that rope, oh weeeeeeeell!), but each individual operation can be performed more easily.
In the following subsections I discuss how you can offer that person a helicopter ride to the top.
Polymorphic behavior is mostly a problem with respect to copying. Sharing (having two or more
pointers to the same object) is problematic for objects for the same reasons as sharing is
problematic in real life, namely that without full control you dont have full control, and its no
fun when things have been moved or adjusted or even destroyed by some co-sharer, without
your knowledge or consent. Both problem areas are strongly tied to and involve lifetime
management, so instead of discussing the problems separately I discuss the solutions separately.

Copyright 2005 Alf P. Steinbach

1.3.1

75

Object ownership and the C++ std::auto_ptr.

Summary:
Basically, a smart pointer is an object that wraps a raw pointer (the languages own pointers) and which destructor
is responsible for calling delete on that raw pointer, if necessary. A smart pointer typically also provides the usual
pointer operations such as -> and * and delegate them to the raw pointer inside. That means you can use a typical
smart pointer in much the same way as an ordinary raw pointer, hence the name smart pointer.
The only smart pointer in the C++98 standard library is std::auto_ptr from <memory>.
std::auto_ptr is an ownership transfer smart pointer. If you copy a std::auto_ptr object, e.g. via

assignment, you transfer the ownership of the contained raw pointer, and the raw pointer in the source for the copy
operation is nulled. Ideally that means that the raw pointer will at any time only exist in one place, namely in the
std::auto_ptr object that currently owns it.
If you need to upcast a std::auto_ptr the most general way is to use C++ direct initialization, like
std::auto_ptr<Base> p( aFunctionProducingADerived() );

Use of the C-style = syntax for initialization, copy initialization, is not fully supported for upcasting, and since
initialization of a formal function argument is formally copy initialization, that also applies to function calls. At least
one popular compiler (MSVC version 7.1) incorrectly accepts invalid such code, and then produces a program that
will just hang. When this is a problem one solution is to use e.g. std::auto_ptr<Base> everywhere, and another
solution is to use some other smart pointer, such as boost::shared_ptr.
Also, the C++98 standard does not support passing a std::auto_ptr<T> rvalue (such as a function result) as
actual argument where the formal argument is a std::auto_ptr<T> const&.
In spite of these restrictions std::auto_ptr is very useful because it guarantees delete calls, and it can both
simplify cleanup code and make it more safe, especially if exceptions can occur.

The cleanup strategy weve adopted for an expression is known as object ownership, that a
BinaryOperator object owns its left and right argument expressions, which means its
responsible for destroying them. And weve now seen that a destructor is a good place to put
destruction of owned objects. But its a hassle to code such destructors for each relevant class.
Instead we can put each pointer into an object that has as its only responsibility to (if necessary)
call delete on the pointer, in that objects destructor.
Such a pointer-owner object is commonly known as a smart pointer. And a smart pointer
usually provides more than just a guaranteed call to delete on the contained raw pointer. Most
smart pointers also provide most of the usual pointer operations, such as operator * and ->,
delegating these operations to the raw pointer inside, which means a smart pointer can in most
ways be used just as an ordinary raw pointer except that you dont have to call delete!
And as an added bonus its more exception safe than explicitly calling delete on the raw pointer,
because a destructor is called also in the case when an object is destroyed due to an exception.
The current C++ standard, namely C++98 and its technical corrigendum C++2003, is
unfortunately somewhat deficient in the smart pointer department. The only smart pointer
offered by the standard library is std::auto_ptr, from the <memory> header. Its a template
class, like std::vector is, which means that you write e.g. std::auto_ptr<int> to specify an
instantiation that handles int* pointers, and e.g. std::auto_ptr<FooBar> for FooBar*:

76

Pointers chapter 1.

safety/auto_ptr_demo.cpp
#include
<iostream> // std::cout
#include
<memory>
// std::auto_ptr
#include
<ostream>
// <<, std::endl
struct FooBar
{
~FooBar()
{
std::cout << "I'm a FooBar and I'm being destroyed, help!" << std::endl;
}
};
typedef std::auto_ptr<FooBar const> FooBarAutoPtr;
FooBarAutoPtr newFooBar()
{
std::cout << "Creating a FooBar object." << std::endl;
return FooBarAutoPtr( new FooBar );
}
int main()
{
FooBarAutoPtr p = newFooBar();
std::cout << "In main..." << std::endl;
// The auto_ptr's destructor is called and calls delete on the raw pointer.
}

Creating a FooBar object.


In main...
I'm a FooBar and I'm being destroyed, help!

Note the explicit use of a std::auto_ptr constructor the type name used as if it were a
function that produces an instance of that type
return FooBarAutoPtr( new FooBar );

which is the same as writing


return std::auto_ptr<FooBar const>( new FooBar );

as opposed to the implicit usage where you just specify the constructor argument
return new FooBar;

!Not supported.

which you can use with e.g. std::string, but not with std::auto_ptr. I discuss constructors
in the next subsection. But for now: with std::auto_ptr you must must use them.
std::auto_ptr

is limited that way to ensure that a std::auto_ptr isnt re-seated by accident.

Re-seating of a std::auto_ptr is a big deal, something you shouldnt do lightly, because


std::auto_ptr is an ownership transfer smart pointer. That means that if you copy a
std::auto_ptr object, e.g. via assignment, you transfer the ownership of the contained raw
pointer. This allows one to express and enforce that if you accept this pointer you also take
over the ownership, as with the newFooBar function result above, which is Good: knowing who
is responsible for deallocation, and when, is otherwise difficult and a common source of bugs.
As a safety feature a std::auto_ptr ownership transfer transfers the raw pointer: the raw pointer is
not just copied, but the original is nulled, no longer available, so that ideally that raw pointer
value will only exist in one place, namely in the std::auto_ptr object that currently owns it.
First of all this means that you cannot assign or initialize from a const std::auto_ptr:

Copyright 2005 Alf P. Steinbach

77

safety/auto_ptr_no_const_transfer.cpp
// This program intentionally does not compile (and that's the point).
#include
<memory>
// std::auto_ptr
int main()
{
std::auto_ptr<int> const
std::auto_ptr<int>
std::auto_ptr<int>

p( new int(666) );
a( p );
b;

// !Can't do that, p is constant.

b = p;

// !Can't do that, p is constant.

Thus, a const std::auto_ptr is a pointer whose ownership cant be transferred anywhere (at
least not unless you do things to undermine the built-in safety). If you see a local const
std::auto_ptr, such as p above, and you know that the programmer is a competent one, then
you therefore know that this is a dynamically allocated object of local scope. I.e., that for some
reason the object had to be allocated dynamically, even though its just used locally.
While a const std::auto_ptr cant be the source of an initialization or assignment, a
temporary one can. For example, in the previous program the result of newFooBar() is a
temporary std::auto_ptr, that is used to initialize a std::auto_ptr in main. Ordinarily we
lump temporary objects together with const objects with respect to what can be done, but there
is a loophole (you can call non-const member functions on a temporary object), and
std::auto_ptr makes use of that loophole to allow e.g. std::auto_ptr function results
where the temporary is modified as the ownership is transferred from it.
Unfortunately the particular mechanism that std::auto_ptr employs to do that little bit of
apparent magic, doesnt fully support conversion from std::auto_ptr<Derived> to
std::auto_ptr<Base>, i.e., it doesnt fully support upcasting. Thats because std::auto_ptr
was designed at a time when these issues were not well understood; the mechanism was then a
fairly new trick. For a std::auto_ptr upcast you can use C++-style direct initialization,
safety/auto_ptr_upcast_direct_init.cpp
#include
<memory>
// std::auto_ptr
struct Base {};
struct Derived: Base {};
std::auto_ptr<Derived> foo() { return std::auto_ptr<Derived>( new Derived ); }
int main()
{
std::auto_ptr<Base> p( foo() );
}

// Direct initialization, OK.

but with a standard-conforming compiler you can not use C-style copy initialization,
safety/auto_ptr_upcast_no_copy_init.cpp
// This program intentionally does not compile (and that's the point).
#include
<memory>
// std::auto_ptr
struct Base {};
struct Derived: Base {};
std::auto_ptr<Derived> foo() { return std::auto_ptr<Derived>( new Derived ); }
int main()
{
std::auto_ptr<Base> p = foo();
}

// !Can't do the upcast as copy initialization.

78

Pointers chapter 1.

Note: Microsofts Visual C++ (MSVC) compiler version 7.1 gives incorrect results for both
programs above, accepting both programs and for both, issuing the following warning:
c:\program files\microsoft visual studio .net 2003\vc7\include\memory(455) : warning
C4717: 'std::auto_ptr<Derived>::operator<Base> std::auto_ptr_ref<Base>' : recursive on all
control paths, function will cause runtime stack overflow

That means, you get a program that hangs for a while, and then probably crashes.
That tool issue is of course Bad. But even for compilers that are standard-conforming in this
respect there is an issue thats Even Worse. Namely, that the binding of an actual argument to a
formal argument in a function call is formally defined as copy initialization, not as direct
initialization, so the only direct initialization behavior of std::auto_ptr prevents you from
upcasting in function calls:
safety/auto_ptr_upcast_no_function_call.cpp
// This program intentionally does not compile (and that's the point).
#include
<memory>
// std::auto_ptr
struct Base {};
struct Derived: Base {};
std::auto_ptr<Derived> foo() { return std::auto_ptr<Derived>( new Derived ); }
void bar( std::auto_ptr<Base> p ) {}
int main()
{
std::auto_ptr<Derived>

p = foo();

bar( p );
bar( foo() );

// OK because of same types.


// !Copy initialization + upcast.
// !Copy initialization + upcast + temporary.

Here MSVC 7.1 incorrectly accepts the program, with no warning for the first call of bar, and a
warning about use of a non-standard language extension for the second call of bar.
With a standard-conforming compiler this doesnt compile at all.
How, given a derived class specific std::auto_ptr like p above, can you then call a function
like bar? Is it necessary to introduce extra variables or functions to do the upcasting? Well, no.
You can do that, of course, but alternatively you can specify the required upcasting in the call,
using the std::auto_ptr<Base> constructor. That produces a temporary, which is problematic
in general, but now that temporary will be of the same type as the formal argument, and that works:
safety/auto_ptr_explicit_upcast_function_call.cpp
#include
<memory>
// std::auto_ptr
struct Base {};
struct Derived: Base {};
std::auto_ptr<Derived> foo() { return std::auto_ptr<Derived>( new Derived ); }
void bar( std::auto_ptr<Base> p ) {}
int main()
{
std::auto_ptr<Derived>

p = foo();

bar( std::auto_ptr<Base>( p ) );
bar( std::auto_ptr<Base>( foo() ) );
}

// OK because of same types.


// OK because of same types.
// OK because of same types.

Copyright 2005 Alf P. Steinbach

79

Unfortunately, while this works nicely with a standard-conforming compiler, with MSVC 7.1 the
last call of bar produces the same warning seen before,
c:\program files\microsoft visual studio .net 2003\vc7\include\memory(455) : warning
C4717: 'std::auto_ptr<Derived>::operator<Base> std::auto_ptr_ref<Base>' : recursive on all
control paths, function will cause runtime stack overflow

which means the resulting program is incorrect.


So what can you do if youre using MSVC 7.1, or some other compiler that has a similarly nonconforming standard library implementation? Well, you can do even more of the conversion
explicitly, yourself, by calling std::auto_ptr::release. This call gives you the contained raw
pointer and removes ownership the ownership is transferred to thin air and you then just
package that raw pointer in the proper std::auto_ptr type for the bar call, as before:
safety/auto_ptr_explicit_upcast_function_call_msvc.cpp
#include
<memory>
// std::auto_ptr
struct Base {};
struct Derived: Base {};
std::auto_ptr<Derived> foo() { return std::auto_ptr<Derived>( new Derived ); }
void bar( std::auto_ptr<Base> p ) {}
int main()
{
std::auto_ptr<Derived>

p = foo();

// OK because of same types.

// Explicit release() calls necessary for MSVC 7.1:


bar( std::auto_ptr<Base>( p.release() ) );
// OK because of same types.
bar( std::auto_ptr<Base>( foo().release() ) ); // OK because of same types.
}

But this isnt exactly client-code-programmer-friendly, so when you have a class hierarchy like
our Expression class hierarchy,
Its generally impractical for the client code to use factory functions that produce derived
class specific std::auto_ptrs.
When you have this problem and/or you want to fully support ordinary C-style = syntax for
initialization, copy initialization, you can choose one of five solutions:

Dont use smart pointers, just use raw pointers.

This is dangerous.

Use type-specific std::auto_ptrs as above, and


leave it to the client code to manually do the necessary
conversions by calling std::auto_ptr::release().

This is awkward, verbose and somewhat unsafe,


and it can be confusing.

Use a smart pointer that doesnt transfer ownership,


such as e.g. boost::shared_ptr, which (in another
namespace) will probably be part of the next standard.

While that will work in a technical sense,


boost::shared_ptr being the all-round
usable potato of smart pointers, it doesnt
express or enforce ownership transfer.

Use a smart pointer with more intelligent ownership


transfer than std::auto_ptr.

You may have to roll your own, I dont know


about any.

80

Pointers chapter 1.

Use just one std::auto_ptr pointer type, e.g.,


std::auto_ptr<Expression>, and rely on run-time
polymorphism.

This may restrict the client code somewhat,


and/or may lead to a bloated base class
effectively with knowledge of derived classes.

From the list above well choose the last solution, using std::auto_ptr<Expression const>
everywhere. With that were guaranteed that all objects will be deleted. And we can then
remove the BinaryOperator destructor, because the std::auto_ptr destructor will do that
job automagically:
safety/expr_cpp_4.cpp
#include
<iostream>
#include
<memory>
#include
<ostream>
#include
<sstream>
#include
<string>

//
//
//
//
//

std::cout
std::auto_ptr
<<, std::endl
std::ostringstream
std::string

//------------------------------------- class Expression:


struct Expression
{
virtual ~Expression() {}
virtual double valueAt( double x ) const = 0;
virtual std::string toString() const = 0;
};
typedef std::auto_ptr<Expression const> ExpressionAutoPtr;

//------------------------------------- class Identity:


struct Identity: Expression
{
virtual double valueAt( double x ) const

virtual std::string toString() const

};
ExpressionAutoPtr identity()
{
return ExpressionAutoPtr( new Identity );
}
//------------------------------------- class BinaryOperator:
struct BinaryOperator: Expression
{
ExpressionAutoPtr
myPLeft;
ExpressionAutoPtr
myPRight;
virtual double operatorResult( double a, double b ) const = 0;
virtual std::string operatorSymbol() const = 0;
virtual double valueAt( double x ) const

virtual std::string toString() const

Copyright 2005 Alf P. Steinbach

};

81

void init( ExpressionAutoPtr left, ExpressionAutoPtr right )


{
myPLeft = left;
myPRight = right;
}

//------------------------------------- class Sum:


struct Sum: BinaryOperator
{
virtual double operatorResult( double a, double b ) const

virtual std::string operatorSymbol() const

};

Sum* init( ExpressionAutoPtr left, ExpressionAutoPtr right )


{
BinaryOperator::init( left, right );
return this;
}

ExpressionAutoPtr sum( ExpressionAutoPtr left, ExpressionAutoPtr right )


{
return ExpressionAutoPtr( (new Sum)->init( left, right ) );
}

//------------------------------------- Main:
int main()
{
// 2x + 5
ExpressionAutoPtr const e =
sum( product( constant( 2 ), identity() ), constant( 5 ) );

std::cout << e->toString() << ":" << std::endl;


std::cout << std::endl;
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << e->valueAt( x ) << std::endl;
}

There is one more thing you should know about std::auto_ptr, namely that
Youre currently prevented from passing an rvalue, e.g. a function result, to a
std::auto_ptr<T> const& formal argument.
For std::auto_ptr this is good, because most programmers expect to lose ownership when a
std::auto_ptr is passed as actual argument, and with this restriction there is one less way that a
std::auto_ptr can be passed directly as actual argument without losing ownership (the only
remaining case is a formal argument that is a reference to non-const). However, the restriction
is due to a language feature1 that is somewhat problematic in other contexts, and so this

That passing an argument by reference to const can involve creation of a temporary copy, which for an rvalue
argument of class type requires that the arguments class has a copy constructor with reference to const argument.
Im not sure about the validity of lvalue arguments. However, all compilers Ive tested with, including Comeau
Online, accept an lvalue argument of a type that doesnt have a copy constructor with reference to const argument.
1

82

Pointers chapter 1.

restriction will probably be lifted1 in the next revision of the standard, the fabled C++0x. Until
then MSVC 7.1 is one compiler that, by accident, and incorrectly, already allows this.
1.3.2

Exception safety for new: RAII and C++ constructors.

Summary:
A C++ exception is a special automatic control transfer (like a self-homing goto on steroids) that typically indicates
failure, while absence of an exception then indicates success.
A program can have exception handlers. If an exception occurs, and there is a handler for that exception in the
function call chain, then the program execution exits from blocks and functions until that handler is reached. During
this stack unwinding objects that cease to exist have their destructors called, and in particular std::auto_ptr
objects have their destructors called, which provides a way to ensure automatic cleanup, called RAII (Resource
Acquisition Is Initialization) let destructors do the cleanup, automatically.
Code that is exception safe provides relevant cleanup, e.g. via RAII, when exceptions occur.
That cleanup should not call an objects destructor if the exception occurs during the objects initialization, because
the destructor may and probably will assume a successful initialization; hence std::auto_ptr is not a solution for
an object that is allocated but not yet successfully initialized.
Instead initialization should be expressed as a constructor special member function, and new should be given
responsibility for calling the constructor, and deallocate if an exception occurs:
ExpressionAutoPtr sum( ExpressionAutoPtr left, ExpressionAutoPtr right )
{
return ExpressionAutoPtr( new Sum( left, right ) );
Letting new call the constructor.
}

Here, correctly, the Sum destructor is not called if an exception occurs during the initialization, but destructors are
called on all successfully constructed sub-objects.
A constructor for a class T is named T except that the standard insists that it formally has no name and like a
destructor it has no result type, not even void:
struct FooBar
{
FooBar()
{
std::cout << "I'm a FooBar, constructing myself!" << std::endl;
}
};

Except for (ab)using very low-level language features, the C++ rules effectively give you a constructor call
guarantee where if a class T has at least one user-defined constructor, you cant create a T object without having a T
constructor called, and you cant call a T constructor without having a T object created.
If you try to call a constructor as if it were an ordinary member function you therefore just create a temporary object;
to call a base class constructor you have to use a constructor initializer list:
Sum( ExpressionAutoPtr left, ExpressionAutoPtr right )
A constructor (this is within the Sum class).
: BinaryOperator( left, right )
A constructor initializer list, calling a base class constructor.
{
// Ordinary function body statements go here.
}

A constructor initializer list can also be used to initialize ordinary data members, but it does not determine or
influence the initialization order: that order is the declaration order.
1

This is core issue 391, [http://www2.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#391].

Copyright 2005 Alf P. Steinbach

83

A C++ exception causes an immediate exit from the current block, the enclosing block, the
currently executing function, the calling function, and so on, until a handler1 for that exception is
found. Or, to be less imprecise, thats what happens if a handler will ultimately be found, but for
simplicity, lets assume there is a handler to be found. During this stack unwinding destructors
are invoked for initialized local variables and initialized temporary objects that cease to exist2, so
an exception causes an automatic cleanup for local variables and temporary objects.
If there is no suitable handler then an exception causes a call to std::terminate, and its then
undefined whether stack unwinding occurs first, or not. I.e., thats up to the compiler. You are
however guaranteed that, in the words of the standard, an implementation is not permitted to
finish stack unwinding prematurely based on a determination that the unwind process will
eventually cause a call to terminate(), so its essentially an all-or-nothing thing.
Heres what such a call to std::terminate looks like with a program compiled with MingW
g++ 3.4.4, running under Windows XP:
safety/unhandled_exception.cpp
#include
<vector>
// std::vector
int main()
{
std::vector<int>
int
}

x = v.at( 43 );

v( 5 );
x;

// Five elements, that's a nice vector.

// There is no element 43, so we get an exception...

This application has requested the Runtime to terminate it in an unusual way.


Please contact the application's support team for more information.

Typically an exception indicates failure, as in the program above, while absence of an exception
indicates success. But in special cases exceptions can, unconventionally, be used as long-range
jumps, akin to the C longjmp (which is not fully supported in C++). For example, one such
usage is to break out of a nested recursive call, and then an exception might indicate success.
As it happens our expression code is practically exception safe (provides relevant cleanup if an
exception occurs, e.g. no memory leakage), because apart from the use of std::cout in main
the only statements that could cause exceptions are the calls to new, and if a new call succeeds
then there is, in our code, no further calls to new before that newly created pointer is safely
tucked into a std::auto_ptr, which destructor provides automatic cleanup if an exception
should occur.
This way of providing exception safety, letting destructors do the cleanup instead of inserting
exception handlers everywhere, is somewhat misleadingly known as RAII, Resource Acquisition Is
Initialization. Its the technique of choice3 for seasoned C++ programmers, and is perhaps the
most significant difference between C++ and languages like C# and Java. More properly it might
have been called RRID, Resource Release Is Destruction, but that acronym is hardly ever used.

Check out the try-catch statement in your favorite C++ textbook, and/or check out section 1.4.1 here.
To be more formally correct: only successfully constructed objects have their destructors called. Alternatively one
might take the view that an object does not exist until its been successfully constructed.
3
Important facility: ScopeGuard by Petri Marginean and Andrei Alexandrescu, discussed in CUJ December 2000,
[http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/]. Source code for ScopeGuard available at
[ftp://ftp.cuj.com/pub/2000/cujdec2000.zip]. Note: for MS Visual C++ change __LINE__ to __COUNTER__.
1
2

84

Pointers chapter 1.

To understand how this technique could have come to be named RAII, what initialization is all
about in this context, consider what would happen if a statement that could cause an exception
was inserted somewhere in Sum::init, or in BinaryOperator::init. Then if the call to new
in the sum function succeeded, but Sum::init caused an exception, the stack unwinding would
happen before that raw pointer was wrapped by a std::auto_ptr. And because of that, a
failing initialization after the resource acquisition, there would be no std::auto_ptr to do the
cleaning up, and hence no call to delete on that raw pointer, and memory would be leaked.
We could seemingly prevent this potential problem by storing the newed pointer in a local
std::auto_ptr before calling init. But in case of an exception that would not just reclaim the
memory, but also, first, run the objects destructor. Which might rely on the object having been
properly initialized, which is probably not the case when an exception occurs during initialization.
And its not just that a destructor might rely on an assumption of successful, proper initialization:
in order to avoid validity-checks peppered all over a class code, which makes it a haven for bugs
and hard to maintain and use, its good software engineering practice to assume & ensure that
Common assumption:
No object ever exists where its initialization failed.
So, something is needed that, when an exception occurs during initialization, calls delete, without
calling the objects destructor, and that something is the new operators automatic cleanup.
To enable the new operators automatic cleanup all we have to do is to recode init as a C++
constructor, and instead of calling the constructor on the new result (which is technically
possible but awkward and dangerous, and not something you should know about yet!), supplying
the constructor arguments to new and let new call the constructor for us, like
ExpressionAutoPtr sum( ExpressionAutoPtr left, ExpressionAutoPtr right )
{
return ExpressionAutoPtr( new Sum( left, right ) );
Letting new call the constructor.
}

Here you should not regard Sum(left, right) as a constructor call. The constructor isnt
called at that point. Instead, you should regard it as two separate arguments to new, namely Sum,
the type of object to create1, and (left, right), a list of arguments that new should pass to the
Sum types constructor when new calls that constructor. You can, however, regard the whole
new-expression as a constructor call, an indirect one, because thats its effect. It also, first,
allocates memory for the object from the heap, which until now weve thought was primary.
What? Allocation is not primary? What is, then?
Consider that if type safe allocation was news principal responsibility, then we could just as well
have had a library function, like the C librarys malloc, but with C++-like type safety added,
template< typename T >
T* alloc() { return static_cast<T*>( std::malloc( sizeof( T ) ) ); }

There would then be no need for a language-supported construct. The language-supported new
is necessary because new has as its primary responsibility to ensure a constructor call. new is a
This is perhaps more clear if you consider the (unconventional) expression new Sum const (left, right), where
the type of object to create is Sum const.
1

Copyright 2005 Alf P. Steinbach

85

means of ensuring that the storage is properly initialized, if the class requires initialization, and that
if the initialization fails (an exception occurs) the storage is deallocated: for a class that requires
initialization you never get a pointer to uninitialized storage out of new, and thats its purpose.
And that is the essence of RAII: that resource acquisition, here memory allocation, is so tightly
integrated with initialization that they form an indivisible unit of action, which either fails
completely or succeeds completely. Resource acquisition is initialization. And vice versa.
If an exception occurs in the Sum constructor and is passed upward (there is then presumably a
handler for that exception somewhere, further up the function call chain), then new uses magic to
intercept the exception, deallocates the allocated storage, and then lets the exception propagate
further, to whichever exception handler its got in its sights.
The Sum destructor is then not called.
However, destructors are called for all successfully constructed class type sub-objects, such as e.g.
our std::auto_ptrs for left and right subtrees, as well as base class sub-objects.
A constructor for a class T is named T except that the standard insists that it formally has no
name and like a destructor it has no result type, not even void:
struct FooBar
{
FooBar()
{
std::cout << "I'm a FooBar, constructing myself!" << std::endl;
}
};

You can use ordinary assignments in the constructor body to initialize ordinary data members,
but if you try to call a base class constructor as if it were an ordinary member function, instead of
the special member function that it is, youll not get a call on the current object. Instead of the
intended call on the current object, youll then be creating a temporary object of the base class,
and calling that temporary objects constructor. Thats because C++ effectively (there are some
exceptions, involving low-level language features) gives you a constructor call guarantee where
Constructor call guarantee:
Let T be a class with at least one user-defined constructor, like FooBar above:
If you call a T constructor youre also creating (read: making available, conjuring up from
raw bits) a T object, and vice versa.
For example, if you just declare a FooBar object,
FooBar anObject;

then youre creating a FooBar object, and hence calling its constructor, and if you write
FooBar();

86

Pointers chapter 1.

then youre calling the FooBar constructor1 and hence creating a (temporary) FooBar object,
which we used to good effect in section 1.1.6, but then with a constructor with arguments.
The constructor call guarantee is not an absolute guarantee, and its not stated or referred to
anywhere in the standard, but its what the detailed rules are designed to provide; it holds unless
you use very low-level language features2, and its the basis for understanding C++ constructors:
it is the essence, the core understanding. Keeping in mind that the compiler can and will supply a
so-called copy constructor3 if you dont define one, it means that for any object of class type T
with at least one user-defined constructor, youre guaranteed that there will be one T constructor
call, never more than one, and that that call is the first that ever happens with that object. Each
sub-object of an object, in particular each base class sub-object, as well as the object itself,
receives one constructor call, constructing each object from its already constructed parts.
Lets say FooBar is derived from FooBarBase. You cant then just write FooBarBase() in the
FooBar constructor body, because that would just create a temporary FooBarBase object. Or
well, you can, but it wont accomplish what you want. You need instead to say to C++, create
the current objects base class sub-object, with these constructor arguments. And the way to do
that is to use a constructor initializer list, which you can also use for ordinary data members:
Sum( ExpressionAutoPtr left, ExpressionAutoPtr right )
A constructor (this is within the Sum class).
: BinaryOperator( left, right )
A constructor initializer list, calling a base class constructor.
{
// Ordinary function body statements go here.
}

If theres more than one sub-object to be initialized you separate the initializers with commas,
which with editors that automatically indent the :, can be lined up vertically under the :.
Also worth knowing about initializer lists:
A constructor initializer list does not determine or influence the initialization order
(ordinary data members are constructed in the order theyre declared in the class).
Theres very very much more to say about Constructors And Their Practical Application, but the
above is what you need to know relevant to use of pointers, namely the interaction with new and
exception safety. So, heres the even-more-exception-safe version of the program. Its now even
more reduced in code size, which is the general effect of using C++ features: safer, and smaller.
safety/expr_cpp_5.cpp
#include
<iostream>
#include
<memory>
#include
<ostream>
#include
<sstream>
#include
<string>

//
//
//
//
//

std::cout
std::auto_ptr
<<, std::endl
std::ostringstream
std::string

There is a syntactical-level ambiguity for calling a constructor directly, especially one with no arguments: in most
situations a call with an empty argument list will be parsed as a function declaration. So dont write FooBar obj(); to
declare a default-constructed object obj. Just write FooBar obj;, or alternatively write FooBar obj = FooBar();.
2
In particular, placement new. Other relevant low-level language features involve formally Undefined Behavior.
3
Copy constructors are discussed in section 1.3.8.
1

Copyright 2005 Alf P. Steinbach


//------------------------------------- class Expression:
struct Expression
{
virtual ~Expression() {}
virtual double valueAt( double x ) const = 0;
virtual std::string toString() const = 0;
};
typedef std::auto_ptr<Expression const> ExpressionAutoPtr;
//------------------------------------- class Constant:
struct Constant: Expression
{
double myValue;
Constant( double aValue )
: myValue( aValue )
{}
virtual double valueAt( double /*x*/ ) const

virtual std::string toString() const

};
ExpressionAutoPtr constant( double aValue )
{
return ExpressionAutoPtr( new Constant( aValue ) );
}
//------------------------------------- class Identity:
struct Identity: Expression
{
virtual double valueAt( double x ) const

virtual std::string toString() const

};
ExpressionAutoPtr identity()
{
return ExpressionAutoPtr( new Identity );
}
//------------------------------------- class BinaryOperator:
struct BinaryOperator: Expression
{
ExpressionAutoPtr
myPLeft;
ExpressionAutoPtr
myPRight;
BinaryOperator( ExpressionAutoPtr left, ExpressionAutoPtr right )
: myPLeft( left )
, myPRight( right )
{}
virtual double operatorResult( double a, double b ) const = 0;
virtual std::string operatorSymbol() const = 0;
virtual double valueAt( double x ) const
{
return operatorResult( myPLeft->valueAt( x ), myPRight->valueAt( x ) );
}

87

88

Pointers chapter 1.

virtual std::string toString() const


{
return
"(" +
myPLeft->toString() + operatorSymbol() + myPRight->toString() +
")";
}
};
//------------------------------------- class Sum:
struct Sum: BinaryOperator
{
Sum( ExpressionAutoPtr left, ExpressionAutoPtr right )
: BinaryOperator( left, right )
{}
virtual double operatorResult( double a, double b ) const

virtual std::string operatorSymbol() const

};
ExpressionAutoPtr sum( ExpressionAutoPtr left, ExpressionAutoPtr right )
{
return ExpressionAutoPtr( new Sum( left, right ) );
}
//------------------------------------- class Product:
struct Product: BinaryOperator
{
Product( ExpressionAutoPtr left, ExpressionAutoPtr right )
: BinaryOperator( left, right )
{}
virtual double operatorResult( double a, double b ) const

virtual std::string operatorSymbol() const

};
ExpressionAutoPtr product( ExpressionAutoPtr left, ExpressionAutoPtr right )
{
return ExpressionAutoPtr( new Product( left, right ) );
}
//------------------------------------- Main:
int main()
{
// 2x + 5
ExpressionAutoPtr const e =
sum( product( constant( 2 ), identity() ), constant( 5 ) );

std::cout << e->toString() << ":" << std::endl;


std::cout << std::endl;
for( int x = 0; x <= 4; ++x )
{
std::cout << x << " " << e->valueAt( x ) << std::endl;
}

Copyright 2005 Alf P. Steinbach

1.3.3

89

Ensure dynamic allocation by using C++ access specifiers & friendship.

Summary:
One way to ensure dynamic allocation of all objects of a given class is to restrict access to the class constructors, and
provide factory functions to the client code, generally one factory function per relevant constructor in each class.
Another way is to restrict access to the class destructor (because declaring a static, local or member object requires
an eventual destruction of that object, and therefore destructor access). The latter approach requires you to supply
one common destruction function to the client code, as opposed to an unbounded number of factory functions.
A class can restrict access to things via the public, protected and private keywords, called access specifiers.
public means unlimited access, private means access only for members of this class, and protected is
essentially as private except that also derived classes have access.
The C++ keyword class is a synonym for struct with just one difference: the default access to struct subobjects is public, while the default access to class sub-objects is private. Well, OK, theres one more little
difference, mentioned below. But it really doesnt matter much.
A class can grant destructor access to a destruction function (or any freestanding function or class) by using the
friend mechanism, which when used in a class T effectively makes the befriended function or class part of T,
wrt. access friends have access to private things.
To make the destruction function a single implementation that works with all classes you can use compile time
polymorphism via the C++ template mechanism, a template function:
template< typename T >
void callDelete( T* p )
{
delete p;
}

A separate instantiation of callDelete is created for each type actually used, as if youd coded separate typespecific callDelete functions for each relevant class T by hand.
Heres the extra difference between struct and class: within a template parameter list you can use class, but
not struct, as a synonym for typename.
The relevant friend declarations look like
class FooBar
{
template< typename T > friend void callDelete( T* );
template< typename T > friend class std::auto_ptr;
protected:
virtual ~FooBar()

public:
FooBar()

};

Access doesnt apply to friend declarations, so usually it doesnt matter technically where in the class theyre
placed; the top of the class definition is as good a place as any.
friendship isnt inherited in derived classes, so to package this up as a reusable solution the only practical way is as
a macro, e.g. (named for use as a comment on a protected destructor):
#define REQUIRES_DYNAMIC_ALLOCATION
template< typename T > friend void callDelete( T* );
template< typename T > friend class std::auto_ptr;

\
\

90

Pointers chapter 1.

Classes designed for polymorphic behavior, such as our Expression, Sum, Product etc., often
assume and require dynamic allocation. In particular, they can be self-destroying, invoking
delete here and there, and if those objects werent allocated by new, well, bye bye. Folks who
are new to C++ therefore often ask how they can detect whether an object was dynamically
allocated or not. Alas, theres no general way to do that. But you can ensure that objects of a
given class are only allocated dynamically, by restricting which operations client code can use.
To ensure that the client code programmer only allocates objects of your class dynamically, you
can remove the client codes direct access to your class constructors. For creating an object means
calling a constructor (the constructor call guarantee), and if you cant call a constructor, you cant
create an object. Of course, the client code must then be offered some factory function that does
have constructor access, and uses new, so this approach means doing two things: removing
constructor access, and offering a class-specific factory function, or if the class has several
relevant constructors, a set of class-specific factory functions, one per relevant constructor.
In addition or alternatively, you can remove client codes access to your class destructor. Its the
same thing: if you cant call a destructor, then you cant destroy an object. And then youre not
allowed to create it as a static, local or member1 variable, because that requires an eventual
destruction.
Unfortunately1, while the client code is free to new such an object, on its own (without needing a
factory function, which is great), since delete requires destructor access the client code is then
prevented from ever calling delete on those objects! So seemingly its a recipe for disaster! Or
at least, for a goodly memory leak.
Happily there is a solution, namely our old friend the callDelete function, and now, since we
have found out how to delegate polymorphic delete responsibility to C++ (by using a virtual
destructor), callDelete doesnt need to be a member function: it can now be a freestanding
function. And better, it can be a template function, one single function implementation serving all
our different classes, using compile time polymorphism to call delete with the right kind of
pointer a code-once-use-everywhere solution. This sounds great!
Unfortunately2, while its possible for such a class to confer special access to the destructor to the
callDelete function and also to e.g. std::auto_ptr, so that they can call delete, theres no
general way to confer access to the destructor to smart pointer classes that you dont know about
yet, so this will effectively limit the client code to using callDelete on its own, using
std::auto_ptr, and using smart pointer classes that are so decently designed that a destruction
function can be specified (youd of course use callDelete), such as boost::shared_ptr.
Still, its not half-bad, and if you dont plan on supporting esoteric smart pointers and/or moronpointers (smart pointers that dont let you specify a destruction function), then this is the solution:
its simple, it allows the client code to use the usual new syntax, it allows std::auto_ptr, it
allows boost::shared_ptr, and it does not require a bunch of factory functions per every class.
Lets try this out, not yet the full solution, but an initial sketch using C++ access specifiers:
safety/ensured_dynamic_1.cpp
#include
<iostream>
// std:.cout
#include
<ostream>
// <<, std::endl

At least one compiler, Visual C++ 7.1, incorrectly allows creation as a direct class data member when the
containing class does not define its own destructor; however, the compiler issues a warning, and the containing class
then doesnt have a destructor so it cannot be used as a local or static variable, or deleted.
1

Copyright 2005 Alf P. Steinbach

91

struct FooBar
{
private:
~FooBar()
{
std::cout << "I'm a FooBar, and they're at me again!
}

Destroyers!" << std::endl;

public:
FooBar()
{
std::cout << "I'm a FooBar constructing myself." << std::endl;
}
static void callDelete( FooBar const* p )
{
delete p;
// We're in the FooBar class, and can access ~FooBar.
}
};
int main()
{
//FooBar o;
// !Not permitted, because main can't access ~FooBar.
FooBar const* p = new FooBar;

std::cout << "In main()..." << std::endl;


FooBar::callDelete( p );

I'm a FooBar constructing myself.


In main()...
I'm a FooBar, and they're at me again!

Destroyers!

access means just what youd think: anyone can access things that are public. private
access means restricted to this class code. When something is declared private in a class T, not
even classes derived from T can access1 the private thing: only T can access it.
public

Incidentally, this is the first example of a static member function, and as you can see its just
like a freestanding function (depending on how one defines freestanding function it is a
freestanding function), just that its defined in the class.
To define callDelete once and for all for all classes, it must be moved out of the class, and
made into a template function. Lets do the moving-it-out-of-the-class first. In order for
callDelete to then still logically belong to the class, so that it still has access to the private
~Foobar, callDelete must then be declared as a friend of the class in C++ your friends
are Really Good friends, and have free and unlimited access to your private parts (on the
other hand, you dont have the same access to your friends: its a one-way relationship).
While were at it, lets also replace the C-style struct keyword with C++-only class. With one
little exception, discussed below, the only technical difference between struct and class is the
default access: with struct the default access is public, and with class the default access is
private (I dont know why). However, to the human source code reader struct indicates a
POD, Plain Old Data, C-compatible, whereas class indicates a more proper C++ class.

Access: directly use. A derived class can still override a virtual member function that is private in the base class,
even though it cant call the base class implementation (if any). The concept of virtuality is orthogonal to the
concept of accessibility. An ordinary pure virtual member function, one that doesnt have an implementation in the
class its declared pure virtual in, is therefore usually best made public or private; making it protected would
imply an implementation that a derived class could call, which would be meaningless. And in addition to affecting
virtuality of functions private base class members can affect name lookup in sometimes surprising ways, so
private isnt a complete hiding, as one might ideally wish it were: its just a restriction on access, what you can do.
1

92

Pointers chapter 1.

On the left below is shown some struct definitions, and on the right equivalent class
definitions:
struct Base
{
int x;
};

class Base
{
public:
int x;
};

struct Derived: Base


{
int y;
};

class Derived: public Base


{
public:
int y;
};

And conversely,
class Base
{
int x;
};

struct Base
{
private:
int x;
};

class Derived: Base


{
int y;
};

struct Derived: private Base


{
private:
int y;
};

So, heres a version with class and with callDelete as a friendly freestanding function:
safety/ensured_dynamic_2.cpp
#include
<iostream>
// std:.cout
#include
<ostream>
// <<, std::endl
class FooBar
{
friend void callDelete( FooBar const* );
private:
~FooBar()

public:
FooBar()

};
void callDelete( FooBar const* p )
{
delete p;
// We're a friend of the FooBar class, and can access ~FooBar.
}
int main()
{
//FooBar o;
// !Not permitted, because main can't access ~FooBar.
FooBar const* p = new FooBar;
std::cout << "In main()..." << std::endl;
callDelete( p );
}

A templated version of callDelete, one that works for any type whatsoever, relying on
compile-time polymorphism, can look like this:

Copyright 2005 Alf P. Steinbach

93

template< typename T >


void callDelete( T* p )
{
delete p;
}

The compiler will infer the type T from the actual argument, so the calling code need not be
aware that this is actually a template function. A separate instantiation of callDelete is
created for each type actually used, as if youd coded separate type-specific callDelete
functions by hand. Thus, the C++ template mechanism is effectively an automatic codegenerator, only much more advanced and flexible than most other code-generators in existence.
safety/ensured_dynamic_3.cpp
#include
<iostream>
// std:.cout
#include
<ostream>
// <<, std::endl
template< typename T >
void callDelete( T* p )
{
delete p;
}
class FooBar
{
template< typename T > friend void callDelete( T* );
private:
~FooBar()

public:
FooBar()

};
int main()
{
//FooBar o;
// !Not permitted, because main can't access ~FooBar.
FooBar const* p = new FooBar;

std::cout << "In main()..." << std::endl;


callDelete( p );

The template function need not be defined before the class, but I placed it there because thats
where it would typically end up if it were #included from some header file.
In passing, the extra little difference between struct and class mentioned earlier: in the code
above you can write class, but not struct, instead of typename (I believe most of this is
historical accident, that it just turned out this way). In a template parameter list typename and
class mean exactly the same, technically. But to a human code reader typename indicates a
type that can be any type whatsover, whereas class indicates a type that is a class type.
The three versions above dont support std::auto_ptr. std::auto_ptr must be declared a
friend of FooBar if std::auto_ptr is to be used by the client code. This is not an issue with
boost::shared_ptr, because with boost::shared_ptr you can specify the destruction
function: boost::shared_ptr then calls the specified destruction function instead of delete.
And in addition to supporting std::auto_ptr we should support having derived classes. Then
the destructor cant be private, because with private it isnt available to a derived class
destructor (which one can view as calling it). And the destructor shouldnt then be non-virtual.

94

Pointers chapter 1.

Instead of private we can use protected, which is essentially the same as private except that
derived classes have access:
safety/ensured_dynamic_4.cpp
#include
<iostream>
// std:.cout
#include
<memory>
// std::auto_ptr
#include
<ostream>
// <<, std::endl
template< typename T >

class FooBar
{
template< typename T > friend void callDelete( T* );
template< typename T > friend class std::auto_ptr;
protected:
virtual ~FooBar()

public:
FooBar()

};
typedef std::auto_ptr<FooBar const> FooBarAutoPtr;
int main()
{
//FooBar o;
// !Still not permitted, because main can't access ~FooBar.
FooBarAutoPtr p = FooBarAutoPtr( new FooBar );
// But this is now OK.
std::cout << "In main()..." << std::endl;
}

might seem as if its exactly the same as public with respect to derived classes. But
when a friend or member function of a class accesses a non-static protected member of a base
class, that access must, in the standards words, be through a pointer to, reference to, or object
of the derived class itself (or any class derived from that class). And one of the reasons for that
is that otherwise you could access protected members in any object just by deriving a new class
from the one declaring the protected members a crackers dream, but not much protection!
protected

For example, if class Base declares a non-static protected member x, and in class Derived you
have a member function foo(Base const& o), then you dont have access to o.x.
Okay, enough about protected (it works); now all thats left to do is to wrap this solution in a
reusable mechanism, something, that can be easily applied to any class. And the natural thing
would be if the mechanism could be inherited. Unfortunately3,
C++ friendship isnt and cant be inherited.
That means that the only practical way to package the two friend declarations is as a macro:
safety/alloc_util.h
#ifndef ALLOC_UTIL_H
#define ALLOC_UTIL_H

Copyright 2005 Alf P. Steinbach

95

//----------------------------------------- Dependencies:
// None.
//----------------------------------------- Interface:
template< typename T >
void callDelete( T* p )
{
delete p;
}
#define REQUIRES_DYNAMIC_ALLOCATION
template< typename T > friend void callDelete( T* );
template< typename T > friend class std::auto_ptr;
#endif

\
\

// ALLOC_UTIL_H

where the complete source code of the final version of the main program looks like this:
safety/ensured_dynamic_5.cpp
#include
"alloc_util.h" // REQUIRES_DYNAMIC_ALLOCATION
#include
#include
#include

<iostream>
<memory>
<ostream>

// std:.cout
// std::auto_ptr
// <<, std::endl

class FooBar
{
protected:
REQUIRES_DYNAMIC_ALLOCATION
virtual ~FooBar()
{
std::cout << "I'm a FooBar, and they're at me again!
}

Destroyers!" << std::endl;

public:
FooBar()
{
std::cout << "I'm a FooBar constructing myself." << std::endl;
}
};
typedef std::auto_ptr<FooBar const> FooBarAutoPtr;
int main()
{
//FooBar o;
// !Not permitted, because main can't access ~FooBar.
FooBarAutoPtr p = FooBarAutoPtr( new FooBar );
// But this is OK.
}

std::cout << "In main()..." << std::endl;

It doesnt really matter where the friend declarations occur in the class, or within which access
level (access dont apply to them), so I now used REQUIRES_DYNAMIC_ALLOCATION as a kind of
comment for the destructor implementation, explaining why its protected. It must be repeated
in every derived class, since friendship isnt inherited. But, its only one line per class.
The limitations on this technique:

This technique does not support the most primitive kinds of smart pointers (namely smart pointers
that dont allow you to specify a destruction function), except if you explicitly add
friendship for them; but youre not likely to run into many so primitive smart pointers.

96

Pointers chapter 1.

This technique does not by itself help to ensure that all raw pointers are wrapped in smart pointers.
And that is often a concern: use of a particular kind of smart pointer can be a necessary
assumption in a class, and some support for ensuring that the right kind of smart pointer
is always used, or at least that raw pointers wont be used inadvertently, would be nice.
So, well implement some support for this in section 1.3.5; its an independent issue, with
an independent solution set, although restricting access to constructors and providing
factory functions is a (fragile and redundancy-ridden) solution to both dynamicallocation-only and ensure-smart pointer, conceptually simple, and so is very common.

To be 101% fool-proof this technique requires a standard-conforming compiler. That is of course also
the case with any programming technique whatsoever, but at least one widely used
modern compiler, namely Microsofts Visual C++ 7.1, incorrectly allows use of a class
with an inaccessible destructor, as the type of an ordinary data member in another class
U, provided that class U does not have a user-defined destructor. The class U, lacking a
destructor, will however have restricted usage, and youll get warnings when you compile.

Since the third point isnt an in-practice problem, and since well address the second point in
section 1.3.5, the only real, practical issue with this solution is the first point, very primitive smart
pointers. However, presumably the set of different smart pointers used in any given project is
very small. So if, against expectation, that issue should pop up, just add friendship for them.
1.3.4

An aside on wrapping boost::shared_ptr (a.k.a. std::tr1::shared_ptr).

Summary:
When using boost::shared_ptr directly with a protected or private destructor class you must either let
that class grant friendship to boost::shared_ptr, or write things like
typedef boost::shared_ptr<FooBar const> FooBarPtr;

FooBarPtr p = FooBarPtr( new FooBar, &callDelete<FooBar> ); Almost too much to write, and its redundant.

just to allocate a new FooBar object and store the pointer in p, because boost::shared_ptr requires you to
specify a destruction function every time, if you dont want delete.
That can be solved in general, once and for all, by deriving your own wrapper class, e.g. SharedPtr_, from
boost::shared_ptr.
C++ constructors are not inherited (that means: you cannot use some general incantation to make them available for
creating objects of a derived class), so the wrapper class must itself provide all of boost::shared_ptrs
constructors, and forward the constructor arguments to boost::shared_ptrs constructors.

There is a usability issue with boost::shared_ptr, when used with our ensured-dynamicallocation-via-protected-destructor technique. For with boost::shared_ptr (requires the
Boost library1) it can be a hassle to always have to write things like
typedef boost::shared_ptr<FooBar const> FooBarPtr;

FooBarPtr p = FooBarPtr( new FooBar, &callDelete<FooBar> ); Almost too much to write, and its redundant.

[http://www.boost.org].

Copyright 2005 Alf P. Steinbach

97

just to allocate a new FooBar object and store the pointer in p. The verbosity is required
because, unfortunately1, boost::shared_ptr doesnt let you specify a default destruction
function as part of the type, via template arguments: you have to specify the destruction function
as a constructor argument every time you create a pointer, redundantly, and theres no default.
Happily, there is a solution: just derive your own smart pointer class from boost::shared_ptr,
e.g. one called SharedPtr_, that provides e.g. callDelete as a default destruction function.
Unfortunately2, C++ does not support any kind of inheritance or automatic duplication of base
class constructors in a derived class, so SharedPtr_ will have to provide its own versions of all
the constructors that boost::shared_ptr provides, not just the one where a default destruction
function is to be supplied, and for each constructor SharedPtr_ must manually forward the
constructor arguments to boost::shared_ptrs constructors:
safety/shared_ptr.h
#ifndef SHARED_PTR_H
#define SHARED_PTR_H
//----------------------------------------- Dependencies:
#ifndef
CPP_UTIL_H
#
include "alloc_util.h"
#endif

// callDelete, REQUIRES_DYNAMIC_ALLOCATION

#include
#include

// boost::shared_ptr
// std::auto_ptr

<boost/shared_ptr.hpp>
<memory>

//----------------------------------------- Interface:
template< typename T, void (*defaultDestructionFunction)(T*) = &::callDelete<T> >
class SharedPtr_: public boost::shared_ptr<T>
{
private:
typedef boost::shared_ptr<T> Base;
public:
// Ordinary construction:
SharedPtr_(): Base() {}
SharedPtr_( Base const& r ) throw(): Base( r ) {}
SharedPtr_( T* p ): Base( p, defaultDestructionFunction ) {}
template< typename Destroyer > SharedPtr_( T* p, Destroyer d ): Base( p, d ) {}

};

// Special converting construction:


template< class Y > SharedPtr_( boost::shared_ptr<Y> const& r ) throw(): Base( r ) {}
template< class Y > explicit SharedPtr_( boost::weak_ptr<Y> const& r ): Base( r ) {}
template< class Y > explicit SharedPtr_( std::auto_ptr<Y>& r ): Base( r ) {}

#endif // SHARED_PTR_H

Im not going to explain this code! You dont need to really understand it in order to use it. And
I myself, even though I wrote this code, just followed the design of boost::shared_ptr, and
would have to study that design to really understand why exactly these constructors are provided,
in the exact form they are. But for the record, in spite of appearances SharedPtr_ doesnt break
boost::shared_ptrs design goal of one single standard smart pointer type that fosters
interoperability. Thats because it converts implicitly to and from pure boost::shared_ptr.
Now, instead of the long incantation specifying a destruction function, you can write just
typedef SharedPtr_<FooBar const> FooBarPtr;

FooBarPtr p = new FooBar;

Goodie.

98

Pointers chapter 1.

To really support boost::shared_ptr we had to implement seven although trivial, just


argument-forwarding constructors. In contrast, if dynamic allocation was instead ensured by
restricting access to constructors then we, or rather, the client code, would have to implement an
unbounded number of forwarder functions, namely a factory function for each relevant
constructor in the client code classes. And thats why I like destructor-oriented better: the work
is done once and for all, not repeated every time you create a new class or constructor.
1.3.5

Ensure that smart pointers are used, by overloading operator new.

Summary:
Neither the new nor the delete keywords are entities you can refer to, e.g. give friend status, but you can restrict
their usage by restricting access to things they in turn use.
A new call uses three things: an allocation function, to allocate raw memory; the relevant constructor; and a
deallocation function to deallocate in case an exception occurs during object construction.
Restricting access to the allocation function and/or constructor works with most (all?) compilers.
Disregarding special forms, the allocation function is named operator new, and the deallocation function is named
operator delete; you can provide your own versions for a class by defining them as static member functions.
The global allocation function, ::operator new, is used by default. For the argument and function result types,
the void* pointer type is an almost type-less pointer type thats used in low level code where the actual pointer type
isnt known.
You can provide allocation function arguments in a parenthesis after the new keyword, and thats called a
placement new call.
If you define a placement operator new (one with extra arguments) then you should also define a corresponding
placement operator delete, because thats the one called by new when an exception propagates out of the object
constructor; the ordinary operator delete must/should then also be defined because you cant specify
deallocation function arguments to delete.
With this machinery you can restrict usage of new to a very awkward form that will not be used accidentally, and one
practical way to wrap that usage in something easier is to use a macro like
#define NEW( T, args ) T::SmartPtrType( new ((EnsuredSmartPtr())) T args )

which is used like


std::auto_ptr<Foo>

p = NEW( Foo,() );

here effectively restricting the result of dynamic allocation of Foo to std::auto_ptr<Foo>, where the smart
pointer type is passed to the macro via a typedef in class Foo, and the allocation and deallocation functions are
provided by class EnsuredSmartPtr that Foo inherits.

As far as I know there is no way, short of restricting constructor access (which means having to
code redundant factory functions), to enforce that all client code T* pointers should be wrapped
by smart pointers1. And for most smart pointers even restricting constructor access for T doesnt
do the trick, because given a smart pointer p the client code can just do &*p, or some such, and
Its said that a secret known by two persons isnt a secret anymore. And for a pointer the protection offered by
smart pointer wrappers isnt very effective anymore when the raw pointer value is used somewhere. For like a secret,
as soon as the cat is out of the bag (or the raw pointer value is out of a smart pointer) it tends to slip away, and make
unpredictable appearances all over the place. Therefore its important to make it less than automatic to obtain the
raw pointer value. At the very least, the client code should have to explicitly do some special, not ordinary thing.
1

Copyright 2005 Alf P. Steinbach

99

voil, tada!, theres a raw pointer, in the raw, so to speak. So the enforcement problem isnt really
a problem of enforcement: its a matter of avoiding inadvertent use of raw pointers, such as the
client code programmer simply writing new T, possibly with some constructor arguments.
How can we restrict the client codes access to the new keyword, so that it can only be used in the
context of a smart pointer constructor call?
Well, we cant. For one, C++ does not treat the new keyword as an entity you can refer to, so it
cant be given friend status. Thats the same as with delete: it just isnt an entity on its own,
and thats why we couldnt just confer destructor access to the delete operator, but had to list
the delete invokers, the smart pointers and the callDelete function, as friends.
However, we have seen that delete can be restricted by restricting what it in turn has access to
(namely, no destructor access means delete cannot be used), and the situation with new is
similar. We can effectively restrict new by restricting access to the things that new uses. And new
uses three things: an allocation function, to allocate raw memory; the relevant constructor; and
a deallocation function to deallocate in case an exception occurs during object construction.
You can completely remove the client codes access to new, for a given class, by removing access
to that class allocation function or constructors, and if the relevant constructor is defined by the
user and not one generated by the compiler, removing access to the class deallocation function
also removes the client codes access to new. Here Im just reporting the effect with two popular
compilers, which differ slightly in what they allow. Im not sure exactly what the standard
mandates, but allocation function and constructor access works in practice no matter what.
Perhaps devilishly designed to confuse C++ newbies, the allocation functions are named
operator new, and the deallocation functions are correspondingly named operator delete
(there are special forms for arrays, which I dont discuss here). So let me be absolutely clear:
defining operator new does not define the client codes new keyword. Its just an allocation
function thats used by new, and so indirectly affects new, e.g. what new has access to.
The default allocation function is the global allocation function, which is ::operator new,
and ditto for deallocation, which you can use in your own allocation and deallocation functions:
safety/custom_allocation_demo.cpp
#include
<cstddef>
// std::size_t
#include
<iostream>
// std::cout
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
void say( std::string const& s ) { std::cout << s << std::endl; }
class Foo
{
public:
Foo()
~Foo()

{ say( "Constructed." ); }
{ say( "Destroyed." ); }

static void* operator new( std::size_t size )


{
say( "Allocated." ); return ::operator new( size );
}

};

static void operator delete( void* p )


{
say( "Deallocated." ); ::operator delete( p );
}

100

Pointers chapter 1.

int main()
{
delete new Foo;
}

Allocated.
Constructed.
Destroyed.
Deallocated.

The void* pointer type is an almost type-less pointer type thats used in low level code where the
actual pointer type isnt known; its yet another set of valid pointers that cant be dereferenced.
So, we can completely remove access to new, if we want, by the simple expedient of removing
access to the class operator new. But that isnt good at all. Instead, we can just remove access
to the normal way of using new, and that will effectively prevent any inadvertent use of new.
What? Are there more than one way of using new? Yes, there are. So far weve only specified
constructor arguments in our new calls. But you can also provide allocation function arguments
in a new call. Those arguments are then written in a parenthesis right after the new keyword. A
call to new specifying allocation function arguments is called a placement new call (for reasons
that I wont discuss, because if you knew it could and probably would give you Bad, Evil Ideas).
safety/placement_new_demo.cpp
#include
<cstddef>
//
#include
<iostream>
//
#include
<ostream>
//
#include
<string>
//

std::size_t
std::cout
<<, std::endl
std::string

void say( std::string const& s ) { std::cout << s << std::endl; }


class Cheese {};
class Foo
{
public:
Foo()
~Foo()

// The client code has to say "cheese".

{ say( "Constructed." ); }
{ say( "Destroyed." ); }

static void* operator new( std::size_t size, Cheese const& )


{
say( "Allocated." ); return ::operator new( size );
}
static void operator delete( void* p, Cheese const& )
{
say( "Deallocated." ); ::operator delete( p );
}

};

static void operator delete( void* p )


{
operator delete( p, Cheese() );
}

int main()
{
//Foo*
//Foo*
Foo* p
delete
}

p = new Foo;
p = new (Cheese()) Foo;
= new ((Cheese())) Foo;
p;

// !No matching operator new.


// !Syntactically invalid, but may compile.

Copyright 2005 Alf P. Steinbach

101

Allocated.
Constructed.
Destroyed.
Deallocated.

Which client code programmer would accidentally write new ((Cheese())) Foo? No one, thats
who. So all we have to do now is to provide something that calls new in this or a similar form,
and produces a smart pointer instead of the raw pointer, and well have achieved our goal.
But first, some discussion. Although C++ allows you to specify allocation function arguments in
a call to new, the placement new form, you cannot specify deallocation function arguments in a
call to delete. An ordinary delete call does two things, namely, first, object destruction, and
then, deallocation, but will not use the placement operator delete. And if you instead call the
class placement operator delete directly then that will just be a function call, not a delete
invocation, and so you wont get the destruction part. Bummer.
Therefore, the class also has to provide an ordinary, non-placement operator delete. But
given that, why have the placement operator delete there at all? And the answer is that it must
or at least should be there when there is a placement operator new, because thats the
deallocation function that placement new calls if an exception occurs during object construction.
Providing a convenience wrapper for the placement new call is a bit problematic, because if the
constructor argument types have to be specified we could just as well have gone the route of
writing one factory function per constructor. We want the argument types to be inferred by the
usual matching of actual arguments to formal arguments, and the C++ core language does not
yet support such clean argument forwarding (and possibly will never support it). Given the
lack of language support there are, as far as I know, only two practical solutions: (1) assuming
that every constructor argument can be represented as e.g. T const&, or (2) using a macro.
All right, macros are filthy, smelly, evil beasts, but we have already dirtied our hands and visited
macro-land, in section 1.3.1, so whats one more macro, or two, I ask you?
safety/enforced_auto_ptr_demo.cpp
#include
<cstddef>
// std::size_t
#include
<iostream>
// std::cout
#include
<memory>
// std::auto_ptr
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
class EnsuredSmartPtr {};
#define NEW( T, args ) T::SmartPtrType( new ((EnsuredSmartPtr())) T args )
void say( std::string const& s ) { std::cout << s << std::endl; }
class Foo
{
public:
typedef std::auto_ptr<Foo> SmartPtrType;
Foo()
~Foo()

{ say( "Constructed." ); }
{ say( "Destroyed." ); }

static void* operator new( std::size_t size, EnsuredSmartPtr const& )


{
say( "Allocated." ); return ::operator new( size );
}
static void operator delete( void* p, EnsuredSmartPtr const& )
{
say( "Deallocated." ); ::operator delete( p );
}

102

Pointers chapter 1.

};

static void operator delete( void* p )


{
operator delete( p, EnsuredSmartPtr() );
}

int main()
{
std::auto_ptr<Foo>
}

p = NEW( Foo,() );

Allocated.
Constructed.
Destroyed.
Deallocated.

Wrapping this up as a reusable solution the allocation and deallocation functions should be
placed inside EnsuredSmartPtr, so that a class can just inherit from EnsuredSmartPtr instead
of defining these functions in every class with smart pointer requirement. Whether to place the
typedef there too is a judgement call. If it is placed there then EnsuredSmartPtr must be a
template class to support different smart pointers, which may or may not matter, and if its placed
there then in an inheritance hierarchy the uppermost base class will be special, but if its not
placed there then every class that uses this mechanism must have or inherit that typedef.
safety/alloc_util.h
#ifndef ALLOC_UTIL_H
#define ALLOC_UTIL_H
//----------------------------------------- Dependencies:
#include

<cstddef>

// std::size_t

//----------------------------------------- Interface:
//---------------------- Ensure dynamic allocation is used:
template< typename T >
void callDelete( T* p )
{
delete p;
}
#define REQUIRES_DYNAMIC_ALLOCATION
\
template< typename T > friend void callDelete( T* );
\
template< typename T > friend class std::auto_ptr;
// Usage: place this as "comment" on a protected or privat destructor.
//---------------------- Ensure smart pointers are used:
class EnsuredSmartPtr
{
public:
static void* operator new( std::size_t size, EnsuredSmartPtr const& )
{
return ::operator new( size );
}
static void operator delete( void* p, EnsuredSmartPtr const& )
{
::operator delete( p );
}

Copyright 2005 Alf P. Steinbach

103

static void operator delete( void* p )


{
operator delete( p, EnsuredSmartPtr() );
}
};
#define NEW( T, args ) T::SmartPtrType( new ((EnsuredSmartPtr())) T args )
// Usage like: NEW( Foo,( 1, 2, 3 ) )
#endif

// ALLOC_UTIL_H

And the earlier program rewritten to use the packaged solution (of course the allocation and
deallocation functions now wont report that theyve been called):
safety/ensured_smart_ptr_demo.cpp
#include
"alloc_util.h" // EnsuredSmartPtr, NEW
#include
<iostream>
// std::cout
#include
<memory>
// std::auto_ptr
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
void say( std::string const& s ) { std::cout << s << std::endl; }
class Foo: public EnsuredSmartPtr
{
public:
typedef std::auto_ptr<Foo> SmartPtrType;
Foo()
~Foo()

{ say( "Constructed." ); }
{ say( "Destroyed." ); }

};
int main()
{
std::auto_ptr<Foo>
}

p = NEW( Foo,() );

Constructed.
Destroyed.

An interesting thing here is that the client code can declare a local variable (say) of type Foo, but
it cannot easily, inadvertently, obtain a raw pointer to a dynamically allocated Foo. That isnt very
meaningful, and usually youd disallow local variables by using the protected destructor technique
from 1.3.1. But since the techniques are independent you can have one without the other.

104

Pointers chapter 1.

1.3.6

Combat slicing by removing access to copy assignment.

Summary:
Slicing is when you copy a Derived object to a Base object,
Derived derivedObject;
Base
baseObject;
baseObject = derivedObject;

Slicing only the Base part is copied.

where only the Base part, a slice, is copied.


Slicing can scramble data when you assign via a reference (e.g. a dereferenced pointer), where the dynamic type is
different from the static type.
To prevent that you can declare a copy assignment operator as a private member of the class in question. You
dont need to actually implement the operator, if you dont use it yourself. An alternative is to implement
polymorphic assignment, but that implies possible run-time errors, which is generally undesirable.
The copy assignment operator is the assignment operator that copies objects of the class; for a class T its usually
T& operator=( T const& ), although the result type can be anything.
Unless a class declares its own copy assignment operator the compiler will generate one, and that will hide the base
class assignment operators. To unhide the hidden base class operator(s) you can use a using declaration, like,
using Base::operator=;. Alternatively you can qualify calls to the base class operators, or you can call them via
a base class reference or pointer.

Slicing is when you copy a Derived object to a Base object,


Derived derivedObject;
Base
baseObject;
baseObject = derivedObject;

Slicing only the Base part is copied.

where only the Base part, a slice, is copied. And the Base object you copy the Derived object
to doesnt change type to Derived. Its still a Base object after the copying. This may or may
not be a problem, depending on the classes and the context. But generally slicing is a problem.
The most serious problem with slicing is that it can scramble data when you assign via a reference
(e.g. a dereferenced pointer), where the dynamic type is different from the static type.
Consider:
safety/slice_em_dice_em.cpp
#include
<iostream>
// std::cout
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
struct Entity
{
std::string

name;

Entity( std::string const& aName )


: name( aName )
{}
};
struct Person: Entity
{
int
birthYear;

Copyright 2005 Alf P. Steinbach

};

105

Person( std::string const& aName, int aBirthYear )


: Entity( aName ), birthYear( aBirthYear )
{}

int main()
{
Person person1 = Person( "Marian", 1991 );
Person person2 = Person( "Fritz", 1947 );
Entity* e1
= &person1;

*e1 = person2;
std::cout << person1.name << ": " << person1.birthYear << std::endl;

Fritz: 1991

Scrambled data due to slicing in assignment.

To combat the slicing data scrambling problem you have to take charge of assignment: you must
either remove the client codes access to assignment, or make assignment polymorphic. The
latter implies run-time type checking and the possibility of run-time failure, which is seldom if
ever a good idea, so effectively this means removing access to assignment. And all you need to
do to remove access is to declare a copy assignment operator as a private member of the class
in question you dont need to actually implement the operator, if you dont use it yourself.
An operator overload (a user-defined operator) is an ordinary freestanding or member function.
You use the keyword operator followed by the operator symbol as function name, except that
as weve seen new and delete arent really operator overloads, theyre allocation and deallocation
functions. Assignment is operator=. Assignment operators must be member functions, they
cannot be freestanding functions. A given class can have many different assignment operators;
of these the one that copies objects of that class is the class copy assignment operator.
Copy assignment operator:
The usual copy assignment operator for a class T is T& operator=( T const& ).
Formally it can have any result type, but must have exactly one argument, and that
argument must be T by value or reference, any combination of const and volatile. It
can be virtual, but it can not be a template (a template isnt considered to be a copy
assignment operator).
If the class doesnt declare a copy assignment operator, and copy assignment is used, then the
compiler generates a simple one that performs member-wise assignment, if possible.
The copy assignment operators for classes Derived and Base have different signatures, so the
copy assignment operator in Derived can not override the copy assignment operator in Base, if
that one is virtual. However, a class Derived can have an assignment operator with the same
signature as Bases copy assignment operator. So Derived can override Bases copy assignment
operator (otherwise there wouldnt be much point in having it virtual), its just that Deriveds
own copy assignment operator doesnt and cant override Bases copy assignment operator.
An ordinary assignment to a class type object object,
object = something;

is equivalent to calling the operator= member function on object with something as


argument,

106

Pointers chapter 1.

object.operator=( something );

which explains why the there is just one argument, when there are two sides in an assignment.
Also worth knowing about assignment operators: even though assignment operators are inherited
just like other member functions1, in class Derived the generated or user-defined one will by
default hide any assignment operators from Base. That means that the compilers name lookup
wont find the Base assignment operators unless you take steps to make the compiler look in the
right direction. This is also just like other member functions, where hiding occurs and can be
mystifying, except that most other member functions are not automatically generated2.
To access the hidden Base assignment operators for a Derived object you can use a using
declaration in Derived to unhide the hidden members, i.e. make the compiler look in Base,
and/or you can write assignments to Derived objects using the qualified Base::operator=,
and/or you can use a reference or pointer to Base, like this:
safety/unhide_the_hidden.cpp
#include
<iostream>
// std:.cout
#include
<ostream>
// <<, std::endl
struct Base
{
void operator=( int x )
{
std::cout << x << " assigned to Base." << std::endl;
}
};
struct Derived1: Base {};
struct Derived2: Base
{
using Base::operator=;
};
int main()
{
Derived1
Base&
Derived2

o1;
r1 = o1;
o2;

//o1 = 1;
//!Not found: the auto-generated '=' hides the one from Base.
o1.Base::operator=( 1 );
// OK, it's really there.
r1 = 1;
// OK, it's really really there.
o2 = 2;

// OK.

1 assigned to Base.
1 assigned to Base.
2 assigned to Base.

Of course, what we need to avoid slicing scrambling our data is to hide instead of unhide, and
not just hide, but really remove access (and for simplicity removing assignment altogether):
safety/slice_em_dice_em_not.cpp
// Note: this program intentionally does not compile (which is the point).
#include
<iostream>
// std::cout
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string

It is a very common misconception that assignment operators are not inherited.


There are four automatically generated/declared member functions: the default constructor, copy constructor, copy
assignment operator, and destructor, and of these hiding only comes into play for the copy assignment operator.
1
2

Copyright 2005 Alf P. Steinbach


struct Entity
{
private:
Entity& operator=( Entity const& );
public:
std::string

};

107

// No assignment => no scrambling.

name;

Entity( std::string const& aName )


: name( aName )
{}

struct Person: Entity


{
int
birthYear;
Person( std::string const& aName, int aBirthYear )
: Entity( aName ), birthYear( aBirthYear )
{}
};
int main()
{
Person person1 = Person( "Marian", 1991 );
Person person2 = Person( "Fritz", 1947 );
Entity* e1
= &person1;

*e1 = person2;
// !No operator= available.
person1 = person2; // !No operator= available (can't be generated).
std::cout << person1.name << ": " << person1.birthYear << std::endl;

Here the compiler cannot generate a copy assignment operator for Person because that operator
would need access to Entity::operator=, which is private. Since Entity::operator= is
non-virtual and never called or have its address taken it doesnt need to be implemented. Data
scrambling due to slicing solved, hurray!, now on to some other copy-related problems

108

Pointers chapter 1.

1.3.7

Value copying problems & possible solutions.

Summary:
Copying an object via the built-in copy operations can cause unexpected side-effects such as std::auto_ptr
ownership transfer and aliasing, where two or more pointers end up pointing to the same object. Unexpected
aliasing can cause delayed side-effects when one object destroys or modifies an object that also another object refers
to. Using only const objects guards against unexpected modification but not against unexpected destruction.
We say that objects designed for ordinary value copying and use as ordinary static, local and member variables, have
value semantics. Objects intended to be handled via pointers have reference semantics. Ownership transfer and
aliasing are mainly associated with reference semantics.
In contrast to the data scrambling problem ownership transfer problems and aliasing can occur not only for
assignment but also when an object is copy constructed.
A copy constructor for a class T is any non-template T constructor that can be called with a single T object as
argument, and takes that argument by reference.
Generally if you need to take charge of copying or destruction that means your objects contain pointers or other
resource identifiers (except for the case of adding a protected not implemented copy assignment operator just to
prevent slicing, and except for the case of adding a dummy virtual destructor just to support polymorphic delete).
That yields the Law of (the Big) Three: if your class needs a virtual destructor, a copy assignment operator or a
copy constructor, it probably needs all three. Since you can take charge of destruction and copying in more indirect
ways, e.g. via smart pointers, a more precise but less directly guiding formulation is: if you need to take charge of
destruction or copying, you probably need to take charge of both.
Possible solutions to ownership transfer and aliasing problems include

Remove the client codes access to both assignment and copy construction, i.e. no copying via built-in C++
copy operations.

Let the copy constructor (and if assignment is supported, also the copy assignment operator) perform a deep
copy, duplicating the argument object hierarchies.

Use e.g. boost::shared_ptr instead of std::auto_ptr, which means that two different Sum objects,
say, can safely share the same argument objects.

No copying is a limited solution that doesnt prevent all unexpected ownership transfers.

C++ provides two built-in ways to copy an object:

copy construction, and

assignment.

Copy construction is to create an object of type T by calling a T constructor, giving the


constructor some existing T object as the only argument, which it (presumably) will copy.
That constructor, a constructor taking a T argument that it copies, is Ts copy constructor.
For example,

Copyright 2005 Alf P. Steinbach

109

std::string const

a( "Well, hello there!" );

// Conversion construction.

std::string const
std::string const
std::string const*

b = a;
c( a );
d = new std::string( a );

// Copy construction, C-style syntax.


// Copy construction, C++-style syntax.
// Copy construction.

std::string const
std::string

e = "Surprise?";
s;

// Copy construction (from temporary).


// Default construction.

s = std::string( a );

// Copy construction + assignment.

In case e, disregarding optimization a std::string temporary object is constructed with the


literal string as constructor argument, and then e is copy-constructed from that temporary.
Both copy construction and assignment is OK for our raw pointers and smart pointers, because
they are designed for value semantics: being copied and used as direct local or static variables.
But the objects pointed to are typically designed for only being pointed to, never directly copied:
we say that they typically have reference semantics1. And then value copying is generally not
OK. There are three main problems with value copying of objects with reference semantics:

slicing, which is a general problem with derived classes irrespective of value/reference


semantics, but reference semantics more often involve polymorphic usage,

unexpected side-effects (mostly ownership transfer), and

unexpected aliasing.

We have already solved the main aspect of the first problem, namely slicing scrambling our data.
A side effect, the second problem, is anything that happens in addition to the single thing that
ideally should happen, and when a side effect is unexpected it can do harm. For example, when
you copy a Sum object, say, as the Sum class was in sections 1.3.1 and 1.3.1, youre also
copying its std::auto_ptrs to its left and right arguments. And since std::auto_ptr
transfers ownership those pointers are nulled in the source (unexpected side effect)
Aliasing, the third problem, is when two or more pointers end up pointing to the same object.
When you have objects that contain pointers to other objects, except ownership transferring
smart pointers like std::auto_ptr, the built-in copy operations result in aliasing. Aliasing can
be your friend, saving memory by intentionally sharing common objects, and it can be your
enemy, making one object unexpectedly destroy or modify objects that another object uses.
The problems of unexpected side effects and aliasing troubles are strongly related. Solving one
of these problems often at least partly also solves the other. For example, if we consider only the
unexpected-side-effect-of-copying problem with our Sum and Product objects then there are
three natural solutions, in order from clearly insufficient to near ideal:

Remove the client codes access to both assignment and copy construction, i.e. no copying
via built-in C++ copy operations.

In a more abstract sense our Expression objects from the earlier sections can be said to have value semantics
because when you disregard that theyre handled via pointers, they act like values that are never modified. But it can
be more clear to regard reference versus value semantics as a class property (how objects of that class are handled)
that is orthogonal to the mutability property. A mutable class (modifiable objects) can then have reference semantics
or value semantics, and an immutable class (nonmodifiable) can then have reference semantics or value semantics.
1

110

Pointers chapter 1.

Let the copy constructor perform a deep copy, duplicating the argument object hierarchies.

Use e.g. boost::shared_ptr instead of std::auto_ptr, which means that two


different Sum objects, say, can safely share the same argument objects.

The first solution, no copying, solves the immediate problem of unexpected side effects from
copying, and also removes the possibility of copying producing aliasing. But the client code is
still free to reuse an expression as argument to an expression factory function, i.e. as a subexpression, more than once, and that will immediately produce the undesired side effect, nulled
pointers. So this solution is so limited that for our Expression it isnt a solution but it can be
a solution when there is no natural reason to have the same object in more than one place.
The second solution, deep copy for the copy constructor, can be extended so that it becomes a
full solution for Expression, namely by also deep-copying arguments to the factory functions.
Identical sub-expressions will then be represented as identical but separate object hierarchies,
using up some extra memory, but its the price to pay (with this solution) for safety. And it
removes the possibility of copying producing aliasing, so lets mark up this solution as so-so:
works, not ideal for our Expression, but can be useful when internal aliasing must be avoided.
The third solution, intentional sharing, i.e. intentional aliasing, matches the Expression
requirements almost perfectly. Because an Expression object is (intended as, and effectively) an
immutable object, one that cant be modified by the client code, the aliasing doesnt bring any
problems of itself: for immutable objects its only when there are parts that rely on an assumption
of no sharing, such as std::auto_ptrs do, that there are problems. And we can avoid that by
using e.g. boost::shared_ptr instead of std::auto_ptr. The main cost of this solution is
that as of C++98 and its technical corrigendum C++2003 it means bringing in a library or
implementing such functionality from scratch, which is non-trivial. However, the Boost library is
the one library you absolutely should have installed no matter what youre doing in C++.
Ill discuss the boost::shared_ptr solution in section 1.3.9; for now, lets see how the second
solution, copying the entire data structure, works with polymorphic objects.
1.3.8

Cloning as a solution to ownership and aliasing problems.

Summary:
A clone is a dynamically allocated copy of an object, produced by a virtual member function so that it is of the
objects dynamic type. A cloning function typically produces a deep copy duplicating everything, whereas a shallow
copy just copies the top-level object with any owned objects shared between between the original and the copy (the
clone). A cloning function is called a virtual constructor, although it isnt a C++ constructor.
A general way to implement a cloning function is to use the objects copy constructor. A copy constructor for a
class T is any non-template T constructor that can be called with a single T object as argument, and takes that
argument by reference. The standard allows calls to copy constructors to be optimized away in many situations,
irrespective of what side-effects would otherwise be produced; you can therefore generally not rely on side-effects
(such as trace output statements) in copy constructors.
The C++98 standard and its technical corrigendum C++2003 do not allow binding a T const& reference to an
rvalue when T doesnt have or doesnt have an accessible T( T const& ) copy constructor, which in practice means
no passing of rvalue to a T const& formal argument e.g., this is the case for std::auto_ptr.

Copyright 2005 Alf P. Steinbach

111

The main tool for deep-copying a polymorphic data structure is the concept of cloning: a virtual
factory function that produces a dynamically allocated copy of the object its called on; for the
purposes of deep-copying the clone function will produce a deep copy, duplicating everything,
as opposed to a shallow copy where pointer members are just copied, yielding aliasing.
With cloning you dont need to know the dynamic type of the object. You just call the clone
function on the object you have, and since the function is virtual the type-specific
implementation that produces an object of the same type, will be executed. For some reason that
Ive never found out, but suspect has to do with mathematical abstract data type terminology, a
clone function is also called a virtual constructor, although it isnt a C++ constructor.
So, in class Expression we add
virtual ExpressionAutoPtr clone() const = 0;

The implementation in class Identity is particularly simple, since that class has no data
members:
virtual ExpressionAutoPtr clone() const
{
return ExpressionAutoPtr( new Identity );
}

Clone implementation in class Identity.

In the actual code, shown in full later, I instead use the NEW macro we developed in section 1.3.5,
but for now I think that would just make things less clear. Here it is all about clarity and
protecting the client code from one particular kind of accidental harm. In the actual code it is
more about protecting the client code from all possible ways of using the classes incorrectly.
But just for your information, heres also what the actual code for Identity::clone looks like:
virtual AutoPtr clone() const
{
return NEW( Identity,() );
}

Seemingly all that this function does is to produce an empty object, and what good is that? But
the new object is not quite empty. For with any typical C++ implementation that object will
contain a vtable pointer that points to Identitys vtable, so that calls to e.g. valueAt and
toString on the new object will have the right effect.
In class Constant there is a data member myValue, and one way to copy that into the new
object is to use the Constant constructor that we have defined, which takes a value argument:
virtual ExpressionAutoPtr clone() const
A possible clone implementation in class Constant.
{
return ExpressionAutoPtr( new Constant( myValue ) );
}

But another way, which is in general much more powerful, is to use the copy constructor for this
class. We havent defined a copy constructor, but when a class does not declare a copy constructor, a
simple one is automatically generated, just as with the copy assignment operator. So we can use the
automatically generated copy constructor, passing *this as the object to copy:
virtual ExpressionAutoPtr clone() const
{
return ExpressionAutoPtr( new Constant( *this ) );
}

A more general implementation that


calls the copy constructor.

112

Pointers chapter 1.

Copy constructor:
A copy constructor for a class T is any non-template T constructor that can be called with
a single T object as argument, and takes that argument by reference.
You can not rely on side effects such as output in a copy constructor, because calls to copy
constructors can be optimized away in many situations (for example, RVO for function results):
the standard explicitly allows the compiler to assume no side effects in such situations.
Above we just called an automatically generated copy constructor. Down in BinaryExpression
well define our own, to handle the deep copying of the argument expressions. The beauty of
this is that if derived classes implement cloning by calling their copy constructors, as they should,
then those copy constructors will in turn call the BinaryExpression one, which puts the code
for cloning expression binary operator arguments all in one place.
Implementing the BinaryExpression copy constructor is not at all difficult, since the copy
constructor can use the clone function to do the copying (yes, this is a bit recursive!):
BinaryOperator( BinaryOperator const& other )
: myPLeft( other.myPLeft->clone() )
, myPRight( other.myPRight->clone() )
{}

Deep-copy the left side argument expression.


And the right side one.

As you can see, a copy constructor is usually of the form


T( T const& other ) { }

Even further down, in e.g. Sum, the clone function in turn relies recursively on class
BinaryOperators copy constructor, via the automatically generated Sum copy constructor:
virtual ExpressionAutoPtr clone() const
{
return ExpressionAutoPtr( new Sum( *this ) );
}

Calls the copy constructor above (indirectly).

For a simplest possible example of how that works, say you have an ExpressionAutoPtr to an
object representing 9 + 55 . The object you have a pointer to will then actually be a Sum object,
with Constant left and right argument objects. A call to clone on the Sum object executes the
function implementation above, which calls the automatically generated Sum copy constructor,
which in turn calls our BinaryOperator copy constructor, which calls clone on the two
Constant argument objects, and thats it everythings been cloned.
We now have the basic cloning machinery in hand, and must make sure that its engaged for the
arguments whenever the client code creates a new BinaryOperator (only classes derived from
BinaryOperator have factory functions that take expression arguments). But there is a
problem: our factory functions currently take std::auto_ptr arguments. And that means that
no matter how much we clone the arguments we get, the client code will already have lost
ownership of the originals by passing those arguments to our factory function the ownership
has been transferred to our formal arguments, and all the client code has now is nullpointers!

Copyright 2005 Alf P. Steinbach

113

One general way to avoid such side-effects of copying (here: copying std::auto_ptrs) is, as
mentioned, simply not to copy. And for an ordinary argument type copying could be avoided by
passing by reference, which, since we want to support temporary actual arguments, would mean
reference to const formal arguments. But as partially discussed in section 1.3.1,
The C++98 standard and its technical corrigendum C++2003 do not allow binding a
T const& reference to an rvalue when T doesnt have or doesnt have an accessible
T( T const& ) copy constructor.
That rules out our clever pass by reference solution, since std::auto_ptr lacks the normal
copy constructor.
What we can do instead is to make the arguments pure Expression const&. Unlike our very
first C-style version that isnt now a practical problem, both because the client code is now
prevented from producing temporary Expression objects directly, and because we clone em
anyway. In fact its a good thing, because the client code will have to dereference the pointers it
has, and so the client code programmer can see directly that std::auto_ptrs are not passed.
We can centralize the code that clones factory function arguments in the BinaryOperator
constructor that handles such arguments (logical, nicht war?):
BinaryOperator( Expression const& left, Expression const& right )
: myPLeft( left.clone() )
Deep-copy the left side argument expression.
, myPRight( right.clone() )
And the right side one.
{}

and as an example, the code down in Sum that calls this constructor is
Sum( Expression const& left, Expression const& right )
: BinaryOperator( left, right )
{}

Calls the BinaryOperator constructor.

with a freestanding factory function


ExpressionAutoPtr sum( Expression const& left, Expression const& right )
{
return ExpressionAutoPtr( new Sum( left, right ) );
Calls the above Sum constructor.
}

In passing, note that the real code uses EnsuredSmartPtr and therefore has all relevant
constructors public, and I modelled the example above on that so that it corresponds directly to
the real code (shown below). If EnsuredSmartPtr werent used then to ensure smart pointers
the constructors would have to be protected or private, and either friendship granted to the
factory function, or the freestanding factory function would have to call a static member
function. Either way it would be more code and it would also be more fragile code.
safety/expr_pluggable_1.cpp
#include
"alloc_util.h" // REQUIRES_DYNAMIC_ALLOCATION, EnsuredSmartPtr, NEW
#include
#include
#include
#include
#include
#include

<iomanip>
<iostream>
<memory>
<ostream>
<sstream>
<string>

//
//
//
//
//
//

std::setw
std::cout
std::auto_ptr
<<, std::endl
std::ostringstream
std::string

114

Pointers chapter 1.

//------------------------------------- Support:
std::string stringFrom( double x )

//------------------------------------- class Expression:


class Expression: public EnsuredSmartPtr
{
public:
typedef std::auto_ptr<Expression const> AutoPtr;
typedef AutoPtr
SmartPtrType;
virtual ~Expression() {}

// Polymorphic delete.

virtual double valueAt( double x ) const = 0;


virtual std::string toString() const = 0;
virtual AutoPtr clone() const = 0;
};
//------------------------------------- class Constant:
class Constant: public Expression
{
private:
double myValue;
protected:
virtual ~Constant() {} REQUIRES_DYNAMIC_ALLOCATION
public:
Constant( double aValue )
: myValue( aValue )
{}
virtual double valueAt( double /*x*/ ) const

virtual std::string toString() const

virtual AutoPtr clone() const


{
return NEW( Constant,( *this ) );
}
};
Expression::AutoPtr constant( double aValue )
{
return NEW( Constant,( aValue ) );
}
//------------------------------------- class Identity:
class Identity: public Expression
{
protected:
virtual ~Identity() {} REQUIRES_DYNAMIC_ALLOCATION
public:
virtual double valueAt( double x ) const

virtual std::string toString() const

// For NEW.

Copyright 2005 Alf P. Steinbach


virtual AutoPtr clone() const
{
return NEW( Identity,() );
}
};
Expression::AutoPtr identity()
{
return NEW( Identity,() );
}
//------------------------------------- class BinaryOperator:
class BinaryOperator: public Expression
{
private:
AutoPtr myPLeft;
AutoPtr myPRight;
public:
BinaryOperator( Expression const& left, Expression const& right )
: myPLeft( left.clone() )
, myPRight( right.clone() )
{}
BinaryOperator( BinaryOperator const& other )
: myPLeft( other.myPLeft->clone() )
, myPRight( other.myPRight->clone() )
{}
virtual double operatorResult( double a, double b ) const = 0;
virtual std::string operatorSymbol() const = 0;
virtual double valueAt( double x ) const

virtual std::string toString() const

};
//------------------------------------- class Sum:
class Sum: public BinaryOperator
{
protected:
virtual ~Sum() {} REQUIRES_DYNAMIC_ALLOCATION
public:
Sum( Expression const& left, Expression const& right )
: BinaryOperator( left, right )
{}
virtual double operatorResult( double a, double b ) const

virtual std::string operatorSymbol() const

virtual AutoPtr clone() const


{
return NEW( Sum,( *this ) );
}
};
Expression::AutoPtr sum( Expression const& left, Expression const& right )
{
return NEW( Sum,( left, right ) );
}

115

116

Pointers chapter 1.

//------------------------------------- class Product:


class Product: public BinaryOperator
{
protected:
virtual ~Product() {} REQUIRES_DYNAMIC_ALLOCATION
public:
Product( Expression const& left, Expression const& right )
: BinaryOperator( left, right )
{}
virtual double operatorResult( double a, double b ) const

virtual std::string operatorSymbol() const

};

virtual AutoPtr clone() const


{
return NEW( Product,( *this ) );
}

Expression::AutoPtr product( Expression const& left, Expression const& right )


{
return NEW( Product,( left, right ) );
}
//------------------------------------- Main:
void display( std::string const& s1, std::string const& s2, std::string const& s3 )
{
std::cout
<< std::setw( 2 ) << s1 << std::setw( 15 ) << s2 << std::setw( 25 ) << s3
<< std::endl;
}
void display( double d1, double d2, double d3 )
{
display( stringFrom( d1 ), stringFrom( d2 ), stringFrom( d3 ) );
}
int main()
{
Expression::AutoPtr const
e
=
sum( *product( *constant( 2 ), *identity() ), *constant( 5 ) ); // 2x + 5
Expression::AutoPtr const
e2 =
product( *e, *e );
// (2x + 5)^2

x
0
1
2
3
4

display( "x", e->toString(), e2->toString() );


for( int x = 0; x <= 4; ++x )
{
display( x, e->valueAt( x ), e2->valueAt( x ) );
}

((2*x)+5)
5
7
9
11
13

(((2*x)+5)*((2*x)+5))
25
49
81
121
169

As you can see, expressions can now be freely reused, to build up new more complicated
expressions such as e2.
And in case you wonder, the reason that Expression doesnt declare a private assignment
operator (disallowing slicing) is that the client code only has access to const objects, so the
compiler wont allow assignment anyway. Providing only const objects to the client code is a

Copyright 2005 Alf P. Steinbach

117

second way of removing access to assignment, and thereby solving the slicing problem. But its
not as general a solution as a private assignment operator it requires dynamic allocation, and
sometimes you really dont want to restrict the client code to dynamic allocation.
1.3.9

Intentional sharing as a solution to ownership and aliasing problems.

Summary:
Cloning can yield algorithmic inefficiency because a cloning function uses time proportional to the number of nodes
to be copied, and that affects algorithms that build up objects from parts.
One way to instead share common objects is to use a reference-count for each relevant object. Each added pointer
to the object increments the reference count, each removed pointer to the object decrements the count, and when
the count reaches zero the object is deallocated. And that is exactly what typical shared ownership smart pointers
such as boost::shared_ptr, do.
Reference-counting does not automatically deallocate a circular data structure, and typical smart pointers are not able
to magically connect two separately created smart pointers so they share the same reference count: using reference
counted smart pointers therefore requires some care.
Currently boost::shared_ptr is part of the first C++ library Technical Report, the TR1, and is there known as
std::tr1::shared_ptr.
In the next standard boost::shared_ptr will probably be available as std::shared_ptr.

Problems with our clone-it! solution to the copying side-effect and aliasing problems include

More complicated implementation code than necessary.

More awkward client code than necessary (dereferencing, calls to clone).

Algorithmic (not just operational) inefficiency.

Regarding the last, its not really a practical concern since its only when creating an expression,
and presumably in practice expressions wont contain millions of objects. But the fact that its
there is an itch, an indicator that the implementation probably can be improved. For example, if
a client code programmer uses the following to produce an expression that represents x n ,
Expression::AutoPtr powX( unsigned n )
{
Expression::AutoPtr e = constant( 1 );
for( unsigned i = 1; i <= n; ++i )
{
e = product( *e, *identity() );
}
return e;
// Represents x^n.
}

then because of the cloning each iteration of the loop uses time roughly proportional to the
current size of the expression, which means the total time for building the final expression is
roughly proportional to n 2 , which we usually write as ( n 2 ) , and which is generally Bad.

118

Pointers chapter 1.

The idea of sharing rather than cloning fixes all that, at the cost of introducing aliasing. As youll
recall aliasing can lead to two potential problems: unexpected modification of shared objects, and
unexpected and/or multiple destruction of shared objects. When the objects are const, as ours
are, only the latter problem, destruction, remains to be dealt with.
A fairly general solution to the problem of assured but not-to-early and not-multiple destruction
of shared objects is to maintain a single reference-count for each relevant object. I.e., how
many pointers are there to this object? And when that reference count drops to zero, i.e. no
pointers are left, call delete (or the proper destruction function, whatever that is) on that object.
And that is exactly what a typical shared ownership smart pointer such as boost::shared_ptr,
does.
Reference-counting works well in practice, both in C++ and in languages such as Perl, as long as
the programmer is aware that
Reference-counting does not automatically deallocate a circular data structure.
For, when an object o contains a smart pointer that directly or indirectly points back to o, then at
least that smart pointers reference count will never drop to zero by itself. Happily thats not a
problem with our data structure. A sub-expression can be shared, but we provide no way to
hook it up as a sub-expression of itself: our data structure is an acyclic graph, a tree.
Also, in C++ the programmer needs to be aware that there is no real magic, that apparent magic
just hides details. If you create two separate smart pointers that both point to the same object,
then the two smart pointers will not necessarily know about each other, and then you have the
aliasing problems in full again. To make the smart pointers maintain a single, common reference
count for this object, you must create any additional smart pointers to this object by copy
construction and/or assignment from the original smart pointer, or from one created that way.
is one of the most popular reference-counting shared ownership smart
pointers. It will probably be part of the next standard, C++0x. And if so, it will probably be
known as std::shared_ptr, and then youll not have to install the Boost library or a TR1
implementation (TR1: the first C++ library Technical Report2) in order to use it.
boost::shared_ptr1

In a TR1 implementation boost::shared_ptr is known as std::tr1::shared_ptr. Your


compiler vendor may but just may provide a TR1 implementation with your compilers
standard library. There are also TR1 implementations freely available on the net.
Currently, to use the Boost implementation of boost::shared_ptr you need the relevant
headers from the Boost library. Essentially that means that in practice you need to install the
whole Boost library, but all of the smart pointer code is in the header files so theres nothing to
link and nothing that you need to ship and install along with your application. If your compiler
has a TR1 implementation just change boost::shared_ptr to std::tr1::shared_ptr, and
change the #include to whatever it needs to be with your compilers implementation.
Well use the SharedPtr_ wrapper class discussed in section 1.3.4; heres the complete code:
1
2

[http://www.boost.org/libs/smart_ptr/smart_ptr.htm].
[http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2005/n1745.pdf].

Copyright 2005 Alf P. Steinbach

119

safety/expr_pluggable_2.cpp
#include
"shared_ptr.h" // REQUIRES_DYNAMIC_ALLOCATION, EnsuredSmartPtr, NEW,
// SharedPtr_
#include
#include
#include
#include
#include

<iomanip>
<iostream>
<ostream>
<sstream>
<string>

//
//
//
//
//

std::setw
std::cout
<<, std::endl
std::ostringstream
std::string

//------------------------------------- Support:
std::string stringFrom( double x )

//------------------------------------- class Expression:


class Expression: public EnsuredSmartPtr
{
public:
typedef SharedPtr_<Expression const>
typedef SharedPtr

SharedPtr;
SmartPtrType;

virtual ~Expression() {}

// Polymorphic delete.

virtual double valueAt( double x ) const = 0;


virtual std::string toString() const = 0;
};
//------------------------------------- class Constant:
class Constant: public Expression
{
private:
double myValue;
protected:
virtual ~Constant() {} REQUIRES_DYNAMIC_ALLOCATION
public:
Constant( double aValue )
: myValue( aValue )
{}
virtual double valueAt( double /*x*/ ) const

virtual std::string toString() const

};
Expression::SharedPtr constant( double aValue )
{
return NEW( Constant,(aValue) );
}
//------------------------------------- class Identity:
class Identity: public Expression
{
protected:
virtual ~Identity() {} REQUIRES_DYNAMIC_ALLOCATION
public:
virtual double valueAt( double x ) const

virtual std::string toString() const

};

// For NEW.

120

Pointers chapter 1.

Expression::SharedPtr identity()
{
return NEW( Identity,() );
}
//------------------------------------- class BinaryOperator:
class BinaryOperator: public Expression
{
private:
SharedPtr
myPLeft;
SharedPtr
myPRight;
public:
BinaryOperator( SharedPtr left, SharedPtr right )
: myPLeft( left )
, myPRight( right )
{}
virtual double operatorResult( double a, double b ) const = 0;
virtual std::string operatorSymbol() const = 0;
virtual double valueAt( double x ) const

virtual std::string toString() const

};
//------------------------------------- class Sum:
class Sum: public BinaryOperator
{
protected:
virtual ~Sum() {} REQUIRES_DYNAMIC_ALLOCATION
public:
Sum( SharedPtr left, SharedPtr right )
: BinaryOperator( left, right )
{}
virtual double operatorResult( double a, double b ) const

virtual std::string operatorSymbol() const

};
Expression::SharedPtr sum( Expression::SharedPtr left, Expression::SharedPtr right )
{
return NEW( Sum,( left, right ) );
}
//------------------------------------- class Product:
class Product: public BinaryOperator
{
protected:
virtual ~Product() {} REQUIRES_DYNAMIC_ALLOCATION
public:
Product( SharedPtr left, SharedPtr right )
: BinaryOperator( left, right )
{}
virtual double operatorResult( double a, double b ) const

virtual std::string operatorSymbol() const

};

Copyright 2005 Alf P. Steinbach

121

Expression::SharedPtr product( Expression::SharedPtr left, Expression::SharedPtr right )


{
return NEW( Product,( left, right ) );
}
//------------------------------------- Main:
void display( std::string const& s1, std::string const& s2, std::string const& s3 )
{
std::cout
<< std::setw( 2 ) << s1 << std::setw( 15 ) << s2 << std::setw( 25 ) << s3
<< std::endl;
}
void display( double d1, double d2, double d3 )
{
display( stringFrom( d1 ), stringFrom( d2 ), stringFrom( d3 ) );
}
int main()
{
Expression::SharedPtr const e
=
sum( product( constant( 2 ), identity() ), constant( 5 ) );
Expression::SharedPtr const e2 =
product( e, e );

// 2x + 5
// (2x + 5)^2

display( "x", e->toString(), e2->toString() );


for( int x = 0; x <= 4; ++x )
{
display( x, e->valueAt( x ), e2->valueAt( x ) );
}
}

x
0
1
2
3
4

((2*x)+5)
5
7
9
11
13

(((2*x)+5)*((2*x)+5))
25
49
81
121
169

Why have I changed the factory function argument types back from Expression const& (which
we changed them to in the clone-it! version) to ExpressionSharedPtr?
As with the previous change its not just for convenience, its dictated by the technical solution:
the implementation dictating the specification, the tail wagging the dog. For there is no real
magic. If we were to create a new smart pointer instance from a raw pointer obtained by the
address operator, instead of copying an existing a smart pointer instance, then the new instance
would know nothing about the original, and would not share its reference count, and we would
then have two or more smart pointers that each thought they had a License To Kill their object.
And why does the code use the SharedPtr_ wrapper, and not boost::shared_ptr directly?
Well, thats more of a convenience. We have disabled direct instantiation of Expression and
derived classes, by making the destructor protected (for this the destructor must be
implemented in each class, otherwise the compiler helpfully generates a public destructor
implementation). And with the destructor protected, and using boost::shared_ptr directly,
wed have to specify the destruction function again and again, in every factory function, and we
couldnt use the convenient NEW macro, or wed have to provide a NEW macro specially tailored
for boost::shared_ptr, and if we were to use other smart pointers wed have to provide a
specially tailored NEW for each such smart pointer type, instead of having a single general macro.
Finally, moving up from the purely C++ technical level, why does this design not support things
like creating an expression that substitutes one existing expression for the x in another? Thats

122

Pointers chapter 1.

because it would just be more code that would not contribute towards understanding pointers,
and leaving out that functionality gives you a nice little exercise Hint: a new binary operator.

1.4 An introduction to exceptions.


Summary:
If a function doesnt succeed it fails, e.g. converting a string to an Expression fails when the string is not a valid
Expression specification. One way to inform the client code is then to let the function return a nullpointer. That
C-style solution has several problems, including that (1) the client code programmer may forget to check for
nullpointer, so that the nullpointer can cause trouble later on (when nobody knows where it came from), (2) the
nullpointer doesnt tell the client code what failed where, or why, and (3) inside the function implementation one
must check for failure at every point, and exit cleanly out of possibly many levels of function calls, loops and
conditionals, which not only complicates the code but makes it inefficient for the common success case.
C++ exceptions solve these problems.
The effect of an exception was described in 1.3.2.

In preparation for the following section on serialization, 1.5, lets now look at exceptions.
Exceptions have very little to do with pointers, per se, and so I engaged in a long debate with
myself whether to just give you the absolute minimum of cookbook recipes, do-this-do-that,
needed for those examples, as a kind of in-passing note, or whether I should give you some
background and understanding (although not the full story). At the end I decided for the latter.
A function that converts a string typed in by the user, to an Expression, can conceivably be
implemented as a function
Expression::SharedPtr expressionFrom( std::string const& s );

But what should this function produce if the argument string is not a valid specification of an
expression, if the function doesnt succeed in creating an expression, i.e., on failure? Ideally it
should not then produce any result whatsoever, and certainly not some arbitrary expression. And
with C-style coding the natural way to do that is to then let it produce a nullpointer.
There are several problems with the C-style idea of using a nullpointer to represent no result or
failure, including that

The client code programmer may forget to check for nullpointer, so that the nullpointer
can cause trouble later on (when nobody knows where it came from).

The nullpointer doesnt tell the client code what failed where, or why1.

Inside the function implementation one must check for failure at every point, and exit
cleanly out of possibly many levels of function calls, loops and conditionals, which not
only complicates the code but makes it inefficient for the common success case.

The C++ exception mechanism solves all three problems. As explained earlier, in 1.3.2, an
exception transfers control to some handler up in the function call chain, called stack unwinding,
Mostly such extra information is for the purposes of testing, debugging and logging, not for client code decision
making or user interface display, but testing, debugging and logging are important, and should be supported.
1

Copyright 2005 Alf P. Steinbach

123

where on the way destructors are invoked to provide automatic cleanup. Theres then (1) no
natural way the client code programmer can forget to check, because if she or he doesnt provide
a handler then that exception just propagates further up the call chain; (2) the exception can carry
an arbitrary amount of information (by default a string) that can explain what failed where and
why; and (3) in the function implementation theres no need to check for failure at every point.
1.4.1

How to generate and handle exceptions.

Summary:
You generate an exception by using the throw statement, e.g.
throw std::runtime_error( "Whoops! I failed!" );

throw takes a single argument, an exception object that constitutes the information to be carried by the exception.

You should preferentially use an object of a standard exception class, such as std::runtime_error. All
other standard exception classes are derived from std::exception, which is also itself a standard exception class.

An exception can be handled via a try-catch statement, where the catch part is an exception handler. There
can be more than one catch clause in a try-catch; the first that matches the exception is the one executed. But if
all you need is cleanup you should preferentially use RAII (let destructors do the cleanup).
Guidelines:

The main purpose of a catch is to fulfill the contract of the code, not to do cleanup.

Preferentially use RAII (e.g. smart pointers) to ensure cleanup in the face of exceptions.

Rethrow or simply dont catch exceptions that you dont know how to handle (not rethrowing in this
situation is known as swallowing the exception, and is Bad).

Nothing to do with exceptions, but just incidentally popping up in the example code here, and therefore explained:
the inline keyword applied to a function definition lets you avoid multiple definition errors when the function is
defined (identically) in more than one compilation unit.

You generate an exception by using the throw statement, e.g.


throw std::runtime_error( "Whoops! I failed!" );

takes a single argument, an exception object that constitutes the information to be


carried by the exception. That info object can be anything, even an int value, but using int and
the like is frowned on and you should preferentially use an object of a standard exception
class, such as std::runtime_error. The point of that is that the client code can then handle
your exceptions in the same way, and via the same code, that it uses to handle other exceptions.
throw

All standard exception classes are derived from std::exception, which is also a standard
exception class on its own (OK, that formulation reminds me of the barber who shaves all who
dont shave themselves I think I goofed there, but hopefully you get my meaning anyway!).
Mostly you dont need to handle exceptions, you can let them pass through and use RAII for
automatic cleanup, as explained in 1.3.2. But where you do need to actually handle exceptions,
which is done via a try-catch statement, you can just specify that I want all std::exception
exceptions, and then your handler will be used for std::exception, std::runtime_error,
std::bad_alloc and all other standard exceptions, but not for e.g. int or char const*:

124

Pointers chapter 1.

exceptions/exception_demo.cpp
#include
<iostream>
// std::cout, std::cerr
#include
<ostream>
// <<, std::endl
#include
<stdexcept>
// std::exception, std::runtime_error
void foo()
{
std::cout << "foo says: somebody called me!" << std::endl;
throw std::runtime_error( "Oops, I failed!" );
std::cout << "foo says: I've done it all." << std::endl;
}
int main()
{
try
{
foo();
std::cout << "foo succeeded." << std::endl;
return EXIT_SUCCESS;
}
catch( std::exception const& x )
{
std::cerr << "!foo failed and reports: " << x.what() << std::endl;
return EXIT_FAILURE;
}
}

foo says: somebody called me!


!foo failed and reports: Oops, I failed!

The what member function of a standard exception object reports the string that that object
carries.
Here the execution flow is
1. The try-block is entered. This in a sense registers the catch (all catches in this trycatch) as a possible exception handler. That registration can be actual, adding a machine
code instruction or two, or it can be purely conceptual, implicit in the execution address;
it lasts is in effect until the the execution exits from the try-block.
2. foo() is called.
3. The first statement of foo is executed, displaying the first line of output.
4. The throw statement in foo is executed, copying the specified exception object
somewhere.
5. In the current call chain theres only one registered possible exception handler that
corresponds to the type of the exception, namely the catch in main. So, thats where
this exception is headed. Generally, the latest registered applicable handler, one where
the catchs formal argument matches the exception, is the handler for an exception.
6. Blocks and function calls are exited from, and objects that cease to exist as a result of this
stack unwinding have their destructors called; here the only such object is the original
temporary std::runtime_error object that was copied by the throw.
7. Execution continues in the catch block, here with x a reference to exception object
copy.
8. The first statement in the catch block is executed, resulting in the second line of output.

Copyright 2005 Alf P. Steinbach

125

9. The second statement, return EXIT_FAILURE;, is executed, and were finished.


As you can see C++ exceptions are in a sense polymorphic: the foo function above throws a
std::runtime_error, and the exception handler, the catch, catches it as a
std::exception. But the polymorphic behavior is only on the catch side of things, and there
corresponds exactly to the way a formal function argument provides polymorphic behavior (or
slicing, for pass by value). On the throw side the static type of the throw argument is used,
which is unlike a function call, so unless you arrange for the object to execute a throw of itself
within a virtual member function, youll get slicing: this throw slicing is via copy construction.
A try-catch can have multiple catch clauses. The first one that matches the exception is used,
which means that if you have one catch clause for Derived, and one for Base, the one for
Derived should appear first (or else the one for Base will catch all Derived exceptions). The
catch clause catch(...), three dots, an ellipsis, matches, i.e. will catch, any exception
whatsoever, but at the cost of not directly providing any information about the exception.
In a catch or in code called from a catch you can use throw without arguments to rethrow the
current exception, and you should do that or throw some other exception if your code
does not use the catch to fulfill the codes normal case contract (success case contract) in
some alternative way, or e.g. by trying again.
For example, at a low level in the system a function sendMail might have as its normal case
contract to send an e-mail. If some network function that it uses reports failure it is then
contractually obligated to do one of two things: try again, or propagate the exception.
Propagating the exception is usually accomplished by not having a try-catch, using RAII to
clean up if an exception occurs, which is sometimes confusing to programmers with e.g. Java
background. Theyre used to visible try-catches in all exception-aware code, but in C++
exception handling is often near invisible, transparent. You simply let the exceptions do their
magical destructor-calling as they pass through, and structure the code so that that wont hurt.
Then, at a much higher level the function sendMailOrReportFailure might have as its normal
case contract to Really Try to send an e-mail, and if that fails, inform the user of the failure. This
function, as opposed to the low-level sendMail that it uses internally, will typically have a trycatch, and in the catch it will rethrow or throw some other exception if it fails in
reporting its first-goal failure to the user. Not re/throwing when you dont fulfill the contract of
the code is known as swallowing an exception, and thats generally Bad.
Here are some design-level guidelines for C++ exception handling:

The main purpose of a catch is to fulfill the contract of the code, not to do cleanup.

Preferentially use RAII (e.g. smart pointers) to ensure cleanup in the face of exceptions.

Rethrow or, better, simply dont catch exceptions that you dont know how to handle or
that you dont intend to handle.

To streamline & abstract the throwing of exceptions, which improves readability and provides
one single place to change the exception type and/or add additional throw-related actions, it can
be a good idea to define some functions dedicated to throwing exceptions, e.g.

126

Pointers chapter 1.

exceptions/exception_util.h
#ifndef
EXCEPTION_UTIL_H
#define
EXCEPTION_UTIL_H
//--------------------------------- Dependencies:
#include
#include

<stdexcept>
<string>

// std::runtime_error, std::exception
// std::string

//--------------------------------- Interface:
inline void throwException( std::string const& s )
{
throw std::runtime_error( s );
}
inline void throwExceptionIf( bool ungood, std::string const& s )
{
if( ungood ) { throwException( s ); }
}
inline void xAssert( bool good, std::string const& s )
{
throwExceptionIf( !good, s );
}
#endif

// EXCEPTION_UTIL_H

When you use xAssert (the x standing for exception) you know1 that subsequent code can
safely assume that the condition in the xAssert holds, because if the condition doesnt hold an
exception will occur in C++ be thrown and the subsequent code wont be executed. An
xAssert that fires means that some foreseeable failure has occurred, one that might be
corrected or at least dealt with at a higher level. An assert, on the other hand, means that there
is some absolute assumption that might be violated, where its best to just terminate at once.
In passing, the inline keyword here has little to do with optimization: its necessary to avoid
getting a multiple definition error if the header is used in more than one compilation unit.
1.4.2

Exception usage example: conversion between numeric values and text.

Summary:
Conversion to and from text can easily be implemented as type safe C++ i/o operations by using
std::ostringstream and std::istringstream, although in practice its usually not especially efficient, and
although in the pedantic formal, conversion from text (e.g. hexadecimal) can involve Undefined Behavior.
Since streams are not exception-oriented its then important to use the right error checking calls: fail() does not
include end-of-file state checking, while good() does.
You can represent a detected failure as an exception, yielding a simple function signature, and in contrast to direct
usage of streams there is then no problem of the client code having to use the right error checking calls all it has
to do is to use RAII or, if necessary, handle exceptions.

With exception handling in the toolbox we can implement a relatively safe function for
converting a textual specification of a number to, say, a double value. Thats necessary because
we need to represent numbers in our serialized data, and we need to convert those number
representations back to numbers when de-serializing. Ive shown earlier how to convert a
1

Barring evil gotos, switches or longjmps.

Copyright 2005 Alf P. Steinbach

127

to string, using a std::ostringstream, and the opposite conversion can use the same
principle, by way of a std::istringstream except that now there are possible error
conditions, and standard library streams are not exactly programmer-friendly in that respect.
double

There are alternative ways to do this, e.g. using a boost::lexical_cast for simplicity, or using
the lower-level C library strtod for efficiency and pedantic correctness (no possible Undefined
Behavior, which unfortunately is possible when using standard library streams). And the headers
that must be included depend on the actual implementation. So this logical module is best split
up in one header file and one implementation file, where the headers used dont affect the client
code, and so that the client code doesnt come to depend on having those headers available.
The modules header file can look like this:
exceptions/conversion.h
#ifndef CONVERSION_H
#define CONVERSION_H
//---------------------------------------- Dependencies:
#include

<string>

// std::string

//---------------------------------------- Interface:
std::string stringFrom( double x );
double doubleFrom( std::string const& s, bool allowTrailingChars = false );
#endif

// CONVERSION_H

And the stream-based implementation,


exceptions/conversion.cpp
#include
"conversion.h"
//---------------------------------------- Dependencies:
#include
#include

"exception_util.h"
<sstream>

// xAssert
// std::ostringstream, std::istringstream

//---------------------------------------- Implementation:
std::string stringFrom( double x )
{
std::ostringstream stream;
stream << x;
return stream.str();
}
double doubleFrom( std::string const& s, bool allowTrailingChars )
{
std::istringstream stream( s );
double
result;
stream.unsetf( std::ios::skipws );
stream >> result;
xAssert( !stream.fail(), "doubleFrom: invalid number specification" );
if( !allowTrailingChars )
{
xAssert( stream.eof(), "doubleFrom: trailing characters" );
}
return result;
}

128

Pointers chapter 1.

Here its important to use the right error checking calls. fail() does not include end-of-file
state checking, while good() does, so using good() could erronously indicate a failed
conversion. The end-of-file state is set when the number specification isnt followed by more
characters, because the extraction operation has to (internally) read one more character than the
number specification in order to detect the end of the specification, and when attempting to read
that non-existent character end-of-file is detected and the end-of-file state is set.
With such subtle problems possible its generally a good idea to test the code via some simple
driver program that lets you try out various argument values, e.g. (note that since the stream error
checking calls have been transformed to exceptions the client code here is very simple)
exceptions/test_conversion.cpp
#include
"conversion.h"
#include
<iostream>
#include
<ostream>

// doubleFrom, std::string
// std::cout
// <<, std::endl

int main()
{
std::string s;
std::getline( std::cin, s );
try
{
std::cout << doubleFrom( s ) << std::endl;
}
catch( std::exception const& x )
{
std::cerr << "!" << x.what() << std::endl;
}
}

In the Windows command interpreter:


K:\exceptions> echo 3.14|test_conversion
3.14
K:\exceptions> echo 3.14 |test_conversion
!doubleFrom: trailing characters
K:\exceptions> echo 3.14|test_conversion
!doubleFrom: invalid number specification
K:\exceptions> _

Which is all as it should be.


Btw., you may be wondering why initial whitespace should not be accepted. And the reason for
that is that the more variation that is allowed, the more difficult it is to use the function where
precision is required. Functions that are too helpful often turn out to be nearly useless: while
they do accomplish the main task, they also do too much else that one cant easily get rid of.

Copyright 2005 Alf P. Steinbach

1.4.3

129

Exceptions and pointers, including the concept of hard exceptions.

Summary:
The default allocation function throws a std::bad_alloc exception if it fails. Conceptually thats a hard
exception, i.e. one that, except at the highest level of the program, should either just be propagated or else should be
handled by calling std::abort or std::exit. One way to ensure the latter response to (or rather, instead of) a
std::bad_alloc is to install a new handler, which you do by calling std::set_new_handler from the header
<new>.
Hard exceptions can be difficult to handle in a reasonable way, and most technical measures rely on convention.
When you call std::abort or std::exit destructors are not called; the program just terminates.
Dereferencing a nullpointer is not guaranteed to cause an exception. Its just Undefined Behavior. Use a smart
pointer if you want a guaranteed exception.
Its generally not a good idea to use pointers as exceptions objects.

If the default allocation function, ::operator new, fails to allocate storage, it throws a
std::bad_alloc exception. That means you never get a nullpointer out of the ordinary new
operator. So in standard C++ theres no point in checking the pointer after a new: if the code
after a call to new executes then you have a RealGood pointer, and if the allocated objects type is
a class with at least one user-defined constructor, you also have a pointer to an initialized object.
But what should you do if a std::bad_alloc exception comes flying your way?
Basically the only reasonable thing to do is to terminate, e.g. call std::abort or std::exit,
because available memory is exchausted, and not just can cleaning up involve some dynamic
allocations on the way: throwing a standard exception can involve dynamic allocations! Some
people prefer to establish a small reserve of dynamically allocated storage at the start of the
program, and then deallocating that to have some room for cleanup actions to proceed if a
std::bad_alloc exception should occur. But if the memory shortage is due to a runaway
thread, say, then that wont necessarily work: the reserve might then be gobbled up instantly.
All this implies that std::bad_alloc should be treated very specially. In particular, you dont
want a function to try some alternative way to achieve its contractual goal if a std::bad_alloc
exception occurs: std::bad_alloc is conceptually a hard exception that, except at the highest
control levels of a program, should either be propagated or cause immediate program
termination, because with no more memory available for dynamic allocation, doing anything else
might be Very Dangerous. Unfortunately std::bad_alloc is derived from std::exception,
so if a function has a catch that catches std::exception, it will also catch a std::bad_alloc!
Most programmers simply ignore this problem, and the problem of hard exceptions in general,
since (1) allocation failures, and other hard exceptions, are very rare; since (2) its very difficult to
handle an allocation failure, or any other hard exception, in anything like a reasonable way (there
might not be a reasonable way); and since (3) the practical solution for allocation failure, should it
occur, is for the user to buy more memory or a new computer, or reduce the computers load,
assuming the allocation failure is recognized as an allocation failure.
However, here are a number of possibilities if you do want to tackle allocation failures in code:

130

Pointers chapter 1.

Install a so-called new handler that calls abort or exit (those are the only termination
functions its allowed to call); a new handler is installed via std::set_new_handler,
from the <new> header.

Instead of using new directly, use a NEW macro that translates std::bad_alloc to an
exception not derived from std::exception. It might be tempting to do this instead
via the placement new syntax. But if an allocation function throws an exception, the
standard requires that that exception is std::bad_alloc or a class derived from
std::bad_alloc, so although such code could work it would be Undefined Behavior.

Require that except at the highest control levels of a program, no catch clause catches
the general std::exception; instead, at lower levels, catch e.g. std::runtime_error.

Use a HANDLE_HARD_EXCEPTIONS macro that the coding standard requires placed


between any try and its first catch, except at the highest control level of a program (or
when not using it is okayed by the team leader or some senior developer or manager).
This macro might expand to a catch clause (or sequence of catch clauses) with a call to
abort or exit. Or it might throw some non-std::exception exception.

Use a handleHardExceptions function that the coding standard requires called at the
start of every catch clause. Such a function can use a throw with no arguments to
rethrow the current exception, catch any std::bad_exception or other hard exception,
and do whatever for those, and use a catch(...) to ignore other exceptions. This can
yield more redundancy than the corresponding macro solution, but its not a macro.

If I should recommend any of these solutions, Id have to choose the first, because thats the
only one that doesnt rely on convention (always problematic) and where you know what will
happen:
exceptions/allocation_failure.cpp
#include
<iostream>
// std::cout
#include
<limits>
// std::numeric_limits
#include
<new>
// std::set_new_handler
#include
<ostream>
// <<, std::endl
#include
<vector>
// std::vector
void allocateTooMuch()
{
static std::size_t const
maxSize = std::numeric_limits<std::size_t>::max();
std::vector<char>( 0+maxSize ); // Temporary, but it's the size that matters! ;-)
}
void myNewHandler()
{
// In real code one might have to be careful with using iostream output here.
std::cerr << "!Oops, I ran out of memory. Sorry." << std::endl;
std::abort();
}
int main()
{
std::set_new_handler( &myNewHandler );
std::cout << "'new handler' installed, trying some allocation..." << std::endl;
allocateTooMuch();
std::cout << "Huh, the large allocation seemingly succeeded." << std::endl;
}

Copyright 2005 Alf P. Steinbach

131

'new handler' installed, trying some allocation...


!Oops, I ran out of memory. Sorry.
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

Worth noting, if it isnt clear already:


When you call abort or exit, destructors are not called.
There is no similar mechanism for handling failure to allocate space for local variables or
function arguments or temporaries. Thats just Undefined Behavior. Although your compiler
might offer some mechanism to help you out if it turns out to be a problem.
And in general, C++ operations are Undefined Behavior rather than causing guaranteed
exceptions. For example, in C++98 dereferencing a nullpointer is Undefined Behavior1,
although in Java and C# it causes a guaranteed exception; the trade-off in C++ is for efficiency, at
the cost of safety. If you want safety you can use a smart pointer, which builds on the simple and
clean built-in language operations, whereas the opposite would be impossible.
Finally, Id better mention that
Its generally not a good idea to use pointers as exception objects.
Its especially not a good idea to use pointers to dynamically allocated objects as exception
objects. For if such an exception is caught by a catch(...), then unless the code rethrows the
exception there is no way to know that its pointer, much less know what its pointing at. And so
the dynamically allocated object cant then be deallocated, and you have a memory leak.
Conventional C++ exception handling is based on the assumption that pointers to dynamically
allocated objects are never used as exception objects.

1.5 Basic serialization and de-serialization.


Summary:
Collecting all the information in a data structure and converting it to a form suitable for file storage is called
serialization, and the converse process is correspondingly called de-serialization. The part of de-serialization that
recognizes what the various parts of the information represent, is called parsing. If you remove the actual building
of the data structure from the de-serialization code, then whats left is the parsing.
Pointers are not directly de-serializable, and hence an object of a polymorphic class (which in practice contains a
vtable pointer) cant be de-serialized directly from its binary data.
In general its best to use a pure textual representation of the data.

Saving to a file involves collecting all the information in the data structure and converting it to a
form suitable for file storage. Thats called serialization, because the file can in principle
reside on tape, or be a network connection, or something else that only allows you to write
1

Except in a typeid expression, as mentioned in some earlier footnote.

132

Pointers chapter 1.

serially, not inspect whats already written. The converse process is correspondingly called deserialization, and especially if the format is pure text, the part of de-serialization that recognizes
what the various parts of the information represent, is called parsing; if you remove the actual
building of the data structure from the de-serialization code, then whats left is the parsing.
The main problem with serialization of linked data structures is that
Pointers are not directly de-serializable.
You can easily convert a pointer value to something that can be stored in a file (e.g. a
hexadecimal textual representation), and that can be converted back to the exact same pointer
value. But unless the de-serialization happens in the same process, with the data structure still
intact, the de-serialized pointer value will be invalid: there will be no object there. Thus its
necessary to represent the relationships that the pointers implement, not the pointers themselves.
Since an object of a class with at least one virtual member function, a polymorphic class, with
most (or all) C++ implementations contains a vtable pointer, this also means that
An object of a polymorphic class cant be de-serialized directly from its binary data.
If you just store the objects binary data in a file, and read that back to form the object, you may
and probably will get an invalid vtable pointer not to mention that an objects binary data in
general depends on the compiler, the compiler version, the compiler options and the platform,
and so isnt very portable even for objects of POD (Plain Old Data, C-style) classes.
In sum, this means that
In general its best to use a pure textual representation of the data.
In most domains where information is transformed from an internal form to an external form
and back, it is the transforming back that is difficult. For example, its easy to make a computer
speak a text that you have in your word processor. Its much more difficult to make a computer
transcribe its own sound output, or your recitation of a text, back to text in the word processor.
And so it is also with serialization and de-serialization: the former is generally easy (as these
things go), the latter is generally difficult. Therefore its important to understand the deserialization requirements so that one can choose a kind of serialization, a serialized data format,
that makes de-serialization as easy as possible. And therefore well start with de-serialization.

Copyright 2005 Alf P. Steinbach

1.5.1

133

A serialization format for hierarchical data structures.

Summary:
In order to get a simplest possible parser the serialized data for an object should consist of a type id followed by a
represention of the objects constructor arguments: when the parser reads the type id, assuming theres only one
relevant constructor the parser then has the information it needs to parse the following constructor argument data.
For our expression objects a type-id is essentially an operation id, which can be interpreted as an operator. Our
textual serialization format, with the operator first, is then a prefix form. The usual arithmetic notation is called
infix, and the opposite of prefix is postfix. Postfix is also called Polish Notation, and prefix is called Reverse
Polish Notation or RPN. RPN notation is used on some calculators.
+2x5

prefix

2x+5

infix

2x5+

postfix

A type id, number or other smallest meaningful sequence of characters is called


a token. Its easier to programmatically pick tokens out of text if theyre always separated by e.g. spaces or newlines.

Our expressions have a natural textual representation, namely like


2*x+5

But this form presents some problems for parsing, and we want simplest possible parsing.
Furthermore, this form, while natural for arithmetic expressions, does not generalize to
serialization of other data structures. But what could a more general form be like?
In the simplest case the serialized data structure consists of just one node, which for our
expressions then can be of one of two types: Constant or Identity, but which in a more
general case can be of any number of different types. And what the parser needs to know is (1)
which type of object it should create, and (2) the information needed for that types constructor
arguments. Since the constructor argument type information can vary depending on the type of
object to create, its best if the type information, some kind of type id, comes first in the data,
followed by a representation of the constructor arguments.
On reading the type id the parser then knows what constructor argument data to expect,
assuming theres only one relevant constructor. If there are several possible constructors one
might choose to tell the parser which one by identifying the relevant constructor as a variation of
the type id, or e.g. as a constructor id following the type id. Or via some more general scheme
such as a full-fledged per-argument type description, but then we no longer have a simple parser.
So, lets start by rather arbitrarily assigning type ids to our concrete classes:
Class:

Type id:

Constructor arguments:

Constant

A number.

Identity

None.

Sum

Two expressions.

Product

Two expressions.

134

Pointers chapter 1.

In a more general serialization scheme such type ids could be Globally Unique Identifiers, GUIDs,
which are 128-bit statistically unique ids generated by special tools, and represented as text using
a hexadecimal notation, e.g. {CD2922B3-5AC4-4a93-A1C9-A1E83DF264B4}. But as you can
see, GUIDs are unreadable. Or, the ids could be readable self-describing names organized
hierarchically (like directories), with e.g. a registered internet domain used to ensure uniqueness
across applications there are many such naming systems.
The constant 5 would now be represented as
#5

the expression 2 x as (remember, type id first, then constructor arguments)


* #2 x

and 2 x + 5 as
+ * #2 x #5

which, if you dont immediately see how that works out, you can parenthesize as
+ (* #2 x) (#5)

although our parser will not support parentheses.


For the curious thing is that no matter how complicated the expression is, in this form
parentheses are not needed. Look ma, no parentheses! For example, 2( x + 5 ) is represented as
* #2 + x #5

This form is called prefix notation, because the operator (our type id) comes before its
arguments. The usual arithmetic notation is called infix (the operator in the middle, between its
arguments). And the opposite of prefix (namely, with the operator at the end) is postfix.
+2x5

prefix

2x+5

infix

2x5+

postfix

Prefix form is sometimes called Polish Notation, and postfix form Reverse Polish Notation,
or just RPN. RPN is used on some calculators. It seems natural after a while: first you enter the
arguments, and then the operation, and you dont have to keep track of parentheses.
So, the requirement of our parser knowing the type before trying to parse that types constructor
arguments has led us to prefix form, and somehow the Poles got mixed into this1.
We can do one more thing to make our parser as simple as possible, namely to require that every
token (type id or number) is separated with at least one space from each neighbor. That makes it
easy to pick each token out of the text. Requiring each token to be on a separate line would do
the same, but is less readable and having readable data helps much in debugging and testing.
Instead of
1

The Polish mathematician Jan ukasiewicz introduced prefix form in 1920.

Copyright 2005 Alf P. Steinbach

135

+ * #2 x #5

well then have


+ * # 2 x # 5

which is not exactly user-friendly, but is parser-friendly, and still quite readable.
1.5.2

De-serialization: a simple non-OO recursive descent parser.

Summary:
Retrieving tokens from text can be abstracted as a tokenizer, something that produces the tokens, and for deserialization that frees the client code from using a particular text format.
A recursive descent parser knows what to expect in the input from which function its currently executing. The
functions correspond to possible things that can occur in the input. And the function calls correspond to the order
these things can and must occur in.
Unrelated to parsing, but popping up in the example code and therefore explained: a class can be defined (with some
restrictions, not discussed) inside a function; such a class is called a local class.

In the following I assume that our Expression classes have been packaged in a logical module,
available via the header "expression.h". I dont show the first version since its identical to the
code in the latest version of the expression program, in section 1.3.9. Well, its identical with two
small exceptions. First, if the implementation is provided directly in the header file then the
freestanding factory functions, identity, constant, sum and product, have to be declared
inline in order to avoid multiple definition errors when the header is used in more than one
compilation unit. Second, since we want our de-serialization code to use exactly the same class
ids as the serialization code will use, each class is now equipped with a static function classId.
As youll see the requirements of serialization means well have to add in new things and make
some changes, so from now on Ill start versioning sets of files, not just single files: the first
version of the parser, and all else, will now be in a subdirectory v1, and so on.
With the serialization format weve chosen its relatively easy to break the text up in successive
tokens. Still, having that text handling logic directly in the parser would complicate things, and
would tie our parser very strongly to a given text format. So instead of doing that text handling
stuff directly well define an abstract tokenizer class which provides the tokens to the parser:
serialization/v1/prefix_expression_parser.h
#ifndef
PREFIX_EXPRESSION_PARSER_H
#define
PREFIX_EXPRESSION_PARSER_H
//---------------------------------------- Dependencies:
#include
#include

"expression.h"
<string>

// constant, identity, sum, product, Expression::SharedPtr


// std::string

//---------------------------------------- Interface:
class TokenSequence
{
private:
TokenSequence& operator=( TokenSequence const& );
TokenSequence( TokenSequence const& );

// No assignment.
// No copy construction.

136

Pointers chapter 1.

public:
TokenSequence() {};
virtual ~TokenSequence() {}

// Isn't auto-generated so must be provided.


// Avoid silly-warnings about no virtual destructor.

virtual bool available() const = 0;


virtual std::string current() const = 0;
virtual void advance() = 0;

};

// Initially true if there is a token.


// Empty string if !available().

std::string currentAndAdvance()
{
std::string result = current();
advance();
return result;
}

Expression::SharedPtr expressionFromPrefix( TokenSequence& tokens );


// For space-separated tokens in a single string:
Expression::SharedPtr expressionFromPrefix( std::string const& s );
#endif

// PREFIX_EXPRESSION_PARSER_H

The first overload of expressionFromPrefix sets the client code free to provide its own
TokenSequence object, which might, for example, support a serialized form without extra
spaces. This overload also constitutes the main parser implementation, using the abstract
TokenSequence instead of dealing directly with the raw text. The second overload of
expressionFromPrefix uses a simple TokenSequence implementation that assumes that all
tokens are separated by spaces, and calls the first overload for the actual parsing.
Except for little bit of recursion (and in there, a function pointer used to represent a factory
function) its rather simple and straightforward, readable code, as was our goal.
The only problem is that expressionFromPrefix below is perhaps too simple, so that one may
be left with a feeling that that can not possibly be all, that there must be something more,
something not shown but no, the code presented here is all:
serialization/v1/prefix_expression_parser.cpp
#include
"prefix_expression_parser.h"
//------------------------------------------ Dependencies:
#include
#include
#include

"../exception_util.h"
"../conversion.h"
<sstream>

// xAssert
// doubleFrom( std::string )
// std::istringstream

//------------------------------------------ Implementation:
Expression::SharedPtr binaryExpressionFromPrefix(
Expression::SharedPtr
(*newBinOp)( Expression::SharedPtr, Expression::SharedPtr ),
TokenSequence&
tokens
)
{
// These two consume tokens and so can not be substituted into the newBinOp call,
// since C++ does not guarantee the argument evaluation order for a function call.
Expression::SharedPtr
leftArg
= expressionFromPrefix( tokens );
// Recursive.
Expression::SharedPtr
rightArg
= expressionFromPrefix( tokens );
// Recursive.
}

return newBinOp( leftArg, rightArg );

Copyright 2005 Alf P. Steinbach

137

Expression::SharedPtr expressionFromPrefix( TokenSequence& tokens )


{
std::string const
token = tokens.currentAndAdvance();
Expression::SharedPtr
result;

if( token == Constant::classId() )


{
result = constant( doubleFrom( tokens.currentAndAdvance() ) );
}
else if( token == Identity::classId() )
{
result = identity();
}
else if( token == Sum::classId() )
{
result = binaryExpressionFromPrefix( &sum, tokens );
}
else if( token == Product::classId() )
{
result = binaryExpressionFromPrefix( &product, tokens );
}
else
{
xAssert( false, "expressionFrom: unexpected: \"" + token + "\"" );
}
// A check for extranous trailing input could be placed here.
return result;

Fittingly, this kind of parser is known as a recursive descent parser. It knows what to expect
in the input simply from which function its currently executing, where the functions correspond
directly to possible things that can occur in the input, and the function calls correspond directly
to the order these things can and must occur in. With a recursive descent parser the parsing state
is mainly represented as the current chain of function calls plus execution position in a function.
An alternative is to represent the parsing state explicitly as data, and instead of the parser itself
retrieving tokens, feeding the parser, which then is an object rather than just a function, with
tokens. That kind of parser is very common for parsing HTML and XML as the raw text arrives
from a server on the net. But for our purposes it would only complicate things to do it that way.
Of course the above parser is not very general: it can only handle our specific classes. And
worse, adding a new node type such as e.g. Difference requires the parser to be updated
accordingly. Thats the same large-system maintenance nightmare problem we encountered
earlier, for which the solution was and is object orientation. Id love to show you a more object
oriented version of this parser, but thats a lot of code that doesnt have much to do with
pointers, and that runs into a few severe language and current compiler limitations, with nontrivial solutions (e.g. how to register factories with a factory repository before the corresponding
classes are used). So well leave it at that: the above works fine for our limited, small system.
For the second overload of expressionFromPrefix, the one taking a string as argument, I just
use a std::istringstream to pick out the tokens. Because operator>> does exactly that. Of
course, operator>> requires the tokens to be separated by whitespace, but guess what, by some
miraculous coincidence, of galactic improbability!, thats exactly what we have
The implementation of TokenSequence, a concrete class SimpleTokenSequence, is placed
inside the function as a local class, because thats the only place its used:

138

Pointers chapter 1.

Expression::SharedPtr expressionFromPrefix( std::string const& s )


{
class SimpleTokenSequence: public TokenSequence
{
private:
std::istringstream myChars;
std::string
myCurrent;
public:
SimpleTokenSequence( std::string const& s ): myChars( s ) { advance(); }
virtual bool available() const
{ return !myChars.fail(); }
virtual std::string current() const { return myCurrent; }
virtual void advance()
{ myCurrent = ""; myChars >> myCurrent; }
};

SimpleTokenSequence tokens( s );
return expressionFromPrefix( tokens );

And with this were able to re-create a linked data structure from its serialized data, or e.g., for
testing, from a string typed in by the user:
serialization/v1/expr_from_stdin.cpp
#include
"prefix_expression_parser.h"
#include
"../exception_util.h
#include
"../conversion.h"
#include
#include
#include
#include

<iomanip>
<iostream>
<ostream>
<string>

//
//
//
//

// expressionFromPrefix, Expression::SharedPtr
// xAssert
// stringFrom

std::setw
std::cin, std::cout, std::cerr
<<, std::endl
std::string, std::getline

void display( std::string const& s1, std::string const& s2, std::string const& s3 )
{
std::cout
<< std::setw( 2 ) << s1 << std::setw( 15 ) << s2 << std::setw( 25 ) << s3
<< std::endl;
}
void display( double d1, double d2, double d3 )
{
display( stringFrom( d1 ), stringFrom( d2 ), stringFrom( d3 ) );
}
std::string stringFromStdIn()
{
std::string result;
std::getline( std::cin, result );
return result;

xAssert( !!std::cin, "std::getline failed" );

}
void cppMainBody()
{
Expression::SharedPtr const e
Expression::SharedPtr const e2

= expressionFromPrefix( stringFromStdIn() );
= product( e, e );

display( "x", e->toString(), e2->toString() );


for( int x = 0; x <= 4; ++x )
{
display( x, e->valueAt( x ), e2->valueAt( x ) );
}
}

Copyright 2005 Alf P. Steinbach


int cppMain()
{
try
{
cppMainBody();
return EXIT_SUCCESS;
}
catch( std::exception const& x )
{
std::cerr << "!" << x.what() << std::endl;
return EXIT_FAILURE;
}
}
int main()
{
return cppMain();
}

In the Windows command interpreter:


K:\serialization> echo + * # 2 x # 5 | expr_from_stdin
x
((2*x)+5)
(((2*x)+5)*((2*x)+5))
0
5
25
1
7
49
2
9
81
3
11
121
4
13
169
K:\serialization> echo * # 2 + x # 5 | expr_from_stdin
x
(2*(x+5))
((2*(x+5))*(2*(x+5)))
0
10
100
1
12
144
2
14
196
3
16
256
4
18
324
K:\serialization> _

By now you may be wondering whether those # symbols are really required: without them one
would almost have a calculator-like facility. And the main reason that # was introduced was
indeed just to exemplify the general prefix serialization format and the general way of parsing
such serialized data. However, the # symbols also allow negative numbers to be specified,
whereas without that type id prefix, support for negative numbers would have to be added.
1.5.3

Serialization: a simple generator using intrusive OO code.

Summary:
Serialization generally requires downcasting, and thats simplest & safest accomplished by intrusive code, taskspecific code added to the classes in question, benefiting from C++s safe and automatic downcasting of current
object pointers (the this pointer).

Heres a possible interface to a serialization utility, generating the serialized form:

139

140

Pointers chapter 1.

serialization/v2/prefix_expression_generator.h
#ifndef
PREFIX_EXPRESSION_GENERATOR_H
#define
PREFIX_EXPRESSION_GENERATOR_H
//---------------------------------------- Dependencies:
#include
#include
#include

"expression.h"
<iosfwd>
<string>

// constant, identity, sum, product, Expression::SharedPtr


// std::ostream [forward declaration only]
// std::string

//---------------------------------------- Interface:
void generatePrefixFormOn( std::ostream& stream, Expression::SharedPtr e );
std::string prefixFormFrom( Expression::SharedPtr e );
#endif

// PREFIX_EXPRESSION_GENERATOR_H

The main thing worth noting here has nothing do to with pointers: its the use of the <iosfwd>
header, a small header that only provides forward-declarations of things, to avoid dragging in the
whole iostreams part of the standard library for client code that doesnt use iostreams. For in
contrast to std::string its not a given that the client code will be using iostreams. And that
reality is reflected by the existence of <iosfwd>, which has no counterpart for std::string.
To generate the serialized form our code will have to delve into an expression tree and downcast
pointers as appropriate to get at the data in each node. That is a bit complicated and unsafe to
do in a non-intrusive way. But doing it intrusively, i.e., adding in relevant task-specific support
code in Expression and its derived classes, is simple because then C++ does the downcasting
for us, as weve already seen for the toString function, and for the other member functions.
We now just add a similar function:
serialization/v2/expression.h
#ifndef
EXPRESSION_H
#define
EXPRESSION_H

class Expression: public EnsuredSmartPtr


{
public:

virtual std::string toPrefixForm() const = 0;


};
class Constant: public Expression
{

public:

virtual std::string toPrefixForm() const


{
return classId() + " " + toString();
}
};
class Identity: public Expression
{

public:

virtual std::string toPrefixForm() const


{
return classId();
}
};

Copyright 2005 Alf P. Steinbach

141

class BinaryOperator: public Expression


{

public:

virtual std::string dynamicClassId() const = 0;

};

virtual std::string toPrefixForm() const


{
return
dynamicClassId() + " " +
myPLeft->toPrefixForm() + " " + myPRight->toPrefixForm();
}

class Sum: public BinaryOperator


{

public:

virtual std::string dynamicClassId() const


{
return classId();
}
};
class Product: public BinaryOperator
{

public:

virtual std::string dynamicClassId() const


{
return classId();
}
};

#endif
// EXPRESSION_H

is yet another example of a virtual member function that doesnt need access
to a current object: a function that we might want to call both virtually (on some polymorphic
object) and statically (here represented by the static classId function). The number of such
examples in our problem domain is not representative of their usual rate of occurrence, but if
C++ supported static virtual then that could be defined to mean such a function. And then
wed avoid having to define both a static member function and a virtual wrapper function.
dynamicClassId

serialization/v2/prefix_expression_generator.cpp
#include
"prefix_expression_generator.h"
//---------------------------------------- Dependencies:
#include
#include

"../exception_util.h"
<ostream>

// xAssert
// operator<<

//---------------------------------------- Implementation:
void generatePrefixFormOn( std::ostream& stream, Expression::SharedPtr e )
{
stream << e->toPrefixForm();
}
std::string prefixFormFrom( Expression::SharedPtr e )
{
return e->toPrefixForm();
}

142

Pointers chapter 1.

A simple test program that de-serializes an expression from standard input, and writes its
serialized form (the same except possibly for whitespace) to standard output:
serialization/v2/expr_to_stdout.cpp
#include
"../exception_util.h"
// xAssert
#include
"prefix_expression_parser.h"
// expressionFromPrefix, Expression::SharedPtr
#include
"prefix_expression_generator.h" // prefixFormFrom
#include
#include
#include

<iostream>
<ostream>
<string>

// std::cin, std::cout, std::cerr


// <<, std::endl
// std::string, std::getline

std::string stringFromStdIn()
{
std::string result;
std::getline( std::cin, result );
return result;

xAssert( !!std::cin, "std::getline failed" );

}
void cppMainBody()
{
Expression::SharedPtr const e
= expressionFromPrefix( stringFromStdIn() );
std::cout << prefixFormFrom( e ) << std::endl;
}
int cppMain()
{
try
{
cppMainBody();
return EXIT_SUCCESS;
}
catch( std::exception const& x )
{
std::cerr << "!" << x.what() << std::endl;
return EXIT_FAILURE;
}
}
int main()
{
return cppMain();
}

Of course, this being the second time that we have the exact same code in a test program, namely
cppMain and main, it could be a good idea to write a generic C++ main program once and for
all, but thats a topic unrelated to pointers But the main lesson of this sub-section is that
because pointers lead to dynamic allocation and polymorphic data structures, in practice one may
have to add task-specific code to existing classes in order to support operations that need to
traverse the data structure and do type-specific things. And that intrusive kind of solution may
not be possible when the classes in question are not yours to modify, e.g. some third-part library.

Copyright 2005 Alf P. Steinbach

1.5.4

143

A generator using non-intrusive non-OO code (introducing C++ RTTI).

Summary:
For many classes intrusive code might not be possible or desirable. Then it might be possible to leverage
functionality originally meant for other purposes. Else the classes must support traversal (systematically visiting all
the nodes) and also otherwise have a mostly complete interface where all relevant operations can be performed,
non-intrusively.
The client code must then be able to determine a nodes dynamic type, and the client code must (in our case, and
usually) be able to downcast. Determining a nodes dynamic type requires a dynamic type id, a virtual function that
identifies the dynamic type and is available for all classes. For downcasting one solution is to provide one smart
pointer type for each class. E.g., there are Boost casting functions to use for downcasting, such as
boost::static_pointer_cast, which corresponds to a static_cast for a raw pointer. And
boost::shared_ptr supports implicit smart pointer upcasting just as with raw pointers.
One problem with that is that smart pointers are invariably template classes, and most current compilers, as of 2005,
produce almost totally incomprehensible error messages for template code.
A custom dynamic type id is generally needed anyway for serialization. But for the purpose of determining a nodes
dynamic type you can and probably should instead use the more robust and dependable C++ RTTI, Run-Time Type
Information. That support consists of two operators: typeid, which produces an identification of plus some
information about an objects dynamic type, and dynamic_cast, which performs a cast essentially like a
static_cast, but also checks that the cast is dynamically valid: if the cast isnt valid, then dynamic_cast either
throws an exception (for a cast to reference type) or produces a nullpointer (for a cast to a pointer type).
Note: with Microsofts Visual C++ compiler, which is non-standard by default, you have to add the switch /GR to
enable RTTI.
Both typeid and dynamic_cast only look up the dynamic type if the statically known type is a polymorphic type
(a class with at least one virtual member function).
In contrast to using a specific type id directly to determine an objects dynamic type, which just identifies one type:
with dynamic_cast you can easily detect whether a pointer is to an object of a class derived from e.g.
BinaryOperator, and thereby avoid dealing separately with the cases of Sum and Product, and this makes for
code that can work with new classes automagically.
boost::dynamic_pointer_cast, for a boost::shared_ptr, corresponds to a dynamic_cast for a raw

pointer.

typeid is only suitable for internal usage, not for external data, and for most internal usage scenarios
dynamic_cast is better suited than direct usage of typeid.

Besides serializing an expression there are other things we might want to do with expressions,
such as e.g. displaying an expression tree graphically, simplifying an expression, differentiating an
expression with respect to x, and so on, where this functionality is not directly provided by the
classes. Given a set of classes that we cannot or do not want to modify, we then have to find
some non-intrusive way of extracting the information in the data structure. And to do that we
might exploit data structure specific features, features originally meant for other things.
For example, for our expressions, even when we remove the serialization support introduced in
the previous sub-section (consider it done), we have at hand a toString function that gives all
the required information, albeit in an impractical and somewhat inefficient form.
But if were lucky the data structure will support traversal (systematically visiting all the nodes)
and also otherwise have a mostly complete interface where all relevant information can be
extracted, and where all relevant operations can be performed, non-intrusively.

144

Pointers chapter 1.

Unfortunately1 our data structure does not support traversal by the client code. But traversal
support does not need to be complicated: one way, for our data structure, is simply to introduce
left and right functions in BinaryOperator. Consider also that done.
Unfortunately2, in order to call left and right the client code must know for sure that the
given node is a BinaryOperator, and downcast its pointer to point to a BinaryOperator. I.e.,
the client code must be able to determine a nodes dynamic type, and the client code must be able
to downcast. The latter is seemingly easy to solve: we simply provide one smart pointer type for
each class, and when were using boost::shared_ptr, implicit smart pointer upcasting is
supported just as with raw pointers, and there are Boost casting functions to use for downcasting.
While were at it lets also update the factory functions, constant, identity, sum and product,
to return class-specific smart pointers so that the client code can use class-specific features
without having to downcast the result of a factory function, which would be silly. Recall that the
reason for letting all factory functions return the same general pointer type was the limitations of
std::auto_ptr, both language-wise and errors in a particular compilers implementation of the
standard library (section 1.3.1). But now were using the more versatile boost::shared_ptr.
Regarding dynamic type identification, C++ provides a feature whereby we can easily determine
the dynamic type of any node, which I discuss later on in this subsection, but lets at first pretend
that that feature doesnt exist, so we can see what it involves on the inside.
One way is then to introduce a virtual function that identifies the dynamic type, a dynamic type
id, and make it available for all classes, and for that we can move the dynamicClassId function
introduced in the previous sub-section up from class BinaryOperation, all the way up to
Expression (it must be declared there, but need only be defined in the four concrete classes).
With the direct serialization support removed, and traversal and dynamic type id support as
described above, added, our expression classes now look like
serialization/v3/expression.h
#ifndef
EXPRESSION_H
#define
EXPRESSION_H
//---------------------------------------- Dependencies:
#include

"../shared_ptr.h"

#include

<string>

// For inline implementation:


#include
"../conversion.h"

// REQUIRES_DYNAMIC_ALLOCATION, EnsuredSmartPtr, NEW,


// SharedPtr_
// std::string
// stringFrom

//---------------------------------------- Interface:
//
// class
Expression
// class
Constant
// class
Identity
// class
BinaryOperator
// class
Sum
// class
Product

Copyright 2005 Alf P. Steinbach

145

//-------------------- class Expression:


class Expression: public EnsuredSmartPtr
{
public:
typedef SharedPtr_<Expression const>
typedef SharedPtr

SharedPtr;
SmartPtrType;

virtual ~Expression() {}

// For NEW.

// Polymorphic delete.

virtual std::string dynamicClassId() const = 0;


virtual double valueAt( double x ) const = 0;
virtual std::string toString() const = 0;
};
//-------------------- class Constant:
class Constant: public Expression
{
private:
double myValue;
protected:
virtual ~Constant() {} REQUIRES_DYNAMIC_ALLOCATION
public:
typedef SharedPtr_<Constant const>
typedef SharedPtr

SharedPtr;
SmartPtrType;

static std::string classId()


virtual std::string dynamicClassId() const

// For NEW.

{ return "#"; }
{ return classId(); }

Constant( double aValue ): myValue( aValue ) {}


virtual double valueAt( double ) const
virtual std::string toString() const

{ return myValue; }
{ return stringFrom( myValue ); }

};
inline Constant::SharedPtr constant( double aValue )
{
return NEW( Constant,(aValue) );
}
//-------------------- class Identity:
class Identity: public Expression
{
protected:
virtual ~Identity() {} REQUIRES_DYNAMIC_ALLOCATION
public:
typedef SharedPtr_<Identity const>
typedef SharedPtr

SharedPtr;
SmartPtrType;

// For NEW.

static std::string classId()


virtual std::string dynamicClassId() const

{ return "x"; }
{ return classId(); }

virtual double valueAt( double x ) const


virtual std::string toString() const

{ return x; }
{ return classId(); }

};
inline Identity::SharedPtr identity()
{
return NEW( Identity,() );
}

146

Pointers chapter 1.

//-------------------- class BinaryOperator:


class BinaryOperator: public Expression
{
private:
SharedPtr
myPLeft;
SharedPtr
myPRight;
public:
typedef SharedPtr_<BinaryOperator const>
typedef SharedPtr

SharedPtr;
SmartPtrType;

// For NEW.

BinaryOperator( Expression::SharedPtr left, Expression::SharedPtr right )


: myPLeft( left )
, myPRight( right )
{}
virtual double operatorResult( double a, double b ) const = 0;
virtual std::string operatorSymbol() const = 0;
virtual double valueAt( double x ) const
{
return operatorResult( myPLeft->valueAt( x ), myPRight->valueAt( x ) );
}
virtual std::string toString() const
{
return "(" + myPLeft->toString() + operatorSymbol() + myPRight->toString() + ")";
}

};

Expression::SharedPtr left() const


Expression::SharedPtr right() const

{ return myPLeft; }
{ return myPRight; }

//-------------------- class Sum:


class Sum: public BinaryOperator
{
protected:
virtual ~Sum() {} REQUIRES_DYNAMIC_ALLOCATION
public:
typedef SharedPtr_<Sum const>
typedef SharedPtr

SharedPtr;
SmartPtrType;

static std::string classId()


virtual std::string dynamicClassId() const

// For NEW.

{ return "+"; }
{ return classId(); }

Sum( Expression::SharedPtr left, Expression::SharedPtr right )


: BinaryOperator( left, right )
{}
virtual double operatorResult( double a, double b ) const
{ return a + b; }
virtual std::string operatorSymbol() const { return classId(); }
};
inline Sum::SharedPtr sum(
Expression::SharedPtr left, Expression::SharedPtr right
)
{
return NEW( Sum,( left, right ) );
}
//-------------------- class Product:
class Product: public BinaryOperator
{
protected:
virtual ~Product() {} REQUIRES_DYNAMIC_ALLOCATION
public:
typedef SharedPtr_<Product const>
typedef SharedPtr

SharedPtr;
SmartPtrType;

// For NEW.

Copyright 2005 Alf P. Steinbach


static std::string classId()
virtual std::string dynamicClassId() const

147
{ return "*"; }
{ return classId(); }

Product( Expression::SharedPtr left, Expression::SharedPtr right )


: BinaryOperator( left, right )
{}

};

virtual double operatorResult( double a, double b ) const


{ return a*b; }
virtual std::string operatorSymbol() const { return classId(); }

inline Product::SharedPtr product(


Expression::SharedPtr left, Expression::SharedPtr right
)
{
return NEW( Product,( left, right ) );
}
#endif

// EXPRESSION_H

Seemingly weve only added things, and so the earlier expression evaluation program should
compile fine also with this new version, with no change of the code. But in fact we have changed
something, namely the factory function result types. Still, since boost::shared_ptr supports
implicit upcasting just like raw pointers (or almost), that shouldnt matter, should it?
Unfortunately3 it does, and here we run into the main drawback of using smart pointers. Namely
that smart pointers are invariably template classes. And most current compilers, as of 2005,
produce almost totally incomprehensible error messages for template code.
And not only that, but current compilers often get severely confused by errors in template code
or code using template code. The compilers often get so confused that even after painstakingly
decoding the incomprehensible error messages the decoded messages are more inappropriate and
misleading than usual. These error messages effectively have negative practical value:
../alloc_util.h: In function `SharedPtr_<const Expression, callDelete [with T = const Expr
ession]> expressionFromPrefix(TokenSequence&)':
../alloc_util.h:41: error: invalid conversion from `SharedPtr_<const Sum, callDelete [with
T = const Sum]> (*)(SharedPtr_<const Expression, callDelete [with T = const Expression]>,
SharedPtr_<const Expression, callDelete [with T = const Expression]>)' to `SharedPtr_<cons
t Expression, callDelete [with T = const Expression]> (*)(SharedPtr_<const Expression, cal
lDelete [with T = const Expression]>, SharedPtr_<const Expression, callDelete [with T = co
nst Expression]>)'
../alloc_util.h:45: error: invalid conversion from `SharedPtr_<const Product, callDelete [
with T = const Product]> (*)(SharedPtr_<const Expression, callDelete [with T = const Expre
ssion]>, SharedPtr_<const Expression, callDelete [with T = const Expression]>)' to `Shared
Ptr_<const Expression, callDelete [with T = const Expression]> (*)(SharedPtr_<const Expres
sion, callDelete [with T = const Expression]>, SharedPtr_<const Expression, callDelete [wi
th T = const Expression]>)'

The above error message is from MinGW g++ 3.4.4 when compiling the expression evaluation
program, and the main thing wrong with this error message apart from being unreadable and
incomprehensible, is that the error is reported as being in [../alloc_util.h], in operator delete
in EnsuredSmartPtr. But thats not where the error is: the compiler is confused. It would have
been better if the compiler just reported: I detected an error, but have forgotten in which file.
A good strategy in such cases is to try different compilers, and compare their results, so lets see
what Microsofts Visual C++ compiler, version 7.1, has to say about the very same code:

148

Pointers chapter 1.

prefix_expression_parser.cpp(41) : error C2664: 'binaryExpressionFromPrefix' : cannot conv


ert parameter 1 from 'Sum::SharedPtr (__cdecl *)(Expression::SharedPtr,Expression::SharedP
tr)' to 'Expression::SharedPtr (__cdecl *)(Expression::SharedPtr,Expression::SharedPtr)'
This conversion requires a reinterpret_cast, a C-style cast or function-style cast
prefix_expression_parser.cpp(45) : error C2664: 'binaryExpressionFromPrefix' : cannot conv
ert parameter 1 from 'Product::SharedPtr (__cdecl *)(Expression::SharedPtr,Expression::Sha
redPtr)' to 'Expression::SharedPtr (__cdecl *)(Expression::SharedPtr,Expression::SharedPtr
)'
This conversion requires a reinterpret_cast, a C-style cast or function-style cast

Here the line numbers are the same, but the reported file is different and now correct.
Happily this is one of the rare occasions where Visual C++ provides a reasonable error message
for template code, although the fix it recommends is wrong as wrong can be. The error is at lines
41 and 45 of [prefix_expression_parser.cpp]. And what we have at those lines are calls that pass
the sum and product factory function pointers which now are of different, incompatible types:
else if( token == Sum::classId() )
{
result = binaryExpressionFromPrefix( &sum, tokens );
}
else if( token == Product::classId() )
{
result = binaryExpressionFromPrefix( &product, tokens );
}

The sum function has


a different
result type
than
the product function.

One fix is to provide wrapper functions that do have the same signature, and use those wrapper
functions instead of using sum and product directly:
serialization/v3/prefix_expression_parser.cpp

Expression::SharedPtr sumAsExpression(
Expression::SharedPtr left, Expression::SharedPtr right
)
{
return sum( left, right );
}
Expression::SharedPtr productAsExpression(
Expression::SharedPtr left, Expression::SharedPtr right
)
{
return product( left, right );
}

else if( token == Sum::classId() )


{
result = binaryExpressionFromPrefix( &sumAsExpression, tokens );
}
else if( token == Product::classId() )
{
result = binaryExpressionFromPrefix( &productAsExpression, tokens );
}

Problem solved; on with the non-intrusive serializer, which isnt difficult but is sort of ugly:

Copyright 2005 Alf P. Steinbach

149

serialization/v3/prefix_expression_generator.cpp
#include
"prefix_expression_generator.h"
//---------------------------------------- Dependencies:
#include
#include

<cassert>
<ostream>

// assert
// operator<<

//---------------------------------------- Implementation:
template< class T >
bool isDynamicType( Expression::SharedPtr e )
{
return (e->dynamicClassId() == T::classId());
}
std::string prefixFormFromBinaryOperator( Expression::SharedPtr e )
{
typedef BinaryOperator const BinOpC;
BinaryOperator::SharedPtr const pBinOp = boost::static_pointer_cast<BinOpC>( e );

return
pBinOp->dynamicClassId() + " " +
prefixFormFrom( pBinOp->left() ) + " " + prefixFormFrom( pBinOp->right() );

std::string prefixFormFrom( Expression::SharedPtr e )


{
if( isDynamicType<Constant>( e ) )
{
return Constant::classId() + " " + e->toString();
}
else if( isDynamicType<Identity>( e ) )
{
return Identity::classId();
}
else if( isDynamicType<Sum>( e ) || isDynamicType<Product>( e ) )
{
return prefixFormFromBinaryOperator( e );
}
else
{
assert( "prefixFormFrom: unknown expression type" && false );
}
return 0;
// Satisfy compiler, should never get here.
}
void generatePrefixFormOn( std::ostream& stream, Expression::SharedPtr e )
{
stream << prefixFormFrom( e );
}

In passing, boost::static_pointer_cast corresponds to a static_cast for a raw pointer.


And even more in passing, ideally I should have used static or an unnamed namespace for the
internal functions isDynamicType and prefixFormFromBinaryOperator. But since I havent
explained those C++ features, and they have nothing to do with pointers, I didnt.
With this non-intrusive serializer we again have a case where knowledge of all relevant types is
required, and where if some type has been forgotten that will at best only be detected at run-time.
Which is generally UnGood. I discuss a solution to that design problem in the next subsection.
For now well just replace our somewhat fragile home-brewed dynamic type identification
scheme with C++ RTTI, Run-Time Type Information. That support consists of two operators:
typeid, which produces an identification of plus some information about an objects dynamic
type, and dynamic_cast, which performs a cast essentially like a static_cast, but also checks
that the cast is dynamically valid. If the cast isnt valid, then dynamic_cast either throws an
exception (for a cast to reference type) or produces a nullpointer (for a cast to a pointer type).

150

Pointers chapter 1.

Both typeid and dynamic_cast only look up the dynamic type if the statically known
type is a polymorphic type (a class with at least one virtual member function).

With Microsofts Visual C++ compiler you have to add the switch /GR to enable RTTI.
The typeid operator corresponds almost directly to our classId and dynamicClassId
functions. But dynamic_cast is much more powerful for our purposes. For, whereas typeid
can be used to identify one type, just like our dynamicClassId identifies one type, given a pointer
to (say) a Sum object, where the statically known type is pointer to Expression, dynamic_cast
will succeed in casting it to a pointer to Sum, to a pointer to BinaryObject, or to a pointer to
Expression, but will not succeed in casting it to a pointer to any other class type.
Thus, we can now easily detect whether a pointer is to an object of a class derived from
BinaryOperator, and thereby avoid dealing separately with the cases of Sum and Product: we
can deal generally with BinaryOperator. And that means that if, say, a Difference class is
added, our code will deal correctly with Difference, automagically. On the other hand, if a
class that isnt a BinaryOperator is added, the generator code will have to be updated.
Heres the dynamic_cast-based version of our isDynamicType function (this corresponds
more or less to the Java instanceof and C# is keywords):
serialization/v4/prefix_expression_generator.cpp
#include
"prefix_expression_generator.h"
//---------------------------------------- Dependencies:
#include
#include

<cassert>
<ostream>

// assert
// operator<<

//---------------------------------------- Implementation:
template< class T >
bool isDynamicConstType( Expression::SharedPtr e )
{
Expression const* const rawPointer = e.get();
return (dynamic_cast<T const*>( rawPointer ) != 0);
}
std::string prefixFormFromBinaryOperator( Expression::SharedPtr e )
{
typedef BinaryOperator const BinOpC;
BinaryOperator::SharedPtr const pBinOp = boost::dynamic_pointer_cast<BinOpC>( e );
assert( "prefixFormFromBinaryOperator: must be BinaryOperator" && pBinOp != 0 );
return
pBinOp->dynamicClassId() + " " +
prefixFormFrom( pBinOp->left() ) + " " + prefixFormFrom( pBinOp->right() );
}

Copyright 2005 Alf P. Steinbach

151

std::string prefixFormFrom( Expression::SharedPtr e )


{
if( isDynamicConstType<Constant>( e ) )
{
return Constant::classId() + " " + e->toString();
}
else if( isDynamicConstType<Identity>( e ) )
{
return Identity::classId();
}
else if( isDynamicConstType<BinaryOperator>( e ) )
{
return prefixFormFromBinaryOperator( e );
}
else
{
assert( "prefixFormFrom: unknown expression type" && false );
}
return 0;
// Satisfy compiler (should never get here).
}
void generatePrefixFormOn( std::ostream& stream, Expression::SharedPtr e )
{
stream << prefixFormFrom( e );
}

In passing, boost::dynamic_pointer_cast, for a boost::shared_ptr, corresponds to a


dynamic_cast for a raw pointer.
I guess you may now be wondering why the classId and dynamicClassId functions are still
used in the above program. Why isnt typeid used instead as the class id in the serialized data?
Perhaps the typeid result, whatever it is, isnt suitable for a text format representation?
Well, thats not the problem. The problem is that whatever uniquely identifying text is derived
from the typeid result, that text will be compiler-specific. So then if a program is compiled with
one compiler, and that version produces some serialized data, with typeid as class id, then the
same program compiled with another compiler might not be able to de-serialize that data. Its
like a word processor where the new, shiny, feature-laden version isnt able to read the previous
versions documents. Thus, typeid is only suitable for internal usage, not for external data, and
for most internal usage scenarios dynamic_cast is better suited than direct usage of typeid.

152

1.5.5

Pointers chapter 1.

A generator using non-intrusive OO code (introducing the visitor pattern).

Summary:
Intrusive code has the generally undesirable property of being intrusive. When a number of different tasks are
solved this way, the resulting classes become unwieldy and hard to maintain. It also incurs other costs such as a large
number of dependencies (affecting e.g. build time).
The visitor pattern is one solution. E.g., for a class Expression, the client code then passes a visitor object to
some Expression virtual member function, where that Expression member function calls back on the visitor
object, with a properly downcasted current object pointer.
In a simple visitor pattern implementation the client codes visitor object implements a visitor interface (a fully
abstract class that has no data members and where all member functions are pure virtual) that contains one virtual
callback function for each data structure class, centralizing the list of classes, so that if client code forgets to consider
some class, then that will not compile.
With smart pointers an object can produce a smart pointer to itself (to pass to the callback) by storing a weak
pointer: a smart pointer w that conceptually refers to the collection C of smart pointers that exist for a given object,
where w is automatically nulled when there are no more smart pointers in C, and where you can obtain a new smart
pointer from a non-null w.
The weak pointer type corresponding to boost::shared_ptr is boost::weak_ptr, which you can use directly.
Or you can let your class inherit from boost::enable_shared_from_this. That introduces a
boost::weak_ptr data member in the class, lets call it w, plus a member function shared_from_this that
(when w is non-null) produces a new boost::shared_ptr.
For a class hierarchy where concrete classes are derived from, use of boost::enable_shared_from_this
generally requires downcasting in each class, and that can be accomplished by redefining e.g. a pointer function in
each class.

For both de-serialization and serialization weve now encountered the problem of having to
enumerate all relevant types. Thats the evil type-id-switch that causes severe maintenance
problems in large systems, and that object orientation, OO, promised to fix. But the situation is
slightly different for de-serialization and serialization: for de-serialization we dont have any
existing objects that can do OO things for us (wed have to create global objects to do OO for
us, encountering essentially the same problem again), but for serialization we do have objects.
An OO solution to serialization is easy to do with intrusive code, serialization-specific code
infused into the classes, as shown in in section 1.5.3.
But if we were to do, say, a graphical display of an expression tree in the same way, intrusively,
and do some other thing in the same way, intrusively, and so on, the classes would soon be very
fat and unmaintenable for that reason alone. In addition they would acquire dependencies on
just about anything between heaven and earth: graphics, files, network, you name it, would be
used. And last but not least, you simply may not have the option of modifying the classes.
Therefore, a reasonably designed set of classes should give the client code the ability to infuse any
code whatsoever without changing the classes or their dependencies: for example, infusing the
code dynamically, at run time, instead of statically, at compile time. And one way to do that for
e.g. Expression is to pass a client code visitor object to some Expression virtual member
function, where that Expression member function calls back on the visitor object, with a
properly downcasted current object pointer. This is generally known as the visitor pattern.

Copyright 2005 Alf P. Steinbach

153

There are many variations of the visitor pattern, some of which are designed to let client code
handle nodes at the highest possible level of abstraction, using base classes. And in languages
that support introspection1, such as Java, its possible to go all overboard in designing reusable
general visitor pattern facilities, trading efficiency and simplicity for generality. But we just
need a simple way, a simplest possible way, that centralizes the type enumeration in one place, and
makes any forgotten type in the client code a compile time error, as illustrated below:
serialization/v5/visitor_demo.cpp
#include
<iostream>
// std::cout
#include
<ostream>
// <<, std::endl
#include
<sstream>
// std::ostringstream
#include
<string>
// std::string
//------------------------------------ Classes supporting visitation:
struct Dog;
struct Elephant;
struct IVisitor
{
virtual void onCallbackFrom( Dog const& ) = 0;
virtual void onCallbackFrom( Elephant const& ) = 0;
};
struct Animal
{
virtual void callBackOn( IVisitor& ) const = 0;
};
struct Dog: Animal
{
std::string goodness() const { return "rabid"; }
virtual void callBackOn( IVisitor& visitor ) const
{
visitor.onCallbackFrom( *this );
}
};
struct Elephant: Animal
{
std::string iq() const { return "smart"; }
virtual void callBackOn( IVisitor& visitor ) const
{
visitor.onCallbackFrom( *this );
}
};
//------------------------------------ Independent client code:
class WriteDescriptionOn: private IVisitor
{
private:
std::ostream&
myStream;
WriteDescriptionOn( WriteDescriptionOn const& );
// No copy construction.
WriteDescriptionOn& operator=( WriteDescriptionOn const& ); // No assignment.
virtual void onCallbackFrom( Dog const& aDog )
{
myStream << "a " << aDog.goodness() << " dog";
}
virtual void onCallbackFrom( Elephant const& anElephant )
{
myStream << "a " << anElephant.iq() << " elephant";
}

Introspection: the ability to retrieve information about program entities such as classes and functions at run-time, in
a way that at least supports object creation and function calls on the entities youve retrieved information about.
1

154

Pointers chapter 1.

public:
virtual ~WriteDescriptionOn() {}

};

// Just to avoid silly-warning.

WriteDescriptionOn( std::ostream& aStream, Animal const& anAnimal )


: myStream( aStream )
{
anAnimal.callBackOn( *this );
}

std::string stringFrom( Animal const& anAnimal )


{
std::ostringstream stream;

WriteDescriptionOn( stream, anAnimal ); // Temp. object's constructor does the job.


return stream.str();

void doStuff( Animal const& anAnimal )


{
std::cout << "It's " << stringFrom( anAnimal ) << "!" << std::endl;
}
int main()
{
doStuff( Dog() );
}

It's a rabid dog!

When you study this code, keep in mind that *this has type Dog in class Dog, and type
Elephant in class Elephant. The textually same expressions have diffent types, depending on
the class. Therefore the seemingly identical implementations of callBackOn are not really
identical, and cannot be provided as a single, common implementation up in the Animal class1.
Regarding naming, IVisitor has the prefix I because its an interface class: a fully abstract
class, one that has no data members and where all member functions are pure virtual. And the
WriteDescriptionOn class has an imperative name because the class is meant to be used as if it
were an action-oriented function. Imperative class names are generally misleading or indicate
flawed design because classes are generally not entitites that do, but this class is: its the
constructor that does the doing, and thats the only functionality that this class offers.
There isnt a single (explicit) cast in this code, which is very good: its type safe. And if the
WriteDescriptionOn programmer forgets to implement, say, onCallbackFrom(Dog const&),
then the class will have an inherited pure virtual function, will therefore be abstract, and the
compiler will then flag the usage of WriteDescriptionOn. So its also safe against introduction
of new classes derived from Animal, as long as the single IVisitor interface is updated.
Unfortunately, when smart pointers are used things are not all that simple (;-)), at least when
inheritance of concrete visitation-enabled classes can occur. We do not have inheritance of
concrete classes in our expression data structure, but thats common, so Im doing this as if there
were inheritance of concrete classes. Just so youll see what this entails.
With smart pointers we shouldnt give the client code references or raw pointers to the objects:
the client code should deal only with smart pointers, to avoid ownership issues. That means an
object has to produce a smart pointer referring to itself, or else the client code will have to pass a
smart pointer referring to the object, in every callBackOn call. The latter would be a fragile
solution because one could only detect at run-time whether the passed smart pointer was valid.
callBackOn can be abstracted using templates and so called virtual inheritance, and also in a simpler way (but with
other costs) via a macro.
1

Copyright 2005 Alf P. Steinbach

155

An object can not just instantiate a new smart pointer referring to itself, because that would be a
smart pointer unrelated to possible other smart pointers, not sharing their reference count
(remember, theres no real magic). And an object can not directly store a smart pointer to itself,
because that smart pointers reference count would never, by itself, go down to zero. One
solution is to store a weak pointer: a smart pointer w that conceptually refers to the collection C
of smart pointers that exist for a given object, where w is automatically nulled when there are no
more smart pointers in C, and where you can obtain a new smart pointer from a non-null w.
The weak pointer type corresponding to boost::shared_ptr is boost::weak_ptr, but you
dont have to use boost::weak_ptr directly to do the get smart pointer for this object thing:
its so common a problem that its already been done as a reusable solution. You can just let your
class inherit from boost::enable_shared_from_this. That introduces a boost::weak_ptr
data member in the class, lets call it w, plus a member function shared_from_this that (when
w is non-null) produces a new boost::shared_ptr to the object its called on.
But when this is done once-and-for-all for a common base class instead of for each most derived
concrete class, the shared_from_this result will have to be downcasted. And when you have
inheritance from concrete classes this problem cant be avoided. In the code below I do the
downcasting in a member function pointer thats redefined in each class1:
serialization/v5/expression.h
#ifndef
EXPRESSION_H
#define
EXPRESSION_H
//---------------------------------------- Dependencies:
#include
#include
#include

"../shared_ptr.h"

// REQUIRES_DYNAMIC_ALLOCATION, EnsuredSmartPtr, NEW,


// SharedPtr_
<boost/enable_shared_from_this.hpp> // boost::enable_shared_from_this
<string>
// std::string

// For inline implementation:


#include
"../conversion.h"

// stringFrom

//---------------------------------------- Interface:
class Expression;
typedef SharedPtr_<Expression const>
ExpressionSharedPtr;
class
Constant;
typedef SharedPtr_<Constant const>
ConstantSharedPtr;
class
Identity;
typedef SharedPtr_<Identity const>
IdentitySharedPtr;
class
BinaryOperator;
typedef SharedPtr_<BinaryOperator const> BinaryOperatorSharedPtr;
class
Sum;
typedef SharedPtr_<Sum const>
SumSharedPtr;
class
Product;
typedef SharedPtr_<Product const>
ProductSharedPtr;
//-------------------- class IExpressionVisitor:
class IExpressionVisitor
{
public:
virtual void onCallbackFrom(
virtual void onCallbackFrom(
virtual void onCallbackFrom(
virtual void onCallbackFrom(
};

ConstantSharedPtr ) = 0;
IdentitySharedPtr ) = 0;
SumSharedPtr ) = 0;
ProductSharedPtr ) = 0;

If your compiler chokes on the pointer redefinitions, the dummy argument technique in section 1.6.3 might help.

156

Pointers chapter 1.

//-------------------- class Expression:


class Expression:
public EnsuredSmartPtr,
public boost::enable_shared_from_this<Expression>
{
public:
typedef ExpressionSharedPtr
SharedPtr;
typedef SharedPtr
SmartPtrType;
// For NEW.
virtual std::string dynamicClassId() const = 0;
SharedPtr pointer() const { return shared_from_this(); }
virtual void callBackOn( IExpressionVisitor& visitor ) const = 0;
virtual ~Expression() {}

// Polymorphic delete.

virtual double valueAt( double x ) const = 0;


virtual std::string toString() const = 0;
};
//-------------------- class Constant:
class Constant: public Expression
{
private:
double myValue;
protected:
virtual ~Constant() {} REQUIRES_DYNAMIC_ALLOCATION
public:
typedef ConstantSharedPtr
typedef SharedPtr

SharedPtr;
SmartPtrType;

static std::string classId()


virtual std::string dynamicClassId() const

// For NEW.
{ return "#"; }
{ return classId(); }

SharedPtr pointer() const


{ return boost::static_pointer_cast<Constant const>( shared_from_this() ); }
virtual void callBackOn( IExpressionVisitor& visitor ) const
{ visitor.onCallbackFrom( pointer() ); }
Constant( double aValue ): myValue( aValue ) {}
virtual double valueAt( double ) const
virtual std::string toString() const

{ return myValue; }
{ return stringFrom( myValue ); }

};
inline Constant::SharedPtr constant( double aValue )
{
return NEW( Constant,(aValue) );
}
//-------------------- class Identity:
class Identity: public Expression
{
protected:
virtual ~Identity() {} REQUIRES_DYNAMIC_ALLOCATION
public:
typedef IdentitySharedPtr
typedef SharedPtr

SharedPtr;
SmartPtrType;

static std::string classId()


virtual std::string dynamicClassId() const

// For NEW.
{ return "x"; }
{ return classId(); }

SharedPtr pointer() const


{ return boost::static_pointer_cast<Identity const>( shared_from_this() ); }

Copyright 2005 Alf P. Steinbach

157

virtual void callBackOn( IExpressionVisitor& visitor ) const


{ visitor.onCallbackFrom( pointer() ); }

};

virtual double valueAt( double x ) const


virtual std::string toString() const

{ return x; }
{ return classId(); }

inline Identity::SharedPtr identity()


{
return NEW( Identity,() );
}
//-------------------- class BinaryOperator:
class BinaryOperator: public Expression
{
private:
SharedPtr
myPLeft;
SharedPtr
myPRight;
public:
typedef BinaryOperatorSharedPtr
typedef SharedPtr

SharedPtr;
SmartPtrType;

// For NEW.

SharedPtr pointer() const


{ return boost::static_pointer_cast<BinaryOperator const>( shared_from_this() ); }
BinaryOperator( Expression::SharedPtr left, Expression::SharedPtr right )
: myPLeft( left )
, myPRight( right )
{}
virtual double operatorResult( double a, double b ) const = 0;
virtual std::string operatorSymbol() const = 0;
virtual double valueAt( double x ) const
{
return operatorResult( myPLeft->valueAt( x ), myPRight->valueAt( x ) );
}
virtual std::string toString() const
{
return "(" + myPLeft->toString() + operatorSymbol() + myPRight->toString() + ")";
}
Expression::SharedPtr left() const
Expression::SharedPtr right() const

{ return myPLeft; }
{ return myPRight; }

};
//-------------------- class Sum:
class Sum: public BinaryOperator
{
protected:
virtual ~Sum() {} REQUIRES_DYNAMIC_ALLOCATION
public:
typedef SumSharedPtr
typedef SharedPtr

SharedPtr;
SmartPtrType;

static std::string classId()


virtual std::string dynamicClassId() const

// For NEW.
{ return "+"; }
{ return classId(); }

SharedPtr pointer() const


{ return boost::static_pointer_cast<Sum const>( shared_from_this() ); }
virtual void callBackOn( IExpressionVisitor& visitor ) const
{ visitor.onCallbackFrom( pointer() ); }
Sum( Expression::SharedPtr left, Expression::SharedPtr right )
: BinaryOperator( left, right )
{}

158

Pointers chapter 1.

};

virtual double operatorResult( double a, double b ) const


{ return a + b; }
virtual std::string operatorSymbol() const { return classId(); }

inline Sum::SharedPtr sum(


Expression::SharedPtr left, Expression::SharedPtr right
)
{
return NEW( Sum,( left, right ) );
}
//-------------------- class Product:
class Product: public BinaryOperator
{
protected:
virtual ~Product() {} REQUIRES_DYNAMIC_ALLOCATION
public:
typedef ProductSharedPtr
typedef SharedPtr

SharedPtr;
SmartPtrType;

static std::string classId()


virtual std::string dynamicClassId() const

// For NEW.
{ return "*"; }
{ return classId(); }

SharedPtr pointer() const


{ return boost::static_pointer_cast<Product const>( shared_from_this() ); }
virtual void callBackOn( IExpressionVisitor& visitor ) const
{ visitor.onCallbackFrom( pointer() ); }
Product( Expression::SharedPtr left, Expression::SharedPtr right )
: BinaryOperator( left, right )
{}

};

virtual double operatorResult( double a, double b ) const


{ return a*b; }
virtual std::string operatorSymbol() const { return classId(); }

inline Product::SharedPtr product(


Expression::SharedPtr left, Expression::SharedPtr right
)
{
return NEW( Product,( left, right ) );
}
#endif

// EXPRESSION_H

Here Ive placed the pure OO support code, the code overhead, first in each class, and the real
meat at the end in each class. And as is readily apparent from this concrete example (I think), the
code overhead, the OO support code, needed to carry a small payload such as the
Sum::operatorResult and Sum::operatorSymbol functions, the +, which constitute the
essence and externally useful part of that class, is simply incredible at the C++ level!
Unfortunately theres also quite a bit of overhead, although generally not as much as in C++, in
most other statically typed general languages at about the C++ level; e.g., neither Java nor C#
support the visitor pattern directly, and require you to write plumbing code in each class.
However, in C++ there are at least some ways to generate the OO support code so that one
doesnt have to explicitly repeat it textually almost the same in every class. One way that
you might find useful is to simply use a macro that expands to this text. And another way is via
the C++ template mechanism, combined with some language features I havent discussed.
Heres a non-intrusive OO serializer using the visitation functionality (remember, this
functionality can be used for anything, such as e.g. displaying the expression tree graphically):

Copyright 2005 Alf P. Steinbach

159

serialization/v5/prefix_expression_generator.cpp
#include
"prefix_expression_generator.h"
//---------------------------------------- Dependencies:
#include
#include

<cassert>
<sstream>

// assert
// std::ostringstream

//---------------------------------------- Implementation:
class GeneratePrefixFormOn: private IExpressionVisitor
{
private:
std::ostream&
myStream;
GeneratePrefixFormOn( GeneratePrefixFormOn const& );
// No copy construction.
GeneratePrefixFormOn& operator=( GeneratePrefixFormOn const& ); // No assignment.
void onBinaryOperatorCallback( BinaryOperator::SharedPtr e )
{
myStream << e->dynamicClassId() << " ";
GeneratePrefixFormOn( myStream, e->left() );
myStream << " ";
GeneratePrefixFormOn( myStream, e->right() );
}
virtual void onCallbackFrom( Constant::SharedPtr e )
{
myStream << e->dynamicClassId() << " " << e->toString();
}
virtual void onCallbackFrom( Identity::SharedPtr e )
{
myStream << e->dynamicClassId();
}
virtual void onCallbackFrom( Sum::SharedPtr e )
{
onBinaryOperatorCallback( e );
}
virtual void onCallbackFrom( Product::SharedPtr e )
{
onBinaryOperatorCallback( e );
}
public:
GeneratePrefixFormOn( std::ostream& stream, Expression::SharedPtr e )
: myStream( stream )
{
e->callBackOn( *this );
}
};
void generatePrefixFormOn( std::ostream& stream, Expression::SharedPtr e )
{
GeneratePrefixFormOn( stream, e );
}
std::string prefixFormFrom( Expression::SharedPtr e )
{
std::ostringstream stream;
GeneratePrefixFormOn( stream, e );
return stream.str();
}

1.6 Notes on C++ inheritance as it pertains to pointers.


We have come a long way. Some readers (not you, of course!) undoubtedly think that this
exposition has been overly and needlessly detailed. But in fact, in order to keep the discussion

160

Pointers chapter 1.

within not too unreasonable bounds Ive not only omitted discussing whole areas, like arrays; Ive
omitted many details, some so significant that they really should be examined, which I do here.
1.6.1

Passing Base/Derived-class pointers by reference.

Summary:
There is an implicit conversion from Derived* to (an accessible) Base*, but theres no implicit conversion from
Derived** to Base**, and ditto for references to pointers. The implicit conversion from Derived* to Base* is
purely a pointer value conversion, an rvalue conversion where a completely new value is produced from the given
one. This also holds for reasonable re-seatable smart pointers, and means that no matter raw pointer or smart
pointer you have
Good:

Bad (not allowed):

pAnimal = dog();

getDog( pAnimal );

A smart pointer is a simple example of the handle/body pattern where the object you directly refer to (the smart
pointer) just contains a handle (the raw pointer) that identifies some other, managed object (the body). A Window
class in a GUI framework is another example: a C++-level Window object typically just contains a handle to an APIlevel window, which is then the body. Unfortunately, in most GUI frameworks Windows are re-seatable, and
Window classes are connected by inheritance, breaking type safety; e.g., a button might end up really being a listbox.
const pointers, pointers that cant be re-seated, are a different matter: the conversion Derived* const* to Base*
const* is supported, because the (pointed to) pointer cant be re-seated.

The very first thing we noted about C++ inheritance was that it affects pointer casting. In
general, you get an implicit conversion from Derived* to Base*. And in general, you can use a
static_cast to downcast from Base* to Derived*, when youre sure that the object pointed
to is actually a Derived. When youre not sure, and Base is a polymorphic class, you can use a
dynamic_cast to downcast from Base* to Derived*, and check whether the result is a
nullpointer or not. All this contingent on Base being an accessible base class of Derived where
the casting is attempted (that the Base class subobject, if it were named, would be accessible).
But as discussed even earlier, in connection with pointer const correctness, pointers to pointers
are effectively different. Not that they have special rules, but the same safety considerations
work out differently, affecting whats allowed/supported or not. There is an implicit conversion
from Derived* to Base*, but theres no implicit conversion from Derived** to Base**.
Consider:
inheritance/bad_upcast_rawpointer.cpp
// This program intentionally does not compile (and that's the point).
#include
<iostream>
// std::cout
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
void out( std::string const& s ) { std::cout << s << std::endl; }
struct Animal {};
struct Dog: Animal { void bark() const { out( "Woof! Woof!" ); } };
struct Elephant: Animal { void stomp() const { out( "Squish! Squish!" ); } };
Dog
fido;
Elephant
dumbo;
void get( Animal** ppAnimal ) { *ppAnimal = &dumbo; }

Copyright 2005 Alf P. Steinbach

161

int main()
{
Dog* pDog;
get( &pDog );
pDog->bark();

// !Conversion Dog** to Animal** happily not allowed.

Of course the same goes for references to pointers, since any reference can be replaced by a
pointer and is in an abstract sort of way equivalent to a pointer:
inheritance/bad_upcast_reference.cpp
// This program intentionally does not compile (and that's the point).
#include
<iostream>
// std::cout
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
void out( std::string const& s ) { std::cout << s << std::endl; }
struct Animal {};
struct Dog: Animal { void bark() const { out( "Woof! Woof!" ); } };
struct Elephant: Animal { void stomp() const { out( "Squish! Squish!" ); } };
Dog
fido;
Elephant
dumbo;
void get( Animal*& pAnimal ) { pAnimal = &dumbo; }
int main()
{
Dog* pDog;

get( pDog );
pDog->bark();

// !Conversion Dog*& to Animal*& happily not allowed.

And what this means is that there is no implicit conversion of a Derived* pointer variable (or
object) to Base*, for as the program above shows, that would be dangerous in the extreme,
allowing you to make an Elephant bark (cover yer ears). The implicit conversion is purely a
pointer value conversion, an rvalue conversion where a completely new value is produced from
the given one. That has important consequences for smart pointers.
For if a smart pointer SmartPtr is re-seatable (you can make it point at something new after
initialization) then SmartPtr<Derived> should not be a class derived from or otherwise
implicitly reference-convertible to SmartPtr<Base>, for if it were, then one would have that
havoc-wreaking implicit reference conversion where a Dog could really be an Elephant.
Although most experienced programmers have crashed into this enough times to know that it
just isnt on, newcomers to C++ can be baffled that they cannot, say, pass a
boost::shared_ptr<Derived> as actual argument to a boost::shared_ptr<Base>& formal
argument, since you can pass a Derived to a Base&:
inheritance/bad_upcast_smartptr.cpp
// This program intentionally does not
#include
<boost/shared_ptr.hpp> //
#include
<iostream>
//
#include
<ostream>
//
#include
<string>
//

compile (and that's the point).


boost::shared_ptr
std::cout
<<, std::endl
std::string

void out( std::string const& s ) { std::cout << s << std::endl; }


struct Animal {}; typedef boost::shared_ptr<Animal> AnimalSharedPtr;
struct Dog: Animal { void bark() const { out( "Woof! Woof!" ); } };
struct Elephant: Animal { void stomp() const { out( "Squish! Squish!" ); } };

162

Pointers chapter 1.

void get( AnimalSharedPtr& pAnimal ) { pAnimal = AnimalSharedPtr( new Elephant ); }


int main()
{
boost::shared_ptr<Dog>

get( pDog );
pDog->bark();

pDog;

// !Conversion shared_ptr<Dog>& to shared_ptr<Animal>& not allowed.

A smart pointer is a simple example of the handle/body pattern where the object you directly
refer to (the smart pointer) just contains a handle (the raw pointer) that identifies some other,
managed object (the body). A Window class in a GUI framework is another example: a C++level Window object typically just contains a handle to an API-level window, which is then the
body. Unfortunately, in most GUI frameworks Windows are re-seatable, and Window classes are
connected by inheritance, breaking type safety: using e.g. Microsofts WTL or MFC or Windows
Forms its not difficult to make a C++ level button refer to an API-level listbox, or whatever.
Some people think the solution to that conundrum is to use dynamic type checking instead of
static type checking; some people think the solution is to enforce RAII all the way, where
handles are not re-seatable (I rather like that, not as the solution to the type safety problem, but
because it leads to a clean design); and some people think its mostly an academic problem
because if you dont notice that listbox instead of a button, then you havent tested your
program.
To complete the upcast topic, it is of course a different matter if the pointer1 is const. Then it
cant be re-seated, and so it cant be made to point to an Elephant where the client codes type
says it will only ever point to Dogs (say). So just adding a const permits an implicit upcast
conversion that is otherwise not allowed yet another case where adding const allows more:
inheritance/good_upcast_reference.cpp
#include
<iostream>
// std::cout
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
void out( std::string const& s ) { std::cout << s << std::endl; }
struct Animal
{
virtual void doYourThing() const = 0;
};
struct Dog: Animal
{
void bark() const { out( "Woof! Woof!" ); }
virtual void doYourThing() const { bark(); }
};
struct Elephant: Animal
{
void stomp() const { out( "Squish! Squish!" ); }
virtual void doYourThing() const { stomp(); }
};
void doStuff( Animal* const& pAnimal ) { pAnimal->doYourThing(); }

Note that it doesnt matter whether the object pointed to is const or not. For if its not then the worst that can
happen is some slicing, which (unfortunately or not, depending on ones view of things) C++ generally allows. Its
the pointer itself that must const, so that it cant be re-seated.
1

Copyright 2005 Alf P. Steinbach


int main()
{
Dog
Dog*

fido;
pDog

163

doStuff( pDog );

= &fido;
// OK, because of the 'const' in the formal argument.

Okay, lets now turn the problem 180 around. Well now let the get function take a Dog
pointer by a reference (and accordingly rename the function to getDog), and let the main
program use a general Animal pointer. Thus, it will now be a logical downcasting:
inheritance/bad_downcast_reference.cpp
// This program intentionally does not
#include
<iostream>
//
#include
<ostream>
//
#include
<string>
//

compile (and that's the point).


std::cout
<<, std::endl
std::string

void out( std::string const& s ) { std::cout << s << std::endl; }


struct Animal
{
virtual void doYourThing() const = 0;
};
struct Dog: Animal
{
void bark() const { out( "Woof! Woof!" ); }
virtual void doYourThing() const { bark(); }
};
struct Elephant: Animal
{
void stomp() const { out( "Squish! Squish!" ); }
virtual void doYourThing() const { stomp(); }
};
Dog
fido;
Elephant
dumbo;
void getDog( Dog*& pDog ) { pDog = &fido; }
int main()
{
Animal* pAnimal;
getDog( pAnimal );
pAnimal->doYourThing();

// !Implicit conversion Animal*& to Dog*& not allowed.

Huh? Why isnt this allowed? After all, the code above would work perfectly?
The reason is that C++ does not differentiate between in-arguments, out-arguments and in/outarguments, except, as discussed in section 1.1.3, for function results or when we use const to
indicate in-argument, which of course we cant do for an out-argument. So the compiler has no
idea whether we mean the getDog argument to serve as a logical pure out-argument, as we do, or
as a logical in/out-argument. And in the latter case, a logical in/out-argument, it could be that
the main program had an Animal-pointer that pointed to an Elephant, and that the getDog
function did Dog-specific things, such as making that Elephant bark (again: cover yer ears!).

164

Pointers chapter 1.

A polymorphic pointer logical out-argument implemented as an expression-oriented


functions result cannot be transformed to an action-oriented functions reference (or
pointer to pointer) argument.
Good:

Bad (not allowed):

pAnimal = dog();

getDog( pAnimal );

A smart pointer could support this by having some special type representing an out-argument,
where that type allowed no operations other than re-seating. But in practice that would only be
useful where you need to return a collection of polymorphic smart pointers, because for a single
one you can just use the function result. And with shared ownership smart pointers, which
generally support copying, you can represent a collection as e.g. a std::vector.
Good or at least reasonable smart pointers, such as boost::shared_ptr, therefore implement
the same restriction as with ordinary raw pointers, by the simple expedient of not actively
supporting anything else: for a pointer passed by reference to non-const, the type of the pointed
to object must match the functions formal argument type exactly.

Copyright 2005 Alf P. Steinbach

1.6.2

165

The Liskov substitution principle (more about mutable/immutable).

Summary:
The C++ type rules assume that an object of class Derived is an object of class Base, e.g., an Elephant (or a
Dog!) is an Animal. To avoid nasty surprises every Derived class should honor the contract of Base, i.e. always
do whats expected of a Base. This notion of honoring the Base class contract in Derived, is now known as the
Liskov Substitution Principle, or the LSP; Liskov after the MIT professor Barbara Liskov, and substitution
because it allows you to freely use (substitute) a Derived object where a Base object is expected.
When some unscrupulous programmer creates a Derived class which objects act nothing like Base objects should,
we say that that Derived class violates the LSP.
The LSP is one particular notion of subtype, one that works well for OO programming but is in some ways more
restrictive than or just different from e.g. mathematical subtype relationships such as Circle/Ellipse.
This difference hinges on (1) our ability to change an object in a software program, that we have mutable objects,
while in the mathematical world objects are forever unchanging, and it hinges on (2) software objects, in languages
like C++, having types assigned before creation, strong static type checking, while in mathematics the value
determines the type.
One way to capture most of a mathematical subtype relationship in an LSP-compatible way, i.e. in a way that is
practical with an object oriented programming language like C++, is to factor out the mutability aspect,
Ellipse

MutableEllipse

Circle

MutableCircle

where (important) Ellipse and Circle offer no public data members and no public non-const member
functions: no mutability, and no subclass relationship between MutableEllipse and MutableCircle.

We arrived at the restriction for passing pointers by reference to non-const, that the pointed-to
objects type must match exactly, by seeing that anything else could lead to Elephants being
treated as Dogs. And nobody wants to be treated as a Dog. But we could have arrived at the
same conclusions by considering a higher level, language independent view, namely general
object substitutability, the IsA type hierarchy (class hierarchy) discussed in section 1.2.6.
Recap, general principle assumed by the C++ type rules:
An object of class Derived is an object of class Base, e.g., an Elephant (or a Dog!) is an
Animal.
To avoid nasty surprises every Derived class should honor the contract of Base, i.e. always do
whats expected of a Base, so that any Derived object can be safely substituted where a Base is
expected. For example, our expression classes do this. Any Sum object can be safely treated as a
BinaryOperator, and any BinaryOperator can be safely treated as an Expression.
This extended principle, the notion of honoring the Base class contract in Derived, is now
known as the Liskov Substitution Principle, or LSP, after the MIT professor Barbara Liskov.

166

Pointers chapter 1.

And when some unscrupulous programmer creates a Derived class which objects act nothing
like Base objects should, we say that that Derived class violates the LSP. For experienced
programmers, one dividing line between good ones and bad ones is that the good ones know
about and always consider the LSP, and never violate the LSP without very good reasons.
It is important to understand that the LSP is not a Universal Truth about types in general, such as
types in mathematics and everyday life, but is a good, indispensable, extremely important (and so
on, add adjectives here) way to think about and use inheritance in an OO programming language.
Heres one example where our intuitive notion of type clashes with the LSP, and hence with OO
class inheritance: is a Circle an Ellipse (an ellipse is a kind of elongated circle, like
)?
Well, if you want to start a heated discussion in a programming forum, then few questions are
better suited than this, of course after some suitably long period has passed since the last time!
Mathematically the set of all circles is contained in the set of all ellipses, i.e., circles are a subset of
ellipses. Mathematically we therefore consider circles to be a kind of, a subtype of, ellipses: a
mathematical circle IsA mathematical ellipse. And thats not very different from how we can
reason that the set of all Dog instances is contained in the set of all Animal instances, and
therefore Dog is a subtype of (a class derived from) Animal, so havent we arrived at an answer?
Unfortunately no, because while there isnt anything we can do with a general Animal that we
cant also do with a Dog, there are things we can do with an ellipse that we cant do with a circle.
For example, we can elongate an ellipse, and it will still be an ellipse, just more elongated. But we
cant elongate a circle without losing the property of circleness: then it becomes pure ellipse.
This difference hinges on (1) our ability to change an object in a software program, while in the
mathematical world objects are forever unchanging, and it hinges on (2) software objects, in
languages like C++, having types assigned before creation, while in mathematics the value
determines the type. In mathematics you just create new object values from existing ones, much
like our expression objects, and if you create an ellipse with no elongation1, you have created a
circle which is not an option with static type checking. And that means that when mutable
objects are involved, compared to the purely value based mathematical subtype relationship the
LSP is a more restrictive subtype relationship, one where dynamic behavior is important.
So, whats the right way to represent circles and ellipses as LSP-conforming classes, then?
There is no single right way, since it all depends on what you need the objects for. But if we
want to capture the mathematical subtype relationship well have to do that via classes that dont
allow the objects to be changed: immutable classes. Then mutable classes (classes that allow the
objects to be changed) can be derived, and will then not be directly related to each other:
Ellipse

MutableEllipse

Circle

MutableCircle

The mathematical term is eccentricity, denoted e; when the eccentricity of an ellipse is 0 you have no elongation.

Copyright 2005 Alf P. Steinbach

167

Here Ellipse and Circle offer no public data members and no public non-const member
functions: no mutability. And as you can see its here not the case that a MutableCircle is a
MutableEllipse. The Ellipse and Circle classes capture the mathematical relationship, as
far as that is possible (an Ellipse instance with no elongation is not a Circle, typewise, although
it is mathematically a circle), while MutableEllipse and MutableCircle capture the mutable
aspect of software objects, the dynamic behavior, where the LSP dictates no IsA relationship.
If you want another, perhaps more subtle mathematics subtype LSP subtype notion clash,
consider the subset and hence subtype relationships between the sets of natural numbers,
integers, rational numbers, real numbers and complex numbers (and more, if you care). In
programming languages that do represent different kinds of numbers as different OO classes
these classes are generally not such that e.g. Integer is derived from Rational. Why is that?
1.6.3

Covariance, contravariance and invariance.

Summary:
Covariance is when an arguments type becomes more specialized (derived) in a more specialized class.
Contravariance is when an arguments type becomes more general, i.e., less specialized, in a more specialized class.
Invariance is when the arguments type doesnt change.
Covariance is LSP-compatible only for a pure out-argument, such as a function result. C++ supports covariance for
function results of pointer or reference type. For smart pointers you can use a workaround where a public nonvirtual function is redefined in each class, and calls a private or protected virtual function that provides the
dynamic type specific result.
This workaround is also great for older compilers that dont support covariance, but then the non-virtual function
may have to be equipped with a class-specific dummy argument, e.g., for a class T, a T* pointer with nullpointer as
default value.
Contravariance, not supported by C++, is LSP-compatible only for a pure in-argument, and more generally, to be
LSP-compatible the data sources type must be the same as or derived from the receivers type, whether its an inargument or an out-argument.
Consequently, an in/out argument must be invariant to be LSP-compatible.

Now consider an out-argument, such as a function result. If the client codes type is Animal*,
then the function can actually return a Dog*, or an Elephant*, and as long as the LSP is
honored by those classes it will work out OK. From the client codes point of view that actual
Dog* or Elephant* is an Animal*, and behaves exactly as an Animal* is expected to: whatever
variation there is (depending on the dynamic type), is within what class Animals contract allows.
Therefore, if class Base has a virtual member function that returns an Animal*, in class Derived
that function can be safely overridden to return, say, an Elephant* or a Dog*. Given some
Base pointer the client code can call that function and expect an Animal* as the result. But
when that Base pointer actually points to a Derived object, what the client code gets as actual
result is an Elephant* or Dog*, and thats perfectly OK because those are Animals. When the
function result is considered as an out-argument: since the argument type has become more
specialized (Animal Elephant) in the more specialized class (Base Derived) we say that
this argument is covariant. I.e., that its type varies in the same direction as the inheritance.
C++ partially supports covariance, for function result types that are pointers or references. The
support is limited to function result types because thats the only case where the compiler knows

168

Pointers chapter 1.

its a logical out-argument (C++ doesnt support specification of in and out for other arguments).
And presumably the support is limited to pointer and reference types because that makes it easy
to arrange for the caller to allocate space for the function result, knowing only the type in Base:
inheritance/covariance_demo.cpp
#include
<iostream>
// std::cout
#include
<memory>
// std::auto_ptr
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
void out( std::string const& s ) { std::cout << s << std::endl; }
struct Dog
{
virtual void bark() const { out( "Woof! Woof!" ); }
};
struct Chihuahua: Dog
{
virtual void bark() const { out( "Yip! Yip! Yip!" ); }
};
Dog
Chihuahua

fido;
fitzy;

struct Base
{
virtual Dog const* dog() const
};

{ return &fido; }

struct Derived: Base


{
virtual Chihuahua const* dog() const { return &fitzy; }
};
int main()
{
std::auto_ptr<Base> p( new Derived );
}

p->dog()->bark();

// In standard C++ yields "Yip! Yip! Yip!".

Not all C++ compilers do support covariance, however. Those that dont, generally older
compilers that ideally you shouldnt use, are of course not standard-conforming, nor LSPsupporting, but that doesnt help much if youre stuck with such a compiler. Generally one of
two things can then happen: the above program produces Woof! Woof! instead of Yip! Yip!
Yip!, i.e. the dog member function in Derived was regarded as having a completely different
signature from the one in Base, and didnt override the one in Base, or, the compiler issues an
error message, e.g. to the effect that functions cannot be overloaded purely on the result type.
For such compilers there is a workaround thats well worth knowing about.
The main idea is to provide a public non-virtual function, that calls a protected or private
virtual function and downcasts the result. In addition, because an older compiler can choke on a
derived class providing a member function identical to one in a base class except for the return
type, its then also a good idea to add a class-specific dummy argument. A pointer will do nicely.
inheritance/covariance_workaround_demo.cpp
#include
<iostream>
// std::cout
#include
<memory>
// std::auto_ptr
#include
<ostream>
// <<, std::endl
#include
<string>
// std::string
void out( std::string const& s ) { std::cout << s << std::endl; }

Copyright 2005 Alf P. Steinbach

169

struct Dog
{
virtual void bark() const { out( "Woof! Woof!" ); }
};
struct Chihuahua: Dog
{
virtual void bark() const { out( "Yip! Yip! Yip!" ); }
};
Dog
Chihuahua

fido;
fitzy;

class Base
{
private:
virtual Dog const* virtualDog() const { return &fido; }
public:
Dog const* dog( Base* = 0 ) const
{
return virtualDog();
}
};
struct Derived: Base
{
private:
virtual Dog const* virtualDog() const { return &fitzy; }
public:
Chihuahua const* dog( Derived* = 0 ) const
{
return static_cast<Chihuahua const*>( virtualDog() );
}
};
int main()
{
std::auto_ptr<Base> p( new Derived );
p->dog()->bark();

// Even with older compilers yields "Yip! Yip! Yip!".

This technique is great also when you want covariant smart pointers as the function result type(s).
I have already shown an example of that, namely the pointer functions in the latest version of
the expression classes, in section 1.5.5: covariance is nothing new, weve already used it! If you
had trouble compiling that version of the expression classes, it may be that your compiler is old
and requires a class-specific dummy argument for pointer, as shown for dog above.
The opposite of covariant is contravariant, where an argument type gets more general in derived
classes, i.e. varies opposite of the inheritance. Just as covariance is LSP-compatible for a pure
out-argument, contravariance is LSP-compatible for a pure in-argument. Essentially a pure inarguments type is a requirement on the caller to supply at least the stated type, and a derived
class is free to require less from the caller. And more generally, to be LSP-compatible the data
sources type must be the same as or derived from the receivers type, whether its an in-argument
or an out-argument. However, C++ does not support contravariance.
And we can now see how the LSP forces the constraint on a pointer passed by reference to nonconst, that the pointed to objects type must match exactly. For regarded as an out-argument,
which as far as the compiler knows it could be, it can at most be covariant, and regarded as an inargument, which it also could be, it can at most be contravariant. Hence it must be invariant.
And that invariance in the arguments type in derived classes means the argument supports only
one actual argument type. But this is a fairly sophisticated, abstract argument (!), and if it doesnt
convince you, just stick with the examples of how Elephants otherwise could be treated as Dogs.

170

Pointers chapter 1.

1.7 Closing words & acknowledgements.


I hope you liked the Long Tour. Heres a partial list of things that belong at this level but that I
havent discussed:

special cases of casts (weve seen one of those, namely reinterpret_cast to the first
member of a POD, and in addition there is the dynamic_cast to void* to obtain a
complete object pointer, and the C style cast to access an inaccessible base),

pointer conversions and comparisions in general (that involves a whole area that I havent
discussed, namely arrays, plus the standard library, e.g. std::greater),

covariant data,

whats inherited and not (weve seen that friend declarations are not inherited and
constructors are not inherited),

what member functions are automatically declared and generated (I mentioned in a


footnote somewhere that this is the default constructor, copy constructor, copy
assignment operator and destructor),

multiple inheritance (used without mention in the last version of the expression classes,
but there it could be replaced by single inheritance),

virtual inheritance (thats a big and difficult topic, including the dreaded diamond
inheritance case, and how to emulate Java/C#-like inheritance in C++),

and more. So not only is there still much to learn about how to apply the features I have
discussed, theres still much to learn the about pointer-related features of the language itself, so
enjoy!

Copyright 2005 Alf P. Steinbach

171

Thanks to the participants of the [alt.comp.lang.learn.c-c++], [comp.lang.c++],


[no.it.programmering.c++] and [comp.lang.c++.moderated] Usenet groups who commented on
early versions of this document, or commented on such comments (and so on), and provided
useful feedback and suggestions.
In the order I found the names in my news and mail client programs:
Robert Macy, Sigurd Stenersen, BobR (e-mail address at worldnet.att.net), Chris Val, Anand Hariharan,
James Dennett, Anthony Oliver, Greg Comeau, Andre Eisenbach, Marcus Kwok, Vince Morgan and James
Kanze.
Id also like to thank those who helped in an early, pre-writing investigation to find a good useof-pointers example, by creating their own versions of a program to solve the nuns and
cannibals puzzle, all versions, including mine, showing that pointer usage was not natural for
solving that problem: Kai Owe Bux, Andre Eisenbach (thats twice!) and Branimir Macsimovic, and also
those who provided useful feedback in that discussion thread, Old Wolf and Mark P.
All remaining errors are of course mine.
Or, theyre due to gremlins, the Word Processor Devil, international corporations, etc.!
Oslo, Norway, November 21, 2005 December 8, 2005.

End Of Text

Вам также может понравиться