CPP 2006 V42-Lectures
CPP 2006 V42-Lectures
v39 – Edition for 2007 UvA APP Master course © 2006 Wouter Verkerke, NIKHEF
Introduction and Overview
0 Introduction
& Overview
Dialog box
object
• An object has
– Properties : position, shape, text label
– Behavior : if you click on the ‘Cancel button’ a defined action occurs
Dialog box
object
Dialog box
object
• But…
– Object oriented modeling does not substitute for sound thinking
– OO programming does not guarantee high performance, but it
doesn’t stand in its way either
• Nevertheless
– OO programming is currently the best way we know
to describe complex systems
1. Modularity
2. Encapsulation
3. Inheritance
4. Polymorphism
• Example
– Grouping actions and properties of a bank account together
long getBalance()
void print()
void calculateInterest()
char* ownersName
long accountNumber
long accountBalance
Account
long getBalance()
void print() interface
void calculateInterest()
char* ownersName
long accountNumber implementation
long accountBalance (not visible from outside)
Account
long getBalance()
void print() interface
void calculateInterest()
char* ownersName
long accountNumber implementation
long accountBalance (not visible from outside)
Account
char* collateralObject
long collateralValue
MortgageAccount
Point p = Traj->getPos(5.0)
LineTrajectory HelixTrajectory
© 2006 Wouter Verkerke, NIKHEF
Introduction to C++
• Wide choice of OO-languages – why program in C++?
– It depends on what you need…
Simula67
Pascal SmallTalk
C
C++
Eiffel
2. Basics of C++
4. Class Basics
1 The basics
of C++
int main () {
std::cout << "Hello World!“ << std::endl;
return 0;
}
int main () {
std::cout << "Hello World!“ << std::endl;
return 0;
} Anything on line after // in C++ is
considered a comment
int main () {
std::cout << "Hello World!“ << std::endl;
return 0;
} The return statement
passed the return value
back to the calling function
int main() {
int j ; // definition – initial value undefined
int k = 0 ; // definition with assignment initialization
int l(0) ; // definition with constructor initialization
int main() {
const float pi = 3.14159268 ; // constant data object
pi = 2 ; // ERROR – doesn’t compile
}
int main() {
const float pi ; // ERROR – forgot to initialize
int main() {
float pi = 3.14159268 ;
cout << “pi = “ << pi << endl ;
int j = 16 // decimal
int j = 0xF // hexadecimal (leading 0x)
int j = 020 // octal (leading 0)
Type name[size] ;
Type name[size1][size2]…[sizeN] ;
float x[3] ; // OK
int k=5 ;
float x[k] ; // ERROR!
int myArray[5]
float x
char name[256]
• Example
x = 3 ;
cout << x << endl ; // prints ‘3’
cout << y << endl ; // also prints ‘3’
int myArray[5]
float x
float& y = x ;
char name[256]
int myArray[5]
float x
float& y = x
char name[256]
float* y = &x
int x = 3, y = 4 ;
int* px ; // allocate px of type ‘pointer to integer’
px = &x ; // assign ‘memory address of x’ to pointer px
// Length of string
int strlen(const char* str) ;
• Example
int i;
char buf[10] ;
i = 5 ; // OK, i is an lvalue
5 = i ; // ERROR, 5 is not an lvalue
// (it has no memory location)
int i = 3, j = 5 ;
float x = 1.5 ;
• In/Decrement operators
Name Operator
Prefix increment ++x
Postfix increment x++
Prefix decrement --x
Postfix decrement x--
• Note difference
– Prefix operators return value after operation
– Postfix operators return value before operation
• Examples
int x=0 ;
cout << x++ << endl ; // Prints 0
cout << x << endl ; // Prints 1
• Relational operators
Name Operator
Less than x < y
Less than or equal to x <= y
Greater than or equal to x >= y
Greater than x > y
Equal to x == y
Not equal to x != y
• Logical operators
Name Operator
Logical NOT !x
Logical AND x>3 && x<5 Do not confuse
with bit-wise AND (&)
Logical OR x==3 || x==5 and bit-wise OR (|)
float x, y ;
…
if (y!=0. && x/y < 5.2) // safe against divide
© 2006 Wouter
by zero
Verkerke, NIKHEF
Operators and expressions – Bitwise operators
• Bitwise operators
• Remarks
– Bitwise operators cannot be applied to floating point types
– Mostly used in online, DAQ applications where memory is limited
and ‘bit packing is common’
– Do not confuse logical or, and (||,&&) with bitwise or, and (|,&)
• Assignment operators
Name Operator
Assignment x = 5
Addition update x += 5
Subtraction update x -= 5
Multiplication update x *= 5
Division update x /= 5
Modulus update x %= 5
Left shift update x <<= 5
Right shift update x >>= 5
Bitwise AND update x &= 5
Bitwise OR update x |= 5
Bitwise XOR update x ^= 5
x = y = z = 5 // OK! x = ( y = ( z = 5 ))
// x = ( y = 5)
// x = 5
int x[5], i ;
x[i=2] = 3 ; // i is set to 2, x[2] is set to 3
int x = 4 ;
cout << ( x==4 ? ”A” : ”B” ) << endl ; // prints “A” ;
float f = 5 ;
double d = f ; // Automatic conversion occurs here
float f = 3.1
int i = static_cast<int>(f) ; // OK, i=3 (loss of precision)
int* i = static_cast<int*>(f) ; // ERROR float != pointer
float f = 3.1 ;
const float& g = f ;
g = 5.3 ; // ERROR not allowed, g is const
float& h = const_cast<float&>(g) ; // OK g and h of same type
h = 5.3 ; // OK, h is not const
float* f ;
int* i = reintepret_cast<int*>(f) ; // OK, but you take
// responsibility for the ensuing mess…
if (expr) {
statements ; // evaluated if expr is true
}
if (expr) {
statements ; // evaluated if expr is true
} else {
statements ; // evaluated if expr is false
}
if (expr1) {
statements ; // evaluated if expr1 is true
} else if (expr2) {
statements ; // evaluated if expr2 is true
} else {
statements ; // evaluated if neither expr is true
} © 2006 Wouter Verkerke, NIKHEF
Intermezzo – coding style
if (foo==bar) { if (foo==bar)
statements ; {
} else { statements ;
statements ; }
} else
{
statements ;
}
do {
statements ;
} while (expression) ;
– is equivalent to
expression1 ;
while (expression2) {
statements ;
expression3 ;
}
for (;;) {
cout << “Forever more” << endl ;
}
int i,j
for (i=0,j=0 ; i<3 ; i++,j+=2) {
// execute with i=0,j=0, i=1,j=2, i=2,j=4
}
int i = 3 ;
while(true) { // no scheduled exit from loop
i -= 1 ;
if (i<0) break ; // exit loop
cout << i << endl ;
}
– Example prints ‘2’, ’1’ and ’0’. Print statement for i=-1 never
executed
© 2006 Wouter Verkerke, NIKHEF
Control flow constructs – break and continue
• The continue statement
– Continue stops execution of loops statements and returns to
evaluation of conditional expression
case constant2:
case constant3:
statements ; // Evaluated if expr==const2 or const3
break ;
default:
statements ; // Evaluated expression matched none
break ;
}
case blue+green:
cout << “cyan” << end;
break ;
default:
cout << “white” << endl ;
break ;
© 2006 Wouter Verkerke, NIKHEF
}
Some details on the block {} statements
main() {
int i = 1 ;
int a ;
int main() {
int b=0 ;
a, b visible if (b==0) {
a, b, c visible
int c = 1;
}
int a ;
int main() {
int b=0 ;
‘b’ declared in main() visible
if (b==0) {
LEGAL! int b = 1; ‘b’ declared in if() visible
} ‘b’ declared in main() hidden!
} int main() {
int b ;
– NB: It is not legal to have two definitions …
of the same name in the same scope, e.g. int b ; ERROR!
}
© 2006 Wouter Verkerke, NIKHEF
Scoping rules – The :: operator
int a=1 ;
int main() {
int a=0 ; LEGAL, but hides global ‘a’
main() {
func(1) ;
func(2) ;
}
– Output of example
old value = 0 ;
new value = 1 ;
old value = 1 ; Value of static int i preserved between func() calls
new value = 2 ;
© 2006 Wouter Verkerke, NIKHEF
Dynamic memory allocation
// Single objects
Type* ptr = new Type ;
Type* ptr = new Type(initValue) ;
// Arrays of objects
Type* ptr = new Type[size] ;
Type* ptr = new Type[size][size]…[sizeN] ;
© 2006 Wouter Verkerke, NIKHEF
Releasing dynamic memory allocation
// Arrays of objects
delete[] ptr ;
void leakFunc() {
int* array = new int[1000] ;
// do stuff with array
} Leak happens right here
we loose the pointer array
here and with that our only
int main() { possibility to release memory
int i ; in future
for (i=0 ; i<1000 ; i++) {
leakFunc() ; // we leak 4K at every call
}
}
int* allocFunc() {
int* array = new int[1000] ;
// do stuff with array
return array ; allocFunc() allocates memory
} but pointer as return value
memory is not leaked yet
int main() {
int i ;
for (i=0 ; i<1000 ; i++) {
allocFunc() ; Author of main() doesn’t know
} that it is supposed to delete
} array returned by allocFunc()
int i ;
for (i=0 ; i<size ; i++) {
array[i] = 0 ;
}
}
int main() {
// Note: We own array ;
int* array = makearray(1000) ;
delete[] array ;
}
© 2006 Wouter Verkerke, NIKHEF
Files and Functions
2 Files and
Functions
• Declaration
• Definition:
int f() {
g() ; // f calls g – OK g is defined
}
int f(int x) ;
int g() {
f(x*2) ; // g calls f – OK f declared now
}
int f(int x) {
g() ; // f calls g – OK g defined by now
}
© 2006 Wouter Verkerke, NIKHEF
Functions – recursion
if (number<=1) {
return number ;
}
return number*factorial(number-1) ;
}
main() {
int a=3, b=5 ;
swap(a,b) ;
cout << “a=“ << a << “, b=“ << b << endl ;
}
main() {
int a=3, b=5 ;
swap(a,b) ;
cout << “a=“ << a << “, b=“ << b << endl ;
}
main() {
int a=3, b=5 ;
swap(&a,&b) ;
cout << “a=“ << a << “, b=“ << b << endl ;
}
main() {
int* a(0), *b(0) ;
bool ok = allocate(a,b) ;
cout << a << “ “ << b << endl ;
// prints 0x4856735 0x4927847
}
main() {
int array[3] = { 0, 1, 2 } ;
square( sizeof(array)/sizeof(int), array) ;
return 0 ;
}
// Pass 5 x 10 array
int a[5][10] ;
x
f(a) ;
int main(){
const char* foo = “Hello World” ;
print(foo) ;
return 0 ;
}
main() {
double x(0.) ;
int main() {
int a=3,b=5,c=1 ;
float x=4.5,y=1.2,z=-3 ;
int d = minimum3_int(a,b,c) ;
float w = minimum3_float(x,y,z) ;
}
int main() {
int a=3,b=5,c=1 ;
float x=4.5,y=1.2,z=-3 ;
Code calls same function name
int d = minimum3(a,b,c) ;
twice. Compiler selects appropriate
float w = minimum3(x,y,z) ;
overloaded function based on
}
argument list
© 2006 Wouter Verkerke, NIKHEF
Function overloading resolution
– Example
void func(int i) ;
void func(double d) ;
int main() {
int i ;
float f ;
int function(int x) ;
float function(int x) ; // ERROR – only return type is different
int main() {
bool b ;
int i ;
float f ;
– Example
double square(double x) {
return x*x ;
}
int main() {
double (*funcptr)(double i) ; // funcptr is function ptr
funcptr = &square ;
void errorHandler(double x) {
cout << "something is wrong with input value " << x << endl ;
}
int main() {
double x[5] = { 0, 1, 2, -3, -4 } ;
sqrtArray(x , 5, &errorHandler) ;
}
• Declarations file
// capitalize.hh
void convertUpper(char* str) ;
void convertLower(char* str) ; Declarations
• Definitions file
// capitalize.cc
#include “capitalize.hh”
void convertUpper(char* ptr) { Definitions
while(*ptr) {
if (*ptr>=‘a’&&*ptr<=‘z’) *ptr -= ‘a’-’A’ ;
ptr++ ;
}
}
void convertLower(char* ptr) {
while(*ptr) {
if (*ptr>=‘A’&&*ptr<=‘Z’) *ptr += ‘a’-’A’ ;
ptr++ ;
} © 2006 Wouter Verkerke, NIKHEF
}
Using the preprocessor to include declarations
// demo.cc
#include “capitalize.hh”
#ifdef NAME
(#else)
#endif
– NAME can be defined with #define
// capitalize.hh
#ifndef CAPITALIZE_HH
#define CAPITALIZE_HH
}
© 2006 Wouter Verkerke, NIKHEF
Namespaces
namespace foo {
int global = 0 ;
void func() {
// code
cout << global << endl ;
}
void bar() {
cout << foo::global << endl ;
// other code
namespace foo {
int zap = 0 ;
int main() {
cout << foo::zap << endl ;
cout << foo::bar::foobar << endl ;
}
namespace {
int bar = 0 ;
}
void func() {
cout << bar << endl ;
}
int main () {
std::cout << "Hello World!“ << std::endl;
return 0;
}
int main () {
cout << "Hello World!“ << endl;
return 0;
} Imported symbols can now be used
without qualification in this module
– Can also import symbols in a local scope. In that case import valid
only inside local scope
© 2006 Wouter Verkerke, NIKHEF
Using namespaces conveniently
int main () {
cout << "Hello World!“ << endl;
return 0;
}
3 Class
Basics
• Example
write read
write read
‘S’ ‘A’ ‘Z’ ‘Q’ ‘W’ ‘L’
write read
‘S’ ‘A’ ‘Z’ ‘Q’ ‘W’ ‘L’
// Implementation
char s[LEN] ;
‘A’
int rear ;
int front ;
‘Z’ int count ;
‘Q’
‘W’
// Implementation
void init() { front = rear = count = 0 ; }
struct Fifo {
// Implementation
char s[LEN] ;
int front ;
int rear ;
int count ;
// Interface
void init() { front = rear = count = 0 ; }
int nitems() { return count ; }
bool full() { return (count==LEN) ; }
bool empty() { return (count==0) ; }
void write(char c) { count++ ;
if(rear==LEN) rear=0 ;
s[rear++] = c ; }
char read() { count-- ;
if (front==LEN) front=0 ;
return s[front++] ; }
} ; © 2006 Wouter Verkerke, NIKHEF
Characteristics of the ‘struct’ construct
Fifo f ;
f.init() ; // initialize FIFO
• Improving encapsulation
– We improve encapsulation of the FIFO implementation by
restricting access to the member functions and data members
that are needed for the implementation
… members … // Implementation
public:
… members … // Interface
} ;
• Public data
– Access is unrestricted. Situation identical to no access control declaration
• Private data
– Data objects and member functions in the private section can only accessed
by member functions of the struct (which themselves can be either private
or public)
struct Fifo {
private: // Implementation
char s[LEN] ;
int front ;
int rear ;
int count ;
public: // Interface
void init() { front = rear = count = 0 ; }
int nitems() { return count ; }
bool full() { return (count==LEN) ; }
bool empty() { return (count==0) ; }
void write(char c) { count++ ;
if(rear==LEN) rear=0 ;
s[rear++] = c ; }
char read() { count-- ;
if (front==LEN) front=0 ;
return s[front++] ; }
} ;
© 2006 Wouter Verkerke, NIKHEF
Using the redesigned FIFO struct
Fifo f ;
f.init() ; // initialize FIFO
write read
‘S’ ‘A’ ‘Z’ ‘Q’ ‘W’ ‘L’
Equivalent
struct Name { class Name {
private:
… members …
… members …
public:
public:
… members …
… members …
} ; } ;
class Fifo {
public: // Interface
char read() {
…
std::read() ;
…
}
} ; Use scope operator to specify that you want
to call the read() function in the std namespace
rather than yourself
fifo.cc
#include “fifo.hh”
char Fifo::read() {
count-- ;
if (front==len) front=0 ;
return s[front++] ;
}
© 2006 Wouter Verkerke, NIKHEF
Constructors
class ClassName {
…
ClassName() ;
…
} ;
class ClassName {
…
ClassName(argument1,argument2,…argumentN) ;
…
} ;
ClassName obj(arg1,…,argN) ;
ClassName* ptr = new ClassName(Arg1,…ArgN) ;
© 2006 Wouter Verkerke, NIKHEF
Constructor example – a File class
class File {
private:
int fh ;
public:
File(const char* name) {
fh = open(name) ;
}
class File {
private:
Int fh ;
public:
File() {
fh = open(“Default.txt”) ;
}
File(const char* name) {
fh = open(name) ;
}
class File {
private:
Int fh ;
public:
File(const char* name=“Default.txt”) {
fh = open(name) ;
}
class File {
public:
File(const char* name) ;
…
} ;
class Database {
public:
Database(const char* fileName) ;
private:
File f ;
} ;
private
int _size ;
double* _x ; © 2006 Wouter Verkerke, NIKHEF
}
Common initialization in multiple constructors
• Another clever but wrong solution
– Idea: Call Array(size) as if it were a regular member function, which
will then perform the necessary initialization steps
int i ;
for (i=0 ; i<size ; i++) _x[i] = input[i] ;
}
– Problem: It is legal C++ (it compiles fine) but it doesn’t do what you
think it does!
– Calling a constructor like this creates a temporary object that is
initialized with size and immediately destroyed again. It does not
initialize the instance of array you are constructing with the
Array(double*,int) constructor
private:
void initialize(int size) {
_size = size ;
_x = new double[size] ;
}
int _size ;
double* _x ;
© 2006 Wouter Verkerke, NIKHEF
}
Destructors
class File {
private:
int fh ;
close() { ::close(fh) ; }
public:
File(const char* name) { fh = open(name) ; }
~File() { close() ; }
…
} ;
File is automatically closed
when object is deleted
void readFromFile() {
File *f = new File(“theFile.txt”) ; Opens file automatically
// read something from file
delete f ; Closes file automatically
}
void readFromFile() {
File *f = new File(“theFile.txt”) Opens
; file automatically
// read something from file
delete f ; Closes file automatically
}
void readFromFile() {
File f(“theFile.txt”) ; Opens file automatically
// read something from file
} Deletion of automatic
variable f calls destructor
– Great example of abstraction of & closes file automatically
file concept and of encapsulation
of resource control © 2006 Wouter Verkerke, NIKHEF
Classes vs Instances – an important concept
Class Instance
void Array::initialize() {
cout << “I am a array object, my pointer is “ << this << endl ;
}
void classA::memberFunc() {
if (certain_condition) {
externFunction(*this) ;
}
} © 2006 Wouter Verkerke, NIKHEF
Copy constructor – a special constructor
• The copy constructor is the constructor with the
signature
ClassA::ClassA(const ClassA&) ;
ClassA a ;
ClassA aclone(a) ; // aclone is an identical copy of a
class Array {
public:
Array(int size) {
initialize(size) ;
}
~Array() {
delete[] _x ;
}
private:
void initialize(int size) {
_size = size ;
_x = new double[size] ;
}
int _size ;
double* _x ; Watch out! Pointer data member
}
• Add illustration
void example { Array a
double[]
Array a(10) ; _x
// ‘a’ Constructor allocates _x ;
if (some_condition)
Array b(a) ; Array b
// ‘b’ Copy Constructor does
// b._x = a._x ; _x
// b appears to be copy of a
}
// ‘b’ Destructor does Array a
// delete[] _b.x
_x Problem is here:
// BUT _b.x == _a.x Memory b._x points to
// allocated by ‘Array a’ has same array
// been released by ~b() ; as a._x!
<Do something with Array>
// You are dead!
} © 2006 Wouter Verkerke, NIKHEF
Taking good care of your property
class File {
private:
Int fh ;
close() { ::close(fh) ; }
File(const File&) ; // disallow copying
public:
File(const char* name) { fh = open(name) ; }
~File() { close() ; }
…
} ;
© 2006 Wouter Verkerke, NIKHEF
Ownership and defensive programming
• Coding mistakes happen, but by programming
defensively you will spot them easier
– Always initialize owned pointers to zero if you do not allocate your
resources immediately
– Always set pointers to zero after you delete the object they point
to
class Fifo {
…
void print() ;
…
}
main() {
Fifo fifo ;
showTheFifo(fifo) ;
}
main() {
Fifo fifo ;
showTheFifo(fifo) ;
}
class Fifo {
…
void print() const ;
…
int size ;
}
class Fifo {
…
char buf[80] ;
…
char* buffer() const {
return buf ; // ERROR – Const function exposing
non-const pointer to data member
}
}
main() {
Fifo fifo ;
showTheFifo(fifo) ;
}
class FunctionCalculation {
…
mutable float cachedResult ;
…
float calculate() const {
// do calculation
cachedResult = <newValue> ; // OK because cachedResult
// is declared mutable
return cachedResult ;
}
}
– Use sparingly!
© 2006 Wouter Verkerke, NIKHEF
Static data members
class ClassName {
…
static Type Name(Type arg,…) ;
…
}
4 Class Analysis
& Design
• Analysis
– How to divide up your problem in classes
– What should be the functionality of each class
• Design
– What should the interface of your class look like?
© 2006 Wouter Verkerke, NIKHEF
Analysis – Find the class
1. Try to describe briefly in plain English (or Dutch) what you intend
your software to do
• Rationale – This naturally makes you think about your software in a high abstraction
level
Calorimeter
Cell
Calorimeter
Calorimeter
has-a has-a
Coordinate
Position CaloCell
has-a
CaloCell
Coordinate
Position
Calorimeter
has-a has-a
Position CaloCell
has-a
Coordinate
Calorimeter Calorimeter
Position CellGrid
has-a
CaloCell
CaloCell
Position © 2006 Wouter Verkerke, NIKHEF
Analysis – Example from High Energy Physics
• Which solution is better?
– Source of ambiguity: cell coordinate not really intrinsic property of
calorimeter cell
– Path to solution: what are cell coordinates used for? Import for insight in
best solution. Real-life answer: to find adjacent (surrounding cells)
– Solution: Adjacency algorithms really couple strongly to layout of cells, not
to property of individual cells design with layout in separate class
probably better
Calorimeter Calorimeter
has-a has-a has-a has-a
has-a has-a
Coordinate CaloCell
Calorimeter
has-a has-a
CaloCell
– Now we run into some problems with ‘has-a’ semantics: All CaloCells in
Calorimeter are owned by Calorimeter, so CaloCluster doesn’t really
‘have’ them. Solution: ‘Uses-A’ semantic.
– A ‘Uses-A’ relation translates into a pointer or ©
reference to an object
2006 Wouter Verkerke, NIKHEF
Summary on OO analysis
• Choosing classes: You should be able to say what a class is
– A ‘Has-A relation’ translates into data members, a ‘Uses-A’ relation into a
pointer
– Functionality of your natural objects translates in member functions
• Be wary of complexity
– Signs of complexity: repeated identical code, too many function arguments,
too many member functions, functions with functionality that cannot be
succinctly described
– A complex class is difficult to maintain Redesign into smaller units
– Operator overloading
– Algorithmic functions
– I/O functions
– Error processing functions
b.multiply(complex(0,1)) ;
a.add(b) ;
a.multiply(b) ;
b.subtract(a) ;
c = a * b c.assign(a.multiply(b)) ;
complex c = a + b c.operator=(operator+(a,b))
c.operator=(a.operator+(b))
class complex {
public:
complex(double r, double i) : _r(r) _i(i) {} ;
complex& operator=(const complex& other) ;
private:
double _r, _i ;
} ;
// handle self-assignment
if (&other == this) return *this ;
// handle self-assignment
if (&other == this) return *this ;
// copy content
Why ignoring self-assignment of bad
can be other
Image you store_r = other._r
information ;
in a dynamically allocated array
that needs to be_i = other._i
reallocated ;
on assignment…
Returns reference to b
complex&
Not mandatory, complex::operator=(const
but essential complex&
if you want to mimic behavior of built-inother)
types {
// handle self-assignment
if (&other == this) return *this ;
public:
String(const char* str= “”) : _s(0) { insert(str) ; }
String(const String& a) : _s(0) { insert(a._s) ; }
~String() { delete[] _s ; }
public:
String(const char* str= “”) : _s(0) { insert(str) ; }
String(const String& a) : _s(0) { insert(a._s) ; }
~String() { delete[] _s ; }
public:
String(const char* str= “”) : _s(0) { insert(str) ; } Ctor
String(const String& a) : _s(0) { insert(a._s) ; }
Dtor
~String() { delete[] _s ; }
public:
String(const char* str= “”) : _s(0) { insert(str) ; }
String(const String& a) : _s(0) { insert(a._s) ; }
~String() { delete[] _s ; }
class String {
public:
String& operator+=(const String& other) {
int newlen = _len + other._len ; // calc new length
char* newstr = new char[newlen+1] ; // alloc new buffer
• Example implementation
– Standard Library function strcmp return 0 if strings are identical,
less than 0 if s1<s2, and greater than 0 if s1>s2
– Input arguments are const again
– Output type is bool
– Operators <,>,<=,>= similar
char& String::operator[](int i) {
// Don’t forget range check here
return _s[i] ;
}
void example() {
PhoneBook pbook ;
pbook[“Bjarne Stroustrup”] = 0264524 ;
int number = phoneBook[“Brian Kernigan”] ;
}
– Powerful tool for indexed container objects
– More on this later in the Standard Template Library section
ClassA {
operator ClassB() const ; // conversion creates copy
// so operation is const
}
String s(“Hello”) ;
String s2 = s + “ World” ;
String s(“Hello”) ;
String s2 = s + String(“ World”) ;
class String {
explicit String(const char*) ;
}
• Rule of thumb:
– Operators that modify an object should be member functions of
that object
– Operators that don’t modify an object can be either a member
function or a global function
– Sometimes you can use public interface to modify object (e.g. see
string example)
– Sometimes this is not desirable (e.g. interface to reconstitute
object from stream is considered private) – what do you do?
class String {
public:
String(const char*==“”) ;
private:
char* _buf ;
int _len ;
friend istream& operator>>(istream&, String&) ;
} ;
class String {
public:
String(const char*=“”) ;
private:
friend class StringManipulator ;
} © 2006 Wouter Verkerke, NIKHEF
Class string
5 Standard Library –
Using I/O streams
#include <iostream>
using namespace std ;
int main() {
double x;
return 0 ;
}
Writing
Conversion from/to byte stream
Reading
Physical or Logical Device
© 2006 Wouter Verkerke, NIKHEF
I/O classes and operators in C++
• Operators<<(), >>() do step 1, classes istream, ostream do step 2
<iostream> Generic
istream ostream iostream
(e.g.terminal)
<fstream> File ifstream ofstream fstream
char buf[100] ;
int count(99) ;
Function Meaning
bool good() Next operation might succeed
bool eof() End of input seen
bool fail() Next operation will fail
bool bad() Stream is corrupted
• Example – reading lines from a file till the end of the file
ifstream ifs(“file.txt”) ;
char buf[100] ;
cout << “just read ‘” << buf << “’” << Wouter
© 2006 endl Verkerke,
; NIKHEF
}
Some handy abbreviations
while(ifs.getline(buf,100)) {
cout << “just read ‘” << buf << “’” << endl ;
}
shoesize.txt
Bjarne 42
Leif 47
Thor 52
ifstream ifs(“shoesize.txt”) ;
string name ;
int size ;
string name ;
int size
cin.operator>>( cin.operator>>(name), size) ;
Bjarne Stroustrup 42
– Miscellaneous
FileHeader
// Open file for reading and writing
fstream iofile(“file.dat”,ios::in|ios::out) ;
FileDataObj
© 2006 Wouter Verkerke, NIKHEF
Random access streams
File Layout
• Example use of tell(),seek() get() put()
position position
#include <fstream>
FileHeader
// Open file for reading and writing
fstream iofile(“file.dat”,ios::in|ios::out) ;
FileDataObj
© 2006 Wouter Verkerke, NIKHEF
Random access streams
File Layout
• Example use of tell(),seek() get() put()
position position
#include <fstream>
FileHeader
// Open file for reading and writing
fstream iofile(“file.dat”,ios::in|ios::out) ;
FileDataObj
© 2006 Wouter Verkerke, NIKHEF
Random access streams
File Layout
• Example use of tell(),seek() get() put()
position position
#include <fstream>
FileHeader
// Open file for reading and writing
fstream iofile(“file.dat”,ios::in|ios::out) ;
FileDataObj
// Read (fictitious) file data object
FileDataObj fdo ;
iofile >> fdo ;
FileHeader
// Open file for reading and writing
fstream iofile(“file.dat”,ios::in|ios::out) ;
FileDataObj
// Read (fictitious) file data object
FileDataObj fdo ;
iofile >> fdo ;
FileHeader
// Open file for reading and writing
fstream iofile(“file.dat”,ios::in|ios::out) ;
FileDataObj
// Read (fictitious) file data object
FileDataObj fdo ;
iofile >> fdo ;
String s(“Hello”) ;
cout << string << “ World” ;
String s(“Hello”) ;
cout.operator<<(operator<<(cout,string),”World”) ;
6 Generic
Programming
– Templates
int m = 43, n = 56 ;
cout << max(m,n) << endl ; // displays 56 (CORRECT)
int m = 43, n = 56 ;
cout << max(m,n) << endl ; // displays 56 (CORRECT)
template<class TYPE>
TYPE max(const TYPE& a, const TYPE& b) {
return (a>b) ? a : b ;
}
int m = 43, n = 56 ;
cout << max(m,n) << endl ; // displays 56 (CORRECT)
template<class TYPE>
TYPE max(const TYPE& a, const TYPE& b) {
return (a>b) ? a : b ;
}
int m = 43, n = 56 ;
// compiler automatically instantiates max(int&, int&)
cout << max(m,n) << endl ; // displays 56 (CORRECT)
template<class TYPE>
TYPE max(const TYPE& a, const TYPE& b) {
return (a>b) ? a : b ;
}
– Template definition
template <class TYPE>
TYPE function_name(TYPE& t){
// body
}
• What’s OK
– Multiple template classes allowed
template <class TYPE1, class TYPE2,…class TYPEN>
TYPE1 function_name(TYPE1&, TYPE2&,… TYPEN&) ;
int myFunction() {
template <class T> // ERROR – not allowed
void myTemplFunc(T& t) ;
}
template<class TYPE>
TYPE max(const TYPE& a, const TYPE& b) {
return (a>b) ? a : b ; // comparing pointer not sensible
}
template<>
const char* max(const char* a, const char* b) {
return strcmp(a,b)>0 ? a : b ; // Use string comparison instead
© 2006 Wouter Verkerke, NIKHEF
}
Template classes
template<class T>
class Triplet {
public:
Triplet(T& t1, T& t2, T& t3) () ;
private:
T _array[3] ;
}
template<class TYPE>
class Stack {
public:
Stack(int size) : _len(size), _top(0) { // constructor
_v = new TYPE[_len] ;
}
Stack(const Stack<TYPE>& other) ; // copy constructor
~Stack() { delete[] _v ; }
private:
TYPE* _v ; Assumptions on TYPE
int _len ; -Default constructor
int _top ; -Assignment defined
void example() {
s.push(1) ;
s.push(2) ;
cout << s.pop() << endl ;
// OUTPUT ‘2’
}
© 2006 Wouter Verkerke, NIKHEF
Non-class template parameters
void example() {
Vector<double,3> ThreeVec ;
Vector<double,4> FourVec ;
}
© 2006 Wouter Verkerke, NIKHEF
Template default parameters
void example() {
Vector ThreeVecDouble ;
Vector<int> ThreeVecInt ;
Vector<float,4> FourVecFloat ;
}
template<class TYPE>
class A {
private:
B<TYPE> member1 ; // OK – generic template member
C<int> member2 ; // OK – specific template member
D member3 ; // OK – non-template member
public:
A(args) : B(args),C(args),D(args) {} // initialization
} ;
– Composition
template<class TYPE> class Vec { … } ; // Vector container
template<class TYPE> class Arr { … } ; // Array container
template<class TYPE> class Sta { … } ; // Stack container
7 Standard Library II
the Template Library
• Containers
– Storage facility of objects
Container
• Iterators
– Abstract access mechanism to collection contents
– “Pointer to container element” with functionality to move pointer
• Algorithms
– Operations (modifications) of container organization of contents
– Example: Sort contents, apply operation to each of elements
• STL is efficient
– The various containers provide different data structures.
– No inheritance nor virtual functions are used (we’ll cover this shortly).
– You can choose the container that is most efficient for the type of
operations you expect
– stack
Adapters of fundamental
– queue containers
that provide a modified functionality
– priority_queue
0 1 2
v[0] = 80 ;
v.push_back(70) ; // creates v[10] and sets to 11
front end
front end
– No random access
– Example iterator ‘pointer’ in collection
#include <list>
list<double> l ;
l.push_front(30.5) ; // append element in front
l.insert(somewhere,47.5) ; // insert in middle
rear front
• Example
bottom
void sender() {
stack<string> s ;
s.push(“Aap”) ;
s.push(“Noot”) ;
s.push(“Mies”) ;
receiver(s) ;
}
void receiver(stack<int>& s) {
while(!s.empty()) cout << s.pop() << “ “ ;
}
33 17
push() pop()
returns and removes
35 40 42 element with
highest priority
void sender() {
priority_queue<int> s ;
s.push(10) ; s.push(30) ;s.push(20) ;
receiver(s) ;
}
void receiver(stack<int>& s) {
while(!s.empty()) cout << s.pop() << “ “ ;
}
// output “30 20 10” © 2006 Wouter Verkerke, NIKHEF
Sequential container performance comparison
Insert/remove element
index [] In Middle At Front At Back
vector const O(n) + const +
list const const const
deque const O(n) const const
stack const +
queue const const +
prio_que O(logN) O(logN)
list<int> l ;
double tmp = *(l.begin()) ; // 1st element of list
front end
pair<int,float> calculation() {
return make_pair(42,3.14159) ;
}
int main() {
pair<int,float> result = calculation() ;
cout << “result = “ << pair.first
<< “ “ << pair.second << endl ;
}
© 2006 Wouter Verkerke, NIKHEF
Map <map>
map<T1,T2>
pair<const T1,T2>
Bjarne 33
Gunnar 42
Leif 47
Thor 52
• Map example
map<string,int> shoeSize ;
shoeSize.insert(pair<string,int>(“Leif”,47)) ;
showSize.insert(make_pair(“Leif”,47)) ;
shoeSize[“Bjarne”] = 43 ;
shoeSize[“Thor”] = 52 ;
– No duplicates allowed
Gunnar
people.insert(“Leif”) ;
people.insert(“Bjarne”) ; Thor
people.insert(“Stroustrup”) ;
• Multiset
– Identical to set
– Except that multiple keys of the same value are allowed
• Multimap
– Identical to map
– Except that multiple keys of the same value are allowed
int i = 0 ;
double* ptr ; vector<double>::iterator iter ;
while(i<10) { while(iter!=v.end()) {
cout << *ptr << endl ; cout << *iter << endl ;
++ptr ; ++iter ;
++i ; }
}
int i = 0 ;
double* ptr ; vector<double>::iterator iter ;
while(i<10) { while(iter!=v.end()) {
cout << *ptr << endl ; cout << *iter << endl ;
++ptr ; ++iter ;
++i ; }
}
int i = 0 ;
double* ptr ; vector<double>::iterator iter ;
while(i<10) { while(iter!=v.end()) {
cout << *ptr << endl ; cout << *iter << endl ;
++ptr ; ++iter ;
++i ; }
}
int i = 0 ;
double* ptr ; vector<double>::iterator iter ;
while(i<10) { while(iter!=v.end()) {
cout << *ptr << endl ; cout << *iter << endl ;
++ptr ; ++iter ;
++i ; }
}
int i = 0 ;
double* ptr ; vector<double>::iterator iter ;
while(i<10) { while(iter!=v.end()) {
cout << *ptr << endl ; cout << *iter << endl ;
++ptr ; ++iter ;
++i ; }
}
Access the element the pointer Access the element the iterator
is currently pointing to is currently pointing to
int i = 0 ;
double* ptr ; vector<double>::iterator iter ;
while(i<10) { while(iter!=v.end()) {
cout << *ptr << endl ; cout << *iter << endl ;
++ptr ; ++iter ;
++i ;
} }
// Iterator loop
vector<int> v(10) ;
vector<int>::iterator iter ;
for (iter=v.begin() ;
operator++
iter!=v.end() ;
moves iterator one
++iter) {
element ahead
*iter = 0 ;
}
dereference
operator*() returns
T& to pointed-to
element end() returns iterator pointing
to start of container
assignment between
iterators transfer
‘current position’
© 2006 Wouter Verkerke, NIKHEF
Why iterators are a better interface
#include <map>
multimap<string,int> pbook ;
pbook.insert(pair<string,int>(“Bjarne”,00205726666)) ; // office phone
pbook.insert(pair<string,int>(“Bjarne”,00774557612)) ; // home phone
pbook.insert(pair<string,int>(“Bjarne”,0655765432)) ; // cell phone
pbook.insert(pair<string,int>(“Fred”,0215727576)) ; // office phone
multimap<string,int>::iterator iter
begin=pbook.lower_bound(“Bjarne”),
end=pbook.upper_bound(“Bjarne”) ;
• Input iterator
– Special iterator for input, for example from keyboard
– Iterator allows to read input and must be incremented before next
input is allowed
var = *iter++ ;
• Output iterator
– Special iterator for output sequence, for example to standard
output
– Iterator allows to write and must be incremented before next
output is allowed
*iter++ = var ;
• Forward iterator
– Input and output are both allowed
– Iteration must occur in positive increments of one
*iter = var ;
var = *iter ;
© 2006 Wouter Verkerke, NIKHEF
++iter ;
Types of iterators
• Bidirectional iterator
– Can move forward and backward in steps of one
*iter = var ;
var = *iter ;
++iter ;
--iter ;
*iter = var ;
var = *iter ;
++iter ;
--iter ;
iter[3] = var ;
iter += 5 ;
iter -= 3 ;
list l<string> l ;
// Sort list elements according to operator< ranking
sort(l.begin(),l.end()) ;
• Example
vector<int> grades(100) ;
sort(grades.begin(),grades.end()) ; // sort all elements
• Notes
– Pair of iterators is used to indicate range to be sorted
– Range does not include element pointed to by upper bound iterator
• Function end() returns a ‘past the end’ iterator so that the last element of
the container is included in the range when end() is used as endpoint
• Example
// the call back function
void printRoot(float number) {
cout << sqrt(number) << endl ;
}
• Example
list<int> l(10) ;
vector<int> v(10) ;
• Note on ostream_iterator
– Construct output iterator tied to given ostream.
– Optional second argument is printed after each object is printed
© 2006 Wouter Verkerke, NIKHEF
STL algorithm overview
unique_copy upper_bound
vector<int> v(10) ;
• Class complex
– STL implements complex numbers as template
8 Inheritance &
Polymorphism
• Example
– Class employee holds employee personnel record
class Employee {
public:
Employee(const char* name, double salary) ;
const char* name() const ;
double salary() const ;
private:
string _name ;
double _salary ;
} ;
– Company also employs managers, which in addition to being
employees themselves supervise other personnel
• Manager class needs to contain additional information: list of subordinates
Declaration of public
inheritance
list<Employee*>subs(1) ;
subs.push_back(wouter) ;
// Pointer-to-derived IS Pointer-to-base
void processEmployee(Employee& emp) {
cout << emp.name() << “ : “ << emp.salary() << endl ;
}
processEmployee(*emp) ;
processEmployee(*mgr) ; // OK Manager IS Employee
return *this ;
}
© 2006 Wouter Verkerke, NIKHEF
Inheritance – Destructors, call sequence
class B : public A {
B() { cout << “B constructor” << endl ;}
~B() { cout << “B destructor” << endl ; }
}
Output
int main() {
A constructor
B b ;
B constructor
cout << endl ;
}
B destructor
A destructor © 2006 Wouter Verkerke, NIKHEF
Sharing information – protected access
protected:
void setSalary(double newSalary) {
if (newSalary<_salary) }
cout << “ERROR: salary must always increase” << endl ;
} else {
_salary = newSalary ;
}
}
The setSalary() function is
private: protected:
string _name ;
double _salary ; Public cannot change salary
} except in controlled way
through public
annualRaise() method
protected:
void setSalary(double newSalary) {
if (newSalary<_salary) }
cout << “ERROR: salary must always increase” << endl ;
} else {
_salary = newSalary ;
}
} class Manager : public Employee {
public:
Note how accessor/modifier
private: Manager(const char* name, double salary,
stringpattern
_name ; list<Employee*> subs) ;
salary()/setSalary() is also
double _salary ;
useful for protected access giveBonus(double amount) {
}
setSalary(salary()+amount) ;
Manager is only allowed to
change salary through }
controlled method: negative private:
bonuses are not allowed… list<Employee*> _subs ;
© 2006 Wouter Verkerke, NIKHEF
}
Object Oriented Analysis & Design with Inheritance
– In plain English:
Employee emp(“Wouter”,10000) ;
Manager mgr(“Stan”,20000,&emp) ;
Employee emp(“Wouter”,10000) ;
Manager mgr(“Stan”,20000,&emp) ;
class Employee {
public:
Employee(const char* name, double salary) ;
virtual const char* name() const ;
double salary() const ;
private:
…
} ;
class Employee {
public:
Employee(const char* name, double salary) ;
~Employee() ;
private:
…
} ;
class B : public A {
} © 2006 Wouter Verkerke, NIKHEF
Virtual functions – Watch the destructor
• Watch the destructor declaration if you define virtual functions
– Example
killTheEmployee(emp) ; // OK
killTheEmployee(mgr) ; // LEGAL but WRONG!
// calls ~Employee() only, not ~Manager()
class Trajectory
public: Interface
virtual Point x(float& t)=0; only
int main() {
// Allocate array of trajectory pointers
Trajectory* tracks[3] ;
Abstract
Base Common
Interface
Concrete Concrete
Derived Implementation Implementation
I II
• Will look into this in a bit more detail in the next sides…
© 2006 Wouter Verkerke, NIKHEF
Collections of Polymorphic objects – storage
LineTrajectory track1(…) ;
HelixTrajectory track2(…) ;
– Why Error: list<X> calls default constructor for X, but can not
instantiate X if X is an abstract classes such as Trajectory
– Solution: make a collection of pointers
list<Trajectory*> trackList ; // OK
trackList.push_back(&track1) ;
trackList.push_back(&track2) ;
• Technical Solution
– Write a new container class, or inherit it from a STL container
class that takes ownership of objects pointed to.
– NB: This is not so easy – think about what happens if replace
element in container: does removed element automatically get
deleted on the spot?
• Bookkeeping Solution
– Document clearly in function that creates trackList that contents
of tracklist is owned by caller in addition to list itself
– More prone to mistakes © 2006 Wouter Verkerke, NIKHEF
Collections of polymorphic objects – copying
list<Trajectory*>::iterator iter ;
for(iter=trackList.begin() ; iter!=trackList.emd() ; ++iter) {
Trajectory* track = *iter ;
– Can you still tell what the true type is given a base class pointer?
if (lineTrack != 0) {
cout << “track was a LineTrajectory” << endl ;
} else {
cout << “track was something else” << endl ;
} © 2006 Wouter Verkerke, NIKHEF
Run time type identification
9 Exception
handling
int main() {
double x(-3) ;
double y = calc(x) ;
}
double calc(double x) {
if (x<0) {
// run-time error condition
throw x ;
}
return sqrt(x) ;
}
int main() {
SomeClass obj ;
double x(-3) ;
double y = calc(x) ;
}
time line
1) throw f
double calc(double x) { 2) ofs destructor
ofstream ofs ; (closes file)
if (x<0) { 3) obj destructor
// run-time error condition 4) exit
throw x ;
}
return sqrt(x) ;
© 2006 Wouter Verkerke, NIKHEF
}
Catching exceptions
int main() {
double x(-3) ;
try {
double y = wrapper(x) ;
}
catch (float x) {
cout << “oops, sqrt of “
<< ”negative number”
double wrapper(double x) {
<< endl ;
// do some other stuff
}
return calc(x) – 5 ;
}
}
double calc(double x) {
if (x<0) {
// run-time error condition
throw f ;
}
return sqrt(x) ;
} © 2006 Wouter Verkerke, NIKHEF
Solving the problem
// do some housekeeping
If allocate throws
an exception again
if (problem_solved) {
it will not be caught array = allocate(size) ;
by surrounding catch } else {
block throw ; // give up handling error here
}
}
} © 2006 Wouter Verkerke, NIKHEF
Solving the problem in steps
double wrapper(double x) {
// do some other stuff
try {
calc(x) ;
}
catch (float x) {
// first resort error handling
if (!problem_solved) {
throw ; // forward
}
}
}
double calc(double x) {
throw©f2006
; Wouter Verkerke, NIKHEF
}
There are exceptions and exceptions
int main() {
double x(-3) ;
try {
double y = calc(x) ;
}
catch (int x) {
cout << “oops, sqrt of “
<< ”negative integer number”
<< endl ;
}
catch (float x) {
cout << “oops, sqrt of “
<< ”negative floating point number”
<< endl ;
}
}
© 2006 Wouter Verkerke, NIKHEF
The catchall
int main() {
double x(-3) ;
try {
double y = calc(x) ;
}
catch (int x) {
// deal with int exception
}
catch (float x) {
// deal with float exception
}
catch (…) {
// deal with any other exception
}
}
FloatingPointError
class FloatingPointError {
FloatingPointError(float val) : value(val) {}
public:
float value ;
}
void mathRoutine() {
try {
doTheMath() ;
}
void mathRoutine() {
try {
doTheMath() ;
}
exception
logic_error runtime_error
length_error range_error
invalid_argument ios_base::failure
10 Where to go
from here
• Software design
– ‘Design Patterns’ (Gamma et al.) [ not for beginners ]