Notes
Notes
Notes
examples for some of the material, they do not cover exactly the same
material as the current version of the course. In particular, the current
version of the course now specifically covers some C++ 11 and C++ 14
concepts that are not discussed in these notes. There will also be other
differences, and the emphasis on some concepts will be different.
CS 246
Object-Oriented Software Development
May 1, 2016
Outline
Introduction to basic UNIX software development tools and object-oriented program-
ming in C++ to facilitate designing, coding, debugging, testing, and documenting of
medium-sized programs. Students learn to read a specification and design software
to implement it. Important skills are selecting appropriate data structures and control
structures, writing reusable code, reusing existing code, understanding basic perfor-
mance issues, developing debugging skills, and learning to test a program.
1 Shell 1
1.1 File System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2 Quoting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 Shell Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4 System Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.5 Source-Code Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.5.1 Global Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.5.2 Local Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.6 Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.7 File Permission . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.8 Input/Output Redirection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.9 Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.10 Shift . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1.11 Shell Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1.12 Arithmetic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.13 Routine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.14 Control Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.14.1 Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.14.2 Selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.14.3 Looping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
1.15 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
1.15.1 Hierarchical Print Script . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
1.15.2 Cleanup Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2 C++ 39
2.1 Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.2 C/C++ Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.3 First Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.4 Comment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
2.5 Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
2.5.1 Basic Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.5.2 Variable Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.5.3 Type Qualifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.5.4 Literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.5.5 C++ String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
iii
iv CONTENTS
2.6 Input/Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.6.1 Formatted I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2.6.1.1 Formats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.6.1.2 Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.6.1.3 Input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.7 Expression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.7.1 Conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2.7.2 Coercion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.8 Unformatted I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.9 Math Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
2.10 Control Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.10.1 Block . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
2.10.2 Selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.10.3 Multi-Exit Loop (Review) . . . . . . . . . . . . . . . . . . . . . . . . . . 62
2.10.4 Static Multi-Level Exit . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
2.10.5 Non-local Transfer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
2.10.6 Dynamic Multi-Level Exit . . . . . . . . . . . . . . . . . . . . . . . . . . 70
2.11 Command-line Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
2.12 Type Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
2.12.1 Enumeration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
2.12.2 Pointer/Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
2.12.3 Aggregates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
2.12.3.1 Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
2.12.3.2 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
2.12.3.3 Union . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
2.13 Dynamic Storage Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
2.14 Type Nesting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
2.15 Type Equivalence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
2.16 Namespace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
2.17 Type-Constructor Literal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
2.18 Routine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
2.18.1 Argument/Parameter Passing . . . . . . . . . . . . . . . . . . . . . . . . . 91
2.18.2 Array Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
2.19 Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
2.20 Declaration Before Use, Routines . . . . . . . . . . . . . . . . . . . . . . . . . . 95
2.21 Preprocessor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
2.21.1 File Inclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
2.21.2 Variables/Substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
2.21.3 Conditional Inclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
2.22 Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
2.22.1 Object Member . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
2.22.2 Operator Member . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
2.22.3 Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
2.22.3.1 Literal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
2.22.3.2 Conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
CONTENTS v
Index 189
1 Shell
• Computer interaction requires mechanisms to display information and perform operations.
• Graphical interface easy for simple tasks, but seldom programmable for complex operations.
• Command-line interface harder for simple tasks (more typing), but allows programming.
• Unix shells falls into two basic camps: sh (ksh, bash) and csh (tcsh), each with slightly
different syntax and semantics.
c Peter A. Buhr
1
2 CHAPTER 1. SHELL
• Command typed after prompt not executed until Enter/Return key pressed.
$ dateEnter # print current date
Thu Aug 20 08:44:27 EDT 2016
$ whoamiEnter # print userid
jfdoe
$ echo Hi There!Enter # print any string
Hi There!
• Command comment begins with hash (#) and continues to end of line.
$ # comment text . . . does nothing
$
• Arrow keys △/▽ move forward/backward through command history (see Section 1.3, p. 7).
• Most commands have options, specified with a minus followed by one or more characters,
which affect how the command operates.
$ uname -m # machine type
x86 64
$ uname -s # operating system
Linux
$ uname -a # all system information
Linux ubuntu1204-006 3.13.0-57-generic #95~precise1-Ubuntu SMP
• Options are normally processed left to right; one option may cancel another.
• Shell operation returns an exit status via optional integer N (return code).
exit [ N ]
• Files are containers for data stored on persistent storage (usually disk).
• File names organized in N-ary tree: directories are vertexes, files are leaves.
• Directory named “/ ” is the root of the file system (Windows uses “\”).
• bin, lib, usr, include : system commands, system library and include files.
• tmp : temporary files created by commands (shared among all users).
• home or u : user files are located in this directory.
• Directory for a particular user (jfdoe) is called their home directory.
• Shell special character “ ~” (tilde) expands to user’s home directory.
~/cs246/a1/q1x.C # => /u/jfdoe/cs246/a1/q1x.C
.
◦ “ ” points to current directory.
./cs246/a1/q1x.C # => /u/jfdoe/cs246/a1/q1x.C
• Shell provides concept of working directory (current directory), which is the active loca-
tion in the file hierarchy.
1.2. QUOTING 5
• E.g., after sign on, the working directory starts at user’s home directory.
• File name not starting with “/ ” is prefixed with working directory to create necessary abso-
lute pathname.
• Relative pathname is file name/path prefixed with working directory.
◦ E.g., when user jfdoe signs on, home/working directories set to /u/jfdoe.
.bashrc # => /u/jfdoe/.bashrc
cs246/a1/q1x.C # => /u/jfdoe/cs246/a1/q1x.C
1.2 Quoting
• Quoting controls how shell interprets strings of characters.
• Backslash ( \ ) : escape any character, including special characters.
$ echo .[!.]* # globbing pattern
.bashrc .emacs .login .vimrc
$ echo \.\[\!\.\]\* # print globbing pattern
.[!.]*
• Backquote ( 8 ) or $() : execute text as a command, and substitute with command output.
$ echo 8whoami 8 # $ whoami => jfdoe
jfdoe
$ echo $(date)
Tue Dec 15 22:44:23 EST 2015
• Double quote ( " ) : protect everything except double quote, backquote, and dollar sign
(variables, see Section 1.11), which can be escaped.
• To stop prompting or output from any shell command, type <ctrl>-c (C-c), i.e., press <ctrl>
then c key, causing shell to interrupt current command.
$ echo "abc
> C-c
$
help [command-name]
$ help cd
cd: cd [-L|-P] [dir]
Change the shell working directory. . . .
cd [directory]
• pwd : print absolute path for working directory (when you’re lost).
$ pwd
/u/jfdoe/cs246
• history and “!” (bang!) : print a numbered history of most recent commands entered and
access them.
$ history $ !2 # rerun 2nd history command
1 date whoami
2 whoami jfdoe
3 echo Hi There $ !! # rerun last history command
4 help whoami
5 cd . . jfdoe
6 pwd $ !ec # rerun last history command starting with “ec”
7 history echo Hi There
Hi There
◦ !N rerun command N
◦ !! rerun last command
◦ !xyz rerun last command starting with the string “xyz”
$ alias d=date
$ d
Mon Oct 27 12:56:36 EDT 2008
$ alias off="clear; exit" # why quotes?
$ off # clear screen before terminating shell
◦ Always use quotes to prevent problems.
◦ Aliases are composable, i.e., one alias references another.
$ alias now="d"
$ now
Mon Oct 27 12:56:37 EDT 2008
◦ Without argument, print all currently defined alias names and strings.
$ alias
alias d=’date’
alias now=’d’
alias off=’clear; exit’
◦ Alias CANNOT be command argument (see page 25).
$ alias a1=/u/jfdoe/cs246/a1
$ cd a1 # alias only expands for command
bash: cd: a1: No such file or directory
◦ Alias entered on command line disappears when shell terminates.
◦ Two options for making aliases persist across sessions:
1. insert the alias commands in the appropriate (hidden) .shellrc file,
2. place a list of alias commands in a file (often .aliases) and source (see page 28)
that file from the .shellrc file.
• type (csh which) : indicate how name is interpreted as command.
$ type now
now is aliased to ‘d’
$ type d
d is aliased to ‘date’
$ type date
date is hashed (/bin/date) # hashed for faster lookup
$ type -p date # -p => only print command file-name
/bin/date
$ type fred # no “fred” command
bash: type: fred: not found
$ type -p fred # no output
• man : print information about command, option names (see page 3) and function.
10 CHAPTER 1. SHELL
$ man bash
... # information about “bash” command
$ man man
... # information about “man” command
$ cat q1.h
... # print file q1.h completely
$ more q1.h
... # print file q1.h one screen at a time
# type “space” for next screen, “q” to stop
$ cp f1 f2 # copy file f1 to f2
$ cp f1 f2 f3 d # copy files f1, f2, f3 into directory d
$ cp -r d1 d2 d3 # copy directories d1, d2 recursively into directory d3
1.4. SYSTEM COMMANDS 11
$ rm f1 f2 f2 # file list
$ rm -r d1 d2 # directory list, and all subfiles/directories
$ rm -r f1 d1 f2 # file and directory list
which always uses the -i option (see page 10) on commands cp, mv and rm.
• Alias can be overridden by quoting or escaping the command name.
$ rm -f f1 # override -i and force removal
$ "rm" -r d1 # override alias completely
$ \rm -r d1
• lp/lpstat/lprm/lpstat : add, query and remove files from the printer queues.
lp [ -d printer-name ] file-list
lpstat [ -d ] [ -p [ printer-name ] ]
lprm [ -P printer-name ] job-number
◦ return 0 if files equal (no output) and non-zero otherwise (output difference)
◦ cmp generates the first difference between the files.
file x file y
$ cmp x y
1 a\n a\n x y differ: char 7, line 4
2 b\n b\n
3 c\n c\n
4 d\n e\n
5 g\n h\n
6 h\n i\n
7 g\n
$ diff x y
4,5c4 # replace lines 4 and 5 of 1st file
< d # with line 4 of 2nd file
< g
---
> e
6a6,7 # after line 6 of 1st file
> i # add lines 6 and 7 of 2nd file
> g
◦ Useful for checking output from previous program with current program.
◦ ssh : (secure shell) encrypted, remote-login between hosts (computers).
ssh [ -Y ] [ -l user ] [ user@ ] hostname
global working
repository copy
checkout
CVS checkin
project2
project2 V1
V1 pull V2 checkout
V2 push V3 checkin files
◦ Perform the following steps to setup your userid in the global repository.
◦ Log into https://git.uwaterloo.ca/cs246/1161 (note https) with your WatIAm userid/password
via LDAP login (top right).
◦ Click logout “⇒” (top right) in Dashboard to logout.
◦ These steps activate your account at the University repository.
◦ config : registering.
$ git config --global user.name "Jane F Doe"
$ git config --global user.email jfdoe@uwaterloo.ca
$ git config --list
Jane F Doe
jfdoe@uwaterloo.ca
...
16 CHAPTER 1. SHELL
◦ If globbing pattern does not match any files, the pattern becomes the argument (includ-
ing wildcards).
$ echo q*.ww q[a-z].cc # files do not exist so no expansion
q*.ww q[a-z].cc
csh prints: echo: No match.
◦ Hidden files, starting with “.” (dot), are ignored by globbing patterns
∗ ⇒ * does not match all file names in a directory.
◦ Pattern .* matches all hidden files:
∗ match “.”, then zero or more characters, e.g., .bashrc, .login, etc., and “.”, “ . .”
∗ matching “.”, “ . .” can be dangerous
$ rm -r .* # remove hidden files, and current/parent directory!!!
◦ Pattern .[!.]* matches all single “.” hidden files but not “ .” and “ . .” directories.
∗ match “.”, then any character NOT a “ .”, and zero or more characters
∗ ⇒ there must be at least 2 characters, the 2nd character cannot be a dot
∗ “.” starts with dot but fails the 2nd pattern requiring another character
∗ “. .” starts with dot but the second dot fails the 2nd pattern requiring non-dot char-
acter
◦ recursively find only file names in file list (excluding hidden files) to a maximum depth
of 3 matching patterns t* or *.C.
$ find * -maxdepth 3 -a -type f -a \( -name "t*" -o -name "*.C" \)
test.cc
q1.C
testdata/data.C
• egrep : (extended global regular expression print) search & print lines matching pattern in
files (Google). (same as grep -E)
egrep -irnv pattern-string file-list
◦ list lines that match start of line “^”, “#include”, 1 or more space or tab “[ ]+”, either
“"” or “<”, 1 or more characters “.+”, either “"” or “>”, end of line “$” in files with
suffix “.h” or “.cc”
$ egrep ’^#include[ ]+["<].+[">]$’ *.{h,cc} # why quotes ?
egrep: *.h: No such file or directory
q1.cc:#include <iostream>
q1.cc:#include <iomanip>
q1.cc:#include “q1.h”
• File or directory has permissions, read, write, and execute/search for the 3 sets of users.
• Columns are: permissions, #-of-directories (including “.” and “ . .”), owner, group, file size,
change date, file name.
◦ directory in which the user has read, write and execute permissions,
◦ group has only read and execute permissions,
◦ others have no permissions at all.
◦ file: rw- r-- ---, owner read/write, group only read, other none.
◦ directory: rwx --- ---, owner read/write/execute, group/other none.
chmod g-r,o-r,g-w,o-w foo # long form, remove read/write for group/others users
chmod go-rw foo # short form
chmod g=rx cs246 # allow group users read/search
chmod -R g+rw cs246/a5 # allow group users read/write, recursively
• To achieve desired access, must associate permission along entire pathname and files.
1.8. INPUT/OUTPUT REDIRECTION 21
• By default, these are connected to the keyboard (input) and screen (output/error).
1
0
command
2
• To close an input file from the keyboard, type <ctrl>-d (C-d), i.e., press <ctrl> then d key,
causing the shell to close the keyboard input file.
• Redirection allows:
• Redirection performed using operators < for input and > / >> for output to/from other sources.
$ sort -n < input 1> output 2> errors
1>
1>> output
0
input sort
< 2> errors
2>>
• Can tie standard error to output (and vice versa) using “>&” ⇒ both write to same place.
$ sort -n < input >& both # stderr (2) goes to stdout (1)
$ sort -n < input 1> output 2>&1 # stderr (2) goes to stdout (1)
$ sort -n < input 2> errors 1>&2 # stdout (1) goes to stderr (2)
1
2>&1 output
0
input sort
< 1>&2 errors
2
$ sort 2>&1 > output # tie stderr to screen, redirect stdout to “output”
$ sort > output 2>&1 # redirect stdout to “output”, tie stderr to “output”
• Shell pipe operator | makes standard output for a command the standard input for the next
command, without creating intermediate file.
1.9 Script
• A shell program or script is a file (scriptfile) containing shell commands to be executed.
#!/bin/bash [ -x ]
date # shell and OS commands
whoami
echo Hi There
• First line begins with magic comment: “#! ” (sha-bang) with shell pathname for executing
the script.
• Forces specific shell to be used, which is run as a subshell.
• If “#! ” is missing, the subshell is the same as the invoking shell for sh shells (bash) and sh
is used for csh shells (tcsh).
• Optional -x is for debugging and prints trace of the script during execution.
• Script can be invoked directly using a specific shell:
$ bash scriptfile # direct invocation
Sat Dec 19 07:36:17 EST 2009
jfdoe
Hi There!
or as a command if it has executable permissions.
$ chmod u+x scriptfile # Magic, make script file executable
$ ./scriptfile # command execution
Sat Dec 19 07:36:17 EST 2009
jfdoe
Hi There!
Why no World?
$ cat scriptfile
#!/bin/bash
echo ${#} # number of command-line arguments
echo ${0} ${1} ${2} ${3} ${4} # some arguments
echo "${*}" # all arguments as a single string
echo "${@}" # all arguments as separate strings
echo ${$} # process id of executing subshell
exit 21 # script exit status
$ ./scriptfile a1 a2 a3 a4 a5
5 # number of arguments
scriptfile a1 a2 a3 a4 # script-name / args 1-4
a1 a2 a3 a4 a5 # args 1-5, 1 string
a1 a2 a3 a4 a5 # args 1-5, 5 strings
27028 # process id of subshell
$ echo ${?} # print script exit status
21
1.10. SHIFT 25
1.10 Shift
• shift [ N ] : destructively shift parameters to the left N positions, i.e., ${1}=${N+1}, ${2}=${N+2},
etc., and ${#} is reduced by N.
◦ If no N, 1 is assumed.
◦ If N is 0 or greater than ${#}, there is no shift.
• Beware concatenation.
$ cd $a1data # change to /u/jfdoe/cs246/a1data
Where does this move to?
• Always use braces to allow concatenation with other text.
$ cd ${a1}data # cd /u/jfdoe/cs246/a1data
• A script executes in its own subshell with a copy of calling shell’s environment variables
(works across different shells), but not calling shell’s locals or arguments.
$ ./scriptfile # execute script in subshell
Envir:
.. $E0 $E1 $E2... Shell
.
copied
• When a (sub)shell ends, changes to its environment variables do not affect the containing
shell (environment variables only affect subshells).
• Only put a variable in the environment list to make it accessible by subshells.
1.12 Arithmetic
• Arithmetic requires integers, 3 + 7, not strings, "3" + "17".
$ i=3+1
$ j=${i}+2
$ echo ${i} ${j}
3+1 3+1+2
1.13 Routine
• Routine is a script in a script.
routine name() { # number of parameters depends on call
# commands
}
duplicate duplicate
dup
refactor source dup duplicate source dup
1.14.1 Test
• test ( [ ] ) command compares strings, integers and queries files.
test operation
string1 = string2 equal (not ==)
string1 != string2 not equal
integer1 -eq integer2 equal
integer1 -ne integer2 not equal
integer1 -ge integer2 greater or equal
integer1 -gt integer2 greater
integer1 -le integer2 less or equal
integer1 -lt integer2 less
-d file exists and directory
-e file exists
-f file exists and regular file
-r file exists with read permission
-w file exists with write permission
-x file exists with executable or searchable
$ i=3
$ test 3 -lt 4 # integer test
$ echo ${?} # true
0
$ [ 8whoami 8 = "jfdoe" ] # string test, need spaces
$ echo ${?} # false
1
$ [ 2 -lt ${i} -o 8whoami 8 = "jfdoe" ] # compound test
$ echo ${?} # true
0
$ [ -e q1.cc ] # file test
$ echo ${?} # true
0
1.14.2 Selection
• An if statement provides conditional control-flow.
if test-command if test-command ; then
then
commands commands
elif test-command elif test-command ; then
then
commands commands
... ...
else else
commands commands
fi fi
if diff file1 file2 > /dev/null ; then # ignore diff output, check exit status
echo "same files"
else
echo "different files"
fi
if [ -x /usr/bin/cat ] ; then
echo "cat command available"
else
echo "no cat command"
fi
1.14. CONTROL STRUCTURES 31
• When dereferencing, always quote variables, except for safe variables ${#}, ${$}, ${?},
which generate numbers.
• A case statement selectively executes one of N alternatives based on matching a string
expression with a series of patterns (globbing), e.g.:
case expression in
pattern | pattern | . . . ) commands ;;
...
* ) commands ;; # optional match anything (default)
esac
• When a pattern is matched, the commands are executed up to “;;”, and control exits the case
statement.
• If no pattern is matched, the case statement does nothing.
• E.g., command with only one of these options:
-h, --help, -v, --verbose, -f file, --file file
use case statement to process option:
usage() { . . . } # print message and terminate script
verbose=no # default value
case "${1}" in # process single option
’-h’ | ’--help’ ) usage ;;
’-v’ | ’--verbose’ ) verbose=yes ;;
’-f’ | ’--file’ ) # has additional argument
shift 1 # access argument
file="${1}"
;;
* ) usage ;; # default, has to be one argument
esac
if [ ${#} -ne 1 ] ; then usage ; fi # check only one argument remains
... # execute remainder of command
1.14.3 Looping
• while statement executes its commands zero or more times.
while test-command while test-command ; do
do
commands commands
done done
32 CHAPTER 1. SHELL
• for statement is a specialized while statement for iterating with an index over list of whitespace-
delimited strings.
for index [ in list ] ; do
commands
done
for name in ric peter jo mike ; do
echo ${name}
done
for arg in "${@}" ; do # process parameters, why quotes?
echo ${arg}
done
• A while/for loop may contain break and continue to terminate loop or advance to the next
loop iteration.
i=1 # process files data1, data2, . . .
while [ 0 ] ; do # while true, infinite loop
file=data${i} # create file name
if [ ! -f "${file}" ] ; then break ; fi # file not exist, stop ?
... # process file
if [ ${?} -ne 0 ] ; then continue ; fi # bad return, next file
... # process file
i=$((${i} + 1)) # advance to next file
done
34 CHAPTER 1. SHELL
1.15 Examples
• How many times does word $1 appear in file $2?
#!/bin/bash
cnt=0
for word in 8cat "${2}" 8 ; do # process file one blank-separated word at a time
if [ "${word}" = "${1}" ] ; then
cnt=$((${cnt} + 1))
fi
done
echo ${cnt}
8
18
25
1.15. EXAMPLES 35
opt= ; files= ;
while [ ${#} -ne 0 ] ; do # option and files in any order
case "${1}" in
-l) opt=l ;;
-d) opt=d ;;
-h | -help | --help | -*)
echo ’Usage: hi [ -l | -d | -s ] directory-list . . .’ 1>&2
exit 1 ;;
*) files="${files} ${1}" ;;
esac
shift
done
case $opt in
l) find ${files} -exec ls -ldh {} \; | sort -k9,9f | \
sed ’s|\./| |’ | sed ’s|[^ /]*/| |g’ ;; # add tab then spaces
d) du -ah ${files} | sort -k2,2f | sed ’s|[^ /]*/| |g’ ;; # replace tab
*) find ${files} -print | sort -f | sed ’s|[^/]*/| |g’ ;; # sort ignore case
esac
exit 0
$ hi $ hi -d jfdoe $ hi -l
. 64K jfdoe drwx------ 3 jfdoe jfdoe 4.0K May 1 07:05 .
jfdoe 60K cs246 drwx------ 3 jfdoe jfdoe 4.0K May 1 07:05 jfdoe
cs246 28K a1 drwx------ 4 jfdoe jfdoe 4.0K May 1 07:06 cs246
a1 4.0K q1x.C drwx------ 2 jfdoe jfdoe 4.0K May 1 07:32 a1
q1x.C 8.0K q2y.cc -rw------- 1 jfdoe jfdoe 3.2K May 1 07:32 q1x.C
q2y.h 8.0K q2y.h -rw------- 1 jfdoe jfdoe 8.0K May 1 07:32 q2y.h
q2y.cc 4.0K q3z.cpp -rw------- 1 jfdoe jfdoe 4.1K May 1 07:32 q2y.cc
q3z.cpp 28K a2 -rw------- 1 jfdoe jfdoe 160 May 1 07:32 q3z.cpp
a2 16K q1p.C drwx------ 2 jfdoe jfdoe 4.0K May 1 07:34 a2
q1p.C 4.0K q2q.cc -rw------- 1 jfdoe jfdoe 14K May 1 07:33 q1p.C
q2q.cc 4.0K q2r.h -rw------- 1 jfdoe jfdoe 800 May 1 07:33 q2q.cc
q2r.h -rw------- 1 jfdoe jfdoe 160 May 1 07:33 q2r.h
1.15. EXAMPLES 37
2.1 Design
• Design is a plan for solving a problem, but leads to multiple solutions.
• Need the ability to compare designs.
• Aim is to achieve lowest coupling or highest independence (i.e., each module can stand alone
or close to it).
• Module is read and understood as a unit ⇒ changes do not effect other modules and can be
isolated for testing purposes (like stereo components).
1. Before compilation, preprocessor language (cpp) modifies (text-edits) the program (see
Section 2.21, p. 97).
2. During compilation, template (generic) language adds new types and routines (see
Section 2.33, p. 149).
3. During compilation,
◦ C programming language specifying basic declarations and control flow.
◦ C++ programming language specifying advanced declarations and control flow.
• A programmer uses the three programming languages as follows:
39
40 CHAPTER 2. C++
(translator) cc1plus
-W, -v, -g, -S, -O1/2/3, -c
assembly code
(assembler) as
object code
other object-code -o, -l, -L
files and libraries ld (linker)
./a.out object
Java C C++
import java.lang.*; // implicit #include <stdio.h> #include <iostream> // access to output
class Hello { using namespace std; // direct naming
public static
void main( String[ ] args ) { int main() { int main() { // program starts here
System.out.println("Hello!"); printf( "Hello!\n" ); cout << "Hello!" << endl;
System.exit( 0 ); return 0; return 0; // return 0 to shell, optional
} } }
}
• #include <iostream> copies (imports) basic I/O descriptions (no equivalent in Java).
• using namespace std allows imported I/O names to be accessed directly (otherwise quali-
fication is necessary, see Section 2.16, p. 87).
• cout << "Hello!" << endl prints "Hello!" to standard output, called cout (System.out in
Java, stdout in C).
• Routine exit (Java System.exit) terminates a program at any location and returns a code to
the shell, e.g., exit( 0 ) (#include <cstdlib>).
2.4. COMMENT 41
◦ Literals EXIT SUCCESS and EXIT FAILURE indicate successful or unsuccessful ter-
mination status.
◦ e.g., return EXIT SUCCESS or exit( EXIT FAILURE ).
• C program-files use suffix .c; C++ program-files use suffixes .C / .cpp / .cc.
• create shell alias for g++ to use options g++ -Wall -g -std=c++11
2.4 Comment
• Comment may appear where whitespace (space, tab, newline) is allowed.
Java / C / C++
1 /* . . . * /
2 // remainder of line
2.5 Declaration
• A declaration introduces names or redeclares names from previous declarations.
42 CHAPTER 2. C++
• Java types short and long are created using type qualifiers (see Section 2.5.3).
Java / C / C++
char a, b, c, d;
int i, j, k;
double x, y, z;
id :
• C/C++ provide size (short, long) and signed-ness (signed ⇒ positive/negative, unsigned
⇒ positive only) qualifiers.
• int provides relative machine-specific types: usually int ≥ 4 bytes for 32/64-bit computer,
long ≥ int, long long ≥ long.
• #include <climits> specifies names for lower/upper bounds of a type’s range of values for a
machine, e.g., a 32/64-bit computer:
2.5. DECLARATION 43
• C/C++ provide two basic real-floating types float and double, and one real-floating type
generated with type qualifier.
• #include <cfloat> specifies names for precision and magnitude of real-floating values.
2.5.4 Literals
• Variables contain values; values have constant (C) or literal (C++) meaning.
3 = 7; // disallowed
• C/C++ and Java share almost all the same literals for the basic types.
type literals
boolean false, true
character ’a’, ’b’, ’c’
string "abc", "a b c"
integral decimal : 123, -456, 123456789
octal, prefix 0 : 0144, -045, 04576132
hexadecimal, prefix 0X / 0x : 0xfe, -0X1f, 0xe89abc3d
real-floating .1, 1., -1., 0.52, -7.3E3, -6.6e-2, E/e exponent
44 CHAPTER 2. C++
• Literal are undesignated, compiler chooses smallest type, or designated, programmer chooses
type with suffixes: L/l ⇒ long, LL/ll ⇒ long long, U/u ⇒ unsigned, and F/f ⇒ float.
-3 // undesignated, int
-3L // designated, long int
1000000000000000000 // undesignated, long long int (why?)
1000000000000000000LL // designated, long long int
4U // designated, unsigned int
100000000000000000ULL // designated, unsigned long long int
3.5E3 // undesignated, double
3.5E3F // designated, float
◦ "abc" is 4 characters: ’a’, ’b’, ’c’, and ’\0’, which occupies 4 bytes.
◦ String cannot contain a character with the value ’\0’.
◦ Computing string length requires O(N) search for ’\0’.
• Escape sequence provides quoting of special characters in a character/string literal using a
backslash, \.
’\\’ backslash
’\’’ single quote
’\"’ double quote
’\t’, ’\n’ (special names) tab, newline, ...
’\0’ zero, string termination character
• C/C++ provide user literals (write-once/read-only) with type qualifier const (Java final).
Java C/C++
final char Initial = ’D’; const char Initial = ’D’;
final short int Size = 3, SupSize; const short int Size = 3, SupSize = Size + 7;
SupSize = Size + 7; disallowed
final double PI = 3.14159; const double PI = 3.14159;
2.5. DECLARATION 45
• C/C++ const variable must be assigned a value at declaration (see also Section 2.22.6, p. 113);
the value can be the result of an expression.
• A constant variable can (only) appear in contexts where a literal can appear.
Size = 7; // disallowed
• Good practise is to name literals so all usages can be changed via its initialization value.
(see Section 2.12.1, p. 75)
const short int Mon=0, Tue=1, Wed=2, Thu=3, Fri=4, Sat=5, Sun=6;
• find routines return value string::npos of type string::size type, if unsuccessful search.
46 CHAPTER 2. C++
• Note different call syntax c.substr( 2, 3 ) versus substr( c, 2, 3 ) (see Section 2.22, p. 99).
• Count and print words in string line containing words composed of lower/upper case letters.
0 1 2 3 4 5 6 7 8 9 ...
line The ”q u i c k” b r o w n\n find first
b r o w n\n substr
2.6 Input/Output
• Input/Output (I/O) is divided into two kinds:
1. Formatted I/O transfers data with implicit conversion of internal values to/from human-
readable form.
2. Unformatted I/O transfers data without conversion, e.g., internal integer and real-
floating values.
48 CHAPTER 2. C++
• Formatted I/O occurs to/from a stream file, and values are conversed based on the type of
variables and format codes.
• C++ has three implicit stream files: cin, cout and cerr, which are implicitly declared and
opened (Java has in, out and err).
• C has stdin, stdout and stderr, which are implicitly declared and opened.
• #include <iostream> imports all necessary declarations to access cin, cout and cerr.
• Stream files other than 3 implicit ones require declaring each file object.
2.6. INPUT/OUTPUT 49
• File types, ifstream/ofstream, indicate whether the file can be read or written.
• File-name type, "myinfile"/"myoutfile", is char * (not string, see page 53).
• Declaration opens an operating-system file making it accessible through the variable name:
where both files are located in the directory where the program is run.
• Check for successful opening of a file using the stream member fail, e.g., infile.fail(), which
returns true if the open failed and false otherwise.
if ( infile.fail() ) . . . // open failed, print message and exit
if ( outfile.fail() ) . . . // open failed, print message and exit
• C++ I/O library overloads (see Section 2.19, p. 93) the bit-shift operators << and >> to per-
form I/O.
• C I/O library uses fscanf(outfile,. . .) and fprintf(infile,. . .), which have short forms scanf(. . .)
and printf(. . .) for stdin and stdout.
• Both I/O libraries can cascade multiple I/O operations, i.e., input or output multiple values
in a single expression.
2.6.1.1 Formats
• Format of input/output values is controlled via manipulators defined in #include <iomanip>.
• Manipulators are not variables for input/output, but control I/O formatting for all liter-
als/variables after it, continuing to the next I/O expression for a specific stream file.
• Except manipulator setw, which only applies to the next value in the I/O expression.
• endl is not the same as ’\n’, as ’\n’ does not flush buffered data.
• For input, skipws/noskipws toggle between ignoring whitespace between input tokens and
reading whitespace (i.e., tokenize versus raw input).
2.6.1.2 Output
• Java output style converts values to strings, concatenates strings, and prints final long string:
• C/C++ output style has a list of formats and values, and output operation generates strings:
cout << i << " " << j << endl; // print each string as formed
• No implicit conversion from the basic types to string in C++ (but one can be constructed).
• While it is possible to use the Java string-concatenation style in C++, it is incorrect style.
#include <stdio.h>
fprintf( stdout, "i:%2d r:%7.2f c:%c s:%s\n", i, r, c, s );
2.6.1.3 Input
• C++ formatted input has implicit character conversion for all basic types and is extensible to
user-defined types (Java/C use an explicit Scanner/fscanf).
2.6. INPUT/OUTPUT 51
Java C C++
import java.io.*; #include <stdio.h> #include <fstream>
import java.util.Scanner; FILE *in = fopen( "f", "r" ); ifstream in( "f" );
Scanner in =
new Scanner(new File("f")); FILE *out = fopen( "g", "w" ); ofstream out( "g" );
PrintStream out =
new PrintStream( "g" ); int i, j; int i, j;
int i, j; for ( ;; ) { for ( ;; ) {
while ( in.hasNext() ) { fscanf( in, "%d%d", &i, &j ); in >> i >> j;
i = in.nextInt(); j = in.nextInt(); if ( feof(in) ) break; if ( in.fail() ) break;
out.println( "i:"+i+" j:"+j ); fprintf(out,"i:%d j:%d\n",i,j); out << "i:" << i
} } <<" j:"<<j<<endl;
in.close(); close( in ); }
out.close(); close( out ); // in/out closed implicitly
• Numeric input values are C/C++ undesignated literals : 3, 3.5e-1, etc., separated by whites-
pace.
• Character/string input values are characters separated by whitespace.
• Type of operand indicates the kind of value expected in the stream.
◦ stream member eof returns true if the end of file is reached and false otherwise.
◦ stream member fail returns true for invalid values OR no value if end of file is reached,
and false otherwise.
• Safer to check fail and then check eof.
for ( ;; ) {
cin >> i;
if ( cin.eof() ) break; // should use “fail()”
cout << i << endl;
}
52 CHAPTER 2. C++
• If "abc" is entered (invalid integer value), fail becomes true but eof is false.
• Generates infinite loop as invalid data is not skipped for subsequent reads.
• Stream is implicitly converted to bool in an integer context: if ! fail(), true; otherwise false.
while ( cin >> i ) . . . // read and check cin to != 0
• After unsuccessful read, call clear() to reset fail() before next read.
#include <iostream>
#include <limits> // numeric limits
using namespace std;
int main() {
int n;
cout << showbase; // prefix hex with 0x
cin >> hex; // input hex value
for ( ;; ) {
cout << "Enter hexadecimal number: ";
cin >> n;
if ( cin.fail() ) { // problem ?
if ( cin.eof() ) break; // eof ?
cerr << "Invalid hexadecimal number" << endl;
cin.clear(); // reset stream failure
cin.ignore( numeric limits<int>::max(),’\n’); // skip until newline
} else {
cout << hex << "hex:" << n << dec << " dec:" << n << endl;
}
}
cout << endl;
}
• getline( stream, string, char ) reads strings with white spaces allowing different delimiting
characters (no buffer overflow):
getline( cin, c, ’ ’ ); // read characters until ’ ’ => cin >> c
getline( cin, c, ’@’ ); // read characters until ’@’
getline( cin, c, ’\n’ ); // read characters until newline (default)
2.6. INPUT/OUTPUT 53
• Read in file-names, which may contain spaces, and process each file:
#include <fstream>
using namespace std;
int main() {
ifstream fileNames( "fileNames" ); // open list of file names
string fileName;
for ( ;; ) { // process each file
getline( fileNames, fileName ); // name may contain spaces
if ( fileNames.fail() ) break; // handle no terminating newline
ifstream file( fileName ); // open each file
// read and process file
}
}
• In C, routine feof returns true when eof is reached and fscanf returns EOF.
• Parameters in C are always passed by value (see Section 2.18.1, p. 91), so arguments to
fscanf must be preceded with & (except arrays) so they can be changed.
#include <sstream>
string tok, line = " The \"quick\" brown\n";
stringstream ss;
ss.str( line ); // initialize input stream
while ( ss >> tok ) { // read each word
cout << tok << endl; // print word
}
ss.clear(); // reset
ss.str( "17" ); // initialize input stream
int i;
ss >> i; // convert characters to number
cout << i << endl; // print number
2.7 Expression
Java C/C++ priority
postfix ., [ ], call ::, ., -> [ ], call, cast high
prefix +, -, !, ~, cast, +, -, !, ~, &, *, cast,
(unary) new new, delete, sizeof
binary *, /, % *, /, %
+, - +, -
bit shift <<, >>, >>> <<, >>
relational <, <=, >, >=, instanceof <, <=, >, >=
equality ==, != ==, !=
bitwise & and &
^ exclusive-or ^
| or |
logical && short-circuit &&
|| ||
conditional ?: ?:
assignment =, +=, -=, *=, /=, %= =, +=, -=, *=, /=, %=
<<=, >>=, >>>=, &=, ^=, |= <<=, >>=, &=, ^=, |=
comma , low
• Subexpressions and argument evaluation is unspecified (Java left to right)
( i + j ) * ( k + j ); // either + done first
( i = j ) + ( j = i ); // either = done first
g( i ) + f( k ) + h( j ); // g, f, or h called in any order
f( p++, p++, p++ ); // arguments evaluated in any order
• Beware of overflow.
unsigned int a = 4294967295, b = 4294967295, c = 4294967295;
(a + b) / c; // => 0 as a+b overflows leaving zero
a / c + b / c; // => 2
Perform divides before multiplies (if possible) to keep numbers small.
• C++ relational/equality return false/true; C return 0/1.
• Referencing (address-of), &, and dereference, *, operators (see Section 2.12.2, p. 76) do not
exist in Java because access to storage is restricted.
• General assignment operators only evaluate left-hand side (lhs) once:
v[ f(3) ] += 1; // only calls f once
v[ f(3) ] = v[ f(3) ] + 1; // calls f twice
• Bit-shift operators, << (left), and >> (right) shift bits in integral variables left and right.
2.7.1 Conversion
• Conversion transforms a value to another type by changing the value to the new type’s
representation (see Section 2.22.3.2, p. 104).
• Conversions occur implicitly by compiler or explicitly by programmer using cast operator
or C++ static cast operator.
int i; double d;
d = i; // implicit (compiler)
d = (double) i; // explicit with cast (programmer)
d = static cast<double>( i ); // C++
• C++ supports casting among user defined types (see Section 2.22, p. 99).
2.7.2 Coercion
• Coercion reinterprets a value to another type but the result is may not be meaningful in the
new type’s representation.
◦ E.g., when a value is truncated or converting non-zero to true, the result is nonsense in
the new type’s representation.
• Also, having type char represent ASCII characters and integral (byte) values allows:
char ch = ’z’ - ’a’; // character arithmetic!
• But the most common coercion is through pointers (see Section 2.12.2, p. 76):
int i, *ip = &i; // ip is a pointer to an integer
double d, *dp = &d; // dp is a pointer to a double
dp = (double *) ip; // lie, say dp points at double but really an integer
dp = reinterpret cast<double *>( ip );
Using explicit cast, programmer has lied to compiler about type of ip.
• Signed/unsigned coercion.
unsigned int size;
cin >> size; // negatives become positives
if ( size < 0 ) cout << "invalid range" << endl;
int arr[size];
• cin does not check for negative values for unsigned ⇒ -2 read as 4294967294.
• Good practice is to limit narrowing conversions and NEVER lie about a variable’s type.
2.8. UNFORMATTED I/O 57
• When data does not have to be seen by a human, use efficient unformatted I/O so no conver-
sions.
• read and write routines directly transfer bytes from/to a file, where each takes a pointer to
the data and number of bytes of data.
read( char *data, streamsize num );
write( char *data, streamsize num );
• Read/write of types other than characters requires a coercion cast (see Section 2.7.2) or C++
reinterpret cast.
#include <iostream>
#include <fstream>
using namespace std;
int main() {
const unsigned int size = 10;
int arr[size];
{ // read array
ifstream infile( "myfile" ); // open input file “myfile”
infile.read( reinterpret cast<char *>(&arr), size * sizeof( arr[0] ) ); // coercion
} // close file
. . . // modify array
{ // print array
ofstream outfile( "myfile" ); // open output file “myfile”
outfile.write( (char *)&arr, size * sizeof( arr[0] ) ); // coercion
} // close file
}
• Need special command to view unformatted file as printable characters (not shell cat).
• E.g., view internal values as byte sequence for 32-bit int values (little endian, no newlines).
$ od -t u1 myfile
0000000 0 0 0 0 1 0 0 0 2 0 0 0 3 0 0 0
0000020 4 0 0 0 5 0 0 0 6 0 0 0 7 0 0 0
0000040 8 0 0 0 9 0 0 0
M E 2.7182818284590452354 // e
M LOG2E 1.4426950408889634074 // log 2 e
M LOG10E 0.43429448190325182765 // log 10 e
M LN2 0.69314718055994530942 // log e 2
M LN10 2.30258509299404568402 // log e 10
M PI 3.14159265358979323846 // pi
M PI 2 1.57079632679489661923 // pi/2
M PI 4 0.78539816339744830962 // pi/4
M 1 PI 0.31830988618379067154 // 1/pi
M 2 PI 0.63661977236758134308 // 2/pi
M 2 SQRTPI 1.12837916709551257390 // 2/sqrt(pi)
M SQRT2 1.41421356237309504880 // sqrt(2)
M SQRT1 2 0.70710678118654752440 // 1/sqrt(2)
• pow(x,y) (xy ) is computed using logarithms, 10 y log x (versus repeated multiplication), when
y is non-integral value ⇒ y ≥ 0
pow( -2.0, 3.0 ); −23 = −2 × −2 × −2 = −8
pow( -2.0, 3.1 ); −23.1 = 103.1×log−2.0 = nan (not a number)
int main() {
double a = 3.5, b = 2.1, c = -1.2;
double dis = sqrt( b * b - 4.0 * a * c ), dem = 2.0 * a;
cout << "root1: " << ( -b + dis ) / dem << endl;
cout << "root2: " << ( -b - dis ) / dem << endl;
}
2.10. CONTROL STRUCTURES 59
2.10.1 Block
• Compound statement serves two purposes:
• Good practice is use a block versus single statement to allow adding statements.
no block block
if ( x > y ) if ( x > y ) {
x = 0; x = 0;
}
• Nested block variables are allocated last-in first-out (LIFO) from the stack memory area.
{ // block1 stack
// variables free
block2
block1
• Variable names can be reused in different blocks, i.e., possibly shadow (hiding) prior vari-
ables.
int i = 1; . . . // first i
{ int k = i, i = 2, j = i; . . . // k = first i, second i overrides first
{ int i = 3;. . . // third i (overrides second)
2.10.2 Selection
• C/C++ selection statements are if and switch (same as Java).
• For nested if statements, else matches closest if, which results in the dangling else problem.
• E.g., reward WIDGET salesperson who sold $10,000 or more worth of WIDGETS and dock
pay of those who sold less than $5,000.
• Redundant if statement.
if ( a < b ) return true; return a < b;
else return false;
• Assign in expressions causes problems because conditional expression is tested for 6= 0, i.e.,
expr ≡ expr != 0 (use -Wall).
• Only one label for each case clause but a list of case clauses is allowed.
• Once case label matches, the clauses statements are executed, and control continues to the
next statement. (comment each fall through)
• If no case clause is matched and there is a default clause, its statements are executed, and
control continues to the next statement.
• Unless there is a break statement to prematurely exit the switch statement.
• It is a common error to forget the break in a case clause.
• Otherwise, the switch statement does nothing.
• case label does not define a block:
switch ( i ) {
case 3: { // start new block
int j = i; // can now declare new variable
...
}
}
62 CHAPTER 2. C++
• Exit should not be restricted to only top and bottom, i.e., can appear in the loop body:
loop
...
exit when i >= 10;
...
end loop
• Loop exit has ability to change kind of loop solely by moving the exit line.
• In general, your coding style should allow changes and insertion of new code with minimal
changes to existing code.
• Loop exit is outdented or commented or both (Eye Candy) ⇒ easy to find without searching
entire loop body.
• The for version is more general as easily modified to have a loop index.
for ( int i = 0; i < 10; i += 1 ) { // add/remove loop index
◦ flag variable is used solely to affect control flow, i.e., does not contain data associated
with a computation.
• Short-circuit && does not exist in all programming languages (Shell test), and requires
knowledge of Boolean algebra (false and anything is?).
• Extra test after loop can be eliminated by moving it into loop body.
for ( i = 0; ; i += 1 ) {
if ( i >= size ) { ... // not found
break;
} // exit
if ( key == list[i] ) { ... // found
break;
} // exit
} // for
for ( int i = 0; ; i += 1 ) {
if ( i >= size ) {
list[size].count = 1; // add element to list
list[size].data = key;
size += 1; // needs check for array overflow
break;
} // exit
if ( key == list[i].data ) {
list[i].count += 1; // increment counter
break;
} // exit
} // for
• None of these approaches is best in all cases; select the approach that best fits the problem.
• Labels have routine scope (see Section 2.5.2, p. 42), i.e., cannot be overridden in local
blocks.
int L1; // identifier L1
L2: ; // identifier L2
{
double L1; // can override variable identifier
double L2; // cannot override label identifier
}
• Labelled break/continue transfer control out of the control structure with the corresponding
label, terminating any block that it passes through.
L1: ;
...
goto L1; // transfer backwards, up
goto L2; // transfer forward, down
...
L2: ;
{
. . . declarations . . .
switch ( . . . ) {
for ( . . . ) {
. . . goto L1; . . . // exit block
. . . goto L2; . . . // exit switch
. . . goto L3; . . . // exit loop
}
L3: ; // empty statement
...
}
L2: ;
...
}
L1: ;
• Why are labels at the end of control structures not as good as at start?
int i, j; int i, j;
bool flag1 = false;
for ( i = 0; i < 10; i += 1 ) { for ( i = 0; i < 10 && ! flag1; i += 1 ) {
bool flag2 = false;
for ( j = 0; j < 10; j += 1 ) { for ( j = 0; j < 10 &&
! flag1 && ! flag2; j += 1 ) {
... ...
if ( . . . ) goto B2; // outdent if ( . . . ) flag2 = true;
else {
. . . // rest of loop . . . // rest of loop
if ( . . . ) goto B1; // outdent if ( . . . ) flag1 = true;
else {
. . . // rest of loop . . . // rest of loop
} // if
} // if
} B2: ; } // for
if ( ! flag1 ) {
. . . // rest of loop . . . // rest of loop
} // if
} B1: ; } // for
◦ Flag variables are the variable equivalent to a goto because they can be set/reset/tested
at arbitrary locations in a program.
◦ Cannot be used to create a loop (i.e., cause a backward branch); hence, all repeated
execution is clearly delineated by loop constructs.
◦ Cannot be used to branch into a control structure.
• Only use goto to perform static multi-level exit, e.g., simulate labelled break and continue.
• Multi-level exits appear infrequently, but are extremely concise and execution-time efficient.
• Modularization: any contiguous code block can be factored into a (helper) routine and
called from anywhere in the program (modulo scoping rules).
68 CHAPTER 2. C++
• Modularized version fails to compile because labels only have routine scope (local vs
non-local scope).
◦ Normal return transfers to statement after the call, often implying completion of rou-
tine’s algorithm.
◦ Non-local return transfers to statement not after the call, indicating an ancillary com-
pletion (but not necessarily an error).
• Multiple returns often simulated with return code, i.e., value indicating kind of return.
2.10. CONTROL STRUCTURES 69
• A routine implicitly can raise exceptions or have exceptions propagate through it.
• A routine can explicitly specify if it does or does not have alternate outcomes, i.e., may or
may not propagate exceptions.
void f(. . .) noexcept(true) { /* can NOT propagate exceptions */ }
void g(. . .) noexcept(false) { /* can propagate exceptions */ }
◦ useful if a handler cannot deal with an exception but needs to propagate same exception
to handler further down the stack.
◦ provided by a throw statement without an exception type:
. . . throw; // no exception type
where a raise must be in progress.
2.11. COMMAND-LINE ARGUMENTS 71
• Used as a general cleanup when a non-specific exception occurs and reraise to continue
exception.
• Robustness results because exceptions are active versus passive (return codes), forcing pro-
grams to react immediately when an exceptional event occurs.
• Second form is used to receive command-line arguments from the shell, where the command-
line string-tokens are transformed into C/C++ parameters.
72 CHAPTER 2. C++
• argc is the number of string-tokens on the command line, including the command name.
• Java does not include command name, so number of tokens is one less.
• Because shell only has string variables, a shell argument of "32" does not mean integer 32,
and may have to converted.
Java C/C++
class Prog {
public static void main( String[ ] args ) { int main( int argc, char *argv[ ] ) {
switch ( args.length ) { switch( argc ) {
case 0: . . . // no args case 1: . . . // no args
break; break;
case 1: . . . args[0] . . . // 1 arg case 2: . . . args[1] . . . // 1 arg
break; break;
case . . . // others args case . . . // others args
break; break;
default: . . . // usage message default: . . . // usage message
System.exit( 1 ); exit( EXIT FAILURE );
} }
... ...
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib> // exit
using namespace std; // direct access to std
2.12.1 Enumeration
• Can create literals with const declaration (see page 44).
const short int Mon=0,Tue=1,Wed=2,Thu=3,Fri=4,Sat=5,Sun=6;
short int day = Sat;
days = 42; // assignment allowed
• An enumeration is a type defining a set of named literals with only assignment, comparison,
and conversion to integer:
enum Days {Mon,Tue,Wed,Thu,Fri,Sat,Sun}; // type declaration, implicit numbering
Days day = Sat; // variable declaration, initialization
enum {Yes, No} vote = Yes; // anonymous type and variable declaration
enum Colour {R=0x1,G=0x2,B=0x4} colour; // type/variable declaration, explicit numbering
colour = B; // assignment
Enumerators Red and Black conflict. (Java enumerators are always qualified).
2.12.2 Pointer/Reference
• pointer/reference is a memory address.
int x, y;
int *p1 = &x, *p2 = &y, *p3 = 0; // or p3 is uninitialized
• Used to access the value stored in the memory location at pointer address.
◦ pointer assignment
p1 = &x; // pointer assignment
p2 = p1; // pointer assignment
no dereferencing to access values.
◦ value assignment
*p2 = *p1; // value assignment, y = x
dereferencing to access values.
• Value used more often than pointer.
• C++ reference pointer provides extra implicit dereference to access target value:
int &r1 = x, &r2 = y;
r2 = ((r1 + r2) * (r2 - r1)) / (r1 - r2);
• Hence, difference between plain and reference pointer is an extra implicit dereference.
◦ I.e., do you want to write the “*”, or let the compiler write the “*”?
2.12. TYPE CONSTRUCTOR 77
• C++ solves the missing pointer assignment by making reference pointer a constant (const),
like a plain variable.
◦ Hence, a reference pointer cannot be assigned after its declaration, so pointer assign-
ment is impossible.
◦ As a constant, initialization must occur at declaration, but initializing expression has
implicit referencing because address is always required.
int &r1 = &x; // error, should not have & before x
• Java solves this problem by only using reference pointers, only having pointer assignment,
and using a different mechanism for value assignment (clone).
• Is there one more solution?
• Since reference means its target’s value, address of a reference means its target’s address.
int &r = x;
&r; ⇒ &x not &r
• Hence, cannot initialize reference to reference or pointer to reference.
int & &rr = r; // reference to reference, rewritten &r
int &*pr = &r; // pointer to reference
pr r x
70 100 5
80 70 100
• Type qualifiers (see Section 2.5.3, p. 42) can be used to modify pointer types.
• p4 may point at any short int variable (const or non-const) and may not change its value.
Why can p4 point to a non-const variable?
• p5 may only point at the int variable x and may change the value of x through the pointer.
◦ * const and & are constant pointers but * const has no implicit dereferencing like &.
• p6 may only point at the long int variable z and may not change its value.
• Pointer variable has memory address, so it is possible for a pointer to address another pointer
or object containing a pointer.
int *px = &x, **ppx = &px,
&rx = x, *prx = ℞
ppx px
108 100
124 108
5 x
prx rx 100
100 100
132 116
• Pointer/reference type-constructor is not distributed across the identifier list.
2.12.3 Aggregates
• Aggregates are a set of homogeneous/heterogeneous values and a mechanism to access the
values in the set.
2.12.3.1 Array
• Array is a set of homogeneous values.
int array[10]; // 10 int values
• Array type, int, is the type of each set value; array dimension, 10, is the maximum number
of values in the set.
• An array can be structured to have multiple dimensions.
int matrix[10][20]; // 10 rows, 20 columns => 200 int values
char cube[5][6][7]; // 5 rows, 6 columns, 7 deep => 210 char values
Common dimension mistake: matrix[10, 20]; means matrix[20] because 10, 20 is a comma
expression not a dimension list.
• Number of dimensions is fixed at compile time, but dimension size may be:
2.12. TYPE CONSTRUCTOR 79
Java C/C++
int x[ ] = new int[6] int x[6]
x 6 1 7 5 0 8 -1 x 1 7 5 0 8 -1
• Variables pvar and parr have same type but one points at a variable and other an array!
• Programmer decides if variable or array by not using or using subscripting.
*pvar // variable
*parr // variable, arr[0]
parr[0], parr[3] // array, many
pvar[3] // array, but wrong
80 CHAPTER 2. C++
◦ parenthesize type qualifiers based on operator priority (see Section 2.7, p. 54),
◦ read inside parenthesis outwards,
◦ start with variable name,
◦ end with type name on the left.
2.12.3.2 Structure
• Structure is a set of heterogeneous values, including (nested) structures.
Java C/C++
class Foo { struct Foo {
int i = 3; int i = 3; // C++11
. . . // more fields . . . // more members
} }; // semi-colon terminated
• Structure fields are called members subdivided into data and routines1 .
• All members of a structure are accessible (public) by default.
• Structure can be defined and instances declared in a single statement.
struct Complex { double re, im; } s; // definition and declaration
◦ C/C++ are unique in having the priority of selection operator “.” higher than dereference
operator “*”.
◦ Hence, *p.f executes as *(p.f), which is incorrect most of the time. Why?
◦ To get the correct effect, use parenthesis: (*p).f.
(*sp1).name.first[0] = ’a’;
(*sp1).age = 34;
(*sp1).marks[5] = 95;
◦ performs dereference and selection in correct order, i.e., p->f rewritten as (*p).f.
sp1->name.first[0] = ’a’;
sp1->age = 34;
sp1->marks[5] = 95;
◦ for reference pointers, -> is unnecessary because r.f means (*r).f, so r.f makes more
sense than (&r)->f.
• Makes switching from a pointer to reference difficult (. ↔ ->).
• Structures must be compared member by member.
◦ comparing bits (e.g., memcmp) fails as alignment padding leaves undefined values be-
tween members.
• Recursive types (lists, trees) are defined using a self-referential pointer in a structure:
struct Student {
... // data members
Student *link; // pointer to another Student
}
2.12.3.3 Union
• Union is a set of heterogeneous values, including (nested) structures, where all members
overlay the same storage.
union U {
char c[4]; c
u. i 01100011 01100001 01110100 00000000
int i; f
float f; 0 1 2 3
} u;
82 CHAPTER 2. C++
• Used to access internal representation or save storage by reusing it for different purposes at
different times.
u.c[0] = ’c’; u.c[1] = ’a’; u.c[2] = ’t’; u.c[3] = ’\0’;
cout << u.c << " " << u.i << " " << u.f << " " << bitset<32>(u.i) << endl;
produces:
cat 7627107 1.06879e-38 00000000011101000110000101100011
• Reusing storage is dangerous and can usually be accomplished via other techniques.
• C/C++ are unmanaged languages because the programmer is involved in memory manage-
ment, e.g., no garbage collection so dynamic storage must be explicitly freed.
Java C C++
class Foo { char c1, c2; } struct Foo { char c1, c2; }; struct Foo { char c1, c2; };
Foo r = new Foo(); struct Foo *p = Foo *p = new Foo();
r.c1 = ’X’; (struct Foo *) // coerce p->c1 = ’X’;
// r garbage collected malloc( // allocate delete p; // explicit free
sizeof(struct Foo) // size Foo &r = *new Foo();
); r.c1 = ’X’;
p->c1 = ’X’; delete &r; // explicit free
free( p ); // explicit free
heap stack
Foo
free
Foo
code static
r
memory
low address high address
• C++ operator new performs all 3 steps implicitly; each step is explicit in C.
2.13. DYNAMIC STORAGE MANAGEMENT 83
◦ C has implicit cast from void * (pointer to anything) to specific pointer (dangerous!).
◦ Good practise in C is to use a cast so compiler can verify type compatibility on assign-
ment.
• Parenthesis after the type name in the new operation are optional.
• For reference r, why is there a “*” before new and an “&” in the delete?
• Storage for dynamic allocation comes from a memory area called the heap.
• If heap is full (i.e., no more storage available), malloc returns 0, and new raises an exception.
• Before storage can be used, it must be allocated.
Foo *p; // forget to initialize pointer with “new”
p->c1 = ’R’; // places ’R’ at some random location in memory
Called an uninitialized variable.
• After storage is no longer needed it must be explicitly deleted.
Foo *p = new Foo;
p = new Foo; // forgot to free previous storage
Called a memory leak.
• After storage is deleted, it must not be used:
delete p;
p->c1 = ’R’; // result of dereference is undefined
Called a dangling pointer.
• Unlike Java, C/C++ allow all types to be dynamically allocated not just object types, e.g.,
new int.
• As well, C/C++ allow all types to be allocated on the stack, i.e., local variables of a block.
• Declaration of a pointer to an array is complex in C/C++ (see also page 79).
• Because no array-size information, no dimension for an array pointer.
int *parr = new int[10]; // think parr[ ], pointer to array of 10 ints
• Variables pvar and parr have the same type but one is allocated with the basic new and the
other with the array new.
84 CHAPTER 2. C++
• Special syntax must be used to call the corresponding deletion operation for a variable or an
array (any dimensions):
delete pvar; // basic delete : single element
delete [ ] parr; // array delete : multiple elements (any dimension)
• If basic delete is used on an array, only the first element is freed (memory leak).
• If array delete is used on a variable, storage after the variable is also freed (often failure).
• Never do this:
delete [ ] parr, pvar; // => (delete [ ] parr), pvar;
• Stack allocation eliminates explicit storage-management (simpler) and is more efficient than
heap allocation — use it whenever possible.
{ // good, use stack { // bad, unnecessary dynamic allocation
int size; int *sizep = new int;
cin >> size; cin >> *sizep;
int arr[size] int *arr = new int[*sizep];
... ...
delete [ ] arr;
delete sizep;
} // size, arr implicitly deallocated }
• Declaration of a pointer to a matrix is complex in C/C++, e.g., int *m[5] could mean:
m 9 ... m 92640
8 ... ..
.
1 ...
2 ...
3 ...
• References to types nested inside another type are qualified with “::”.
enum Colour { R, G, B, Y, C, M };
enum Colour2 { R2, G2, B2 }; // prevent name clashes
struct Face {
Colour2 Eyes, Hair;
};
struct Person {
Colour shirt;
Colour2 pants;
Face looks[10];
};
Colour c = R;
Colour2 pc = R2;
Face pretty;
struct Foo {
struct Bar { // moved outside
int i;
};
struct Bar bars[10];
};
struct Foo foo;
struct Bar bar; // no qualification
• Types T1 and T2 are structurally equivalent, but have different names so they are incom-
patible, i.e., initialization of variable t2 is disallowed.
• An alias is a different name for same type, so alias types are equivalent.
• All combinations of assignments are allowed among s1, s2 and s3, because they have the
same type name “short int”.
• Use to prevent repetition of large type names:
void f( map<string, pair<vector<string>, map<string,string> > > p1,
map<string, pair<vector<string>, map<string,string> > > p2 );
2.16 Namespace
• C++ namespace is used to organize programs and libraries composed of multiple types and
declarations to deal with naming conflicts.
• E.g., namespace std contains all the I/O declarations and container types.
• Names in a namespace form a declaration region, like the scope of block.
• C++ allows multiple namespaces to be defined in a file, as well as among files (unlike Java
packages).
• Types and declarations do not have to be added consecutively.
Java C++
Foo.T t = new Foo.T(); Foo::T *t = new Foo::T();
88 CHAPTER 2. C++
Java C++
import Foo.T; using Foo::T; // declaration
import Foo.*; using namespace Foo; // directive
• using declaration unconditionally introduces an alias (like typedef, see Section 2.15, p. 86)
into the current scope for specified entity in namespace.
• using directive conditionally introduces aliases to current scope for all entities in names-
pace.
◦ If name already exists in current scope, alias is ignored; if name already exists from
using directive in current scope, using fails.
namespace Foo { int i = 0; }
namespace Bar { int i = 1; }
{
int i = 2;
using namespace Foo; // i exists in scope, alias ignored
}
{
using namespace Foo;
using namespace Bar; // i exists from using directive
i = 0; // conflict failure, ambiguous reference to ’i’
}
◦ May appear in namespace and block scope, but not structure scope.
int j = 0; // global
int main() {
int j = 3; // local
using namespace Foo; // conditional import: Colour, i, C, Bar (not j)
Colour c; // Foo::Colour
cout << i << endl; // Foo::i
C x; // Foo::C
cout << ::j << endl; // global
cout << j << endl; // local
cout << Foo::j << " " << Bar::j << endl; // qualification
using namespace Bar; // conditional import: shrint, C() (not j)
shrint s = 4; // Bar::shrint
using Foo::j; // disallowed : unconditional import
C(); // disallowed : ambiguous “struct C” or “int C()”
}
• Never put a using declaration/directive in a header file (.h) (pollute local namespace) or
before #include (can affect names in header file).
• C uses 0/NULL and C++ uses nullptr to initialize pointers (Java null).
• Array and structure initialization can occur as part of a declaration.
int m[2][3] = { {93, 67, 72}, {77, 81, 86} }; // multidimensional array
struct { int i; struct { double r, i; } s; } d = { 1, { 3.0, 2.1 } }; // nested structure
• It is possible to leave out the first dimension, and its value is inferred from the number of
values in that dimension:
char s[ ] = "abcde"; // 1st dimension inferred as 6 (Why 6?)
int v[ ] = { 0, 1, 2, 3, 4 } // 1st dimension inferred as 5
int m[ ][3] = { {93, 67, 72}, {77, 81, 86} }; // 1st dimension inferred as 2
2.18 Routine
• Routine with no parameters has parameter void in C and empty parameter list in C++:
. . . rtn( void ) { . . . } // C: no parameters
. . . rtn() { . . . } // C++: no parameters
◦ In C, empty parameters mean no information about the number or types of the param-
eters is supplied.
• If a routine is qualified with inline, the routine is expanded (maybe) at the call site, i.e.,
unmodularize, to increase speed at the cost of storage (no call).
• Routine cannot be nested in another routine (possible in gcc).
• Java requires all routines to be defined in a class (see Section 2.22.1, p. 101).
• Each routine call creates a new block on the stack containing its parameters and local vari-
ables, and returning removes the block.
• Variables declared outside of routines are defined in implicit static block.
int i; // static block, global
const double PI = 3.14159;
void rtn( double d ) // code block
{ static const int w = 7; // create static block
} // remove stack block
int main() // code block
{ int j; // create stack block
{ int k; // create stack block
rtn( 3.0 );
} // remove stack block
} // remove stack block
PI
w
heap
d
k
i
0 3.1 7 memory
• Static block is a separate memory area from stack and heap areas and is default zero filled.
◦ constants (anywhere)
bool check( int key ) {
static const int vals[ ] = { 12, 15, 34, 67, 88 }; // allocated ONCE
...
• Java/C, parameter passing is by value, i.e., basic types and object references are copied.
• C++, parameter passing is by value or reference depending on the type of the parameter.
• For value parameters, each argument-expression result is copied into the corresponding pa-
rameter in the routine’s block on the stack, which may involve an implicit conversion.
• For reference parameters, each argument-expression result is referenced (address of) and this
address is pushed on the stack as the corresponding reference parameter.
92 CHAPTER 2. C++
struct S { double d; };
void r1( S s, S &rs, S * const ps ) {
s.d = rs.d = ps->d = 3.0;
}
int main() {
S s1 = {1.0}, s2 = {2.0}, s3 = {7.5};
r1( s1, s2, &s3 );
// s1.d = 1.0, s2.d = 3.0, s3.d = 3.0
}
s1 s2 s3 s1 s2 s3
argument 1.0 2.0 7.5 1.0 3.0 3.0
100 200 300 100 200 300
parameter 1.0 200 300 3.0 200 300
s rs ps s rs ps
call return
• C-style pointer-parameter simulates the reference parameter, but requires & on argument and
use of -> with parameter.
• Value passing is most efficient for small values or for large values accessed frequently be-
cause the values are accessed directly (not through pointer).
• Reference passing is most efficient for large values accessed less frequently because the
values are not duplicated in the routine but accessed via pointers.
• Use type qualifiers to create read-only reference parameters so the corresponding argument
is guaranteed not to change:
void r2( const int &i, const Complex &c, const int v[ ] ) {
i = 3; // disallowed, read only!
c.re = 3.0;
v[0] = 3;
}
r2( i + j, (Complex){ 1.0, 7.0 }, (int [3]){ 3, 2, 7 } ); // allowed!
• Provides efficiency of pass by reference for large variables, security of pass by value as
argument cannot change, and allows literals and temporary variables as arguments.
• C++ parameter can have a default value, which is passed as the argument value if no argu-
ment is specified at the call site.
2.19. OVERLOADING 93
• In a parameter list, once a parameter has a default value, all parameters to the right must
have default values.
• In a call, once an argument is omitted for a parameter with a default value, no more argu-
ments can be specified to the right of it.
• Instead, array argument is a pointer to the array that is copied into the corresponding array
parameter (pass by value).
• A formal parameter array declaration can specify the first dimension with a dimension value,
[10] (which is ignored), an empty dimension list, [ ], or a pointer, *:
double sum( double v[5] ); double sum( double v[ ] ); double sum( double *v );
double sum( double *m[5] ); double sum( double *m[ ] ); double sum( double **m );
• Good practice uses middle form as it clearly indicates variable can be subscripted.
double sum( int cols, double v[ ] ) { double sum( int rows, int cols, double *m[ ] ) {
double total = 0.0; double total = 0.0;
for ( int c = 0; c < cols; c += 1 ) for ( int r = 0; r < rows; r += 1 )
total += v[c]; for ( int c = 0; c < cols; c += 1 )
return total; total += m[r][c];
} return total;
}
2.19 Overloading
• Overloading is when a name has multiple meanings in the same context.
• Most languages have overloading, e.g., most built-in operators are overloaded on both inte-
gral and real-floating operands, i.e., + operator is different for 1 + 2 than for 1.0 + 2.0.
• Overloading requires disambiguating among identical names based on some criteria.
94 CHAPTER 2. C++
• E.g., after changing a variable’s type from int to double, all operations implicitly change
from integral to real-floating.
• Number and unique parameter types but not the return type are used to select among a
name’s different meanings:
int r( int i, int j ) { . . . } // overload name r three different ways
int r( double x, double y ) { . . . }
int r( int k ) { . . . }
r( 1, 2 ); // invoke 1st r based on integer arguments
r( 1.0, 2.0 ); // invoke 2nd r based on double arguments
r( 3 ); // invoke 3rd r based on number of arguments
• Subtle cases:
int i; unsigned int ui; long int li;
void r( int i ) { . . . } // overload name r three different ways
void r( unsigned int i ) { . . . }
void r( long int i ) { . . . }
r( i ); // int
r( ui ); // unsigned int
r( li ); // long int
• Reference parameter types with same base type are ambiguous at call:
int r( int i ) {. . .} // cannot be called
int r( int &i ) {. . .} // cannot be called
int r( const int &i ) {. . .} // cannot be called
int i = 3;
const int j = 3;
r( i ); // disallowed : ambiguous
r( j ); // disallowed : ambiguous
Cannot cast argument to select r( int i ), r( int &i ) or r( const int &i ).
• Overload/conversion confusion: I/O operator << is overloaded with char * to print a C string
and void * to print pointers.
char c; int i;
cout << &c << " " << &i << endl; // print address of variables
type of &c is char *, so printed as C string, which is undefined; type of &i is int *, which is
converted to void *, so printed as an address.
• Overlap between overloading and default arguments for parameters with same type:
If the overloaded routine bodies are essentially the same, use a default argument, other-
wise use overloaded routines.
int i = 3;
{
cout << i << endl; // which i?
int i = 4;
cout << i << endl;
}
• E.g., allowing main routine to appear first, and for separate compilation (see Section 2.23, p. 115).
2.21 Preprocessor
• Preprocessor is a text editor that modifies the program text before compilation.
• -E run only the preprocessor step and write preprocessor output to standard out.
$ g++ -E *.cc . . .
... much output from the preprocessor
• C++ convention drops suffix “.h” for its standard libraries and has special file names for
equivalent C files, e.g., cstdio versus stdio.h.
#include <stdio.h> // C style
#include <cstdio> // C++ style
#include "user.h"
◦ files within the directory can now be referenced by relative name using #include <file-name>.
98 CHAPTER 2. C++
2.21.2 Variables/Substitution
• #define statement declares a preprocessor string variable or macro, and its value/body is the
text after the name up to the end of line.
• Replace #define constants with enum (see Section 2.12.1, p. 75) for integral types; other-
wise use const declarations (see Section 2.5.3, p. 42) (Java final).
• Use inline routines in C/C++ rather that #define macros (see page 149).
• -D define and optionally initialize preprocessor variables from the compilation command:
#define DEBUG 2
#define ASSN 1
• Conditional if uses the same relational and logical operators as C/C++, but operands can only
be integer or character values.
#define DEBUG 0 // declare and initialize preprocessor variable
...
#if DEBUG == 1 // level 1 debugging
# include "debug1.h"
...
#elif DEBUG == 2 // level 2 debugging
# include "debug2.h"
...
#else // non-debugging code
...
#endif
• By changing value of preprocessor variable DEBUG, different parts of the program are in-
cluded for compilation.
• Like Shell, possible to check if a preprocessor variable is defined or not defined using #ifdef
or #ifndef:
#ifndef MYDEFS H // if not defined
#define MYDEFS H 1 // make it so
...
#endif
• Used in an #include file to ensure its contents are only expanded once (see Section 2.23, p. 115).
• Note difference between checking if a preprocessor variable is defined and checking the
value of the variable.
• The former capability does not exist in most programming languages, i.e., checking if a
variable is declared before trying to use it.
2.22 Object
• Object-oriented programming was developed in the mid-1960s by Dahl and Nygaard and
first implemented in SIMULA67.
100 CHAPTER 2. C++
• Object programming is based on structures, used for organizing logically related data (see Sec-
tion 2.12.3, p. 78):
unorganized organized
struct Person {
int people age[30]; int age;
bool people sex[30]; bool sex;
char people name[30][50]; char name[50];
} people[30];
• An object provides both data and the operations necessary to manipulate that data in one
self-contained package.
• Both approaches use routines as an abstraction mechanism to create an interface to the in-
formation in the structure.
• Interface separates usage from implementation at the interface boundary, allowing an ob-
ject’s implementation to change without affecting usage.
• E.g., if programmers do not access Complex’s implementation, it can change from Cartesian
to polar coordinates and maintain same interface.
• Developing good interfaces for objects is important.
◦ e.g., mathematical types (like complex) should use value semantics (functional style)
versus reference to prevent changing temporary values.
2.22. OBJECT 101
References in s to members g and r in Foo disallowed because must know the this for specific
Foo object, i.e., which x, y or z.
• To sum x and y, write x.add(y), which looks different from normal addition, x + y.
102 CHAPTER 2. C++
• Because addition is a binary operation, add needs a parameter as well as the implicit context
in which it executes.
• Like outside a type, C++ allows overloading members in a type.
2.22.3 Constructor
• A constructor member implicitly performs initialization after object allocation to ensure the
object is valid before use.
◦ implicit constructor
struct Complex {
double re = 0.0, im = 0.0; // C++11
. . . // other members
};
◦ explicit constructor
struct Complex {
double re, im;
Complex() { re = im = 0.0; } // default constructor
. . . // other members
};
• Explicit constructor can perform arbitrary execution (e.g., ifs, loops, calls).
Complex x; x.Complex();
Complex x; implicitly Complex *y = new Complex;
Complex *y = new Complex; rewritten as y->Complex();
2.22. OBJECT 103
• Constructor without parameters is the default constructor, for initializing a new object.
• Unlike Java, C++ does not initialize all object members to default values.
• Constructor normally initializes members not initialized via other constructors, i.e., some
members are objects with their own constructors.
• A constructor may have parameters but no return type (not even void).
struct Complex {
double re, im;
Complex( double r = 0.0, double i = 0.0 ) { re = r; im = i; }
...
};
See Section 2.22.5, p. 108 for difference between initialization and assignment.
• If only non-default constructors are specified, i.e., ones with parameters, an object cannot
be declared without an initialization value:
struct Foo {
// no default constructor
Foo( int i ) { . . . }
};
Foo x; // disallowed!!!
Foo x( 1 ); // allowed
• Constructor can be called explicitly in another constructor (see Section 2.22.6, p. 113 for
constructor initialization syntax):
Java C++
class Foo { struct Foo {
int i, j; int i, j;
Foo( int p ) { i = p; j = 1; } Foo( int p ) { i = p; j = 1; }
Foo() { this( 2 ); } Foo() : Foo( 2 ) {} // C++11
} };
2.22.3.1 Literal
• Constructors can be used to create object literals (see Section 2.17, p. 89):
Complex x, y, z;
x = { 3.2 }; // Complex( 3.2 )
y = x + { 3.2, 4.5 }; // disallowed
y = x + (Complex){ 3.2, 4.5 }; // g++
z = x + Complex( 2 ) + y; // 2 widened to 2.0, Complex( 2.0 )
2.22.3.2 Conversion
• Constructors are implicitly used for conversions (see Section 2.7.1, p. 55):
2.22. OBJECT 105
int i;
double d;
Complex x, y;
x = 3.2; x = Complex( 3.2 );
y = x + 1.3; implicitly y = x.operator+( Complex(1.3) );
y = x + i; rewritten as y = x.operator+( Complex( (double)i );
y = x + d; y = x.operator+( Complex( d ) );
• Note, two implicit conversions are performed on variable i in x + i: int to double and then
double to Complex.
• Solution, move operator + out of the object type and made into a routine, which can also be
called in infix form (see Section 2.19, p. 93):
struct Complex { . . . }; // same as before, except operator + removed
Complex operator+( Complex a, Complex b ) { // 2 parameters
return Complex( a.re + b.re, a.im + b.im );
}
x + y; operator+(x, y)
1.3 + x; implicitly operator+(Complex(1.3), x)
x + 1.3; rewritten as operator+(x, Complex(1.3))
• Compiler checks for an appropriate operator in object type or an appropriate routine (it is
ambiguous to have both).
◦ For operator in object type, applies conversions to only the second operand.
◦ For operator routine, applies conversions to both operands.
• I/O operators << and >> often overloaded for user types:
106 CHAPTER 2. C++
• Standard C++ convention for I/O operators to take and return a stream reference to allow
cascading stream operations.
• << operator in object cout is used to first print string value, then overloaded routine << to
print the complex variable x.
• Why write as a routine versus a member?
2.22.4 Destructor
• A destructor (finalize in Java) member implicitly performs uninitialization at object deallo-
cation:
Java C++
class Foo { struct Foo {
... ...
finalize() { . . . } ~Foo() { . . . } // destructor
} };
• Object type has one destructor; its name is the character “~” followed by the type name (like
a constructor).
• Destructor has no parameters nor return type (not even void):
• Destructor is only necessary if an object is non-contiguous, i.e., composed of multiple
pieces within its environment, e.g., files, dynamically allocated storage, etc.
• A contiguous object, like a Complex object, requires no destructor as it is self-contained
(see Section 2.24, p. 119 for a version of Complex requiring a destructor).
• Destructor is invoked before an object is deallocated, either implicitly at the end of a block
or explicitly by a delete:
{ { // allocate local storage
Foo x, y( x ); Foo x, y; x.Foo(); y.Foo( x );
Foo *z = new Foo; Foo *z = new Foo; z->Foo();
... implicitly ...
delete z; rewritten as z->~Foo(); delete z;
... ...
y.~Foo(); x.~Foo();
} } // deallocate local storage
• For local variables in a block, destructors must be called in reverse order to constructors
because of dependencies, e.g., y depends on x.
2.22. OBJECT 107
• Destructor is more common in C++ than finalize in Java as no garbage collection in C++.
• If an object type performs dynamic storage allocation, it is non-contiguous and needs a
destructor to free the storage:
struct Foo {
int *i; // think int i[ ]
Foo( int size ) { i = new int[size]; } // dynamic allocation
~Foo() { delete [ ] i; } // must deallocate storage
...
};
◦ y’s destructor called at end of inner try block, it raises an exception E, which unwinds
destructor and try, and handled at inner catch
◦ x’s destructor called at end of outer try block, it raises an exception E, which unwinds
destructor and try, and handled at outer catch
• A destructor cannot raise an exception during propagation.
struct F {};
try {
C x; // raise on deallocation
. . . throw F(); . . .
} catch( E ) {. . .}
catch( F ) {. . .}
2. x’s destructor called during unwind, it raises an exception E, which terminates program
• Reason:
1. Cannot start second exception without handler to deal with first exception, i.e., cannot
drop exception and start another.
2. Cannot postpone first exception because second exception may remove its handlers
during its stack unwinding.
• Allocation/deallocation (RAII – Resource Acquisition Is Initialization)
struct Alloc {
Complex *ptr;
int size;
public:
Alloc( Complex *ptr, int size ) : ptr( ptr ), size( size ) {
for ( int i = 0; i < size; i += 1 ) {
ptr[i] = new Complex( i, i + 2.0 );
}
}
~Alloc() {
for ( int i = 0; i < size; i += 1 ) {
delete ptr[i];
}
}
};
void f(. . .) {
Complex *ap[10], *bp[20]; // array of complex pointers
Alloc alloca( ap, 10 ), allocb( bp, 20 ); // allocate complex elements
. . . // normal, local and non-local return
} // automatically delete objs by destructor
• Declaration initialization:
Complex y = x; implicitly rewritten as Complex y; y.Complex( x );
◦ usually most efficient to use reference for parameter and return type.
◦ value on the right-hand side of “=” is argument to assignment operator.
x = y; implicitly rewritten as x.operator=( y );
• If a copy constructor or assignment operator is not defined, an implicit one is generated that
does a memberwise copy of each subobject.
110 CHAPTER 2. C++
struct B {
B() { cout << "B() "; }
B( const B &c ) { cout << "B(&) "; }
B &operator=( const B &rhs ) { cout << "B= "; }
};
struct D { // implicit copy and assignment
int i; // basic type, bitwise
B b; // object type, memberwise
B a[5]; // array, element/memberwise
};
int main() {
cout << "b a" << endl;
D i; cout << endl; // B’s default constructor
D d = i; cout << endl; // D’s default copy-constructor
d = i; cout << endl; // D’s default assignment
}
• Often only a bitwise copy as subobjects have no copy constructor or assignment operator.
• When an object type has pointers, it is often necessary to do a deep copy, i.e, copy the
contents of the pointed-to storage rather than the pointers (see also Section 2.24, p. 119).
struct Shallow {
int *i;
Shallow( int v ) { i = new int; *i = v; }
~Shallow() { delete i; }
};
struct Deep {
int *i;
Deep( int v ) { i = new int; *i = v; }
~Deep() { delete i; }
Deep( Deep &d ) { i = new int; *i = *d.i; } // copy value
Deep &operator=( const Deep &rhs ) {
*i = *rhs.i; return *this; // copy value
}
};
initialization
Shallow x(3), y = x; Deep x(3), y = x;
y x y x
shallow copy
deep copy
new x.i 3 3 3
assignment
Shallow x(3), y(7); y = x; Deep x(3), y(7); y = x;
y x y x
shallow copy
deep copy
new y.i 7 new x.i 3 7 3 3
memory leak dangling pointer
• Deep copy does not change the pointers only the values associated within the pointers.
◦ realloc uses any free space at end of allocation; otherwise does delete/new/copy.
◦ Handles self-assignment. Why?
◦ Copy/swap performance is 4+ times slower for pointers to data as no storage reused.
x y
realloc
=
reuse storage
◦ Resize array, add/remove nodes to equal right-hand side, and reuse remaining nodes.
• May also need an equality operator (operator==) performing a deep compare, i.e., compare
values not pointers.
• Add constructor:
Foo( const int i = 3, Bar * const p = &bar, Bar &rp = bar, Bar b = { 7 } ) {
Foo::i = i; // disallowed
Foo::p = p; // disallowed
Foo::rp = rp;
Foo::b = b;
}
114 CHAPTER 2. C++
Foo( const int i = 3, Bar * const p = &bar, Bar &rp = bar, Bar b = { 7 } )
: i( i ), p( p ), rp( rp ), b( b ) { // special initialization syntax
cout << i << endl; // now value in constant
}
struct Foo {
Complex c;
int k;
Foo() : c( 1, 2 ), k( 14 ) { // initialize c, k
c = Complex( 1, 2 ); // or assign c, k
k = 14;
}
};
struct Foo {
static int cnt; // one for all objects
int i; // one per object
...
};
• Static routine-member, used to access static data-members, has no this parameter (i.e., like
a regular routine)
2.23. SEPARATE COMPILATION, ROUTINES 115
::Foo::cnt
free
main
i
heap y
x
memory
• Separate compilation divides a program into units, where each unit can be independently
compiled.
• Disadvantage: TUs depend on each other because a program shares many forms of informa-
tion, especially types (done automatically in Java).
116 CHAPTER 2. C++
◦ Hence, need mechanism to import information from referenced TUs and export infor-
mation needed by referencing TUs.
• As well, there are references within TU, e.g., main references f and g.
prog.cc
exec
program
monolithic executable
g++ prog.cc -o exec
unit1.cc
unit1.o
TU1 program1 exec
• Compile each TUi with -c compiler flag to generate executable code in .o file (Java has
.class file).
generates files unit1.o containing a compiled version of source code (machine code).
• Separate program into 3 TUs in files f.cc, g.cc and prog.cc (arbitrary names):
f.cc g.cc
#include <iostream> // import #include <iostream> // import
using namespace std; #include <cmath>
extern double g( double ); using namespace std;
int cntf = 0; // export extern double f( double );
double f( double d ) { double g( double d ) { // export
cntf += 1; // count f calls cout << d << endl;
cout << d << endl; return f( sqrt( d ) );
return g( d ); }
}
prog.cc
#include <iostream> // import
using namespace std;
extern double f( double );
extern double g( double );
extern int cntf;
int main() { // export
cout << cntf << " " << f( 3.5 ) << " " << g( 7.1 ) << endl;
}
• TU explicitly imports using extern declarations, and implicitly exports variable and routine
definitions.
• All .o files MUST be compiled for same hardware architecture, e.g., x86.
• (Usually) no code, just descriptions : preprecessor variables, C/C++ types and forward dec-
larations (see Section 2.20, p. 95).
• Implementation is composed of definitions and code.
• extern qualifier means variable or routine definition is located elsewhere (not for types).
• Preprocessor #includes indirectly import by copying in extern declarations, e.g., iostream
copies in extern ostream cout;.
2.24. SEPARATE COMPILATION, OBJECTS 119
• Use preprocessor trick (see Section 2.21.3, p. 99) to only expand file once:
f.h
#ifndef F H // if not defined
#define F H 1 // make it so
extern double f( double );
#endif
prog.cc
#include <iostream> // import
#include <cmath> // sqrt
using namespace std;
struct Complex {
static int objects; // shared counter
double re, im;
Complex( double r = 0.0, double i = 0.0 ) { objects += 1; . . .}
double abs() const { return sqrt( re * re + im * im ); };
static void stats() { cout << objects << endl; }
};
int Complex::objects; // declare
Complex operator+( Complex a, Complex b ) {. . .}
. . . // other arithmetic and logical operators
ostream &operator<<( ostream &os, Complex c ) {. . .}
const Complex C 1( 1.0, 0.0 );
int main() {
Complex a( 1.3 ), b( 2., 4.5 ), c( -3, -4 );
cout << a + b + c + C 1 << c.abs() << endl;
Complex::stats();
}
• As well, there are many references within the TU, e.g., main references Complex.
• Separate original program into two TUs in files complex.cc and prog.cc:
120 CHAPTER 2. C++
complex.cc
#include <iostream> // import
#include <cmath>
using namespace std;
struct Complex {
static int objects; // shared counter
double re, im; // implementation
Complex( double r = 0.0, double i = 0.0 ) { objects += 1; . . .}
double abs() const { return sqrt( re * re + im * im ); }
static void stats() { cout << objects << endl; }
};
int Complex::objects; // declare
Complex operator+( Complex a, Complex b ) {. . .}
. . . // other arithmetic and logical operators
ostream &operator<<( ostream &os, Complex c ) {. . .}
const Complex C 1( 1.0, 0.0 );
prog.cc
int main() {
Complex a( 1.3 ), b( 2., 4.5 ), c( -3, -4 );
cout << a + b + c + C 1 << c.abs() << endl;
Complex::stats();
}
• Complex interface placed into file complex.h, for inclusion (import) into TUs.
complex.h
#ifndef COMPLEX H
#define COMPLEX H // protect against multiple inclusion
#include <iostream> // import
// NO “using namespace std”, use qualification to prevent polluting scope
struct Complex {
static int objects; // shared counter
double re, im; // implementation
Complex( double r = 0.0, double i = 0.0 );
double abs() const;
static void stats();
};
extern Complex operator+( Complex a, Complex b );
. . . // other arithmetic and logical operator descriptions
extern std::ostream &operator<<( std::ostream &os, Complex c );
extern const Complex C 1;
#endif // COMPLEX H
complex.cc
#include "complex.h" // do not copy interface
#include <cmath> // import
using namespace std; // ok to pollute implementation scope
int Complex::objects; // defaults to 0
void Complex::stats() { cout << Complex::objects << endl; }
Complex::Complex( double r, double i ) { objects += 1; . . .}
double Complex::abs() const { return sqrt( re * re + im * im ); }
Complex operator+( Complex a, Complex b ) {
return Complex( a.re + b.re, a.im + b.im );
}
ostream &operator<<( ostream &os, Complex c ) {
return os << c.re << "+" << c.im << "i";
}
const Complex C 1( 1.0, 0.0 );
int main() {
Complex a( 1.3 ), b( 2., 4.5 ), c( -3, -4 );
cout << a + b + c + C 1 << c.abs() << endl;
Complex::stats();
}
2.25 Testing
• A major phase in program development is testing (> 50%).
• This phase often requires more time and effort than design and coding phases combined.
• Testing is the process of “executing” a program with the intent of determining differences
between the specification and actual results.
• Debugging is the process of determining why a program does not have an intended testing
behaviour and correcting it.
◦ Studies show 30–70% of logic design and coding errors can be detected in this manner.
• Machine Testing : systematic running of program using test data designed to discover prob-
lems.
◦ Speed up testing, occur more frequently, improve testing coverage, greater consistency
and reliability, use less people-time testing
• Start with the black-box approach and supplement with white-box tests.
• Black-Box Testing
2.26 Assertions
• Assertions document program assumptions:
#include <cassert>
unsigned int stopping distance( Car car ) {
. . . assert( ("Internal error", distance > 0) ); . . .
}
$ a.out
a.out: test.cc:19: unsigned int stopping distance(Car):
Assertion (’"Internal error", distance > 0)’ failed.
• Assertions in hot spot, i.e., point of high execution, can significantly increase program cost.
• Compiling a program with preprocessor variable NDEBUG defined removes all asserts.
2.27 Debugging
• Debugging is the process of determining why a program does not have an intended be-
haviour.
• However, debugging can be applied to fixing other kinds of problems, like poor performance.
• Before using debugger tools it is important to understand what you are looking for and if
you need them.
• It takes more time, but the alternative is wasting hours trying to figure out what the program
is doing.
• The two aspects of a program that you need to know are: where the program is executing
and what values it is calculating.
• Debug print statements show the flow of control through a program and print out intermediate
values.
• E.g., every routine should have a debug print statement at the beginning and end, as in:
2.27. DEBUGGING 125
int p( . . . ) {
// declarations
cerr << "Enter p " << parameter variables << endl;
...
cerr << "Exit p " << return value(s) << endl;
return r;
}
• Result is a high-level audit trail of where the program is executing and what values are being
passed around.
• Finer resolution requires more debug print statements in important control structures:
if ( a > b ) {
cerr << "a > b" << endl;
for ( . . . ) {
cerr << "x=" << x << ", y=" << y << endl;
...
}
} else {
cerr << "a <= b" << endl;
...
}
• By examining the control paths taken and intermediate values generated, it is possible to
determine if the program is executing correctly.
• Gradually comment out debug statements as parts of the program begin to work to remove
clutter from the output, but do not delete them until the program works.
• When you go for help, your program should contain debug print-statements to indicate some
attempt at understanding the problem.
• Use a preprocessor macro to simplify debug prints for printing entry, intermediate, and exit
locations and data:
#include <iostream>
#include "DPRT.h"
int test( int a, int b ) {
DPRT( ENTER, "a:" << a << " b:" << b );
if ( a < b ) DPRT( a < b, "a:" << a << " b:" << b );
DPRT( , a + b ); // empty title
DPRT( HERE, "" ); // empty expression
DPRT( EXIT, a );
return a;
}
2.28 Valgrind
• Incorrect memory usage is difficult to detect, e.g., memory leak or dangling pointer (see
Section 2.13, p. 82).
• Valgrind is a program that detects memory errors.
• Valgrind has false positives, i.e., claim memory errors that are not errors.
• Note, valgrind significantly slows program execution.
• Control output from valgrind for an empty program:
int main() {
}
$ g++ -g test.cc
$ valgrind ./a.out
==61795== Memcheck, a memory error detector
==61795== Copyright (C) 2002-2011, and GNU GPL’d, by Julian Seward et al.
==61795== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==61795== Command: ./a.out
==61795==
==61795== HEAP SUMMARY:
==61795== in use at exit: 0 bytes in 0 blocks
==61795== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==61795==
==61795== All heap blocks were freed -- no leaks are possible
==61795==
==61795== For counts of detected and suppressed errors, rerun with: -v
==61795== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
• Output states:
◦ HEAP SUMMARY:
2.28. VALGRIND 127
∗ how much heap memory was in use when the program exited
∗ how much heap memory was allocated in total.
◦ Note, allocs = frees does not ⇒ no memory leaks.
◦ Must see “All heap blocks were freed – no leaks are possible”.
• Control output from valgrind for an allocation:
int main() {
int *p = new int;
delete p;
}
$ valgrind ./a.out
...
==61570== HEAP SUMMARY:
==61570== in use at exit: 72,704 bytes in 1 blocks
==61570== total heap usage: 2 allocs, 1 frees, 72,708 bytes allocated
==61570==
==61570== LEAK SUMMARY:
==61570== definitely lost: 0 bytes in 0 blocks
==61570== indirectly lost: 0 bytes in 0 blocks
==61570== possibly lost: 0 bytes in 0 blocks
==61570== still reachable: 72,704 bytes in 1 blocks
==61570== suppressed: 0 bytes in 0 blocks
...
• Only 1 dynamic allocation, but 2 reported as C++ runtime does not free all memory.
• LEAK SUMMARY:
◦ Non-zero values for definitely, indirectly, and possibly lost ⇒ memory leak.
◦ Non-zero value for still reachable ⇒ memory leak from C++ runtime (ignore).
• Introduce memory leak:
1 int main() {
2 struct Foo { char c1, c2; };
3 Foo *p = new Foo;
4 p = new Foo; // forgot to free previous storage
}
==45326== HEAP SUMMARY:
==45326== in use at exit: 72,708 bytes in 3 blocks
==45326== total heap usage: 3 allocs, 0 frees, 72,708 bytes allocated
==45326==
==45326== LEAK SUMMARY:
==45326== definitely lost: 4 bytes in 2 blocks
==45326== indirectly lost: 0 bytes in 0 blocks
==45326== possibly lost: 0 bytes in 0 blocks
==45326== still reachable: 72,704 bytes in 1 blocks
==45326== suppressed: 0 bytes in 0 blocks
==45326== Rerun with --leak-check=full to see details of leaked memory
128 CHAPTER 2. C++
• What happened?
◦ valgrind identifies all memory errors, giving the line number where each error occurred.
◦ For Invalid free()
∗ first 3 lines indicate the delete on line 10 is already freed.
∗ next 3 lines indicate the memory previously freed on line 9.
2.29. RANDOM NUMBERS 129
• E.g., lottery numbers, suit/value of shuffled cards, value of rolled dice, coin flipping.
• While programmers spend much time ensuring computed values are not random, random
values are useful:
• All pseudo random-number generators (PRNG) involve some technique that scrambles
the bits of a value, e.g., multiplicative recurrence:
seed = 36969 * (seed & 65535) + (seed >> 16); // scramble bits
• Multiplication of large values adds new least-significant bits and drops most-significant bits.
• By dropping bits 63-32, bits 31-0 become scrambled after each multiply.
• E.g., struct PRNG generates a fixed sequence of LARGE random values that repeats after
232 values (but might repeat earlier).2
struct PRNG {
uint32 t seed ; // same results on 32/64-bit architectures
2 http://www.bobwheeler.com/statistics/Password/MarsagliaPost.txt
130 CHAPTER 2. C++
• Problem:
◦ generate next random number via routine call prng( 5 ) not member call prng.get( 5 )
◦ but routine cannot retain state (seed ) between calls (), so must use an object.
• Use function-call operator member, with name (), (functor), allowing object to behave like
a routine but retain state between calls in object.
PRNG prng; // often create single generator for entire program
prng(); // [0,UINT MAX], prng.operator()()
prng( 5 ); // [0,5], prng.operator()( 5 )
prng( 5, 10 ); // [5,10], prng.operator()( 5, 10 )
• Scaled large values from prng using modulus, e.g., random number between 5-21:
for ( int i = 0; i < 10; i += 1 ) {
cout << prng() % 17 + 5 << endl; // values 0-16 + 5 = 5-21
cout << prng( 16 ) + 5 << endl;
cout << prng( 5, 21 ) << endl;
}
2.30 Encapsulation
• Encapsulation hides implementation to support abstraction (access control).
• Access control applies to types NOT objects, i.e., all objects of the same type have identical
levels of encapsulation.
• Abstraction and encapsulation are neither essential nor required to develop software.
2.30. ENCAPSULATION 131
• E.g., programmers could follow a convention of not directly accessing the implementation.
• C features work largely among source files, and are indirectly tied into separate compilation
(see Section 2.23, p. 115).
Java C++
class Foo { struct Foo {
private . . . private: // within and friends
... // private members
protected . . . protected: // within, friends, inherited
... // protected members
public . . . public: // within, friends, inherited, users
... // public members
}; };
• C++ groups members with the same encapsulation, i.e., all members after a label, private,
protected or public, have that visibility.
• Visibility labels can occur in any order and multiple times in an object type.
• Note, private/protected members are still visible to programmer but inaccessible (see page 133
for invisible implementation.
struct Complex {
private:
double re, im; // cannot access but still visible
public:
// interface routines
};
• struct has an implicit public inserted at beginning, i.e., by default all members are public.
• class has an implicit private inserted at beginning, i.e., by default all members are private.
132 CHAPTER 2. C++
struct S { class C {
// public: // private:
int z; int x;
private: protected:
int x; int y;
protected: public:
int y; int z;
}; };
• Use encapsulation to preclude object copying by hiding copy constructor and assignment
operator:
class Lock {
Lock( const Lock & ); // definitions not required
Lock &operator=( Lock & );
public:
Lock() {. . .}
...
};
void rtn( Lock f ) {. . .}
Lock x, y;
rtn( x ); // disallowed, no copy constructor for pass by value
x = y; // disallowed, no assignment operator for assignment
• Prevent object forgery (lock, boarding-pass, receipt) or copying that does not make sense
(file, database).
• Encapsulation introduces problems when factoring for modularization, e.g., previously ac-
cessible data becomes inaccessible.
• Implementation is factored into a new type Cartesian, “+” operator is factored into a routine
outside and output “<<” operator must be outside (see Section 2.22.3.2, p. 104).
• Both Complex and “+” operator need to access Cartesian implementation, i.e., re and im.
• Creating get and set interface members for Cartesian provides no advantage over full access.
2.30. ENCAPSULATION 133
• C++ provides a mechanism to state that an outside type/routine is allowed access to its im-
plementation, called friendship.
• Cartesian makes re/im accessible to friends, and Complex makes impl accessible to friends.
• Alternative design is to nest the implementation type in Complex and remove encapsulation
(use struct).
class Complex {
friend Complex operator+( Complex a, Complex b );
friend ostream &operator<<( ostream &os, Complex c );
class Cartesian { // implementation type
double re, im;
} impl;
public:
Complex( double r = 0.0, double i = 0.0 ) {
impl.re = r; impl.im = i;
}
};
...
• To completely hide the implementation requires a (more expensive) reference, called bridge
or pimpl pattern:
134 CHAPTER 2. C++
Pimple Pattern
Complex Complex ComplexImpl
impl impl
re, im re, im
visible invisible
implementation implementation
complex.h
#ifndef COMPLEX H
#define COMPLEX H // protect against multiple inclusion
#include <iostream> // import
// NO “using namespace std”, use qualification to prevent polluting scope
class Complex {
friend Complex operator+( Complex a, Complex b );
friend std::ostream &operator<<( std::ostream &os, Complex c );
static int objects; // shared counter
struct ComplexImpl; // hidden implementation, nested class
ComplexImpl &impl; // indirection to implementation
public:
Complex( double r = 0.0, double i = 0.0 );
Complex( const Complex &c ); // copy constructor
~Complex(); // destructor
Complex &operator=( const Complex &c ); // assignment operator
double abs() const;
static void stats();
};
extern Complex operator+( Complex a, Complex b );
extern std::ostream &operator<<( std::ostream &os, Complex c );
extern const Complex C 1;
#endif // COMPLEX H
complex.cc
#include "complex.h" // do not copy interface
#include <cmath> // import
using namespace std; // ok to pollute implementation scope
int Complex::objects; // defaults to 0
struct Complex::ComplexImpl { double re, im; }; // implementation
Complex::Complex( double r, double i ) : impl(*new ComplexImpl) {
objects += 1; impl.re = r; impl.im = i;
}
Complex::Complex( const Complex &c ) : impl(*new ComplexImpl) {
objects += 1; impl.re = c.impl.re; impl.im = c.impl.im;
}
Complex::~Complex() { delete &impl; }
Complex &Complex::operator=( const Complex &c ) {
impl.re = c.impl.re; impl.im = c.impl.im; return *this;
}
2.31. DECLARATION BEFORE USE, OBJECTS 135
• A copy constructor and assignment operator are used because complex objects now contain
a reference pointer to the implementation (see page 111).
• To hide global variables/routines (but NOT class members) in TU, qualify with static.
complex.cc
static int Complex::objects; // not exported
Complex::Complex( double r, double i ) : impl(*new ComplexImpl) {
objects += 1; impl.re = r; impl.im = i;
}
...
◦ here static means linkage NOT storage allocation (see Section 2.22.7, p. 114).
complex.cc
namespace {
int Complex::objects; // not exported
}
// equivalent to
namespace UNIQUE {} // compiler generates unique name
using namespace UNIQUE; // make contents visible in TU
namespace UNIQUE { // add items local to TU
...
}
Java C++
void g() {} // not selected by call in T::f
class T { struct T {
void f() { c = Colour.R; g(); } void f() { c = R; g(); } // c, R, g not DBU
void g() { c = Colour.G; f(); } void g() { c = G; f(); } // c, G not DBU
Colour c; enum Colour { R, G, B }; // type must be DBU
enum Colour { R, G, B }; Colour c;
}; };
• Unlike Java, C++ requires a forward declaration for mutually-recursive declarations among
types:
Java C++
class T1 { struct T1 {
T2 t2; T2 t2; // DBU failure, T2 size?
T1() { t2 = new T2(); }
}; };
class T2 { struct T2 {
T1 t1; T1 t1;
T2() { t1 = new T1(); }
}; };
T1 t1 = new T1(); T1 t1;
• C++ version disallowed because DBU on T2 means it does not know the size of T2.
• An object declaration and usage requires the object’s size and members so storage can be
allocated, initialized, and usages type-checked.
• Solve using Java approach: break definition cycle using a forward declaration and pointer.
Java C++
struct T2; // forward
class T1 { struct T1 {
T2 t2; T2 &t2; // pointer, break cycle
T1() { t2 = new T2(); } T1() : t2( *new T2 ) {} // DBU failure, size?
}; };
class T2 { struct T2 {
T1 t1; T1 t1;
T2() { t1 = new T1(); } };
};
• Given just a type name, only pointer/reference declarations to the type are possible, which
allocate storage for an address versus an object.
• C++’s solution still does not work as the constructor cannot use type T2.
• Use forward declaration and syntactic trick to move member definition after both types are
defined:
• Use of qualified name T1::T1 allows a member to be logically declared in T1 but physically
located later (see Section 2.24, p. 119).
2.32 Inheritance
• Object-oriented languages provide inheritance for writing reusable program-components.
Java C++
class Base { . . . } struct Base { . . . }
class Derived extends Base { . . . } struct Derived : public Base { . . . };
• Substantially reduces the time to generate and debug a new object type.
Composition Inheritance
struct Engine { // Base struct Engine { // Base
int cyls; int cyls;
int r(. . .) { . . . } int r(. . .) { . . . }
Engine() { . . . } Engine() { . . . }
}; };
struct Car { // Derived struct Car : public Engine { // implicit
Engine e; // explicit composition // composition
int s(. . .) { e.cyls = 4; e.r(. . .); . . . } int s(. . .) { cyls = 4; r(. . .); . . . }
Car() { . . . } Car() { . . . }
} vw; } vw;
vw.e.cyls = 6; // composition reference vw.cyls = 3; // direct reference
vw.e.r(. . .); // composition reference vw.r(. . .); // direct reference
vw.s(. . .); // direct reference vw.s(. . .); // direct reference
• Constructors and destructors must be invoked for all implicitly declared objects in inheri-
tance hierarchy as for an explicit member in composition.
• If base type has members with the same name as derived type, it works like nested blocks:
inner-scope name overrides outer-scope name (see Section 2.5.2, p. 42).
• Still possible to access outer-scope names using “::” qualification (see Section 2.14, p. 85) to
specify the particular nesting level.
2.32. INHERITANCE 139
Java C++
class Base1 { struct Base1 {
int i; int i;
} };
class Base2 extends Base1 { struct Base2 : public Base1 {
int i; int i; // overrides Base1::i
} };
class Derived extends Base2 { struct Derived : public Base2 {
int i; int i; // overrides Base2::i
void s() { void r() {
int i = 3; int i = 3; // overrides Derived::i
this.i = 3; Derived::i = 3; // this.i
((Base2)this).i = 3; // super.i Base2::i = 3;
((Base1)this).i = 3; Base2::Base1::i = 3; // or Base1::i
} }
} };
• Unfortunately, having to inherit all of the members is not always desirable; some members
may be inappropriate for the new type (e.g, large array).
• As a result, both the inherited and inheriting object must be very similar to have so much
common code.
• Type inheritance extends name equivalence (see Section 2.12, p. 74) to allow routines to
handle multiple types, called polymorphism, e.g.:
• Since types Foo and Bar are structurally equivalent, instances of either type should work as
arguments to routine r (see Section 2.15, p. 86).
• Even if type Bar has more members at the end, routine r only accesses the common ones at
the beginning as its parameter is type Foo.
• Type inheritance relaxes name equivalence by aliasing the derived name with its base-type
names.
Foo Bar
int i Foo
double d
• E.g., create a new type Mycomplex that counts the number of times abs is called for each
Mycomplex object.
• Use both implementation and type inheritance to simplify building type Mycomplex:
2.32. INHERITANCE 141
• Derived type Mycomplex uses the implementation of the base type Complex, adds new mem-
bers, and overrides abs to count each call.
• Why is the qualification Complex:: necessary in Mycomplex::abs?
• Allows reuse of Complex’s addition and output operation for Mycomplex values, because of
the relaxed name equivalence provided by type inheritance between argument and parameter.
• Redeclare Complex variables to Mycomplex to get new abs, and member calls returns the
current number of calls to abs for any Mycomplex object.
• Two significant problems with type inheritance.
1. ◦ Complex routine operator+ is used to add the Mycomplex values because of the
relaxed name equivalence provided by type inheritance:
int main() {
Mycomplex x;
x = x + x; // disallowed
}
◦ However, result type from operator+ is Complex, not Mycomplex.
◦ Assignment of a complex (base type) to Mycomplex (derived type) disallowed be-
cause the Complex value is missing the cntCalls member!
◦ Hence, a Mycomplex can mimic a Complex but not vice versa.
◦ This fundamental problem of type inheritance is called contra-variance.
◦ C++ provides various solutions, all of which have problems and are beyond this
course.
2. void r( Complex &c ) {
c.abs(); // calls Complex::abs not Mycomplex::abs
}
int main() {
Mycomplex x;
x.abs(); // direct call of abs
r( x ); // indirect call of abs
cout << "x:" << x.calls() << endl;
}
◦ While there are two calls to abs on object x, only one is counted! (see Sec-
tion 2.32.6, p. 144)
142 CHAPTER 2. C++
2.32.3 Constructor/Destructor
• Constructors are executed top-down, from base to most derived type.
• Mandated by scope rules, which allow a derived-type constructor to use a base type’s vari-
ables so the base type must be initialized first.
• Destructors are executed bottom-up, from most derived to base type.
• Mandated by the scope rules, which allow a derived-type destructor to use a base type’s
variables so the base type must be uninitialized last.
• To pass arguments to other constructors, use same syntax as for initializing const members.
Java C++
class Base { struct Base {
Base( int i ) { . . . } Base( int i ) { . . . }
}; };
class Derived extends Base { struct Derived : public Base {
Derived() { super( 3 ); . . . } Derived() : Base( 3 ) { . . . }
Derived( int i ) { super( i ); . . . } Derived( int i ) : Base( i ) {. . .}
}; };
2.32.5 Overloading
• Overloading a member routine in a derived class overrides all overloaded routines in the base
class with the same name.
class Base {
public:
void mem( int i ) {}
void mem( char c ) {}
};
class Derived : public Base {
public:
void mem() {} // overrides both versions of mem in base class
};
• Inheritance masks actual object type, but expectation is both calls should invoke Derived::r
as argument b and reference bp point at an object of type Derived.
• If variable d is replaced with b, expectation is the calls should invoke Base::r.
Base &bp = b or d
b d
void r() void r()
int i
void r()
• To invoke a routine defined in a referenced object, qualify member routine with virtual.
• To invoke a routine defined by the type of a pointer/reference, do not qualify member routine
with virtual.
2.32. INHERITANCE 145
• Once a base type qualifies a member as virtual, it is virtual in all derived types regardless
of the derived type’s qualification for that member.
• Programmer may want to access members in Base even if the actual object is of type Derived,
which is possible because Derived contains a Base.
Java C++
class Base { struct Base {
public void f() {} // virtual void f() {} // non-virtual
public void g() {} // virtual void g() {} // non-virtual
public void h() {} // virtual virtual void h() {} // virtual
} };
class Derived extends Base { struct Derived : public Base {
public void g() {} // virtual void g() {}; // replace, non-virtual
public void h() {} // virtual void h() {}; // replace, virtual
public void e() {} // virtual void e() {}; // extension, non-virtual
} };
final Base bp = new Derived(); Base &bp = *new Derived(); // polymorphic assignment
bp.f(); // Base.f bp.f(); // Base::f, pointer type
((Base)bp).g(); // Derived.g bp.g(); // Base::g, pointer type
bp.g(); // Derived.g ((Derived &)bp).g(); // Derived::g, pointer type
((Base)bp).h(); // Derived.h bp.Base::h(); // Base::h, explicit selection
bp.h(); // Derived.h bp.h(); // Derived::h, object type
// cannot access “e” through bp
• Virtual members are only necessary to access derived members through a base-type refer-
ence or pointer.
• If a type is not involved in inheritance (final class in Java), virtual members are unnecessary
so use more efficient call to its members.
• C++ virtual members are qualified in base type as opposed to derived type.
• Hence, C++ requires the base-type definer to presuppose how derived definers might want
the call default to work.
• Good practice for inheritable types is to make all routine members virtual.
• Any type with virtual members needs to make the destructor virtual (even if empty) so the
most derived destructor is called through a base-type pointer/reference.
146 CHAPTER 2. C++
class Base {
int x, y; // data members
virtual void m1(. . .); // routine members
virtual void m2(. . .);
};
m1 m1 m1
m2 m2 x m2
x x y Virtual Routine
y y Table
2.32.7 Downcast
• Type inheritance can mask the actual type of an object through a pointer/reference (see Sec-
tion 2.32.2, p. 139).
• The Java operator instanceof and the C++ dynamic cast operator perform a dynamic check
of the object addressed by a pointer/reference (not coercion):
Java C++
Base bp = new Derived(); Base *bp = new Derived;
Derived *dp;
if ( bp instanceof Derived ) dp = dynamic cast<Derived *>(bp);
((Derived)bp).rtn(); if ( dp != 0 ) { // 0 => not Derived
dp->rtn(); // only in Derived
• To use dynamic cast on a type, the type must have at least one virtual member.
2.32.8 Slicing
• Polymorphic copy or assignment can result in object truncation, called slicing.
2.32. INHERITANCE 147
struct B {
int i;
};
struct D : public B {
int j;
};
void f( B b ) {. . .}
int main() {
B b;
D d;
f( d ); // truncate D to B
b = d; // truncate D to B
}
• Contains at least one pure virtual member that must be implemented by derived class.
class Shape {
int colour;
public:
virtual void move( int x, int y ) = 0; // pure virtual member
};
• Define type hierarchy (taxonomy) of abstract classes moving common data and operations
as high as possible in the hierarchy.
Java C++
abstract class Shape { class Shape {
protected int colour = White; protected: int colour;
public public:
Shape() { colour = White; }
abstract void move(int x, int y); virtual void move(int x, int y) = 0;
} };
abstract class Polygon extends Shape { class Polygon : public Shape {
protected int edges; protected: int edges;
public abstract int sides(); public: virtual int sides() = 0;
} };
class Rectangle extends Polygon { class Rectangle : public Polygon {
protected int x1, y1, x2, y2; protected: int x1, y1, x2, y2;
public:
public Rectangle(. . .) {. . .} Rectangle(. . .) {. . .} // init corners
public void move( int x, int y ) {. . .} void move( int x, int y ) {. . .}
public int sides() { return 4; } int sides() { return 4; }
} };
class Square extends Rectangle { struct Square : public Rectangle {
// check square // check square
Square(. . .) { super(. . .); . . .} Square(. . .) : Rectangle(. . .) {. . .}
} };
• Use public/protected to define interface and implementation access for derived classes.
• Provide (pure) virtual member to allow overriding and force implementation by derived
class.
• Provide default variable initialization and implementation for virtual routine (non-abstract)
to simplify derived class.
• Provide non-virtual routine to force specific implementation; derived class should not over-
ride these routines.
• Concrete class inherits from one or more abstract classes defining all pure virtual members,
i.e., can be instantiated.
• Cannot instantiate abstract class, but can declare pointer/reference to it.
• Pointer/reference used to write polymorphic data structures and routines:
void move3D( Shape &s ) { . . . s.move(. . .); . . . }
Polygon *polys[10] = { new Rectangle(), new Square(), . . . };
for ( unsigned int i = 0; i < 10; i += 1 ) {
cout << polys[i]->sides() << endl; // polymorphism
move3D( *polys[i] ); // polymorphism
}
2.33. TEMPLATE 149
• To maximize polymorphism, write code to the highest level of abstraction3, i.e. use Shape
over Polygon, use Polygon over Rectangle, etc.
2.33 Template
• Inheritance provides reuse for types organized into a hierarchy that extends name equiva-
lence.
• Template provides alternate kind of reuse with no type hierarchy and types are not equiva-
lent.
• E.g., overloading (see Section 2.19, p. 93), where there is identical code but different types:
• template introduces type parameter T used to declare return and parameter types.
• Template routine is called with value for T, and compiler constructs a routine with this type.
• Inferred type must supply all operations used within the template routine.
◦ e.g., types used with template routine max must supply operator>.
• E.g., collection data-structures (e.g., stack), have common code to manipulate data structure,
but type stored in collection varies:
3 Also called “program to an interface not an implementation”, which does not indicate the highest level of abstrac-
tion.
150 CHAPTER 2. C++
• Type parameter, T, specifies the element type of array elems, and return and parameter types
of the member routines.
• Unlike template routines, type cannot be inferred by compiler because type is created at
declaration before any member calls.
Stack<> si; // stack of int, 10
si.push( 3 ); // si : 3
si.push( 4 ); // si : 3 4
si.push( 5 ); // si : 3 4 5
cout << si.top() << endl; // 5
int i = si.pop(); // i : 5, si : 3 4
Stack<double> sd; // stack of double, 10
sd.push( 5.1 ); // sd : 5.1
sd.push( 6.2 ); // sd : 5.1 6.2
cout << sd << endl; // 5.1 6.2
double d = sd.pop(); // d : 6.2, sd : 5.1
Stack<Stack<int>,20> ssi; // stack of (stack of int, 10), 20
ssi.push( si ); // ssi : (3 4)
ssi.push( si ); // ssi : (3 4) (3 4)
ssi.push( si ); // ssi : (3 4) (3 4) (3 4)
cout << ssi << endl; // 3 4 3 4 3 4
si = ssi.pop(); // si : 3 4, ssi : (3 4) (3 4)
Why does cout << ssi << endl have 2 spaces between the stacks?
• Specified type must supply all operations used within the template type.
• Compiler requires a template definition for each usage so both the interface and imple-
mentation of a template must be in a .h file, precluding some forms of encapsulation and
separate compilation.
• C++03 requires space between the two ending chevrons or >> is parsed as operator>>.
2.33. TEMPLATE 151
• Data structures are called containers: vector, map, list (stack, queue, deque).
• In general, nodes of a data structure are either in a container or pointed-to from the container.
container
• To copy a node into a container requires its type have a default and/or copy constructor so
instances can be created without constructor arguments.
• Standard library containers use copying ⇒ node type must have default constructor.
• All containers are dynamic sized so nodes are allocated in heap (arrays can be on stack).
• To provide encapsulation (see Section 2.30, p. 130), containers use a nested iterator type
(see Section 2.14, p. 85) to traverse nodes.
• Iterator operator “++” moves forward to the next node, until past the end of the container.
• For bidirectional iterators, operator “--” moves in the reverse direction to “++”.
152 CHAPTER 2. C++
2.33.1.1 Vector
• vector has random access, length, subscript checking (at), and assignment (like Java array).
std::vector<T>
vector() create empty vector
vector( size, [initialization] ) create vector with N empty/initialized elements
~vector() erase all elements
unsigned int size() vector size
bool empty() size() == 0
T &operator[ ]( int i ) access ith element, NO subscript checking
T &at( int i ) access ith element, subscript checking
vector &operator=( const vector & ) vector assignment
void push back( const T &x ) add x after last element
void pop back() remove last element
void resize( int n ) add or erase elements at end so size() == n
void clear() erase all elements
iterator begin() iterator pointing to first element
iterator end() iterator pointing AFTER last element
iterator rbegin() iterator pointing to last element
iterator rend() iterator pointing BEFORE first element
iterator insert( iterator posn, const T &x ) insert x before posn
iterator erase( iterator posn ) erase element at posn
push
pop
0 1 2 3 4
• Vector declaration may specify an initial size, e.g., vector<int> v(size), like a dimension.
• When size is known, more efficient to dimension and initialize to reduce dynamic allocation.
int size;
cin >> size; // read dimension
vector<int> v1( size ); // think int v1[size]
vector<int> v2( size, 0 ); // think int v2[size] = { 0 }
int a[ ] = { 16, 2, 77, 29 };
vector<int> v3( a, &a[4] ); // think int v3[4]; v3 = a;
vector<int> v4( v3 ); // think int v4[size]; v4 = v3
#include <vector>
int i, elem;
vector<int> v; // think: int v[0]
for ( ;; ) { // create/assign vector
cin >> elem;
if ( cin.fail() ) break;
v.push back( elem ); // add elem to vector
}
vector<int> c; // think: int c[0]
c = v; // array assignment
for ( i = c.size() - 1; 0 <= i; i -= 1 ) {
cout << c.at(i) << " "; // subscript checking
}
cout << endl;
v.clear(); // remove ALL elements for reuse
• Optional second argument is initialization value for each element, i.e., 5 rows of vectors each
initialized to a vector of 4 integers initialized to zero.
• All loop bounds use dynamic size of row or column (columns may be different length).
• Alternatively, each row is dynamically dimensioned to a specific size, e.g., triangular matrix.
154 CHAPTER 2. C++
std::vector<T>::iterator
++, -- (insertion order) forward/backward operations
+, +=, -, -= (random order) movement operations
begin() end()
++ --
φ φ
0 1 2 3 4
rend() - - ++ rbegin()
• Iterator’s value is a pointer to its current vector element ⇒ dereference to access element.
vector<int> v(3);
vector<int>::iterator it;
v[0] = 2; // initialize first element
it = v.begin(); // intialize iterator to first element
cout << v[0] << " " << * v.begin() << " " << *it << endl;
• Use iterator like subscript for random access by adding/subtracting from begin/end.
vector<int> v;
for ( int i = 0 ; i < 5; i += 1 ) // create
v.push back( 2 * i ); // values: 0, 2, 4, 6, 8
2.33.1.2 Map
• map (dictionary) has random access, sorted, unique-key container of pairs (Key, Val).
• set (dictionary) is like map but the value is also the key (array maintained in sorted order).
pair
first second
blue 2
keys green 1 values
red 0
156 CHAPTER 2. C++
#include <map>
map<string, int> m; // Key => string, Val => int
m["green"] = 1; // create, set to 1
m["blue"] = 2; // create, set to 2
m["red"]; // create, set to 0 for int
m["green"] = 5; // overwrite 1 with 5
cout << m[ "green" ] << endl; // print 5
m.insert( pair<string,int>( "yellow", 3 ) ); // m[“yellow”] = 3
map<string, int> c( m ); // Key => string, Val => int
c = m; // map assignment
if ( c.count( "black" ) != 0 ) // check for key “black”
c.erase( "blue" ); // erase pair( “blue”, 2 )
• Iterator returns a pointer to a pair, with fields first (key) and second (value).
#include <map>
map<string,int>::iterator f = m.find( "green" ); // find key position
if ( f != m.end() ) // found ?
cout << "found " << f->first << ’ ’ << f->second << endl;
map<string,int>::reverse iterator r;
for ( r = m.rbegin(); r != m.rend(); r ++ ) // decreasing order
cout << r->first << ’ ’ << r->second << endl;
m.clear(); // remove ALL pairs
2.33.1.3 List
• In certain cases, it is more efficient to use a single (stack/queue/deque) or double (list) linked-
list container than random-access container.
• Examine list (arbitrary removal); stack, queue, deque are similar (restricted insertion/removal).
2.33. TEMPLATE 157
std::list<T>
list() create empty list
list( size, [initialization] ) create list with N empty/initialized elements
~list() erase all elements
unsigned int size() list size
bool empty() size() == 0
list &operator=( const list & ) list assignment
T front() first node
T back() last node
void push front( const T &x ) add x before first node
void push back( const T &x ) add x after last node
void pop front() remove first node
void pop back() remove last node
void clear() erase all nodes
iterator begin() iterator pointing to first node
iterator end() iterator pointing AFTER last node
iterator rbegin() iterator pointing to last node
iterator rend() iterator pointing BEFORE first node
iterator insert( iterator posn, const T &x ) insert x before posn
iterator erase( iterator posn ) erase node at posn
node
push ... push
pop pop
front back
• Like vector, list declaration may specify an initial size, like a dimension.
• When size is known, more efficient to dimension and initialize to reduce dynamic allocation.
int size;
cin >> size; // read dimension
list<int> l1( size );
list<int> l2( size, 0 );
int a[ ] = { 16, 2, 77, 29 };
list<int> l3( a, &a[4] );
list<int> l4( l3 );
#include <list>
struct Node {
char c; int i; double d;
Node( char c, int i, double d ) : c(c), i(i), d(d) {}
};
list<Node> dl; // doubly linked list
for ( int i = 0; i < 10; i += 1 ) { // create list nodes
dl.push back( Node( ’a’+i, i, i+0.5 ) ); // push node on end of list
}
list<Node>::iterator f;
for ( f = dl.begin(); f != dl.end(); f ++ ) { // forward order
cout << "c:" << f->c << " i:" << f->i << " d:" << f->d << endl;
}
while ( 0 < dl.size() ) { // destroy list nodes
dl.erase( dl.begin() ); // remove first node
} // same as dl.clear()
• An action routine is called for each node in the container passing the node to the routine for
processing (Lisp apply).
#include <iostream>
#include <list>
#include <vector>
#include <algorithm> // for each
using namespace std;
void print( int i ) { cout << i << " "; } // print node
int main() {
list< int > int list;
vector< int > int vec;
for ( int i = 0; i < 10; i += 1 ) { // create lists
int list.push back( i );
int vec.push back( i );
}
for each( int list.begin(), int list.end(), print ); // print each node
for each( int vec.begin(), int vec.end(), print );
}
• Type of the action routine is void rtn( T ), where T is the type of the container node.
• Use functor (see page 130) (retain state between calls) for more complex actions.
• E.g., an action to print on a specified stream must store the stream and have an operator()
allowing the object to behave like a function:
2.34. GIT, ADVANCED 159
struct Print {
ostream &stream; // stream used for output
Print( ostream &stream ) : stream( stream ) {}
void operator()( int i ) { stream << i << " "; }
};
int main() {
list< int > int list;
vector< int > int vec;
...
for each( int list.begin(), int list.end(), Print(cout) );
for each( int vec.begin(), int vec.end(), Print(cerr) );
}
• Expression Print(cout) creates a constant Print object, and for each calls operator()(Node)
in the object.
$ mkdir project
$ cd project
$ git status
# On branch master
#
# Initial commit
nothing to commit (create/copy files and use "git add" to track)
◦ Add only source files into repository, e.g., *.o, *.d, a.out, do not need to be versioned.
◦ Create hidden file .gitignore in projct directory and list working files not versioned.
# ignore self
.gitignore
# build files
*.[do]
a.out
◦ Options -u origin master only required for first push of newly created repository.
◦ Always make sure your code compiles and runs before pushing; it is unfair to pollute
a shared global-repository with bugs.
$ git pull
Username for ’https://git.uwaterloo.ca’: jfdoe
Password for ’https://jfdoe@git.uwaterloo.ca’: xxxxxxxx
All developers must periodically pull the latest global version to local repository.
2.34.3 Modifying
• Editted files in working copy are implicitly scheduled for update on next commit.
$ mkdir gizmo
$ cd gizmo
$ emacs Makefile x.h x.cc y.h y.cc # add text into these files
$ ls -aF
./ . ./ Makefile x.cc x.h y.cc y.h
$ git add Makefile x.cc x.h y.cc y.h
2.34. GIT, ADVANCED 163
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>. . ." to unstage)
#
# new file: Makefile
# new file: x.cc
# new file: x.h
# new file: y.cc
# new file: y.h
#
# Changes not staged for commit:
# (use "git add <file>. . ." to update what will be committed)
# (use "git checkout -- <file>. . ." to discard changes in working directory)
#
# modified: . ./README.md
commit 0f4162d3a95a2e0334964f95495a079341d4eaa4
Author: Jane F Doe <jfdoe@uwaterloo.ca>
Date: Sat May 2 07:24:40 2015 -0400
commit e025356c6d5eb2004314d54d373917a89afea1ab
Author: Jane F Doe <jfdoe@uwaterloo.ca>
Date: Sat May 2 07:04:10 2015 -0400
initial commit
2.34. GIT, ADVANCED 165
◦ Count top to bottom for relative commit number, or use a commit name
ecfbac4b80a2bf5e8141bddfdd2eef2f2dcda799.
• Copy files in the repository by copying working-copy files and add them.
$ cp y.h z.h
$ cp y.cc z.cc
$ git add z.*
$ git status
# On branch master
# Your branch is ahead of ’origin/master’ by 3 commits.
#
# Changes to be committed:
# (use "git reset HEAD <file>. . ." to unstage)
#
# renamed: x.cc -> w.cc
# renamed: x.h -> w.h
# new file: z.cc
# new file: z.h
$ git push
Counting objects: 19, done.
Delta compression using up to 48 threads.
Compressing objects: 100% (10/10), done.
Writing objects: 100% (17/17), 1.34 KiB, done.
Total 17 (delta 1), reused 0 (delta 0)
To gitlab@git.uwaterloo.ca:jfdoe/project.git
e025356. .0c5f473 master -> master
Branch master set up to track remote branch master from origin.
2.34.4 Conflicts
• When multiple developers work on SAME files, source-code conflicts occur.
jfdoe kdsmith
modify y.cc modify y.cc
remove y.h
add t.cc
$ git push
To gitlab@git.uwaterloo.ca:jfdoe/project.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to ’gitlab@git.uwaterloo.ca:jfdoe/project.git’
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. ’git pull’) before pushing again. See the
’Note about fast-forwards’ section of ’git push --help’ for details.
$ git pull
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 5 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (5/5), done.
From git.uwaterloo.ca:jfdoe/project
2a49710. .991683a master -> origin/master
Removing gizmo/y.h
Auto-merging gizmo/y.cc
CONFLICT (content): Merge conflict in gizmo/y.cc
Automatic merge failed; fix conflicts and then commit the result.
$ ls -aF
./ . ./ Makefile t.cc w.cc w.h y.cc z.cc z.h
$ cat y.cc
<<<<<<< HEAD
I like file y.cc
=======
This is file y.cc
>>>>>>> 5d89df953499a8fdfd6bc92fa3a6be9c8358dbd1
• Conflict resolution tools exist to help with complex conflicts (unnecessary for this course).
2.35 UML
• Unified Modelling Language (UML) is a graphical notation for describing and designing
software systems, with emphasis on the object-oriented style.
• UML modelling has multiple viewpoints:
◦ class model : describes static structure of the system for creating objects
◦ object model : describes dynamic (temporal) structure of system objects
◦ interaction model : describes the kinds of interactions among objects
Focus on class modelling.
• Note / comment
• Classes diagram defines class-based modelling, where a class is a type for instantiating
objects.
168 CHAPTER 2. C++
• Class has a name, attributes and operations, and may participate in inheritance hierarchies.
♦
Car Tire
0..1 0..*
◦ car can have 0 or more tires and a tire can only be on 0 or 1 car
◦ aggregate may not create/destroy its parts, e.g., many different tires during car’s life-
time and tires may exist after car’s lifetime (snow tires).
class Car {
Tires *tires[4]; // array of pointers to tires
...
at a time (owns-a).
Car Brake
1 4
class Car {
DiscBrake brakes[4]; // array of brakes
...
Person Car
... ...
Person Car
... ...
owns : Car owned : Person
◦ If UML graph is cluttered with lines, create association in class rather than using a line.
◦ E.g., if 20 classes associated with Car, replace 20 lines with attributes in each class.
Polygon
• For abstract class, the class name and abstract operations are italicized.
Classes Diagram
Vehicle * 1 Client Insurance Policy
- make: String - name: String 1 1 - company: String
- model: String Lease - phone: String - policy: String
- colour: String - start: Date *
1 0..1 1 + rate(): Double
- end: Date 1
*
Accessory
- surcharge: Double no charge
+ surcharge(): Double during sales
• UML has many facilities, supporting complex descriptions of relationships among entities.
2.36. COMPOSITION / INHERITANCE DESIGN 171
• Types created from multiple composite classes; types created from multiple superclasses.
Composition Inheritance
class A {. . .}; class A {. . .};
class B { A a; . . .}; class B : A {. . .};
class C {. . .}; class C {. . .};
class D { B b; C c; . . .}; class D : B, C {. . .};
• Both approaches:
Vehicle
Construction
Heavy Machinery
Crane, Grader, Back-hoe
Haulage
Semi-trailer, Flatbed
Passenger
Commercial
Bus, Fire-truck, Limousine, Police-motorcycle
Personal
Car, SUV, Motorcycle
• For maximum reuse and to eliminate duplicate code, place variables/operations as high in
the hierarchy as possible.
◦ derived class should also have behavioural compatibility with base class.
• However, all taxonomies are an organizational compromise: when is a car a limousine and
vice versa.
class Car {
SteeringWheel s; // fixed
Donut spare;
Wheel *wheels[4]; // dynamic
Engine *eng;
Transmission *trany;
public:
Car( Engine *e = fourcyl, Transmission *t = manual ) :
eng( e ), trany( t ) { wheels[i] = . . .}
rotate() {. . .} // rotate tires
wheels( Wheels *w[4] ) {. . .} // change wheels
engine( Engine *e ) {. . .} // change engine
};
• Allow each users to have they own declaration but still access same value.
• Alternative is global variable, which forces name and may violate abstraction.
• alternative design has interested objects pull the events from the observer
PlainWindow w1;
Scrollbar vsb1( w1, Scrollbar::Ver ); // add vertical scroll bar
Border rb5( vsb1, Border::RED, 5 ); // add red border
Border borderedScrollable( rb5, Border::BLUE, 10 ); // add blue border
PlainWindow w2;
Scrollbar vsb2( w2, Scrollbar::Ver ); // add vertical scrollbar
Scrollbar hsb( vsb2, Scrollbar::Hor ); // add horizontal scrollbar
Title titledScrollable( hsb ); // add title
• decorator can be applied multiple times in different ways to produce new combinations at
runtime
PizzeriaFactory pizzeriaFactory;
BurgerFactory burgerFactory;
Restaurant *pizzaHut = pizzeriaFactory.create();
Restaurant *burgerKing = burgerFactory.create();
Food *dispatch( Restaurant::Kind food ) { // parameterized creator
switch ( food ) {
case Restaurant::Pizza: return pizzaHut->order();
case Restaurant::Burger: return burgerKing->order();
default: ; // error
}
}
• clients obtain a concrete product (Pizza, Burger) from a concrete factory (PizzaHut, BugerK-
ing), but product type is unknown
• client interacts with product object through its abstract interface (Food)
2.38. DEBUGGER 177
2.38 Debugger
• An interactive, symbolic debugger effectively allows debug print statements to be added and
removed to/from a program dynamically.
• Some systems do not have a debugger or the debugger may not work for certain kinds of
problems.
• A good programmer uses a combination of debug print statements and a debugger when
debugging a complex program.
• A debugger does not debug a program, it merely helps in the debugging process.
• Therefore, you must have some idea (hypothesis) about what is wrong with a program before
starting to look.
2.38.1 GDB
• The two most common UNIX debuggers are: dbx and gdb.
1 int r( int a[ ] ) {
2 int i = 100000000;
3 a[i] += 1; // really bad subscript error
4 return a[i];
5 }
6 int main() {
7 int a[10] = { 0, 1 };
8 r( a );
9 }
• Compile program using the -g flag to include names of variables and routines for symbolic
debugging:
$ g++ -g test.cc
• Start gdb:
$ gdb ./a.out
. . . gdb disclaimer
(gdb) ← gdb prompt
(gdb) run
Starting program: /u/userid/cs246/a.out
Program received signal SIGSEGV, Segmentation fault.
0x000106f8 in r (a=0xffbefa20) at test.cc:3
3 a[i] += 1; // really bad subscript error
◦ If there are no errors in a program, running in GDB is the same as running in a shell.
◦ If there is an error, control returns to gdb to allow examination.
◦ If program is not compiled with -g flag, only routine names given.
(gdb) backtrace
#0 0x000106f8 in r (a=0xffbefa08) at test.cc:3
#1 0x00010764 in main () at test.cc:8
◦ stack has 2 frames main (#1) and r (#0) because error occurred in call to r.
• print command prints variables accessible in the current routine, object, or external area.
(gdb) print i
$1 = 100000000
(gdb) print a
$2 = (int *) 0xffbefa20
(gdb) p *a
$3 = 0
(gdb) p a[1]
$4 = 1
(gdb) p a[1]+1
$5 = 2
• set variable command changes the value of a variable in the current routine, object or exter-
nal area.
(gdb) set variable i = 7
(gdb) p i
$6 = 7
(gdb) set var a[0] = 3
(gdb) p a[0]
$7 = 3
◦ investigate how the program behaves with new values without recompile and restarting
the program,
◦ to make local corrections and then continue execution.
• frame [n] command moves the current stack frame to the nth routine call on the stack.
(gdb) f 0
#0 0x000106f8 in r (a=0xffbefa08) at test.cc:3
3 a[i] += 1; // really bad subscript error
(gdb) f 1
#1 0x00010764 in main () at test.cc:8
8 r( a );
• break command establishes a point in the program where execution suspends and control
returns to the debugger.
(gdb) break main
Breakpoint 1 at 0x10710: file test.cc, line 7.
(gdb) break test.cc:3
Breakpoint 2 at 0x106d8: file test.cc, line 3.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /u/userid/cs246/a.out
Breakpoint 1, main () at test.cc:7
7 int a[10] = { 0, 1 };
(gdb) p a[7]
$8 = 0
• Once a breakpoint is reached, execution of the program can be continued in several ways.
• step [n] command executes the next n lines of the program and stops, so control enters
routine calls.
(gdb) step
8 r( a );
(gdb) s
r (a=0xffbefa20) at test.cc:2
2 int i = 100000000;
(gdb) s
Breakpoint 2, r (a=0xffbefa20) at test.cc:3
3 a[i] += 1; // really bad subscript error
(gdb) <Enter>
Program received signal SIGSEGV, Segmentation fault.
0x000106f8 in r (a=0xffbefa20) at test.cc:3
3 a[i] += 1; // really bad subscript error
(gdb) s
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
◦ If the next line is a routine call, control enters the routine and stops at the first line.
• next [n] command executes the next n lines of the current routine and stops, so routine calls
are not entered (treated as a single statement).
2.38. DEBUGGER 181
(gdb) run
...
Breakpoint 1, main () at test.cc:7
7 int a[10] = { 0, 1 };
(gdb) next
8 r( a );
(gdb) n
Breakpoint 2, r (a=0xffbefa20) at test.cc:3
3 a[i] += 1; // really bad subscript error
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
0x000106f8 in r (a=0xffbefa20) at test.cc:3
3 a[i] += 1; // really bad subscript error
• continue [n] command continues execution until the next breakpoint is reached.
(gdb) run
...
Breakpoint 1, main () at test.cc:7
7 int a[10] = { 0, 1 };
(gdb) c
Breakpoint 2, r (a=0x7fffffffe7d0) at test.cc:3
3 a[i] += 1; // really bad subscript error
(gdb) p i
$9 = 100000000
(gdb) set var i = 3
(gdb) c
Continuing.
Program exited normally.
(gdb) list
1 int r( int a[ ] ) {
2 int i = 100000000;
3 a[i] += 1; // really bad subscript error
4 return a[i];
5 }
6 int main() {
7 int a[10] = { 0, 1 };
8 r( a );
9 }
(gdb) run
...
Breakpoint 1, main () at test.cc:7
7 int a[10] = { 0, 1 };
1: a[0] = 67568
(gdb) quit
The program is running. Exit anyway? (y or n) y
• When one TU is changed, other TUs that depend on it must change and be recompiled.
• For a large numbers of TUs, the dependencies turn into a nightmare with respect to re-
compilation.
2.39.1 Dependencies
• A dependency occurs when a change in one location (entity) requires a change in another.
◦ loosely coupled, e.g., changing source code may require a corresponding change in
user documentation, or
◦ tightly coupled, changing source code may require recompiling of some or all of the
components that compose a program.
• Cycles in #include dependencies are broken by #ifndef checks (see page 99).
• However, it is inefficient and defeats the point of separate compilation to recompile all pro-
gram components after a change.
• Often no mechanism to know the kind of change made within a file, e.g., changing a com-
ment, type, variable.
• Hence, “change” may be coarse grain, i.e., based on any change to a file.
• UNIX directory stores the time a file is last changed, with second precision.
• Using time to denote change means dependency graph is temporal ordering where root has
newest (or equal) time and leafs oldest (or equal) time.
◦ File x.h includes y.h and z.h, file y.h includes z.h, file z.h includes y.h,
◦ Files x.o, y.o and z.o created at 1:00 from compilation of files created before 1:00.
◦ File a.out created at 1:01 from link of x.o, y.o and z.o.
◦ Changes are subsequently made to x.h and x.C at 2:00 and 2:30.
◦ Only files x.o and a.out need to be recreated at 3:00 and 3:01. (Why?)
2.39.2 Make
• make is a system command taking a dependency graph and uses file change-times to trigger
rules that bring the dependency graph up to date.
• make does not understand relationships among sources, one that exists at the source-
code level and is crucial.
184 CHAPTER 2. C++
x.C
x.o x.h
y.C
a.out y.o y.h
z.h
z.o z.C
• E.g., source x.C depends on source x.h but x.C is not a product of x.h like x.o is a product of
x.C and x.h.
• Two most common UNIX makes are: make and gmake (on Linux, make is gmake).
• Like shells, there is minimal syntax and semantics for make, which is mostly portable across
systems.
• Most common non-portable features are specifying dependencies and implicit rules.
• Basic make file has string variables with initialization, and a list of targets and rules.
• Make file can have any name, but make implicitly looks for a file named makefile or Makefile
if no file name is specified.
• Each target has a list of dependencies, and possibly a set of commands specifying how to
re-establish the target.
• make builds the dependency graph and decorates the edges with time stamps for the specified
files.
• If any of the dependency files (leafs) is newer than the target file, or if the target file does
not exist, the commands are executed by the shell to update the target (generating a new
product).
◦ -n builds and checks the dependencies, showing rules to be triggered (leave off to
execute rules)
◦ -f Makefile is the dependency file (leave off if named [Mm]akefile)
◦ a.out target name to be updated (leave off if first target)
• Generalize and eliminate duplication using variables:
CXX = g++ # compiler
CXXFLAGS = -g -Wall -c # compiler flags
OBJECTS = x.o y.o z.o # object files forming executable
EXEC = a.out # executable name
◦ make can deduce simple rules when dependency files have specific suffixes.
◦ E.g., given target with dependencies:
x.o : x.C x.h y.h z.h
make deduces the following rule:
${CXX} ${CXXFLAGS} -c -o x.o # special variable names
where -o x.o is redundant as it is implied by -c.
186 CHAPTER 2. C++
• Because dependencies are extremely complex in large programs, programmers seldom con-
struct them correctly or maintain them.
• Without complete and up to date dependencies, make is useless.
• Automate targets and dependencies:
CXX = g++ # compiler
CXXFLAGS = -g -Wall -MMD # compiler flags
OBJECTS = x.o y.o z.o # object files forming executable
DEPENDS = ${OBJECTS:.o=.d} # substitute “.o” with “.d”
EXEC = a.out # executable name
◦ Phony target clean removes product files that can be rebuilt (save space).
$ make clean # remove all products (don’t create “clean”)
!, 7, 54 ., 54
!=, 45, 54 ., 80
", 6, 90 .C, 41
#, 2 .c, 41
#define, 98 .cc, 41, 120
#elif, 99 .cpp, 41
#else, 99 .h, 97, 120
#endif, 99 .snapshot, 11
#if, 99 /, 4, 54
#ifdef, 99 \, 5, 44
#ifndef, 99 /=, 54
#include, 97 :, 65
$, 2, 25 ::, 54, 86, 137, 138
${}, 25 ;;, 31
%, 2 <, 21, 45, 54
&, 54 <<, 49, 54, 105
&&, 54 <<=, 54
&=, 54 <=, 45, 54
’, 5, 43 <ctrl>-c, 6
<ctrl>-d, 21, 51
*, 54, 80
=, 7, 25, 45, 54
*=, 54
==, 45, 54, 113
+, 45, 54
>, 5, 21, 45, 54
++, 151
>&, 21, 22
+=, 54
>=, 45, 54
,, 54
>>, 49, 54, 105
-, 54
>>=, 54
--, 151
?:, 54
-=, 54
[ ], 29, 45, 84
->, 54
%, 54
-MD, 186
%=, 54
-MMD, 186
&, 54
-W, 41
^, 54
-c, 117
^=, 54
-g, 41, 177 8
,5
-o, 41, 117 ~, 4, 54
-std, 41
-v, 97 a.out, 72
189
190 CHAPTER 2. C++
noshowpoint, 49 string, 45
noskipws, 49 struct, 54
npos, 45 selection, 137
null character, 44 other, 19
nullptr, 89 output, 40, 47, 50
<<, 105
object, 99, 100 endl, 40
anonymous member, 138 formatted, 48
assignment, 108, 142 manipulators
const member, 113 boolalpha, 49
constructor, 102, 138, 142 dec, 49
copy constructor, 108, 132, 142 endl, 49
default constructor, 103 fixed, 49
destructor, 106, 138, 142 hex, 49
initialization, 103, 142 iomanip, 49
literal, 104 left, 49
member, 101 noboolalpha, 49
pure virtual member, 147, 148 noshowbase, 49
static member, 114 noshowpoint, 49
virtual member, 144, 146 oct, 49
object model, 167 right, 49
object-oriented, 99, 137 scientific, 49
observer, 173 setfill, 49
oct, 49 setprecision, 49
ofstream, 48 setw, 49
open, 49 showbase, 49
file, 49 showpoint, 49
operation, 168 standard error
operators cerr, 48
*, 54 standard output
<<, 49, 105 cout, 40, 48
>>, 49, 105 overflow, 54
&, 54 overload, 71
arithmetic, 54 overloading, 49, 93, 102, 105
assignment, 54 override, 138, 141, 144, 145
bit shift, 54 owns-a, 169
bitwise, 54
cast, 54 paginate, 10
comma expression, 54 parameter, 91
control structures, 54 array, 93
logical, 54 constant, 92
overloading, 49, 102 default value, 92
pointer, 54 pass by reference, 91
relational, 54 pass by value, 91
selection, 86, 138 prototype, 96
198 CHAPTER 2. C++
stream <, 45
cerr, 48 <=, 45
cin, 48 =, 45
clear, 52 ==, 45
cout, 48 >, 45
fail, 49 >=, 45
formatted, 48 [ ], 45
fstream, 48 c str, 45
ifstream, 48 find, 45
ignore, 52 find first not of, 45
input, 40 find first of, 45
cin, 48 find last not of, 45
end of file, 51 find last of, 45
eof, 51 npos, 45
fail, 51 replace, 45
manipulators rfind, 45
boolalpha, 49 size type, 45
dec, 49 substr, 45
endl, 49 C
fixed, 49 [ ], 45
hex, 49 strcat, 45
iomanip, 49 strcpy, 45
left, 49 strcspn, 45
noboolalpha, 49 strlen, 45
noshowbase, 49 strncat, 45
noshowpoint, 49 strncpy, 45
noskipws, 49 strspn, 45
oct, 49 strstr, 45
right, 49 null termination, 44
scientific, 49 stringstream, 53, 72
setfill, 49 strlen, 45
setprecision, 49 strncat, 45
setw, 49 strncpy, 45
showbase, 49 strong safety, 71
showpoint, 49 strspn, 45
skipws, 49 strstr, 45
ofstream, 48 struct, 100, 131
output, 40 structurally equivalent, 86
cout, 40 structure, 74, 80, 89, 100
endl, 40 member, 80, 100
stream file, 48 data, 80
string, 43, 45 routine, 80
C++ visibility
!=, 45 default, 80
+, 45 public, 80
2.39. COMPILING COMPLEX PROGRAMS 201
declaration, 88 write, 19
directive, 88
xterm, 1
valgrind, 126
zero-filled, 89
value parameter, 91
variable declarations
type qualifier, 42, 43
variables
constant, 45
dereference, 54
reference, 54
vector, 151, 152
[ ], 152
at, 152
begin, 152
clear, 152
empty, 152
end, 152
erase, 152
insert, 152
pop back, 152
push back, 152
rbegin, 152
rend, 152
resize, 152, 154
size, 152
version control, 14
virtual, 144, 146
virtual members, 144, 146–148
visibility, 85
default, 80
private, 131
protected, 131
public, 80, 131
void *, 83
wchar t, 42
which, 8
while, 31
white-box testing, 122, 123
whitespace, 41, 51
widening, 55
wildcard, 16
working directory, 4, 6, 7, 10, 16
wrapper member, 143