A Transition Guide To C++
A Transition Guide To C++
A Transition Guide To C++
x to C++
Michael H. Goldwasser David Letscher
1
Contents
1 Introduction 4
5 Control Structures 18
5.1 While Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5.2 Conditionals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5.3 Nonboolean Expressions as Conditions . . . . . . . . . . . . . . . . . . . . . . . . . . 19
5.4 For Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.5 Defining a Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
7 Classes in C++ 28
7.1 Using Instances of a Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
7.2 Defining a Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
7.3 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2
9 Generic Programming and Templates 47
9.1 Templated Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
9.2 Templated Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
9.3 Using Templated Functions and Classes . . . . . . . . . . . . . . . . . . . . . . . . . 50
3
2. PROGRAMMING LANGUAGE DESIGN Page 4
1 Introduction
Python is a wonderful programming language. However, more than a thousand programming
languages have been developed over time, with perhaps a hundred that are still actively used for
program development. Each language has its own strengths and weaknesses in trying to support
the development of efficient, maintainable, and reusable software. Software professionals must
become accustomed to programming in different languages. Fortunately, once a person has a solid
foundation in one language, it becomes easier to transition to programming in another language.
This document is designed for Python programmers choosing C++ as a second language. C++
is a widely used language in industry and, as an object-oriented language, it has much in common
with Python. Yet there exist significant differences between the two languages. This transition
guide is not meant to serve as a complete, self-contained reference for C++. Our goal is to provide
an initial bridge, built upon the knowledge and terminology introduced in our book Object-Oriented
Programming in Python.
C++ is a direct extension of an earlier programming language named C. The C programming
language was introduced in 1973 and widely used for software development for decades (in fact,
its use is still prevalent). Its greatest strength is its run-time efficiency, however it is not object-
oriented. In the early 1980s, developers at Bell Labs began work on C++, adding support for
object orientation while preserving aspects of the original syntax of C. As a result, C++ provides
a more robust set of existing libraries while still providing the ability to create fast executables.
C and C++ provide great flexibility in controlling many of the underlying mechanisms used
by an executing program. A programmer can control low-level aspects of how data is stored, how
information is passed, and how memory is managed. When used properly, this control can lead
to a more streamlined result. Furthermore, because of the long history of C and C++ and their
widespread use, the technology has been highly optimized.
The greatest weakness of C++ is its complexity. Ironically, this weakness goes hand-in-hand
with the very issues that we described as strengths of the language. With decades of prominence,
its evolution has been somewhat restricted by the desire to remain backward compatible in support
of the large body of existing software. Some additional features have been retrofitted in a more
awkward way than if the language had been developed with a clean slate. As a result, parts of the
syntax have grown cryptic. More significantly, the flexibility given to a programmer for controlling
low-level aspects comes with responsibility. Rather than one way to express something, there may
be five alternatives. An experienced and knowledgeable developer can use this flexibility to pick
the best alternative and improve the result. Yet both novice and experienced programmers can
easily choose the wrong alternative, leading to less efficient or flawed software.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
2. PROGRAMMING LANGUAGE DESIGN Page 5
reliability and maintainability of software. But, that high-level software must be translated back
to a CPU’s machine language in order to execute on that computer. This translation is typically
automated in the form of an interpreter or compiler.
Python is an example of an interpreted language. We “run” a typical Python program by
feeding its source code as input to another piece of software known as the Python interpreter. The
Python interpreter is the software that is actually executing on the CPU, in effect adapting its
outward behavior to match the semantics indicated by the given source code. The translation from
high-level to low-level operations is performed on-the-fly1 .
In contrast, C++ is an example of a compiled language. The translation of high-level source
code to low-level machine code takes place in advance of the software being executed by the end
user, relying on a distinct two-phase process. During the first phase (“compile-time”), the source
code is fed as input to a special piece of software known as the compiler. The compiler analyzes
the source code based on the syntax of the language. If there are syntax errors, they are reported
and the compilation fails. Otherwise, the compiler translates the high-level code into machine code
for the computing system, generating a file known as an executable. During the second phase
(“run-time”), the executable is started on a computer (often by a user). We note that the compiled
executable is catered to one particular machine language; different versions of the executable must
be distributed for use on different computing platforms. Yet, the compiler and the original source
code are not required to run the executable on a given computer (they would only be needed by
the developer to regenerate a new executable if any change were to be made to the software).
The greatest advantage of the compilation model is execution speed. In essence, the more that
can be handled at compile-time, the fewer CPU cycles are spent at run-time. By performing the
full translation to machine code in advance, the execution of the software is streamlined so as to
perform only those computations that are a direct part of the software application. A secondary
advantage is that the executable can be distributed to customers as free-standing software (i.e.,
without need for an installed interpreter), and without exposing the original source code that was
used to generate it (although some companies choose to “open source” their software).
The primary advantage of an interpreted language is greater platform-independence, as the
primary source code can be widely distributed, with the platform-specific translations enacted by
a locally-installed interpreter. A secondary advantage of interpreters is that they often support
more dynamic language features, since analysis (or even modifcation) of code fragments can be
performed at run-time, and they can serve a dual role as a development platform, providing the
programmer with more robust feedback and interaction when problems arise.
age = 42
We happen to know that age is being assigned to an integer value in this case, yet we did not make
any syntactic declaration regarding the data type. That same identifier could later be reassigned
to the string 'Stone'. Types are not formally associated with the identifiers, but rather with the
1
Technically, a small amount of compilation takes place in Python when parsing the source code. That portion of
the translation results in a saved .pyc file that can be reused on a later execution to avoid re-parsing the code.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
2. PROGRAMMING LANGUAGE DESIGN Page 6
underlying objects (in effect, the value 42 “knows” that it is an integer). When identifiers are used
in expressions, the legitimacy depends upon the type of the underlying object. The expression
age + 1 will be valid when age is an integer yet invalid when age is a string. The method call
age.lower( ) will be legal when age is a string yet illegal when age is an integer.
In Python, these expressions are evaluated at run-time. When encountering an expression such
as age.lower( ), the interpreter determines whether the object currently associated with the name
age supports the syntax lower( ). If so, the expression is evaluated successfully; if not, a run-
time error occurs. The same principle of dynamic typing applies to the declaration of functions.
The formal parameters in the signature serve as placeholders for the required number of actual
parameters, yet there is no explicit statement of type; these identifiers are assigned to the actual
objects sent by the caller at run-time. Class definitions also rely on dynamic typing for the data
members, which are generally initialized in the constructor but never explicitly declared.
Python style of waiting until run-time to evaluate the legitimacy of commands is known as
“duck typing” (if it walks like a duck and quacks like a duck, then for all intents and purposes it is
a duck). This flexibility allows for various forms of polymorphism. For example, the sum function
accepts a parameter that is assumed to be a sequence of numbers. It works whether that sequence is
in the form of a list, a tuple, or a set, so long as the parameter is iterable. Yet Python also allows you
to query the type of an object at run-time, allowing for another form of polymorphism. A function
can vary its behavior based upon the type of an actual parameter. For example in Chapter 6.2 of
our book we provided a Point. mul implementation that specialized the multiplication semantics
depending upon whether the actual parameter was a scalar value or another point.
C++ is a statically-typed language. An explicit type declaration is required for every identifier.
The following demonstrates a type declaration followed by an assignment.
int age;
age = 42;
The first line is a declaration that establishes the identifier age as an integer value in the current
scope, and the second line is an assignment statement that sets the value of the variable (it is also
possible to initialize the value as part of a declaration statement; see Section 4.2 for details). Type
declarations appear in many contexts, making explicit the type of parameters and return values for
functions, and the type of data members stored by an instance of a class.
The reason for requiring programmers to make such declarations is that it allows for significantly
more work to be done at compile-time rather than run-time. For example the legality of the
subsequent assignment age = 42 is apparent at compile-time based upon knowledge of the data
type. In similar spirit, if a programmer attempts to send a string to a function that expects
a floating-point number, as in sqrt("Hello"), this error can be detected at compile-time. Type
declarations also help the system in better managing the use of memory.
The choice between dynamically versus statically-typed languages is often (though not always)
paired with the choice between interpreted and compiled languages. The primary advantage of
static typing is the earlier detection of errors; this early detection is more significant with a compiled
language, for which there is a distinction between compile-time errors and run-time errors. Even if
static typing is used in a purely interpreted language, those errors will not arise until the program is
executed. The primary advantages of dynamic typing are the reduced syntactical burden associated
with explicit declarations together with more direct support for polymorphism.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
3. A FIRST GLANCE AT C++ Page 7
Python C++
Figure 1: Programs for computing a greatest common divisor, as written in Python and C++.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
3. A FIRST GLANCE AT C++ Page 8
For the most part, the use of whitespace is irrelevant in C++. Although our sample code is
spaced with one command per line and with indentation to highlight the block structure, these are
not formal requirements of the language syntax. For example, the definition of the gcd function
could technically be expressed in a single line as follows:
To the compiler, this is the same definition as our original. Of course, to a human reader this
version is nearly incomprehensible. So as you transition from Python, we ask that you continue
using whitespace to make your source code legible.
This first example demonstrates a few other superficial differences. C++ requires the boolean
condition for a while loop to be expressed within parentheses (see line 8). We do not need to do
so in Python (see line 4), although parentheses can be used optionally. We also see a difference
in the symbols used when providing inlined comments. In Python, the # character is used to
designate the remainder of the line as a comment. Two different commenting styles are allowed in
C++. An inlined comment is supported using the // pattern, as seen at line 9. Another style is
demonstrated on lines 5 and 6, starting with the /* pattern, and ending with the */ pattern. This
style is particularly convenient as it can span multiple lines of source code.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
3. A FIRST GLANCE AT C++ Page 9
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
4. DATA TYPES AND OPERATORS Page 10
In C++, source code must first be compiled. A popular compiler is distributed by gnu.org and
typically installed on a system as a program named g++. If our source code were saved in a file
named gcd.cpp, the compiler could be invoked from the operating system with the command,
g ++ -o gcd gcd . cpp
The compiler will report any syntax errors that it finds. If all goes well it produces a new file named
gcd (or gcd.exe with the Windows operating system) that is an executable. It can be started on
the computer just as you would start any other executable. It is also possible to compile C++ code
using an integrated development environment (akin to Python’s IDLE). An IDE typically relies
upon the same underlying compiler, but provides more interactive control of the process.
The flow of control for the execution of a C++ program begins with an implicit call to the
function named main. We see this function definition starting at line 16 of our sample C++ source
code. The int return value for main is a technical requirement. The value is returned to the
operating system at the conclusion of the program. It is up to the operating system to interpret
that value, although zero historically indicates a successful execution while other values are used as
error codes (such a return value can be specified in Python as a parameter to the sys.exit function).
As a final comment, we note that the use of a main function in C++ is not quite the same as the
if name == '__main__' construct in Python. We discussed how the Python technique could
be used to provide a unit test that would be executed when the file is the primary source code,
but ignored when that module was imported from elsewhere. When a project is implemented with
multiple source files in C++, the compiler requires that precisely one of them has a main routine.
As a consequence, the gcd function as provided in our sample gcd.cpp file could not be used as
part of a larger project (because there would be conflicting definitions for main). With a more
typical C++ style, such a utility function would be provided in a file without a main function, and
imported as needed by other applications. We will discuss the development of multifile projects in
Section 12.
Boolean values
The logical bool type is supported by both languages, although the literals true and false are
uncapitalized in C++ while capitalized in Python. In both languages, boolean values are stored
internally as integers, with false represented using value 0, and true represented as 1.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
4. DATA TYPES AND OPERATORS Page 11
unsigned type would have range from 0 to 2b − 1. The greater range of positive numbers can be
used for contexts when a value cannot be negative (such as when describing the size of a container).
C++ also supports two different floating-point types, float and double, with a double histori-
cally represented using twice as many bits as a float. In C++, the double is most commonly used
and akin to what is named float in Python.
Finally, we note that Python’s long type serves a completely different purpose, representing
integers with unlimited magnitude. There is no such standard type in C++ (although some C++
packages for arbitrary-precision integers are distributed independently).
Character strings
C++ supports two different types for representing text. The char type provides an efficient repre-
sentation of a single character of text, while the string class serves a purpose similar to Python’s
str class, representing a sequence of characters (which may happen to be an empty string or a
single-character string). To distinguish between a char and a one-character string, a string literal
must be designated using double quote marks (as in "a"). The use of single quotes is reserved for a
char literal (as in 'a'). An attempt to misuse the single-quote syntax, as in 'impossible', results
in a compile-time error.
From a technical point of view, the string class is not a built-in type; it must be included from
among the standard C++ libraries. Although the formal methods of the C++ string class are
not the same as the Python str class, many behaviors are common. However, in contrast with
Python’s immutable str class, a C++ string is mutable. So the expression s[index] can be used
to access a particular character, or to alter that character with an assignment s[index] = newChar.
There is a similar discrepancy between the C++ syntax s.append(t), which mutates instance s by
appending the contents of string t, and the syntax s+t, which produces a concatenation as a third
string, leaving the two original strings unchanged. A summary of the most commonly used string
operations is given in Figures 3 and 4, with Figure 3 describing the nonmutating behaviors and
Figure 4 describing the mutating behaviors.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
4. DATA TYPES AND OPERATORS Page 12
Syntax Semantics
s.size( ) Either form returns the number of characters in string s.
s.length( )
s.empty( ) Returns true if s is an empty string, false otherwise.
s[index] Returns the character of string s at the given index
(unpredictable when index is out of range).
s.at(index) Returns the character of string s at the given index
(throws exception when index is out of range).
s == t Returns true if strings s and t have same contents, false otherwise.
s<t Returns true if s is lexicographical less than t, false otherwise.
s.compare(t) Returns a negative value if string s is lexicographical less than string t, zero if
equal, and a positive value if s is greater than t.
s.find(pattern) Returns the least index (greater than or equal to index pos, if given), at which
s.find(pattern, pos) pattern begins; returns string::npos if not found.
s.rfind(pattern) Returns the greatest index (less than or equal to index pos, if given) at which
s.rfind(pattern, pos) pattern begins; returns string::npos if not found.
s.find first of(charset) Returns the least index (greater than or equal to index pos, if given) at which a
s.find first of(charset, pos) character of the indicated string charset is found; returns string::npos if not found.
s.find last of(charset) Returns the greatest index (less than or equal to index pos, if given) at which a
s.find last of(charset, pos) character of the indicated string charset is found; returns string::npos if not found.
s+t Returns a concatenation of strings s and t.
s.substr(start) Returns the substring from index start through the end.
s.substr(start, num) Returns the substring from index start, continuing num characters.
s.c str( ) Returns a C-style character array representing the same sequence of characters as s.
Syntax Semantics
s[index] = newChar Mutates string s by changing the character at the given index to the new character
(unpredictable when index is out of range).
s.append(t) Mutates string s by appending the characters of string t.
s += t Same as s.append(t).
s.insert(index, t) Inserts copy of string t into string s starting at the given index.
s.insert(index, num, c) Inserts num copies of character c into string s starting at the given index.
s.erase(start) Removes all characters from index start to the end.
s.erase(start, num) Removes num characters, starting at given index.
s.replace(index, num, t) Replace num characters of current string, starting at given index, with the first num
characters of t.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
4. DATA TYPES AND OPERATORS Page 13
Arrays
The standard structure for storing a mutable sequence of values in Python is the list class. This
class provides many convenient behaviors for processing the sequence, and it provides support for
seemlessly expanding the size of the sequence as needed. The standard C++ libraries, which we
will discuss further in Section 10, includes a vector class with similar properties.
However, C++ also supports a more low-level sequence, known as an array, which has its
origin in the C programming language (in fact all of the built-in types for C++ were defined for
C). An array is a contiguous chunk of memory used to store a data sequence. What makes an array
different from a structure such as a Python list is that the size of the array must be fixed when the
array is constructed and that the contents of the array must have the same data type (because the
values are stored directly in the array, rather than referentially). As with Python, C++ arrays are
zero-indexed and rely on the syntax of square brackets for indexing. For example, if measurements
is a variable representing an array of double values, then the expression measurements[7] is used to
access the double at index 7 of the sequence (that is, the eighth entry).
Finally, we note that in the earlier C language, which does not have a string class, a character
sequence is represented directly as an array of char values. In C++, the string class relies upon an
underlying character array for storage, but provides more robust support for convenient operations.
That said, we note that the c str( ) method of the string class produces a C-style character array
for a string instance, as is sometimes needed when working with legacy code.
int r;
It is possible to declare several variables of the same type in a single declaration. For example,
line 17 of that same program declared the variables a and b within the main function as follows,
int a,b;
A declaration alerts the C++ compiler as to the type of data that will be stored by the named
variable. This allows it to verify subsequent syntax at compile-time. Knowledge of the data type
also allows the system to reserve the appropriate amount of memory for representing an instance
of the type. It is important to note that the system does not necessarily initialize the variable. For
primitive types, the initial value is indeterminate, as it is based upon the previous setting of the
newly reserved bits of memory. While it could be initialized using a separate assignment statement,
we prefer the use of what we term the constructor syntax when the initial value is known at the
time of the declaration. An example of the syntax is
int age(42);
This declares a new integer variable age, yet initializes it to have the indicated value 42. When
using this syntax, we can use any valid expression to designate the initial value, such as,
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
4. DATA TYPES AND OPERATORS Page 14
Although we noted that declared variables of primitive types are not automatically initial-
ized, a declared variable of a class type will be initialized by automatically invoking a form of that
class’s constructor. Using the string class as an example, consider the following three declarations.
The first version invokes what is known as the default constructor for the class. This is a zero-
parameter version of the constructor, which in the case of strings produces an empty string. The sec-
ond of these lines formally invokes the constructor with a single parameter, the character sequence
"Hello". The third example invokes a two-parameter form of the string constructor, resulting in
a sequence of n consecutive copies of a given character.
Arrays
If the desired size of an array is known at compile time, it can be declared using a syntax such as
double measurements[300];
This declares the name measurements to be an array of doubles and causes the system to allocate
memory for storing precisely 300 entries. However, the values of the individual entries are indeter-
minate (just as when declaring a single double). Typically, the declaration of such an array might
be followed by a loop to initialize the entries to meaningful values. Yet it is possible to initialize
values of an array as part of the declaration, using a syntax such as the following.
int daysInMonth[ ] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
Notice that we did not explicitly give the size of the array between the square brackets; it will be
implicitly set based on the number of indicated entries. If we had given an explicit size for the
array that was larger than the indicated initialization list, the beginning of the array will be filled
in using the specified values and the rest is set to zero. In the special case of an array of characters,
it is possible to initialize the array using a literal, as follows.
As a technicality, this becomes an array with size 6 because all C-style character sequences are
explicitly terminated with an extra zero-value to designate the end of the sequence.
In the case of class types, the declaration of an array causes not only the allocation of memory
but the default initialization of each individual entry. For example, in the following declaration all
entries of the array are guaranteed to be initialized to empty strings.
string messages[20];
Thus far, our declarations have assumed that the size of an array is known at compile time. An
approach for dynamically allocating arrays at run-time will be presented in Section 8.5.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
4. DATA TYPES AND OPERATORS Page 15
Mutability
Python and C++ have very different approaches to the concept of mutability. For built-in types,
Python makes a clear distinction between which are mutable and which are immutable. For exam-
ple, it offers a list class for representing mutable sequences, and a tuple class for representing
immutable sequences (a similar discrepancy exists between the set and frozenset classes).
C++ takes a different approach. All types are assumed to be mutable, but particular instances
can be designated as immutable by the programmer. Furthermore, the immutability is strictly
enforced by the compiler. Syntactically, the immutability is declared using the keyword const in
specific contexts. For example, a new variable can be declared as a constant, as in
With this declaration, any subsequent attempt to change that value results in a compile-time
error (e.g., age++). While a programmer may declare a non-const variable yet leave its value
unchanged, the explicit declaration of const in this context allows the compiler to better optimize
the program. It also serves as a meaningful label for another programmer who is reading the code.
More significant uses of the const keyword arise when describing the treatment of parameters in
function signatures, or the effect of method calls upon the state of an object. We will discuss those
uses in later sections.
4.3 Operators
Figure 5 provides a comparison between the operators supported by Python and C++. The oper-
ators are largely the same between the two languages, but there are some notable discrepancies.
Numeric types
For numeric values, Python differentiates between true division (i.e., /), integer division
(i.e., //), and modular arithmetic (i.e., %), as originally discussed in Chapter 2.4 of our book.
C++ supports operators / and %, but not //; in fact, we already saw that symbol used to des-
ignate inline comments in C++. The semantics of the C++ / operator depends upon the type
of operands. When both operands are integral types, the result is the integer quotient; if one or
both of the operands are floating-point types, true division is performed. To get true division with
integral types, one of the operands must be explicitly cast to a float (see Section 4.4).
Both Python and C++ support an operator-with-assignment shorthand for most binary opera-
tors, as with x += 5 as a shorthand for x = x + 5. Yet, C++ supports an additional ++ operator
for the common task of incrementing a number by one. In fact, there are two distinct usages known
as pre-increment (e.g., ++x) and post-increment (e.g., x++). Both of these add one to the
value of x, but they can be used differently in the context of a larger expression. For example,
if indexing a sequence, the expression groceries[i++] retrieves the entry based upon the original
index i, yet subsequently increments that index. In contrast, the syntax groceries[++i] causes the
value of the index to be incremented before accessing the associated entry of the sequence. Similar
support exists for decrementing a value by one with the −− operator. The pre- and post- versions
of these operators are valuable tools for an experienced programmer, but their use leads to subtle
code and potential mistakes. We recommend that they be used in isolated contexts until mastered.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
4. DATA TYPES AND OPERATORS Page 16
Arithmetic Operators
−a −a (unary) negation
a+b a+b addition
a−b a−b subtraction
a*b a*b multiplication
⊲ a ** b exponentiation
a/b a/b standard division (depends on type)
⊲ a // b integer division
a%b a%b modulus (remainder)
⊲ ++a pre-increment operator
⊲ a++ post-increment operator
⊲ −−a pre-decrement operator
⊲ a−− post-decrement operator
Boolean Operators
⊲ and && logical and
⊲ or || logical or
⊲ not ! logical negation
⊲ a if cond else b cond ? a : b conditional expression
Comparison Operators
a<b a<b less than
a <= b a <= b less than or equal to
a>b a>b greater than
a >= b a >= b greater than or equal to
a == b a == b equal
⊲ a<b<c a < b && b < c chained comparison
Bitwise Operators
˜a ˜a bitwise complement
a&b a&b bitwise and
a|b a|b bitwise or
aˆb aˆb bitwise XOR
a << b a << b bitwise left shift
a >> b a >> b bitwise right shift
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
4. DATA TYPES AND OPERATORS Page 17
Boolean operators
While Python uses the words and, or, and not for the basic logical operators, C++ relies on
the respective symbols &&, ||, and ! (although some C++ compilers now support such named
operators). The history of those symbols dates back to the C language. It is important not to
confuse the logical operators && and || with the bitwise operators & and |.
Another pitfall for Python programmers converting to C++ is use of a syntax such as a < b < c
for numeric types. In Python, operators can be chained in such an expression that is true if both
a < b and b < c. In C++, this logic must be expressed as a < b && b < c. The more significant
problem is that a < b < c is legal C++ syntax, yet with unexpected semantics. This is parsed as
(a < b) < c. The boolean expression (a < b) evaluates to either false or true. However, that result
will be coerced into the integer 0 or 1 for the comparison with c. The result of the full expression
then depends upon whether c is greater than that 0 or 1 value.
Class types
By default, most operators cannot be used with instances of a class type. However C++ allows
the author of a class to provide specialized semantics for operators, when desired. As an example,
the string class overloads the + operator so that the expression s + t results in a new string that
is the concatenation of existing strings s and t. Yet the expression s * 3 is illegal when s is a string,
because no such behavior is defined for the C++ string class (in contrast to Python, which supports
such an operator for the str class). The syntax for defining overloaded operators in C++ will be
introduced in Section 7.2, when we discuss the robust version of our user-defined Point class.
int a(5);
double b;
b = a; // sets b to 5.0
The final command causes b to store a floating-point representation of the value 5.0 rather than the
integer representation. This is because variable b was explicitly designated as having type double.
We can also assign a double value to an int variable, but such an implicit cast may cause the loss
of information, as any fractional portion of that value will be truncated.
int a;
double b(2.67);
a = b; // sets a to 2
There are other scenarios in which C++ implicitly converts between types that would not normally
be considered compatible. Some compilers will issue a warning to draw attention to such cases, but
there is no guarantee.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
5. CONTROL STRUCTURES Page 18
On a related note, there are times when we want to force a type conversion that would not
otherwise be performed. Such an explicit cast is done using a syntax similar to Python, where
the name of the target type is used as if a function.
The first assignment to c results in 1.0 because the coercion to a double is not performed until
after the integer division a/b is performed. In the second example, the explicit conversion of a’s
value to a double causes a true division to be performed (with b implicitly coerced). However, we
cannot use this casting syntax to perform all type conversions. For example, we cannot safely mimic
Python’s approach for converting a number to a string, as with str(17), or to convert a string to
the corresponding number, as with int("17"). Unfortunately, conversions back and forth between
strings require more advanced techniques. We will discuss one such approach in Section 6.6 using
an object known as a stringstream.
5 Control Structures
5.1 While Loops
We demonstrated an example of a while loop as part of our first example in Section 3. The logic is
similar to Python, but with superficial differences in syntax. Most notably, parentheses are required
around the boolean condition in C++. In that first example, curly braces were used to delimit the
commands that comprise the body of the loop. Technically those braces are only needed when the
body uses two or more distinct statements. In the absence of braces, the next single command is
assumed to be the body.
C++ also supports a do-while syntax that can be a convenient remedy to the “loop-and-a-half”
problem, as seen with while loops on page 165 of our book. Here is a similar C++ code-fragment
for requesting a number between 1 and 10, repeating until receiving a valid choice:
int number;
do {
cout << "Enter a number from 1 to 10: ";
cin >> number;
} while (number < 1 || number > 10);
Please note that we have not properly handled the exceptional case when a noninteger is entered.
5.2 Conditionals
A basic if statement is quite similar in style, again requiring parentheses around the boolean con-
dition and curly braces around a compound body. As a simple example, here is a construct to set
a number to its absolute value.
if (x < 0)
x = −x;
Notice that we did not need braces for a body with one command.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
5. CONTROL STRUCTURES Page 19
C++ does not use the keyword elif for nesting conditionals, but it is possible to nest a new if
statement within the body of an else clause. Furthermore, a conditional construct is treated syn-
tactically as a single command, so nesting does not require excessive braces. Our first example of a
nested conditional in Python was given on page 144 of Chapter 4.4.2 of our book. That code could
be written in C++ to mimic Python’s indentation as follows (assuming groceries is an adequate
container):
For class types, C++ allows the author of the class to determine whether values of that type
can be coerced into a bool (or any other primitive type, for that matter). So it would be possible
to define a container type that mimics Python’s treatment, with empty containers treated as false
and nonempty containers treated as true. We should note, however, that neither the standard
container classes or strings support such coercion in C++.
The implicit conversion from numbers to booleans in C++, together with the treatment of an
assignment statement, is to blame for a common pitfall exemplified by the falling errant code.
double gpa;
cout << "Enter your gpa: ";
cin >> gpa;
if (gpa = 4.0)
cout << "Wow!" << endl;
Do you see the problem? The mistake is the use of the assignment operator (gpa = 4.0) rather
than the equivalence operator (gpa == 4.0). The above code is syntactically valid in C++, but
it does not behave as you might expect. Rather than comparing the inputted value of gpa, that
statement reassigns variable gpa the value of 4.0, essentially overwriting the response given by the
user. Furthermore, C++ considers the value of the assignment expression in the larger context to
be the newly assigned value, which itself is implicitly coerced to a boolean value. Thus, no matter
what the user enters, the gpa is reset to 4.0, coerced to true, and the conditional body is entered.
It is natural to ask why the designers of C and C++ allow such a behavior. One reason is to
support the chaining of assignments using the following syntax.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
5. CONTROL STRUCTURES Page 20
C++ treats this as two separate operations, evaluated from right-to-left, as if written as
a = (b = 4.0);
So b is assigned the value 4.0, and then the new value of b serves as the result of the parenthesized
subexpression. This allows a to be subsequently assigned.
To avoid such a common pitfall, Python disallows use of an assignment statement in the context
of a conditional. Code such as if (gpa = 4.0) results in a syntax error. Python support the chaining
syntax a = b = 4.0 using a different mechanism, just as it does with inequalities like a < b < c.
Within the parentheses of the for loop are three distinct components, each separated by a semicolon.
The first is an initialization step that is performed once, before the loop begins. The second portion
is a loop condition that is treated just as a loop condition for a while loop; the condition is tested
before each iteration, with the loop continuing while true. Finally we give an update statement that
is performed automatically at the end of each completed iteration. The for loop syntax is just a
convenient alternative to a while loop that better highlights the logic in some cases. The previous
example is essentially identical in behavior to the following version:
The for loop is far more general. It is possible to express multiple initialization or update steps
in a for loop. This is done by using commas to separate the individual statements (as opposed to
the semicolon that delimits the three different components of the syntax). For example, the sum
of the values from 1 to 10 could be computed by maintaining two different variables as follows:
It is also possible to omit the initialization or update steps, so long as the semicolons remain as
separators. The loop condition is the only strictly required component.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
6. INPUT AND OUTPUT Page 21
void countdown( ) {
for (int count = 10; count > 0; count−−)
cout << count << endl;
}
We used an alternative version of this function in Chapter 5.2.2 of our book, to demonstrate the
use of optional parameters in Python. The same technique can be used in C++, with the syntax
This signature will support a calling syntax such as countdown(5,2) to go from 5 to 2, countdown(8)
to go from 8 to 1, or countdown( ) to go from 10 to 1. There is a technical distinction between Python
and C++ regarding the instantiation of a default parameter. In Python, the default parameter
value is instantiated once upon declaration of the function, and then used when the function is
called. This is occasionally significant, especially when using mutable objects as default values. In
C++, default parameter values are (re)instantiated as needed with each call to the function.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
6. INPUT AND OUTPUT Page 22
#include <iostream>
using namespace std;
The first of these statements imports the iostream library (short for “input/output stream”). The
second brings the definitions into our default namespace. In addition to the basic class definitions,
this library defines two special instances for handling input to and from the standard console. cout
(short for “console output”) is an ostream instance used to print messages to the user, and cin
(short for “console input”) is an istream instance that reads input from the keyboard.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
6. INPUT AND OUTPUT Page 23
Python C++
Figure 7: Demonstration of console output in Python and C++. We assume that variables first
and last have previously been defined as strings, and that total is an integer.
The use of the % sign in this context is designed to mimic a long-standing routine named printf,
which is part of the C programming language. Since C++ is a direct descendant of C, that function
is available through a library, but it is not the recommended approach for C++ because it does
not inherently support non-primitive data types (e.g., the C++ string class). Instead, formatted
output can be generated through the output stream. Since data types are automatically converted
to strings, the above example can be written in C++ as
cout << team << ": ranked " << rank << " of " << total << " teams" << endl;
More effort is needed to control other aspects of the formatting, such as the precision for floating-
point values. As an example, assume that the variable pi holds the value 3.14159265. In Python,
the expression 'pi is %.3f'% pi produces the result 'pi is 3.142'. The closest equivalent to
the display of variable pi in C++ might be accomplished as
cout << "pi is " << fixed << setprecision(3) << pi << endl;
This command would result in the output pi is 3.142. The objects fixed and setprecision(3) are
known as manipulators and must be included from the <iomanip> library. When inserted into
the output stream with << they affect the format for subsequent values. In this particular case,
the fixed manipulator says to print floating-point numbers with trailing decimal digits even when
zero, and not to use scientific notation even for very large or very small values. The setprecision(3)
manipulator specifies the number of digits beyond the decimal point to be used in the fixed format.
The minimum width of displayed values and the justification within that width can be con-
trolled by additional manipulators. As an example, we might print an entry on a receipt as
cout << setw(10) << item << " " << setw(5) << quantity << endl;
This is equivalent to the Python command print '%10s %5d'% (item, quantity). If we execute this
command once with values pencil and 50, and then with values pen and 100, the output is aligned as:
pencil 50
pen 100
As is the case with Python, data in a fixed-width field will be right-justified by default. We can
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
6. INPUT AND OUTPUT Page 24
switch to left-justification in C++ by inserting the manipulator left into the stream. For example,
if we repeat the previous exercise using the command
cout << left << setw(10) << item << " "<< right << setw(5) << quantity << endl;
we get a result of
pencil 50
pen 100
It is worth noting that the C++ manipulators are different than Python’s formatting tools,
in that most of the manipulators we have demonstrated change the state of the output stream
object. For example, once the fixed manipulator has been inserted into a stream, all subsequent
numbers for that stream will be displayed in that format (unless a conflicting manipulator such as
scientific is subsequently inserted). In our most recent example, notice that we explicitly inserted
the manipulator right before outputting quantity to re-establish right-justification; otherwise, the
formatting would still be affected by the earlier insertion of left. However, there are some exceptions
to this rule. The setw manipulator, demonstrated above, only affects the next piece of displayed
output. In our example, the insertion of setw(10) affected the display of the string item, yet it did
not dictate a minimum width for the subsequent string " ".
In C++, keyboard input is managed through an input stream named cin (just as console output
is managed by cout). The behavior of the Python example can be replicated with a C++ function
named getline. Its calling syntax appears as
string person;
cout << "What is your name? "; // prompts the user (without a newline)
getline(cin, person); // stores result directly in variable 'person'
Both Python’s raw input and C++’s getline read and remove all characters from the input stream
up to and including the next newline, yet with the newline itself omitted from the resulting string.
However, direct use of getline is atypical in C++. Instead of reading a line at a time, program-
mers can use the >> operator for extracting individual pieces of formatted data from an input
stream. As an example, consider the task of reading a single integer from the user. In Python, we
have to first get the raw string and separately compute the integer that those characters represent.
Consider, for example, number = int(raw input('Enter a number from 1 to 10: ')). In C++,
the corresponding code fragment might appear as follows.
int number;
cout << "Enter a number from 1 to 10: "; // prompt without newline
cin >> number; // read an integer from the user
The >> operator extracts data from the stream and stores it in the indicated variable. The static
typing of C++ is advantageous in this context. Because number was already designated as an
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
6. INPUT AND OUTPUT Page 25
integer, the input characters are automatically converted to the corresponding integer value.
Much as the << operator can be chained in order to insert several pieces of data into an output
stream in a single command, it is possible to chain the >> operator can be chained to read in sev-
eral pieces of data. For example, here is a code fragment that asks the user to enter two numbers
and computes their sum.
int a, b;
cout << "Enter two integers: ";
cin >> a >> b;
cout << "Their sum is " << a + b << "." << endl;
The third line has the effect of inputting two separate integers, the first of which is stored in
variable a and the second in b. Formally, the >> operator works by skipping any whitespaces
(e.g., spaces or newlines) that reside at the front of the stream, and then interpreting the first
non-whitespace token as an integer. The second occurrence of the >> operator on that line causes
intermediate whitespace to be removed from the stream and the next token to be interpreted as an
integer. Any subsequent characters (including further whitespace) remain on the stream.
When using the stream operator, it does not matter whether the user enters the integers on
the same line or different lines, as intermediate newlines and spaces are treated similarly. It also
does not matter whether the programmer uses the chained syntax cin >> a >> b or two separate
commands cin >> a followed by cin >> b. Both forms are valid, regardless of the user’s spacing.
Note that this management of input streams is quite different from our usage of the raw input
command in Python, which reads a single line. If we expect the user to enter two integers on a
single line of input, in Python we have to read the line, then split it into two pieces based on white
space, and then attempt to convert each of those pieces to the corresponding integer. A Python
version of such a program is given as the solution to Practice 2.31 in the book.
To emphasize the different treatment of whitespace, we reconsider our initial example of query-
ing a person’s name. Our original C++ solution used the getline function, to more accurately
mirror Python’s style. As an alternative, we could write the C++ code as
string person;
cout << "What is your name? "; // prompt the user (without a newline)
cin >> person; // input the response
Yet this code does not strictly have the same behavior. To highlight the difference, consider the
following user session.
After executing the C++ code, the variable person will be assigned the string "Guido", while the
subsequent characters (" van Rossum\n") remain on the stream, as it extracted starting with the
first non-whitespace character and stopping prior to the next subsequent whitespace. If the use of
newlines is significant to the context, a programmer should use a function like getline. However,
careful consideration of the whitespace treatment is important when interspersing calls to getline
with use of the >> operator. A call to getline removes the ending newline from the stream, but
use of the extraction operator reads a token while leaving subsequent whitespace on the stream.
Consider the following code fragment.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
6. INPUT AND OUTPUT Page 26
int age;
string food;
cout << "How old are you? ";
cin >> age;
cout << "What would you like to eat? ";
getline(cin, food);
The problem is that after executing the above code, the variable food will be set to the empty
string "". The first extraction properly read the age as 42, but the newline character that the user
entered after those characters remains on the stream. Even though the user had entered additional
input, the call to getline first encounters an apparent empty line because of the remaining newline
from the first response. This can be remedied by intentionally reading the blank line before reading
the food. A more robust approach relies on use of an ignore function supported by input streams.
Another approach for handling line-based input is to always rely on getline to read a single line into
a string, and then to use a class named stringstream that supports extracting formatted data from
a string; we will discuss the stringstream class in Section 6.6.
Finally, we address the issue of what happens when something goes wrong while attempting to
read input. For example, the user might enter the characters hello when an integer was expected.
Other unexpected situations may cause failure of the input stream, such as it being closed when a
user types cntr-D into the console. In Python, such errors result in a formal exception being thrown.
For example, a ValueError is reported when trying to get an integer value from an improperly
formatted string, and an IOError is reported if the raw input call fails. In C++, an attempt at
extracting data from a stream does not throw an exception by default. Instead, the flow of control
continues, but with the value of the inputted variable left indeterminate. To support more robust
behaviors, the streams have methods that allow a programmer to check various aspects of their
state. For example, if a user types non-numeric characters when an integer is expected, the stream’s
“fail bit” is set to true internally and can be tested with a call to cin.fail( ). That bit remains set
until explicitly cleared by the programmer. Other state bits can be queried to determine whether
the “end of file” has been reached on a stream, or if some other bad state has been reached.
Page 182 of our book gives a more robust Python fragment for reading a valid integer from 1 to
10. For comparison, Figure 8 of this document presents a similar code fragment in C++.
ifstream mydata("scores.txt");
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
6. INPUT AND OUTPUT Page 27
1 number = 0;
2 while (number < 1 || number > 10) {
3 cout << "Enter a number from 1 to 10: ";
4 cin >> number;
5 if (cin.fail( )) {
6 cout << "That is not a valid integer." << endl;
7 cin.clear( ); // clear the failed state
8 cin.ignore(std::numeric limits<int>::max( ), '\n'); // remove errant characters from line
9 } else if (cin.eof( )) {
10 cout << "Reached the end of the input stream" << endl;
11 cout << "We will choose for you." << endl;
12 number = 7;
13 } else if (cin.bad( )) {
14 cout << "The input stream had fatal failure" << endl;
15 cout << "We will choose for you." << endl;
16 number = 7;
17 } else if (number < 1 || number > 10) {
18 cout << "Your number must be from 1 to 10" << endl;
19 }
20 }
If the filename is not known in advance, the stream can be initially declared without a filename
and opened as a later operation. The open method accepts the filename as a parameter but, for
historical reasons, requires that the name be expressed as a C-style string. Here is an example usage:
ifstream mydata;
string filename;
cout << "What file? ";
cin >> filename;
mydata.open(filename.c str( )); // parameter to open must be a C−style string
The same techniques can be used with an ofstream instance for writing to a file. By default,
opening an ofstream causes the target file to be overwritten by a new file, just as with Python’s
open('scores.txt', 'w'). If you want to append to the end of an existing file, as with Python’s
open('scores.txt', 'a'), the C++ command is
The more general fstream class can be used to simultaneously manage input and output from
the same file, although coordinating such manipulations takes more care.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
7. CLASSES IN C++ Page 28
corresponding characters used to display that number (e.g., "42"). But what if we want to compute
the string representation of an integer, not for immediate output, but perhaps to save in a string
variable? In Python, the syntax displayedAge = str(age) produces such a string.
We avoided this issue at the end of Section 4, because the conversion is not quite as direct in
C++. Instead, we can use the stringstream class, included from the <sstream> library. This class
allows us to use the stream operators to insert formatted data into a string or to extract formatted
data from that string. For example, here is code that produces a string based upon an integer value.
int age(42);
string displayedAge;
stringstream ss;
ss << age; // insert the integer representation into the stream
ss >> displayedAge; // extract the resulting string from the stream
String streams can also be used to convert in the other direction, starting with a string and parsing
it to extract pieces of data. For example, in Section 6.4 we discussed differences between tokenized
input in C++ versus Python’s style of using raw input to read a line and then subsequently splitting
the tokens on that line. In C++, we can emulate such a style by using getline to read a single line
as a string, and then using a stringstream to manage subsequent extractions of formatted data.
7 Classes in C++
Classes provide the same abstraction for storing and manipulating information in C++ as they
do in Python. In Section 7.1 we discuss classes from the perspective of a user of a class, with
rather similar syntax between Python and C++, other than the need for a clear type declaration
in C++. In Section 7.2 we introduce the syntax for defining classes in C++. We discuss the
additional complexities that arise when defining a class in C++ due to the additional burden of
type declarations and other such specifications that assist in more rigorous compile-time checking.
In the first declaration, a new string instance is created using the default constructor of the class,
that is, a constructor that accepts zero parameters. In the case of strings, the default constructor
produces the empty string. The second syntax invokes a form of the string class constructor that
accepts a parameter to designate the initial contents of the new string.
As an aside, we wish to warn against a few potential mistakes in the declaration syntax. First,
when relying upon the default constructor, we did not use empty parentheses. Had we used paren-
theses, it would have looked like the following.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
7. CLASSES IN C++ Page 29
Although the intent may be to send zero parameters to the constructor, the C++ parser deems
this syntax as the signature for a function named s, taking zero parameters and returning a string
(that is, after all, how such a function’s signature would appear). As a second example, we wish to
emphasize that when parameters are specified in a declaration, they are included after the variable
name rather than after the class name. Compare the following illegal syntax with the original
correct syntax.
With that said, there is a scenario in which the parameters to a constructor appear after the
class name. This happens when a programmer wishes to construct an unnamed instance as part of
a larger expression, such as cout << string(20, '-'). We will see another such use in the coming
section involving the robust version of a Point class. A line of that code will appear as follows.
The syntax Point( ) in this expression causes the construction of a new Point instance based on the
default constructor and in this context, the explicit parentheses are necessary.
Once an instance of a class has been constructed, C++ uses typical object-oriented syntax such
as greeting.replace(1, 4, "ey") to call the replace method on the string identified as greeting.
Constructor
Line 7 of our code is the constructor, although the syntax requires some explanation. The line
begins with the name of the class itself (i.e., Point) followed by parentheses. The constructor is a
function, with this particular example accepting zero parameters. However, unlike other functions,
there is no designated return value in the signature (not even void).
The next piece of syntax is the colon followed by x(0), y(0). This is what is known as an
initializer list in C++. It is the preferred way to establish initial values for the attributes (we
are not allowed to express initial values on lines 3 and 4). Finally, we see the syntax { }. This is
technically the body of the constructor. Some classes use the constructor body to perform more
intricate initializations. In this case, having already initialized the two variables, there is nothing
else for us to do. So the { } serves syntactically as a placeholder for the function body (somewhat
like pass in Python).
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
7. CLASSES IN C++ Page 30
1 class Point {
2 private:
3 double x; // explicit declaration of data members
4 double y;
5
6 public:
7 Point( ) : x(0), y(0) { } // constructor
8
9 double getX( ) const { // accessor
10 return x;
11 }
12
13 void setX(double val) { // mutator
14 x = val;
15 }
16
17 double getY( ) const { // accessor
18 return y;
19 }
20
21 void setY(double val) { // mutator
22 y = val;
23 }
24
25 }; // end of Point class (semicolon is required)
Implicit self-reference
A careful reader will have already noticed another major distinction between the class definition in
C++ and the same class in Python. The self reference does not appear as a formal parameter nor is
it used when accessing members of the instance. Remember that we have explicitly declared x and
y to be attributes of a point. Because of this, the compiler recognizes those identifiers when used
within the body of our methods (for example at line 10). For those who miss the self-reference, it
is implicitly available in C++ yet with the name this. It can be useful, for example, when passing
the object as a parameter to an outside function. Technically, this is a pointer variable (a concept
that will be introduced in Section 8.3).
Access control
Another distinction in C++ is the use of the terms public and private within the class definition.
These relate to the issue of encapsulation. With Python, we addressed this issue in Chapter 7.6 of
our book, differentiating at the time between what we considered “public” versus “private” aspects
of a class design. Public aspects are those that we expect other programmers to rely upon, while
private ones are considered to be internal implementation details that are subject to change. Yet
Python does not strictly enforce this designation. Instead, we relied upon naming conventions,
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
7. CLASSES IN C++ Page 31
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
7. CLASSES IN C++ Page 32
1 class Point {
2 private:
3 double x;
4 double y;
5
6 public:
7 Point(double initialX=0.0, double initialY=0.0) : x(initialX), y(initialY) { }
8
9 double getX( ) const { return x; } // same as simple Point class
10 void setX(double val) { x = val; } // same as simple Point class
11 double getY( ) const { return y; } // same as simple Point class
12 void setY(double val) { y = val; } // same as simple Point class
13
14 void scale(double factor) {
15 x *= factor;
16 y *= factor;
17 }
18
19 double distance(Point other) const {
20 double dx = x − other. x;
21 double dy = y − other. y;
22 return sqrt(dx * dx + dy * dy); // sqrt imported from cmath library
23 }
24
25 void normalize( ) {
26 double mag = distance( Point( ) ); // measure distance to the origin
27 if (mag > 0)
28 scale(1/mag);
29 }
30
31 Point operator+(Point other) const {
32 return Point( x + other. x, y + other. y);
33 }
34
35 Point operator*(double factor) const {
36 return Point( x * factor, y * factor);
37 }
38
39 double operator*(Point other) const {
40 return x * other. x + y * other. y;
41 }
42 }; // end of Point class (semicolon is required)
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
7. CLASSES IN C++ Page 33
a Point and (coincidentally) returns a double. Providing two separate declarations of a method is
termed overloading the signature. Since all data is explicitly typed, C++ can determine which
of the two forms to invoke at compile-time, based on the actual parameters.
Line 42 ends our formal Point class declaration. However, we provide two supporting definitions
following that line. The first of those is used to support a syntax such as 3.25 * p. The earlier
definition of operator* from lines 35–37 supports the * operator when a Point instance is the left-
hand operand (e.g., p * 3.25). C++ does not allow the class definition of the right-hand operand
to directly impact the behavior. Yet if the left-hand operand’s type does not provide an adequate
definition (as with type double in the expression 3.25 * p), C++ looks for a free-standing operator*
function with a matching signature. So at lines 44–46, we provide a definition for how * should
behave when the first operand is a double and the second is a Point. Notice that both operands
appear as formal parameters in this signature since we are no longer within the context of a class
definition. The body of our method uses the same simple trick as in our Python implementation,
commuting the order so that the point becomes the left-hand operand (thereby, invoking our
previously defined version). As an aside, notice that we did not have to have such an additional
definition for operator+ since both operands (and thus the left-hand one) are Point instances for
addition.
Finally, lines 48–51 are used to produce a text representation of a point when inserted into an
output stream. A typical syntax for such a behavior is cout << p. Again, we define this behavior
outside of the context of the Point class because the left-hand operand is the output stream, and
because we are not the authors of the ostream class. In our implementation, line 49 inserts our
desired output representation into the given output stream. We use the formal parameter out
rather than cout so that a user can apply this behavior to any output stream instance. The
declared return type on line 48 and the return statement at line 50 are technically required to
allow for multiple << operations to be chained in a single expression. For example, the syntax
cout << p << " is good" is evaluated as (cout << p) << " is good", with the result of the
first evaluation being an output stream used in the second operation. The use of the & symbol
twice on line 48 (for both the return type and the first parameter type) is a technicality that we
will address in Section 8.2.
7.3 Inheritance
In Chapter 9 of our book, we provided several examples of the use of inheritance in Python. We
will show two of those examples, translated to C++. First we define a DeluxeTV class modeled
closely after the version in Figure 9.2 of the book which used a SortedSet. Although we omit the
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
7. CLASSES IN C++ Page 34
presumed definition for a basic Television class, our complete code for the DeluxeTV class is given
in Figure 12. The use of inheritance is originally indicated at line 1 by following the declaration
of the new class with a colon and then the expression public Television. With that designation2 ,
our DeluxeTV class immediate inherits all attributes (e.g., powerOn, channel) and all methods (e.g.,
setChannel) from the parent. What remains is for us to define additional attributes or to provide
new or updated implementations for methods that we want supported.
At line 3, we declare a new attribute to manage the set3 of favorite channel numbers. We wish
to draw particular attention to the use of the word protected at line 2. Until now, we have used
two forms of access control: public and private. Members that are public can be accessed by code
outside of the class definition, while members that are private can only be accessed from within the
original class definition. The purpose of privacy is to encapsulate internal implementation details
that should not be relied upon by others. Yet with the use of inheritance, there is need for a third
level of access. When one class inherits from another, the question arises as to whether code for
the child class should have access to members inherited from the parent. This is determined by the
access control designated by the parent. A child class cannot directly access any members declared
as private by the parent. However, the child is granted access to members designated as protected
by the parent.
2
For the sake of simplicity, we will not discuss the precise significance of the term public on line 1.
3
We will discuss the set class and other containers in Section 10.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
7. CLASSES IN C++ Page 35
In this particular setting, the important point is not actually our use of protected at line 2.
What matters to us is how the original attributes of the Television class were defined. For our
DeluxeTV code to work, it must be that television attributes were originally declared as
protected:
bool powerOn;
int channel;
...
If those had been declared as private, we would not have the necessary access to implement our
DeluxeTV. The original designer of the television may not have known that we would come along
and want to inherit from it, but an experienced C++ programmer will consider this possibility when
designing a class. In our DeluxeTV definition, the declaration of attribute favorites as protected
is not for our own benefit, but to leave open the possibility that someone else may one day want
to design a SuperDeluxeTV that improves upon our model. As an alternative to protected data, a
parent can provide protected member functions to encapsulate the private state.
The second aspect of our example we wish to discuss is the definition of our constructor, at
lines 6–9. In our Python version, the new constructor begins with an explicit call to the parent
constructor, using the syntax, Television. init (self). That was used to establish the default settings
for all of the inherited attributes. In C++, we can invoke the parent constructor as part of the
initializer list using the syntax Television( ) at line 7. This calls the parent constructor without
sending any explicit parameters. To be honest, in this particular example, line 7 is superfluous. If
we do not explicitly call the parent constructor, C++ will do so implicitly. However, an explicit call
is necessary when parameters are to be sent to the parent constructor (as in our second example).
In this example, our default initialization of favorites at line 8 is also superfluous.
Lines 11–23 of our DeluxeTV code provides three new behaviors. The precise details of those
methods depend on knowledge of the set class; as such, we will revisit some of this code in Section 10.
Our purpose for the moment is to demonstrate the use of inheritance. We draw attention to the
fact that we are able to access the inherited attributes, powerOn and channel, as well as our new
attribute favorites when implementing the methods. We also call the inherited method setChannel.
A Square class
As a second example of inheritance, Figure 13 provides a C++ rendition of our original Square class
from Chapter 9.4.2 of our book. The Square inherits from a presumed Rectangle class. We do not
introduce any new attributes for this class, so our only responsibility for the constructor is to ensure
that the inherited attributes are properly initialized. To this end, we invoke the parent constructor
at line 4. In this case, we need the explicit call in order to pass the appropriate dimensions and
center. Had we not done so, an implicit call would have been made to the default version of the
rectangle constructor, leading to incorrect semantics for our square.
The remainder of the definition is meant to provide new getSize and setSize methods, while
also overriding the existing setHeight and setWidth methods so that a change to either dimension
affects both. We use the same approach as our Python version. We override the existing methods
at lines 7 and 8, changing their behaviors to call our new setSize method. Our setSize method
then relies upon the parent versions of the overridden setWidth and setHeight methods to enact the
individual changes to those values. The expression Rectangle:: before the method names at lines 11
and 12 is a scope resolution, indicating our desire to invoke the definitions of those behaviors
from the parent Rectangle class, rather than the corresponding methods of the Square class.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 36
def isOrigin(pt):
return pt.getX( ) == 0 and pt.getY( ) == 0
Now assume that the caller invokes this function as isOrigin(bldg), where bldg is an identifier that
references a Point instance. Figure 14 diagrams the underlying configuration. This scenario is
the precise result of the system performing an implicit assignment pt = bldg, setting the formal
parameter to the actual parameter.
bldg pt
Point
x = −90.233
y = 38.636
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 37
C++ provides more fine-tuned control than Python, allowing the programmer a choice between
three different semantic models for storing and passing information. In this section, we examine
the correspondence between identifiers and underlying values in C++.
Point a;
Point b(5,7);
cause the system to reserve memory for storing newly constructed points. Because all data members
for a Point are explicitly declared in the class definition, the system can determine precisely how
much memory is required for each instance. The translation from a name to a particular instance
is handled at compile-time, providing greater run-time efficiency than Python’s run-time mapping.
To portray the semantics of a value variable, we prefer a diagram in the style of Figure 15,
without any independent concept of a reference. The assignment semantics for a value variable
is very different from Python’s. The command a = b assigns Point a the value currently held by
Point b, as diagrammed in Figure 16. Notice that names a and b still represent two distinct points.
The semantics of value variables is manifested as well in the passing of information to and from
a function. Consider the following C++ analog to our earlier Python example.
When a caller invokes the function as isOrigin(bldg), the formal parameter Point pt is implicitly
initialized as if using the copy constructor syntax,
Point pt(bldg);
Note that the formal parameter pt does not become an alias for the actual parameter. It is a newly
allocated Point instance with state initialized to match that of the actual parameter bldg. Figure 17
portrays this scenario. As a result, changes made to the parameter from within the function body
have no lasting effect on the caller’s object. This style of parameter passing is generally termed
pass-by-value, as originally discussed on page 351 of our book.
a : Point b : Point
x = 0.0 x = 5.0
y = 0.0 y = 7.0
a : Point b : Point
x = 5.0 x = 5.0
y = 7.0 y = 7.0
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 38
c
a : Point b : Point
x = 0.0 x = 5.0
y = 0.0 y = 7.0
Syntactically, the distinguishing feature is the use of the ampersand. This designates c as a new
name, but it is not a new point. Instead, it becomes an alias for the existing point, a. We choose
to diagram such a situation as in Figure 18.
This is closer to the spirit of Python’s model, but still not quite the same. A C++ reference
variable must be bound to an existing instance upon declaration. It cannot be a reference to nothing
(as is possible in Python with the None value). Furthermore, the reference variable’s binding is
static in C++; once declared, that name can no longer be re-associated with some other object.
The name c becomes a true alias for the name a. The assignment c = b does not rebind the name c;
this changes the value of c (also known as a).
Reference variables are rarely used as demonstrated above, because there is little need in a
local context for a second name for the same object. Yet the reference variable semantics becomes
extremely important in the context of functions. We can use a pass-by-reference semantics by
using the ampersand in the declaration of a formal parameter, as in the following revision of isOrigin.
This leads to a model similar to Python in that the formal parameter becomes an alias for the
actual parameter. There are several potential advantages of this style. For larger objects, passing
the memory address is more efficient than creating and passing a copy of the object’s value. Passing
by reference also allows a function to intentionally manipulate the caller’s object. If we do not want
to allow a function to mutate its parameter, yet we want the efficiency of passing it by reference,
a const modifier can be declared with the formal parameter, as in
With such a signature, the point will be passed by reference but the function promises that it will
in no way modify that point (a promise that is enforced by the compiler).
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 39
Given this discussion of reference variables, we revisit our earlier definition of the robust Point
class from Section 7.2. Notice that our original signature for the distance method on line 19 of
Figure 10 appeared as
The point serving as the parameter is being passed “by value,” causing a local copy to be made.
As a value parameter, we do not bother to make a const declaration because it does not matter
whether the function body modifies the local value (although it does not). Since a point has several
fields, it may be more efficient to pass it by reference. In that case, we will clearly designate it as
a constant reference with the following revised signature.
The const for the parameter declaration assures the caller that the the function will not modify its
value, while the const declaration that follows the signature designates the method as an accessor,
meaning that the point upon which it is invoked is unchanged.
We might similarly revise the operator+ method to receive its parameter as a constant reference.
Note that the return type remains declared as a value rather than a reference. This is because our
local return value is a transient object that will soon be destroyed. It would be unsafe to return a
reference to such an object to the caller, so we must send its value.
As a final example, our original version of the operator<< method from Figure 11 appeared as
While we could revise this definition to send the Point as a constant reference, we wish to draw
attention to the treatment of the ostream as both a parameter and return value. Notice that the out
parameter is designated as a reference. This is because we wish to insert data into the actual stream
indicated by the caller, not a “copy” of the stream (in fact, streams cannot legally be copied). Note
as well that this parameter is not designated as a const reference, because our insertion of data
impacts the state of that stream.
Because streams cannot be copied, we must return it as a reference using ostream& as the
declared return type. In our earlier discussion of operator+ we emphasized that it was not safe to
return its resulting point as a reference. The problem there was that the result had been created
in the local scope of the function. In the case of operator<<, the stream that we are returning
originated with the caller. Therefore, we may safely return a reference to it knowing that it will
continue to exist after our function completes.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 40
d : Point*
0xbffff1234
c
a : Point b : Point
x = 0.0 x = 5.0
y = 0.0 y = 7.0
Figure 19: Variable d is an example of a pointer whose value is the address of instance b.
The asterisk in this context declares that d is not a Point itself, but a variable that can store the
memory address of a Point. Pointers are more general than reference variables in that a pointer is
allowed to point to nothing (using the keyword NULL in C++) and a pointer can be dynamically
reassigned to the address of another instance. A typical assignment statement is as follows:
d = &b;
Technically, what happens is that we declare a new local pointer, with the value of that pointer
set to the value of the pointer sent by the caller. As a result, the function body has indirect access
to the same underlying object that the pointer addresses. This provides similar opportunity to
when parameters are passed by value, but it allows the additional possibility of the caller sending a
NULL pointer (recall that reference variables must be bound to something). The following section
introduces another use of pointers in C++, that of managing dynamically-allocated objects.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 41
With this code fragment, two different pieces of memory are being used. A certain number of bits
are set aside for managing the pointer variable p, while another set of bits are set aside for storing
the state of the underlying Point instance. The key is that with the dynamic allocation of the
point, that instance will remain in memory even when the variable p goes out of scope (causing the
reclamation of the variable p but not the object to which it points).
Management of dynamically-allocated objects requires more care. If a program were to lose
track of the memory location of the object (such as by reassigning a pointer variable to a different
location), the original object would remain in memory yet be inaccessible4 . Such a mistake is known
as a memory leak and a program that continues to allocate such objects while never deallocating
them consumes more and more of the computer’s memory as it runs. In C++, the programmer
has the burden of explicitly destroying a dynamically-allocated object when it is no longer needed.
This is done by using a syntax such as delete p in the above example. The expression after the
keyword delete specifies the address of the object to be deleted. However, delete must only be
used on objects that were dynamically-allocated. If you were to specify the address of a standard
value variable an error occurs when the system subsequently attempts the automatic deallocation.
double measurements[300];
This declaration causes the system to create an array of 300 double values. A particular entry of
that array can be accessed with an expression such as measurements[7]. In this section, we wish to
discuss the treatment of the array as a whole.
4
In contrast, Python detects inaccessible objects and reclaims them, as discussed in Chapter 10.1.3 of the book.
However, Python must perform additional bookkeeping for all objects to perform that task, thus trading efficiency
for convenience.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 42
The expression measurements, without any explicit indexing, is a legal syntax in C++. Intu-
itively, that expression relates to the array as a whole, but the semantics is not the same as with
a primitive type. The expression measurements represents the memory address of the beginning of
the array. In fact, the formal data type for this expression is a double* (i.e., a pointer to a double).
The significance of this semantics can be demonstrated with an example. Assume that our
previous declared measurements has been populated with data. We might wish to create a second
array and to copy the original data to the secondary array. The following is an errant attempt.
double backup[300];
backup = measurements; // does not actually copy the underlying array
The first line is a legitimate declaration of a second array, able to store 300 double values. The
problem is that the assignment backup = measurements changes the pointer backup to have a value
equal to the address currently known as measurements. In affect, we have made the variable backup
an alias for the original array (while leaving the newly allocated array inaccessible).
There is no direct way to make a copy of an entire array. Instead, we could write our own loop
to initialize the second array with the same values stored in the original.
double backup[300];
for (int i=0; i < 300; i++)
backup[i] = measurements[i];
The array variable can be used directly as a pointer. Since measurements is a pointer to
the first array location, the expression *measurements represents the double stored at that loca-
tion (i.e., measurements[0]). C++ pointers support a notion of arithmetic in that the expres-
sion measurements + 7 represents the address that is seven entries beyond the start of the array.
Thus *(measurements + 7) is equivalent in meaning to measurements[7].
We should also note that C++ does not make any effort to ensure that our index is legitimate.
For example, given our earlier declaration, use of the expression measurements[350] is legal, but the
affect is likely disastrous. Given knowledge of the starting address for the array, this expression
refers to the “double” that is located 350 entries away from the start of the array. However, since
our array was declared with only 300 entries, the memory location that is 350 steps away from the
beginning is not part of the array. It could vary well be bits that are being used to represent some
other object. In comparing this treatment to that of Python’s list, we note the trade-off between
efficiency and convenience. If we were to attempt to access measurements[350] in Python for a list
that did not actually have that many entries, an IndexError is thrown. Python checks the validity
of the index at run-time, yet this check requires a few extra steps internally. C++ opts for greater
efficiency by blindly going to the indicated memory location, assuming that the programmer has
designated a valid index.
The treatment of arrays in C++ also impacts the way in which we send an array as a parameter
to a function. For example, we might wish to support a function sum that would support a natural
syntax such as total = sum(measurements). Unfortunately, such a calling signature is nontrivial.
The challenge relates to the fact that arrays cannot be directly copied en masse. When sending
measurements as a parameter, what is really being sent is the pointer value. Yet within the context
of the function, we need to know not only where the array begins in memory, but also how many
entries there are. So a common approach to implementing such a function involves two separate
parameters, as demonstrated in the following example.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 43
The first parameter designates that data is in effect an array of doubles, although all that is sent is
a pointer to the beginning of the array. The second parameter is used to denote the array’s length.
The caller must use a syntax such as total = sum(measurements, 300) when invoking our function.
Inside the body of the function, we index the array using a standard syntax such as data[i]. Because
the first parameter is really just a pointer, some authors will use the equivalent signature
Although it is not syntactically clear that data points to an array of doubles rather than a single
double, documentation for such a function would designate the proper usage.
We also note that either forms of our sum function can be used to compute the sum of a desired
subarray, simply by designating the “start” of the subarray as the first parameter and the length
of the subarray as the second. For example, the call sum(&measurements[50], 10) will compute the
equivalent of Python’s sum(measurements[50:60]). It involves the subarray starting at the address
of measurements[50], containing ten entries. Fans of pointer arithmetic could make the same call
using the syntax sum(measurements + 50, 10).
Finally, we wish to note that arrays can be dynamically-allocated rather than relying on auto-
matic memory management. This is particular useful when we need to create an array, but we do
not know the proper size of the array until run-time. In that case we can begin by declaring our
“array” as a pointer to the base type, such as
double *measurements;
When we wish to dynamically allocate an array of a particular size, we can do so using a syntax,
This dynamically allocates an array of double values, returning the address of the first entry.
As was the case in Section 8.4, a programmer who dynamically allocates an array is ultimately
responsible for releasing the memory when it is no longer needed. However, to deallocate an array,
a programmer must use the delete[ ] operator rather than delete. Thus if measurements were
dynamically allocated as above, it can be destroyed with the command delete[ ] measurements.
Point a;
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 44
The constructor for the more robust Point class of Figure 10 supports additional calling signatures
for the user’s convenience. It could be invoked for example as
Point b(5,7);
The Point class also supports another constructor form known as a copy constructor, even
though we did not provide any explicit code as such. This allows a new instance to be initialized
based upon the value of an existing instance. For example, given our earlier definition of point b,
we could instantiate a new point as follows.
This creates and initializes a new instance c, which is given an initial value patterned upon
instance b. But it is important to understand that the new instance is independent, in the sense
that it has its own memory and that subsequent changes to one of these points will not affect the
other. The first is simply used as a model for the second, as the new point’s x value is set to the
same as the original’s x, and the new y is initialized to the value of the original y. If we were to
describe the behavior of the copy constructor it would be coded as follows.
Yet we did not explicitly need such a definition in our Point class. C++ provides an implicit
copy constructor for every class that does not explicitly define one. By default, it performs a
member-by-member copy. The primary reason for a default copy constructor is that the system
needs to do its own copying when passing value parameters. Recall that when a parameter is passed
by value, the formal parameter is a local instance that is initialized to match the caller’s actual
parameter. The copy constructor is the mechanism used to create that local instance.
However, some classes need a more specialized behavior to provide correct copying semantics.
In particular, there is a potential ambiguity for a class that contains a pointer as a data member.
That pointer might be addressing some memory that was allocated specifically for the state of the
given instance, or it might be pointing to memory that inherently “belongs” to some other object.
In the former case, the correct semantics is to do a deep copy of the object being referenced. In the
latter case, the correct semantics is likely to replicate the pointer but not to replicate the underlying
object. The member-by-member copying mechanism of the default copy constructor in effect is a
shallow copy, creating a pointer whose value is the same address as the pointer that is being
copied5 . Yet C++ allows the designer of a class to override the semantics of the copy constructor.
As a motivating example, we develop a simple version of a TallySheet class for integer values,
akin to that used in Chapter 8.5.3 of our book for computing frequency counts. We wish to have an
array of counters for the various numbers that are added to the data set, yet we do not know how
big of an array to declare until we are told the range of values that may occur. For that reason,
we must begin by declaring an int* data member, and then later set that pointer to the address
of a properly allocated array of integers. The beginning of our class appears in Figure 20. The
pointer tallies is declared at line 6. It is not until the primary constructor is called before we can
compute the required size of the array and then dynamically allocate it (line 10). The complication
arises if someone were to make a copy of a tally sheet. For example, we might imagine a user who
wants to make a copy of a partial tally, and then do further simulation on the copy while ensuring
that the original version of the counts remains unchanged.
5
We strongly recommend that you revisit Chapter 10.2 of our book for a discussion of these issues in Python.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 45
1 class TallySheet {
2 private:
3 int minV;
4 int maxV;
5 int size;
6 int* tallies;
7
8 public:
9 TallySheet(int minVal, int maxVal) :
10 minV(minVal), maxV(maxVal), size(maxVal−minVal+1), tallies(new int[ size]) {
11 for (int i=0; i < size; i++)
12 tallies[i] = 0;
13 }
It would be a mistake for us to rely on the system-provided copy constructor. Because the tallies
member is a pointer, the default copy would result in a new tally sheet with its tallies pointer set to
the same address as the original tallies value. In effect, the two TallySheet instances would be shar-
ing the same underlying array, thereby interfering with an accurate count. The proper semantics
for copying a tally sheet is to create a deep copy, giving the new instance its own array of counts,
albeit initialized based upon the existing counts at that time. We provide a specialized version of
the copy constructor by defining an additional constructor that accepts another TallySheet instance
as a parameter. For our class, we implement the proper copying semantics as follows.
The new sheet gets the same minimum, maximum, and size. But rather than aliasing the existing
array, we make sure to initialize the tallies pointer to a newly allocated array at line 15. In lines 16
and 17, we use a loop to copy the original array entries into the new array.
Destructors
Just as the constructors of a class are used for initializing the state of a newly created object,
there is a method known as a destructor that is invoked each time an object reaches the end of
its lifespan (e.g., a value variable that goes out of scope, or a dynamically-allocated object that is
explicitly deleted). As is the case with the copy constructor, C++ will provide an implicit definition
for a destructor if the programmer does not. The default behavior invokes the destructor upon
each of the data members.
For many classes, such as our Point class, this implicit behavior suffices. However, ambiguity
exists when pointers are used as data members. When an instance is being destroyed, should an
object that it references be destroyed as well? With the default destructor, the memory for the
pointer itself is reclaimed, but the object to which it points is not. For dynamically-allocated
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
8. OBJECT MODELS AND MEMORY MANAGEMENT Page 46
data such as the TallySheet’s underlying array, we want to deallocate its memory as well. We
can override the default behavior by providing an explicit destructor in the class definition. By
convention, the destructor is a method whose name is the class’s name preceded by a ˜, taking
no parameters, and providing no return value (not even void). Our TallySheet destructor appears as,
19 ˜TallySheet( ) {
20 delete[ ] tallies; // deallocate the dynamically−allocated array
21 }
had we not explicitly deleted the dynamically-allocated array, it would remain stranded in mem-
ory indefinitely. This is in contrast to an array declared as a standard value variable, which is
automatically reclaimed upon destruction of an instance.
The assignment of the members x and y is straightforward. The noteworthy aspect is the treatment
of the return value. At first thought, it might seem that an expression a = b is an action, but should
not have a resulting value. Yet it is supposed to have the new value as its result so that assignments
can be chained as a = b = c. This treatment is similar to that of the chaining of stream operators,
as originally demonstrated in Figure 11 on page 33. So we return the Point itself (as a reference,
to avoid unnecessary copying). Internally, we use the keyword this to identify the Point instance
being operated upon. C++’s treatment of this is akin to Python’s self identifier. From a technical
perspective, this is a pointer type in C++. Because we want to return the Point rather than a
pointer to a Point, we dereference *this in the return statement.
While we did not have to explicitly provide the assignment operator for our Point class, greater
care is necessary for classes such as TallySheet that make use of dynamically-allocated memory. The
tallies pointer on the left-hand side would become an alias for the array on the right-hand side, and
the original left-hand array would be lost (i.e., a memory leak). Instead, we must ensure that the
instances maintain their own arrays. Since, the existing array for the left-hand instance may not
be the correct size for the renovated state, we deallocate the old array, and then reallocate a new
array of the correct size. Then we copy the contents. The assignment operator for our TallySheet
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
9. GENERIC PROGRAMMING AND TEMPLATES Page 47
The core piece of code is very reminiscent of the commands used in the copy constructor and destruc-
tor, as we are in essence throwing away our old state and then resetting a new state. However,
we wish to address one subtlety. Most of the body is shielded by the condition (this != &other).
When a user invokes an expression such as s = t, this is the address of the left-hand operand
while other is a reference to the right-hand operand (thus &other is the memory address of that
instance). Typically, if a user is going to bother to do an assignment, the left-hand and right-hand
operands will refer to different instances. But it is possible for someone to invoke what is known as
a self-assignment such as s = s. While it is unlikely that a programmer would author such code,
it is legal. More commonly, a self-assignment may occur when a programmer has two different
references to the same object yet does not realize so. In some contexts, code written as s = t may
actually involve a single instance.
If we recognize that we are being asked to assign an instance its own value, we may as well avoid
doing unnecessary work. The conditional block serves this purpose (notice that the final return
statement must still be executed, even for a self-assignment). Yet, our conditional treatment serves
a far more important role than simply for improved efficiency. If we did not shield a self-assignment
from this block of code, there are disastrous results. The problem is that we intentionally deleted the
“old” array, then we create a new array and subsequently try to copy information from other. tallies
into tallies. While this if fine in the general case, if other happens to be the same instance, we will
have just thrown away all our data and it is too late to recover it.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
9. GENERIC PROGRAMMING AND TEMPLATES Page 48
If we send two integers as parameters, this function properly returns the smaller value. If we send
two strings as parameters, this function returns the one that occurs first alphabetically. We do not
need to explicitly declare the data types. So long as the < operator is well defined for the given
data, the function works at run-time; if not, a run-time error will occur.
With the static typing of C++, we need additional support for writing such a general function.
If we were only interested in such a function for integers, we might write
But we do not wish to write many different versions of such a function when one suffices. The
challenge in C++ is that we must explicitly declare the type of all parameters and of the return
type. Support for such generality in C++ is provided using a mechanism known as a template. We
can define a function in a general way by declaring a placeholder for the type name immediately
before the function definition. In the case of min, the templated function appears as follows.
The first line designates the identifier T as a hypothetical type name. There is not actually a type
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
9. GENERIC PROGRAMMING AND TEMPLATES Page 49
with that name and we could have chosen any new identifier as such. This serves as what is known
as the template parameter. We use it in the signature on the second line to designate the return
value and both parameters as having that same type.
When a call such as min(52,50) is attempted elsewhere in our program, the compiler tries to find
a match for the template parameter T that will satisfy the compile-time checking. In this particular
example, it will recognize that the call matches the function signature when using int in place of
the template parameter T. If a call is made with two string instances as parameters, the compiler
will resolve the template parameter as type string. After matching the template parameters, the
compiler then attempts to compile the templated code. A compilation error would be reported at
that time, for example if min were called on a data type that did not support the < operator used
in the body of our function6 .
As a second example, we consider the basic task of swapping the values of two variables. In
Python, this can conveniently be done with a simultaneous assignment statement, such as a,b = b,a.
In C++, such a swap of values is typically accomplishes through use of a temporary variable using
an approach like the following:
temp = a;
a = b;
b = temp;
However to write such code generically, we would need to make a formal declaration for variable
temp and this depends on the data type being used. The <algorithm> library in C++ provides a
generic implementation of a function with signature swap(a,b) that accomplishes this task. It can
be implemented as a templated function as follows:
Notice that the template parameter is used not only in declaring the parameter types, but also
in declaring the local variable temp. Also note that the parameters are passed by (non-const)
references so that assignments within the function body affect the actual parameters of the caller.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
9. GENERIC PROGRAMMING AND TEMPLATES Page 50
maxVal were integers, as were the data members minV and maxV. In this section, our goal is to
provide a templated version that can operate on integers or characters.
A complete version of the updated class is given in Figure 21. We wish to draw attention to a
few aspects of the code. We begin by examining the data members declared in lines 4–7. Notice
that minV and maxV are declared using the template parameter type rather than specifically an
int. In contrast, size is left as an int because the size of the array is an integer, even if the user is
tracking characters from 'a' to 'z'. Similarly, we will maintain an array of integer counts, even if
those counts represent the number of occurrences of a given character.
The standard constructor can be found at lines 10–14. Notice that the two parameters are typed
as constant references to instances of the template type T in the signature. While the rest of this
constructor is identical to the one given earlier in this section, there is an important subtlety. We
initialize the integer size to be the value (maxVal−minVal+1). When the parameters maxVal and
minVal are integers, this is clearly a valid assignment. Less obviously, the assignment is valid even
when maxVal and minVal are of type char. C++ allows us to subtract one character from another,
producing an integer that is the distance between the two characters in the alphabet encoding.
The copy constructor, destructor, and assignment operators (lines 16–37) are almost verbatim
from the previous section, except for the use of the template parameter when designating other
variables of type TallySheet<T> as opposed to the simpler TallySheet; we will address this issue
further in Section 9.3. We see the use of the template type T for the parameters of the public
methods increment and getCount, as those are values in the user’s domain. As we did in Python, we
convert such a value to the integer index into the underlying array using a private toIndex method.
At line 76, that method relies on the use of subtraction for type T, similar to our computation of
the size in the constructor.
The makeLabel method uses a stringstream to convert the index into a string that appropriately
matches the user’s domain value. That technique was introduced in Section 6.6. At line 82, we
use the syntax T(...) to ensure that the label is formatted as the appropriate data type (e.g., int,
char). Because ind is always an int, the intermediate expression ind + minV will also be an int,
even when minV is a char. By surrounding that value with the syntax T(...), it will be correctly
converted to the desired type before being inserted into the stream.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
9. GENERIC PROGRAMMING AND TEMPLATES Page 51
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
9. GENERIC PROGRAMMING AND TEMPLATES Page 52
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
10. C++ CONTAINERS AND THE STANDARD TEMPLATE LIBRARY Page 53
That is, even though the apparent min and max value sent to the constructor are characters, the
compiler requires that the template parameter be clearly defined as part of the data type. The
correct declaration for such an instance is:
TallySheet<char> frequency('A','Z');
The official type of our frequency variable is TallySheet<char>. Formally, we get a distinct type
definition for each instantiation of the template parameter (ie TallySheet<char> is distinct from
TallySheet<int>). We see this issue arise in our original code for the class, as given in Figure 21. For
example, the copy constructor definition at line 16 takes a parameter that must be a TallySheet<T>
instance, matching the same template parameter as the given instance. This makes sense, as we
cannot make a TallySheet<char> instance as a copy of a TallySheet<int> instance. We see similar
usage in the signature for the assignment operator at line 26, as both the parameter and the return
type are explicitly TallySheet<T>.
vector<string> groceries;
groceries.push back("bread");
groceries.push back("milk");
groceries.push back("cheese");
cout << groceries.size( ) << endl; // will be 3
cout << groceries[2] << endl; // will be ”cheese”
groceries[1] = "juice"; // replaces ”milk”
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
10. C++ CONTAINERS AND THE STANDARD TEMPLATE LIBRARY Page 54
We start by noting that vector is a templated class. In this case, we are declaring a vector of
strings. By default, the newly instantiated vector is empty. The push back method is the C++
analog of Python’s append, adding the new value to the end of the sequence. The length of the
vector is returned by the size( ) method. As is the case with Python, elements are zero-indexed and
can be accessed with a syntax such as groceries[2]. However, unlike Python, C++ does not check
the validity of an index at run-time; it simply trusts the programmer (with potential disaster if the
programmer is wrong). A safer (yet slower) way to access an element in C++ is with the syntax
groceries.at(2); this version performs an explicit run-time check of the given index, throwing an
out of range exception when warranted. There are several other behaviors supported by the vector
class; we refer the interested reader to more detailed documentation elsewhere.
Figure 22: Commonly used classes from C++’s Standard Template Library (STL).
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
10. C++ CONTAINERS AND THE STANDARD TEMPLATE LIBRARY Page 55
method, and we remove an element at line 13 using the erase method. We will discuss the rest of
that example after introducing the concept of iterators in Section 10.3.
Finally, the C++ map class is the analog to the Python dict class. Rather than storing a
set of elements, it manages a collection of key-value pairs. As such, it requires two template
parameters, the first specifying the key-type and the second the associated value-type. As an
example, we could have implemented the TallySheet to keep track of frequencies by maintaining
a map from some key type to an integer frequency. For example, if counting characters, we may
declare map<char, int> tallies. As is the case with sets, C++ uses balanced binary trees to
implement maps, and the key type must define a total ordering, typically with operator<.
10.3 Iterators
Because a string or vector instance is stored in an underlying array, integer indices can be used to
efficiently describe the position of an element to be accessed. For most other STL container types,
integer indices are not supported. This is typically because a container is not inherently ordered or
because the underlying storage mechanism is not consistent with such a convention (for example, a
list instance implemented as a linked list, or a set instance implemented as a balanced search tree).
For this reason, all STL containers (including strings and vectors) provide support for describing
a “position” of an element using an instance of an iterator class. Iterator instances cannot be
directly created by a user, rather they are initially returned by some method of the class. These
iterators are then used to identify a particular element or location within a container in the context
of parameters and return values. Conceptually, they are similar in purpose to a pointer, but they
are not necessarily a direct representation of a memory address. To explore the syntactic use of
iterators, we revisit a code fragment from our DeluxeTV class definition in Figure 12.
17 set<int>::iterator result = favorites.upper bound( channel);
18 if (result == favorites.end( ))
19 result = favorites.begin( ); // wrap around to smallest channel
20 setChannel(*result);
The result variable is defined to have type set<int>::iterator. Formally, this is an instance of an
iterator class that is nested within the scope of the set<int> class. This is different from other
iterator classes, such as set<string>::iterator or vector<int>::iterator.
The value of result is initialized to the result of the call to upper bound, a method specific to the
set class. Before explaining the semantics of upper bound, we describe a related method named find
supported by sets. A call such as favorites.find(value) checks to see if the given value is contained in
the set, akin to the contains method in Python. If found, an iterator representing that element’s
position in the set is returned; if not, the sentinel end( ) is returned, by convention, to designate
the lack of such an element. In contrast, the upper bound method is one that relies on the fact
that C++’s sets are ordered. The formal semantics of a call to upper bound(value) is to locate the
smallest value in the set, if any, that is strictly greater than the parameter. So in the context of
DeluxeTV, we are looking for the next favorite that is found when moving upward from the current
channel setting. If there is no element of the set with a value greater than channel, the end sentinel
is returned, just as is done with find. We detect that case at line 18. For the television model, our
goal was to then wrap around to the smallest of all favorite channels. We accomplish this goal at
line 19 by calling another method named begin. In some sense, this is the opposite of end, except
that the result of begin is an iterator to the first actual position in the set (rather than a sentinel).
In the case of sets, the ordering begins with the least element.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
11. ERROR CHECKING AND EXCEPTIONS Page 56
The final lesson portrayed by the above code involves the use of syntax *result at line 20. In
context, the call to setChannel is used to implement the change of channels and the parameter to
that call must be the desired channel value. Just as there is a distinction between a pointer and
the value to which it points, we must make a distinction between an iterator and the underlying
element to which it refers. In this context, result is the iterator while *result is the corresponding
int value from the set, namely the favorite channel number.
Iterators can also be used to iterate through all elements of an STL container (hence, the term
iterator). As an example, here is code that prints out all favorite channels for our DeluxeTV using
the ++ operator to advance from one position to the next.
Note the significance between the asymmetric convention of begin and end. So long as the iterator
is not equal to the end sentinel, it represents the position of an actual element that can be printed.
When walk becomes equivalent to the end, the loop terminates; note that for an empty set, the
result of begin( ) is precisely that of end( ) and so there are zero iterations of the loop. This
form of loop is supported by all STL containers. We note the correspondence between going from
begin up to but not including end, just as Python handles ranges start:stop. Iterators for many
containers also support backwards iteration with the −− operator. Indexed classes such as vector
and string support random-access iterators that allow arbitrary step sizes using arithmetic such
as begin( )+5 or end( )−3.
Finally, we note that an assignment such as *walk = val can be used to modify an element of a
container in-place. However, if a container is designated as read-only with const, modification of
elements is not allowed. For this circumstance, all STL containers support a second class named
const iterator, that is similar to iterator but without the ability to modify the contents of the
underlying container. Note well that a const iterator is not the same as a const iterator, as the
former can be incremented and decremented but without modifying the underlying container, while
the latter can modify the underlying container but cannot be incremented or decremented.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
11. ERROR CHECKING AND EXCEPTIONS Page 57
def sqrt(number):
if number < 0:
raise ValueError('number is negative')
Python uses the keyword raise to throw an exception, with the remaining argument being an
instance of the exception class to be thrown. In this example, ValueError('number is negative')
denotes the construction of a new instance of type ValueError, with the constructor for that class
accepting an error message as a string parameter. The syntax in C++ is quite similar, except that
we use the keyword throw rather than Python’s raise. The same code fragment would appear as
follows in C++:
try {
// any sequence of commands, possibly nested
} catch (domain error& e) {
// what should be done in case of this error
} catch (out of range& e) {
// what should be done in case of this error
} catch (exception& e) {
// catch other types of errors derived from exception class
} catch (...) {
// catch any other objects that are thrown
}
If an exception is encountered, it will be handled by the first clause with a matching type declaration.
The final clause with parameter ... will match any object. If there were not such a final clause,
and an exception occurs that does not match any of the existing clauses, that exception would
be propagated to any outer nested scope, where it might be caught or if uncaught will cause the
program to terminate. Note that for all but the final case, the exception handler clause has access
to the declared variable e, with local scope, that is a reference to the exception instance that has
been caught. Additional information about that exception can be determined, for example with
the call e.what( ), which returns the string message used when the exception was instantiated.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
11. ERROR CHECKING AND EXCEPTIONS Page 58
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
12. MANAGING LARGE PROJECTS Page 59
Figure 23: A C++ function for robustly opening a file with read-access.
cannot be passed by value, so we use a signature in which the caller passes an uninitialized file
stream as a parameter, and the function’s responsibility is to open the underlying file. Our C++
variant is given in Figure 23. Notice that we rely on polling the result of is open( ). This method is
supported by the file stream classes, in addition to the other accessors such as eof and bad, inherited
from the more general stream classes.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
12. MANAGING LARGE PROJECTS Page 60
gcd.h
1 #ifndef GCD H
2 #define GCD H
3 int gcd(int u, int v); // forward declaration
4 #endif
gcd.cpp gcdTest.cpp
Figure 24: GCD project with source code consisting of three files.
of the call to the gcd function at line 11. Note well that it does not matter in that context how the
gcd function is implemented, only that the type of parameters and return value are known.
The purpose of the gcd.cpp file is to implement the gcd function. We include the header file
at line 1 to ensure consistency of the signature, although this is not technically required.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
12. MANAGING LARGE PROJECTS Page 61
for validity and converting the high-level commands into appropriate machine code. Similarly, the
commands of the main function in gcdTest.cpp can be independently compiled into machine code
without regard to the details of the gcd function body.
The final stage of the compilation process is known as linking. Once the individual pieces have
been converted to machine code, the system must assemble them into a single, coherent executable
that can be executed on the system. The primary goal of the linking stage is to ensure that all
necessary functions have been defined in precisely one of the components. That is, if we tried to
compile the file gcdTest.cpp without gcd.cpp, the linker would report an error that it cannot
find the implementation of the function gcd that is being called from within main. In contrast, if
we were to attempt to compile gcd.cpp without gcdTest.cpp, the linker would complain that it
cannot find a main function, a requirement for any executable.
The given g++ command from above performs both the compilation and linking phases by
default. That said, it is possible to request that a component be compiled, but to defer the linkage.
For example, to compile the gcd function, but not yet link it to any executable, we could execute
the command
g ++ -c gcd . cpp
The -c flag in the command is what designated our desire to perform compilation only. The result
of a successful compilation in this case is a new binary file named gcd.o that is known as object
code. One advantage of having this object code stored in a file is that it allows us greater reuse
without additional compilation. For example, if we wanted to use our gcd function in several
projects, each of those could make use of our pre-compiled object code rather than re-compiling
from the original source code. In our example, if we presume that we have preliminary created
both gcd.o and gcdTest.o as object code, we can invoke the final linking to produce an executable
with the command.
g ++ -o gcd gcd . o gcdTest . o
For large-scale problems, there is another great advantage of separating out the compilation
of object code from that of the linking phase. If you envision a project that might be composed
of hundreds if not thousands of files, the full compilation process is a timely one. During the
development cycle, it is common to have compiled the program, to make changes to one or more
of the source code files, and then to re-compile the entire project. However, with good modular
design, a change to one piece of source code should not effect most of the other components. In
this case, we will need to re-generate the object code for the modified source code, and perhaps a
few other dependent pieces, but the majority of the components will not need to be rebuilt from
scratch. Instead, those few components can be recompiled into object code, and then all of the
object code can be relinked to form a new executable. Although the linking phase requires some
work, it is not nearly as time-consuming as the original compilation.
Finally, we note that for larger problems, there are other tools to assist developers in managing
their projects. For example, many Integrated Development Environments (IDEs) will keep track of
which pieces of source code have been modified, and which need to be re-compiled when building a
new executable. One of the classic tools for developers in managing the (re)building of a project is a
program known as make. This program relies upon a configuration file for the project, conventionally
named makefile. The makefile designates what components comprise the project, and upon which
pieces of source code each component depends. The make command causes a re-build of the entire
project, but relying on the file-system timestamps to determine which pieces of source code have
been edited since the previous build.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
12. MANAGING LARGE PROJECTS Page 62
#include "gcd.h"
#include "Fraction.h"
The problem is that the definition of the Fraction class might also depend upon use of the
gcd function for reducing fractions to lowest terms. So it is possible that there might be an
#include "gcd.h" within the Fraction header file. If this were the case, then the contents of gcd.h
are inherently being included twice in the above code.
To properly avoid errant repetitions of definitions from a header file, those files take advantage of
other preprocessor directives, as shown in our gcd.h file from Figure 24. These directives, beginning
with #, are not formally C++ syntax, rather a separate set of commands that are understood by
the compiler (as with #include). Our gcd.h file begins with the #ifndef GCD H directive on line 1
paired with the #endif directive on line 4. The #ifndef syntax is short for “if not defined.” It looks
for whether the term GCD H has been previously defined, only using the body of that conditional
when it has not been defined. In this context, the first time we included the contents of gcd.h, no
such symbol GCD H has been defined, and so we will include the body of that conditional (lines 2
and 3). The purpose of the #define GCD H directive on line 2 is to introduce the symbol GCD H
to the compiler so that the #ifndef directive at line 1 will fail for any subsequent inclusions of
gcd.h. We will use a similar guard mechanism for all header files in a larger project, choosing a
distinct symbol for each (e.g., GCD H).
Line 3 of the file gcd.h is a forward declaration of the gcd function. This defines the signature
of the function, yet we do not explicitly include the body of the function in that file. The forward
declaration has the relevant information for doing compile-time checking, for example when making
the call to gcd from within gcdTest.cpp, so the function body is not needed. More importantly, it is
important that we do not place the body of the gcd function inside the header file. Otherwise, there
would be a problem if two or more .cpp files include such a header. Each .cpp file is independently
compiled into object code, and so the #ifndef guard would allow the definition to be read once for
each .cpp file. Having the forward declaration in each is permissible, but if the function body were
included, the compiled version of that body would be embedded within the object code associated
with each .cpp file. This in turn would cause a linking-error when the object codes were combined,
as there would be duplicate definitions for the function to contend with. In our actual project, it
is only the compiled version of gcd.cpp that has the implementation of the gcd function, so there
is no ambiguity when compiling and linking.
12.3 Namespaces
Another concern with larger projects is that several components may wish to use the same identifier
for a feature such as a variable, function, or class. If all components were authored by the same
team, perhaps such naming conflicts could be avoided. However, often we need to rely upon software
packages written by others that cannot be modified. For example, a civil engineering application
might need to include an architecture package that defines a Window class (i.e., a plane of glass) and
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
12. MANAGING LARGE PROJECTS Page 63
a graphical user interface package that defines its own Window class (i.e., a rectangle on a computer
screen). Including such conflicting definitions for the identifier Window would be illegal.
Python tackles the issue of naming-conflicts by supporting two forms for import. A command
such as import architecture introduces the name architecture into the default namespace, as an iden-
tifier for the imported module. Then, a qualified name architecture.Window can be used to describe
the Window class from that module. In contrast, a syntax such as from architecture import Window
introduces the name Window directly into the default namespace. However, this form would be con-
flicting with another command from gui import Window. In C++, name conflicts can be mitigated
by organizing related definitions into a separate namespace using a syntax such as the following:
namespace architecture {
class Window {
...
};
class Building {
...
};
}
After this definition, the Window class can be identified with a qualified name architecture::Window.
This style would allow another part of a program to include the architecture code and to include
the gui code, while being able to differentiate unambiguously between architecture::Window and
gui::Window. At the same time, it is possible for a programmer to introduce definitions from such a
namespace into the default namespace. For example, following the above definition, a subsequent
command using architecture::Window; would introduce the architecture::Window definition into the
default namespace with the unqualified name Window (akin to from architecture import Window in
Python.). All definitions from a namespace can be added to the default namespace, for example
using the command using namespace architecture; (akin to from architecture import * in Python).
We have already seen such a use of namespaces in our C++ code. By default, most standard
libraries in C++ introduce their definitions into a special namespace identified as std, rather than
directly in the default namespace. For example, the <iostream> library defines the streams cin and
cout into the std namespace. Since the fully-qualified names std::cin and std::cout require extra
typing, programmers often bring everything from the standard namespace into the default with the
command using namespace std; such as at line 3 of our gcdTest.cpp program in Figure 24.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
12. MANAGING LARGE PROJECTS Page 64
1 #ifndef POINT H
2 #define POINT H
3 #include <iostream> // need ostream definition for operator<< signature
4
5 class Point {
6 private:
7 double x;
8 double y;
9
10 public:
11 Point(double initialX=0.0, double initialY=0.0);
12 double getX( ) const { return x; } // in-lined function body
13 void setX(double val) { x = val; } // in-lined function body
14 double getY( ) const { return y; } // in-lined function body
15 void setY(double val) { y = val; } // in-lined function body
16 void scale(double factor);
17 double distance(Point other) const;
18 void normalize( );
19 Point operator+(Point other) const;
20 Point operator*(double factor) const;
21 double operator*(Point other) const;
22 }; // end of Point class
23
24 // Free-standing operator definitions, outside the formal Point class definition
25 Point operator*(double factor, Point p);
26 std::ostream& operator<<(std::ostream& out, Point p);
27 #endif
state using namespace std from within the header file, to avoid polluting the default namespace
for others including this file. As a result, we explicitly designated the type std::ostream at line 26.
Looking at Figure 26, we see the remaining implementations of the functions from the Point
class. Since this file is not directly being included by others, we promote the std namespace to
the default at line 4 for our convenience. We wish to draw attention to the form of the signatures
in this file, for example, that of Point::scale at line 8. Because this code is not formally included
within the scope of the original Point class definition, we cannot simply write the signature as
That would appear to be the signature of a stand-alone function named scale, rather than the
member function of the Point class with that name. To properly declare that we are implementing
the member function, we must use the qualified function name Point::scale to re-establish the proper
scope. Having done so, the body itself is interpreted with the proper context. Therefore, we are
able to access data members such as x and y, as done on lines 9 and 10. Notice that at line 20,
within the body of the normalize function, we are able to call the distance method within need to
qualify it as Point::distance because the proper scope has already been established for the body.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
12. MANAGING LARGE PROJECTS Page 65
1 #include "Point.h"
2 #include <iostream> // for use of ostream
3 #include <cmath> // for sqrt definition
4 using namespace std; // allows us to avoid qualified std::ostream syntax
5
6 Point::Point(double initialX, double initialY) : x(initialX), y(initialY) { }
7
8 void Point::scale(double factor) {
9 x *= factor;
10 y *= factor;
11 }
12
13 double Point::distance(Point other) const {
14 double dx = x − other. x;
15 double dy = y − other. y;
16 return sqrt(dx * dx + dy * dy); // sqrt imported from cmath library
17 }
18
19 void Point::normalize( ) {
20 double mag = distance( Point( ) ); // measure distance to the origin
21 if (mag > 0)
22 scale(1/mag);
23 }
24
25 Point Point::operator+(Point other) const {
26 return Point( x + other. x, y + other. y);
27 }
28
29 Point Point::operator*(double factor) const {
30 return Point( x * factor, y * factor);
31 }
32
33 double Point::operator*(Point other) const {
34 return x * other. x + y * other. y;
35 }
36
37 // Free−standing operator definitions, outside the formal Point class scope
38
39 Point operator*(double factor, Point p) {
40 return p * factor; // invoke existing form with Point as left operand
41 }
42
43 ostream& operator<<(ostream& out, Point p) {
44 out << "<" << p.getX( ) << "," << p.getY( ) << ">"; // display using form <x,y>
45 return out;
46 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
12. MANAGING LARGE PROJECTS Page 66
As a final comment, we draw attention to our treatment of the two stand-alone functions,
namely operator* and operator<<, that provide support for operator usage when the Point is
a right-hand operand. In our original treatment of the Point class, we explained the need for
having those definitions outside the formal class. They remain as such in our new design, with
forward declarations given at lines 25 and 26 of Point.h and implementations given at lines 39–
46 of Point.cpp. Because these are not formal member functions, note that we do not designate
Point:: scope in their signatures. For example, line 43 of Point.cpp names the function operator<<
rather than Point::operator<<. In similar spirit, line 39 defines operator*, not to be confused from
the forms of Point::operator* that are given at lines 29 and 33.
#include "TallySheet.tcc"
This causes all the definitions from the second file to be explicitly included from within the header,
and thereby indirectly included by any other file that includes the header.
Finally, we look at the style for implementing member functions within the TallySheet.tcc
file, as shown in Figure 29 in the appendix. As was the case with our Point class, it is important
to re-establish the proper scope when declaring the function signature. Because we are defining
functions of a templated class, we must use the formal template syntax for each individual function,
as shown in the following excerpt.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
12. MANAGING LARGE PROJECTS Page 67
if name == '__main__':
That convenient style allows for test code to be run conditionally when the interpreter is executed
directly on that file, but ignored when the module is imported from some other file.
Unfortunately, there is no standard way to embed unit testing within the same source code
files as the code that is to be tested. The issue is that any executable must begin with a call
to a main routine, but when multiple files are compiled and linked, there must be precisely one
main routine defined. This was precisely the problem we pointed out with our original, single-file
implementation of the gcd program from Figure 1 on page 7. Because the main routine is defined
in that file, it becomes impossible for any other applications with their own main routine to be
combined with this definition of the gcd function. That is why the revised version introduced at
the beginning of this section relies on having a separate gcdTest.cpp file for testing. The unit test
can be compiled by combining both gcd.cpp and gcdTest.cpp when compiling. However, we could
build other executables that rely on gcd by linking some source code with gcd.cpp (but without
gcdTest.cpp).
12.7 Documentation
Python also provided a built-in mechanism for embedding documentation strings directly within
the source code files, with standard tools for generating documentation based upon those strings.
Although comments can be embedded within C++ source code, there is no support for generating
documentation from those comments in the standard C++ distribution. That said, there is a
widely-used, third-party tool known as Doxygen (see www.doxygen.org) that can be used to embed
actionable documentation within source code.
Doxygen supports several choices of conventions. One such style relies on comments between
delimiters /** and */. Note that the starting delimiter /** begins with /* and therefore it is
recognized by the C++ compiler as a standard comment. But the additional asterisk causes
Doxygen to treat this as formal documentation. We demonstrate this style of comments throughout
the source code given in the appendix to this document. As an example, Figure 35 of Appendix A.2
shows a documented version of a Pattern class for the game Mastermind. The compareTo function
of that class is documented as follows:
46 /**
47 * \brief Compare the current pattern to another and calculate the score.
48 *
Within the comment, we use reserved control sequences such as \brief, \param, and \return to tag
key pieces of information. When compiled with Doxygen, the documentation for the function might
be rendered as in Figure 27.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 68
A.2 Mastermind
As our final project, we implement a text-based version of the Mastermind game, based closely
upon the design of our Python version from Chapter 7 of our book. This project consists of the
following components:
a Score class as defined in Score.h of Figure 34. There is no Score.cpp because we in-line
all of the function bodies for this simple class.
a Pattern class as defined in Pattern.h of Figure 35 and Pattern.cpp of Figure 36.
a TextInput class as defined in TextInput.h of Figure 37 and TextInput.cpp of Figure 38.
a TextOutput class as defined in TextOutput.h of Figure 39 and TextOutput.cpp of Figure 40.
a Mastermind class as defined in Mastermind.h of Figure 41 and Mastermind.cpp of Figure 42.
The main driver for our text-based game, implemented in Mastermind main.cpp of Figure 43.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 69
1 #ifndef TALLYSHEET H
2 #define TALLYSHEET H
3 #include <iostream>
4
5 /**
6 * \brief Manage tallies for a collection of values.
7 *
22 *
41 ˜TallySheet( );
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 70
42 /**
43 * \brief Increment the tally for the respective value.
44 */
45 void increment(const T& val);
46
47 /**
48 * \brief Return the total number of current tallies for the given value.
49 */
50 int getCount(const T& val) const;
51
52 /**
53 * \brief Return the total number of current tallies.
54 */
60 * Report each value, the count for that value, and the percentage usage.
61 *
69 *
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 71
1 #include <iostream>
2 #include <stdexcept>
3 #include <iomanip>
4 #include <sstream>
5 using namespace std;
6
7 template <typename T>
8 TallySheet<T>::TallySheet(const T& minVal, const T& maxVal) :
9 minV(minVal), maxV(maxVal), size(maxVal−minVal+1), tallies(new int[ size]) {
10 for (int i=0; i < size; i++)
11 tallies[i] = 0;
12 }
13
14 template <typename T>
15 TallySheet<T>::TallySheet(const TallySheet<T>& other) :
16 minV(other. minV), maxV(other. maxV), size(other. size), tallies(new int[ size]) {
17 for (int i=0; i < size; i++)
18 tallies[i] = other. tallies[i];
19 }
20
21 template <typename T>
22 TallySheet<T>::˜TallySheet( ) {
23 delete[ ] tallies;
24 }
25
26 template <typename T>
27 TallySheet<T>& TallySheet<T>::operator=(const TallySheet<T>& other) {
28 if (this != &other) { // ignore self−assignments
29 minV = other. minV;
30 maxV = other. maxV;
31 size = other. size;
32 delete[ ] tallies; // throw away old array
33 tallies = new int[ size]; // create new array
34 for (int i=0; i < size; i++)
35 tallies[i] = other. tallies[i];
36 }
37 return *this;
38 }
39
40 template <typename T>
41 void TallySheet<T>::increment(const T& val) {
42 int ind = toIndex(val);
43 if (!(0 <= ind && ind <= size))
44 throw range error("Parameter out of range");
45 tallies[ind] += 1;
46 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 72
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 73
1 #include "FileUtilities.h"
2 #include <fstream>
3 #include <iostream>
4 using namespace std;
5
6 void openFileReadRobust(ifstream& source) {
7 source.close( ); // disregard any previous usage of the stream
8 while (!source.is open( )) {
9 string filename;
10 cout << "What is the filename? ";
11 getline(cin, filename);
12 source.open(filename.c str( ));
13 if (!source.is open( ))
14 cout << "Sorry. Unable to open file " << filename << endl;
15 }
16 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 74
1 #include "FileUtilities.h"
2 #include "TallySheet.h"
3 #include <cctype> // provides isalpha and toupper
4 #include <iostream>
5 #include <fstream>
6 using namespace std;
7
8 int main( ) {
9 ifstream source;
10 ofstream tallyfile;
11 TallySheet<char> sheet('A', 'Z');
12 cout << "This program counts the frequency of letters." << endl;
13 cout << "Only alphabetic characters are considered." << endl << endl;
14 openFileReadRobust(source);
15 while (!source.eof( )) {
16 char character;
17 source >> character;
18 if (isalpha(character))
19 sheet.increment(toupper(character));
20 }
21 source.close( );
22
23 openFileWriteRobust(tallyfile, "frequencies.txt");
24 sheet.writeTable(tallyfile);
25 tallyfile.close( );
26 return 0;
27 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 75
1 #include "FileUtilities.h"
2 #include "TallySheet.h"
3 #include <iostream>
4 #include <fstream>
5 #include <vector>
6 #include <algorithm>
7 using namespace std;
8
9 int main( ) {
10 ifstream source;
11 ofstream tallyfile;
12
13 cout << "This program tallies a set of integer scores." << endl;
14 cout << "There should be one integer per line." << endl << endl;
15
16 openFileReadRobust(source);
17 vector<int> values;
18 while (source.good( )) {
19 int val;
20 source >> val;
21 if (source.good( ))
22 values.push back(val);
23 else { // ignore noninteger line
24 source.clear( );
25 source.ignore(std::numeric limits<int>::max( ), '\n');
26 }
27 }
28 source.close( );
29
30 int small, large;
31 small = *min element(values.begin( ), values.end( ));
32 large = *max element(values.begin( ), values.end( ));
33 TallySheet<int> sheet(small, large);
34 for (int i=0; i < values.size( ); i++)
35 sheet.increment(values[i]);
36
37 openFileWriteRobust(tallyfile, "frequencies.txt");
38 sheet.writeTable(tallyfile);
39 tallyfile.close( );
40 cout << "The tally has been written." << endl;
41
42 return 0;
43 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 76
1 #ifndef SCORE H
2 #define SCORE H
3
4 /**
5 * \brief A score for a single turn from game of Mastermind.
6 *
11 class Score {
12 private:
13 int numBlack;
14 int numWhite;
15
16 public:
17 /**
18 * \brief Create score with given black and white components.
19 *
Figure 34: Contents of Score.h file for Mastermind project. No Score.cpp is necessary since all
function bodies are in-lined.
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 77
1 #ifndef PATTERN H
2 #define PATTERN H
3
4 #include "Score.h"
5 #include <vector>
6
7 /**
8 * \brief Class for storing a color pattern for Mastermind.
9 */
10 class Pattern {
11 private:
12 std::vector<int> pegList;
13
14 public:
15 /**
16 * \brief Construct a new pattern.
17 *
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 78
46 /**
47 * \brief Compare the current pattern to another and calculate the score.
48 *
51 */
56 *
58 */
1 #include "Pattern.h"
2 #include <set>
3 #include <algorithm> // includes the 'count' method
4 #include <stdlib.h>
5 using namespace std;
6
7 Pattern::Pattern(const int numPegs) : pegList(numPegs) {
8 int i;
9 for (i=0; i < numPegs; i++)
10 pegList[i] = 0;
11 }
12
13 int Pattern::len( ) const {
14 return pegList.size( );
15 }
16
17 int Pattern::getPegColor(const int index) const {
18 return pegList[index];
19 }
20
21 void Pattern::setPegColor(const int index, const int colorId) {
22 pegList[index] = colorId;
23 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 79
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 80
1 #ifndef TEXTINPUT H
2 #define TEXTINPUT H
3
4 #include "Pattern.h"
5 #include <string>
6 #include <vector>
7 using std::string;
8 using std::vector;
9
10 /**
11 * \brief Class for dealing with text−based input for the Mastermind game.
12 */
13 class TextInput {
14 private:
15 int lengthOfPattern;
16 int numColorsInUse;
17 string palette;
18
19 public:
20 /**
21 * \brief Create a new text input instance.
22 *
23 *\param colorNames a list of strings (each color must start with a different letter)
24 /*
34 int queryLengthOfPattern( );
35
36 /**
37 * \brief Ask the user how many colors to use for secret pattern.
38 *
43 int queryNumberOfColors( );
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 81
44 /**
45 * \brief Ask the user maximum number of guesses to be allowed.
46 *
48 */
53 *
55 */
60 *
62 */
68 */
1 #include "TextInput.h"
2 #include <iostream>
3 #include <sstream>
4 using namespace std;
5
6 TextInput::TextInput(const vector<string>& colorNames) :
7 lengthOfPattern(0), numColorsInUse(0), palette("") {
8 vector<string>::const iterator iter;
9 for (iter=colorNames.begin( ); iter!=colorNames.end( ); ++iter)
10 palette.push back((*iter)[0]);
11 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 82
12 int TextInput:: readInt(const string& prompt, int small, int large) const {
13 string buffer;
14 int answer = small − 1; // intentionally invalid
15 while (!(small <= answer && answer <= large)) {
16 cout << prompt << " (from " << small << " to " << large << ")? ";
17 cin >> buffer;
18 stringstream converter;
19 converter << buffer;
20 converter >> answer;
21 if (!converter.fail( )) {
22 if (!(small <= answer && answer <= large))
23 cout << "Integer must be from " << small << " to " << large << "." << endl;
24 }
25 else {
26 cout << "That is not a valid integer." << endl;
27 }
28 }
29 return answer;
30 }
31
32 int TextInput::queryLengthOfPattern( ) {
33 lengthOfPattern = readInt("How many pegs are in the secret", 1, 10);
34 return lengthOfPattern;
35 }
36
37 int TextInput::queryNumberOfColors( ) {
38 numColorsInUse = readInt("How many colors are available", 2, palette.size( ));
39 return numColorsInUse;
40 }
41
42 int TextInput::queryNumberOfTurns( ) const {
43 return readInt("How many turns are allowed", 1, 20);
44 }
45
46 bool TextInput::queryNewGame( ) const {
47 cout << endl;
48 cout << "Would you like to play again? " << endl;
49 string answer;
50 cin >> answer;
51 return (answer == "y" answer == "Y" answer == "yes"
52 answer == "YES" answer == "YES");
53 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 83
1 #ifndef TEXTOUTPUT H
2 #define TEXTOUTPUT H
3
4 #include "Pattern.h"
5 #include "Score.h"
6 #include <string>
7 #include <vector>
8 using std::string;
9 using std::vector;
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 84
10 /**
11 * \brief Provide text−based output for the Mastermind game.
12 */
13 class TextOutput {
14 private:
15 string colorOptions;
16 int currentTurnNum;
17 int lengthOfPattern;
18 int maxNumberOfTurns;
19
20 public:
21 /**
22 * \brief Construct a new TextOutput instance.
23 *
24 *\param colorNames a sequence of strings (each color must start with a different letter)
25 /*
51 */
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 85
1 #include "TextOutput.h"
2 #include <iostream>
3 using namespace std;
4
5 TextOutput::TextOutput(const vector<string>& colorNames) :
6 currentTurnNum(0), lengthOfPattern(0), maxNumberOfTurns(0), colorOptions("") {
7 vector<string>::const iterator iter;
8 for (iter=colorNames.begin( ); iter!=colorNames.end( ); ++iter)
9 colorOptions.push back((*iter)[0]);
10 }
11
12 void TextOutput::startGame(int lengthOfPattern, int maxNumberOfTurns) {
13 currentTurnNum = 0;
14 lengthOfPattern = lengthOfPattern;
15 maxNumberOfTurns = maxNumberOfTurns;
16 }
17
18 void TextOutput::displayTurn(const Pattern& guess, const Score& result) {
19 currentTurnNum++;
20 cout << "On turn " << currentTurnNum << " of " << maxNumberOfTurns
21 << " guess " << patternAsString(guess) << " scored "
22 << result.getNumBlack( ) << " black and " << result.getNumWhite( )
23 << " white." << endl;
24 }
25
26 void TextOutput::announceVictory(const Pattern& secret) const {
27 cout << endl;
28 cout << "Congratulations, you won!" << endl;
29 cout << "The secret was " << patternAsString(secret) << endl;
30 }
31
32 void TextOutput::announceDefeat(const Pattern& secret) const {
33 cout << endl;
34 cout << "The secret was " << patternAsString(secret) << endl;
35 cout << "Good luck next time." << endl;
36 }
37
38 string TextOutput:: patternAsString(const Pattern& thePattern) const {
39 string display;
40 int i;
41 for (i=0; i< lengthOfPattern; i++)
42 display.push back( colorOptions[thePattern.getPegColor(i)]);
43 return display;
44 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 86
1 #ifndef MASTERMIND H
2 #define MASTERMIND H
3
4 #include "TextInput.h"
5 #include "TextOutput.h"
6
7 /**
8 * \brief Main class for the Mastermind game.
9 */
10 class Mastermind {
11 private:
12 TextInput& inputManager;
13 TextOutput& outputManager;
14
15 public:
16 /**
17 * \brief Create a new instance of the Mastermind game.
18 *
19 * \param inputManager instance of class that gathers input from the user
20 * \param outputManager instance of class that displays output to the user
21 / *
27 */
28 void runSingleGame( );
29 };
30
31 #endif
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 87
1 #include "Mastermind.h"
2
3 Mastermind::Mastermind(TextInput& inputManager, TextOutput& outputManager) :
4 inputManager(inputManager), outputManager(outputManager) {
5 bool playAgain = true;
6 while (playAgain) {
7 runSingleGame( );
8 playAgain = inputManager.queryNewGame( );
9 }
10 }
11
12 void Mastermind:: runSingleGame( ) {
13 // get parameters from the user
14 int lengthOfPattern = inputManager.queryLengthOfPattern( );
15 int numberOfColors = inputManager.queryNumberOfColors( );
16 int maxNumberOfTurns = inputManager.queryNumberOfTurns( );
17 outputManager.startGame(lengthOfPattern, maxNumberOfTurns);
18
19 // pick a new secret
20 Pattern secret(lengthOfPattern);
21 secret.randomize(numberOfColors);
22
23 // start playing
24 int round = 0;
25 Pattern guess(lengthOfPattern);
26 bool victory = false;
27 while (round < maxNumberOfTurns && !victory) {
28 round++;
29 // enact a single turn
30 guess = inputManager.enterGuess( );
31 Score result = guess.compareTo(secret);
32 outputManager.displayTurn(guess, result);
33 if (result.getNumBlack( ) == lengthOfPattern)
34 victory = true;
35 }
36
37 if (victory)
38 outputManager.announceVictory(secret);
39 else
40 outputManager.announceDefeat(secret);
41 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher
A. FULL SOURCE CODE Page 88
1 #include "Mastermind.h"
2
3 int main( ) {
4 vector<string> palette;
5 palette.push back("Red");
6 palette.push back("Blue");
7 palette.push back("Green");
8 palette.push back("White");
9 palette.push back("Yellow");
10 palette.push back("Orange");
11 palette.push back("Purple");
12 palette.push back("Turquoise");
13
14 TextInput input(palette);
15 TextOutput output(palette);
16 Mastermind game(input, output);
17
18 return 0;
19 }
A Transition Guide from Python to C++ Michael H. Goldwasser and David Letscher