Professional Documents
Culture Documents
Lecture 7b
Lecture 7b
Lecture 7b
Simon Scheidegger
Today’s Roadmap
Most programming languages require concrete types for the
function definition
C int min_int(int a, int b) { return a<b ? a : b;}
float min_float (float a, float b) { return a<b ? a : b;}
double min_double (double a, double b) { return a<b ? a : b;}
Fortran MIN(), AMIN(), DMIN(), ...
Function overloading in C++
solves one problem immediately: we can use the same name
However be careful:
Look at the assembly language file min.s and also at min.o
nm min.o
The functions actually have different names!
Types of arguments appended to function name
C and Fortran functions just use the function name
Can declare a function to have C-style name by using extern “C”
Using templates we get functions that
work for many types T.
are as generic and abstract as the formal definition.
are one-to-one translations of the abstract algorithm.
Usage Causes Instantiation
Usage Causes Instantiation
Usage Causes Instantiation
Usage Causes Instantiation
Usage Causes Instantiation
Action Required
Classes are an expanded concept of data structures
→ like data structures, they can contain data members, but
they can also contain functions as members.
An object is an instantiation of a class.
In terms of variables, a
Class would be the type.
Object would be the variable.
Classes (recall from Python)
Are a method to create new data types
e.g. a vector or matrix type
Object oriented programming:
Instead of asking: “What are the subroutines?”
We ask:
What are the abstract entities?
What are the properties of these entities?
How can they be manipulated?
→ Then we implement these entities as classes
Advantages:
High level of abstraction possible
Hiding of representation dependent details
Presentation of an abstract and simple interface
Encapsulation of all operations on a type within a class
allows easier debugging
What are classes (in C++) ?
Classes are collections of “members” representing one entity
Members can be
functions
data
types
These members can be split into
public, accessible interface to the outside.
Should not be modified later!
private, hidden representation of the concept. Can be changed
without breaking any program using the class.
Objects of this type can be modified only through these
member functions → localization of access, easier debugging
How to design classes
Ask yourself some questions:
what are the logical entities?
→ classes
what are the internal state variables ?
→ private data members
how will it be created/initialized and destroyed?
→ constructor and destructor
what are its properties?
→ public constant member functions
how can it be manipulated?
→ public operators and member functions
General Structure of a Class
A first class example: a traffic light
Property
The state of the traffic light (green, orange or red)
Operation
Set the state
Construction
Create a light in a default state (e.g. red)
Create a light in a given state
Destruction
Nothing special needs to be done
Internal representation
Store the state in a variable
Alternative: connect via a network to a real traffic light
A first class example: a traffic light
Converting the design into a class
A first class example: a traffic light
Add a public member
A first class example: a traffic light
Add a private data member (variable) of that type:
Programs using a class should thus also not depend on representation
Access declarators
public: only representation-independent interface, accessible to all
private: representation-dependent functions and data members
friend declarators allow related classes access to representation
Note: Since all data members are representations of concepts
(numbers, etc.) they should be hidden (private)!
By default all members are private
In a struct by default all are public
Member access
Usage:
Trafficlight x(Trafficlight::green);
Trafficlight::light l;
l = x.state();
l = Trafficlight::green;
Members accessed with
variable_name.member_name
Type members accessed with scope operator (::)
class_name::member_name
→demo/trafficlight.cpp
Another example – Rectangle
demo/rectangle.cpp
The scope operator (::) specifies the class to which the // example: one class, two objects
#include <iostream>
using namespace std;
member being defined belongs, granting exactly the same class Rectangle {
int width, height;
scope properties as if this function definition was directly public:
void set_values (int,int);
int area () {return width*height;}
included within the class definition. };
void Rectangle::set_values (int x, int y) {
width = x;
height = y;
}
For example, the function set_values has access int main () {
Rectangle rect, rectb;
to the variables width and height, which are private rect.set_values (3,4);
rectb.set_values (5,6);
cout << "rect area: " << rect.area() << endl;
members of class Rectangle, and thus only cout << "rectb area: " << rectb.area() << endl;
return 0;
accessible from other members of the class, such as this. }
Another example
demo1/line.cpp #include <iostream>
class Line {
public:
void setLength( double len );
double getLength( void );
Line(); // This is the constructor
private:
double length;
};
return 0;
}
Action required
Implement a Class “Students” to practice accessing data members.
As public data, we want to get the name of the student.
As public member function, we want a function “printname” that writes
the name of the student to the terminal.
#include <bits/stdc++.h>
using namespace std;
class Students
{
public:
string studentname;
int id;
Students obj1;
obj1.studentname = "xyz";
obj1.id=15;
// call printname()
obj1.printname();
cout << endl;
// call printid()
obj1.printid();
return 0; demo1/function_member_access.cpp
}
Illustration: a point in two dimensions
demo1/point.cpp
Internal state:
x- and y- coordinates
is one possible representation
Construction
default: (0,0)
from x- and y- values
same as another point
Properties:
distance to another point
x- and y- coordinates
polar coordinates
Operations
Inverting a point
assignment
Constructors and Destructors
Let us look at the point example:
Most classes should provide a default constructor
Copy constructor automatically generated (a.k.a. synthesized) as
member-wise copy, unless otherwise specified
Destructor normally empty and automatically generated
Nontrivial destructor only if resources (memory) allocated by the
object. This usually also requires nontrivial copy constructor and
assignment operator. (example: array class)
Default members
Some member functions are implicitly created by the compiler
Copy constructor
A::A(A const&);
defaults to member-wise copy if not specified
Assignment operator
A::operator=(A const&);also defaults to member-wise copy
Destructor
A::~A()
defaults to empty function
Declaration, Definition and Implementation
Declaration Implementation
Definition Constructors
or
const
Variables or data members declared as const cannot be modified
int const a = 1; a = 2; // error!
Member functions declared as const do not modify the object
double Point::abs() const;
Only const member functions can be called for const objects
const Point P(5,6); P.invert(); // error!
friends
Consider geometrical objects: Using friend declaration this is
points, vectors, lines,... possible:
// friend class
#include <iostream>
using namespace std;
class Square;
class Rectangle {
int width, height;
public:
int area ()
{return (width * height);}
void convert (Square a);
};
class Square {
friend class Rectangle;
private:
int side;
public:
Square (int a) : side(a) {}
};
void Rectangle::convert (Square a) {
width = a.side;
height = a.side;
}
int main () {
Rectangle rect;
Square sqr (4);
rect.convert(sqr);
cout << rect.area();
return 0;
}
this
Sometimes the object needs to be accessed from a member function
this is a pointer to the object itself (cf. “self” in Python):
double Point::x() const {
return this->x_; // or (*this).x_ or simply x_
}
Preview for later: copy assignment
class Box {
public:
// Constructor definition
Box(double l = 2.0, double b = 2.0, double h = 2.0) {
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume() {
return length * breadth * height;
}
int compare(Box box) {
return this->Volume() > box.Volume();
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void) {
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
if(Box1.compare(Box2)) {
cout << "Box2 is smaller than Box1" <<endl;
} else {
cout << "Box2 is equal to or larger than Box1" <<endl;
}
return 0;
}
Extra materials for self-study!
Point class
What is?
Extra materials for self-study!
Point class
What is?
Extra materials for self-study!
Point class
What is?
Member-wise...
Extra materials for self-study!
sarray class
Extra materials for self-study!
sarray class
Extra materials for self-study!
sarray class
Extra materials for self-study!
sarray class
What if
Extra materials for self-study!
sarray class
What else?
Extra materials for self-study!
sarray class
How to?
Extra materials for self-study!
sarray class
What does?
Extra materials for self-study!
sarray class
What does?
Member-wise...
Extra materials for self-study!
sarray class
What does?
Member-wise...
Extra materials for self-study!
sarray class
Now?
Extra materials for self-study!
sarray class
Now, problems?
Extra materials for self-study!
sarray class
sarray class
Now, problems?
Extra materials for self-study!
sarray class
sarray class
sarray class
Special members
Constructors
initialize an object
same name as class
Destructors
do any necessary cleanup work when object is destroyed
have the class name prefixed by ~
Operators
Conversion of object A to B
two options:
constructor of B taking A as argument
conversion operator to type B in A:
operator B();
Default versions exist for some of these
Extra materials for self-study!
Operators as functions
Most operators can be redefined for new classes
Same as functions, with function name:
operator symbol(...)
Example:
Matrix A,B,C;
C=A+B;
Extra materials for self-study!
Assignment operators
The assignment operators =, +=, -=, *=, /=, ^=, &=, |=,
%=
can only be implemented as member functions
should always return a const reference to allow expressions like
a=b=c;
a=b+=c;
Example:
Extra materials for self-study!
Symmetric operators
Symmetric operators, e.g. +, -, ... are best implemented as free
functions.
Either the simple-minded way
Or the elegant way
Extra materials for self-study!
We can now print a Point:
Extra materials for self-study!
class Potential {
double operator()(double d) { return 1./d;}
...
};
Don’t get confused by the two pairs of ()()
The first is the name of the operator
The second is the argument list
Extra materials for self-study!
Function objects
Assume a function with parameters:
cannot be used with integrate template!
Solution: use a function object
Definition of member outside the class body
Class templates: example complex template
What if we want a complex type for float, long double, ...?
Same idea as function templates, classes for any given type T
The standard complex class is defined as a template
template <class T> class complex;
It is specialized and optimized for
complex<float>
complex<double>
complex<long double>
but in principle also works for complex<int>, ...
it is a good exercise in template class design to look at the
<complex> header
Class templates: (simple) array example
std::size_t is simply an unsigned integer type that can
store the maximum size of a theoretically possible object of
any type (often used for “array” indexing and loop counting...)
Class templates: (simple) array example
→ Extend our simple array to any (compatible) type:
Similar naming as in STL (Standard Template Library)
Class templates: (simple) array example
See demo2/sarray.cpp ; sarray.hpp
The keyword typename is needed here so that C++ knows
the member is a type and not a variable or function.
Template specialization
See demo2/sarray.cpp ; sarray.hpp
Consider our sarray<T>
An array of size n takes
n*sizeof(T) bytes
Consider sarray<bool>
An array of size n takes
n*sizeof(T) = ? bytes
Template specialization
See demo2/sarray.cpp ; sarray.hpp
Consider our sarray<T>
An array of size n takes
n*sizeof(T) bytes
Consider sarray<bool>
An array of size n takes
n*sizeof(T) = n bytes
An optimized implementation just
needs one bit!
Array<bool>(n) would need
only n/8 bytes
How can we define an optimized version for bool?
Solution: template specialization
Template specialization
Consider our sarray<T>
An array of size n takes
n*sizeof(T) bytes
Consider sarray<bool>
An array of size n takes
n*sizeof(T) = n bytes
An optimized implementation just
needs one bit!
Array<bool>(n) would need
only n/8 bytes
How can we define an optimized version for bool?
Solution: template specialization
Template specialization
See demo2/sarray.cpp ; sarray.hpp
Specialize
Any better?
Template specialization
See demo2/sarray.cpp ; sarray.hpp
Specialize
We store the bits in “words”
We need “something” to get/set individual bits in the words
A bit reference
Specialize
Template specialization
Specialize
Template specialization
New subscript operators:
min<double>(1,2.)
Etc...
Traits types: Motivation
We want to allow the addition of two arrays:
template <typename T>
sarray<T> operator+ (const sarray<T>&, const sarray<T>&)
How do we add two different arrays?
Array<int> + Array<double> makes sense!
template <typename T, typename U>
sarray<?> operator +(const sarray<T>&, const sarray<U>&)
We could again:
template <typename R, typename T, typename U>
Array<R> operator +(const sarray<T>&, const sarray<U>&)
What is the result type?
We want to calculate with types!
The solution is a technique called traits
Traits types: Motivation
We want to do something like:
template <class T, class U>
typename min_type<T,U>::type& min(T const& x, T const& y)
We want to do something like:
template <class T, class U>
A definition of sum_type:
empty template type to trigger error messages if used
template< typename T, typename U > struct sum_type {};
Partially specialized valid templates:
template <class T> struct sum_type<T,T> {typedef T type; };
Fully specialized valid templates:
template <>struct sum_type<double,float> {typedef double type; };
template <>struct sum_type<float,double> {typedef double type; };
template <> struct sum_type<float,int> {typedef float type; };
template <> struct sum_type<int,float> {typedef float type; };
Old style traits
In C++98 traits were big “blobs”:
Later it was realized that this was ugly:
A traits class is a “meta function”, a function operating on types
A blob like numeric limits takes one argument, and returns
many different values
This is not the usual design for functions!
New style traits
Since C++03 all new traits are single-valued functions
Types are returned as the type member:
Constant values are returned as the value member:
Trait types: another example
demo2/average_main.cpp ; average.hpp
Imagine an average() function:
Problems?
Trait types: another example
demo2/average_main.cpp ; average.hpp
Imagine an average() function:
Has problems with e.g. vector<int>, as the average is in
general a floating point number:
v = (1,2,3,4)
Average would be int(10/4) = 2
Solution: traits!
Trait types: another example
A definition of average_type:
The general traits type:
template <class T> struct average_type<T> {typedef T type; };
The special cases:
template <> struct average_type<int> {typedef double type; };
Repeat for all integer types...
Trait types: an automatic solution
A definition of average_type:
The average type: template <typename T>
struct average_type {
typedef typename
helper1<T,std::numeric_limits<T>::is_specialized>::type
type;
};
The first helper:
template<typename T, bool F>
struct helper1 {typedef T type; };
The first helper if numeric limits is specialized for T (a partial
specialization)
template<class T>
struct helper1<T,true> {
typedef typename
helper2<T,std::numeric_limits<T>::is_integer>::type type;
};
Trait types: an automatic solution
A definition of average_type:
The average type:
template <typename T>
struct average_type {
typedef typename
helper1<T,std::numeric_limits<T>::is_specialized>::type type;
};
The second helper:
template<class T, bool F>
struct helper2 {typedef T type; };
The second helper if T is an integer type (a partial specialization)
template<class T>
struct helper2<T,true> {typedef double type; };
Concepts
A concept is a set of requirements, consisting of valid expressions, associated types,
invariants, and complexity guarantees.
A type that satisfies the requirements is said to model the concept.
A concept can extend the requirements of another concept, which is
called refinement.
A concept is completely specified by the following:
Associated Types: The names of auxiliary types associated with the
concept.
Valid Expressions: C++ expressions that must compile successfully.
Expression Semantics: Semantics of the valid expressions.
Complexity Guarantees: Specifies resource consumption (e.g.,execution time,
memory).
Invariants: Pre and post-conditions that must always be true.
Assignable concept
Notation
X A type that is a model of Assignable
x, y Object of type X
CopyConstructible concept
Notation
X A type that is a model of CopyConstructible
x, y Object of type X
Documenting a template function
In addition to
Preconditions
Postconditions
Semantics
Exception guarantees
The documentation of a template function must include
Concept requirements on the types
Note that the complete source code of the template function must
be in a header file
The power method
Is the simplest eigenvalue solver
returns the largest eigenvalue and corresponding eigenvector
Only requirements:
A is linear operator on a Hilbert space
Initial vector y is vector in the same Hilbert space
Can we write the code with as few requirements as possible?
Generic implementation of the power method
A generic implementation is possible
Concepts for the power method
The triple of types (T,V,OP) models the Hilbert space concept if
T must be the type of an element of a field
V must be the type of a vector in a Hilbert space over that field
OP must be the type of a linear operator in that Hilbert space
All the allowed mathematical operations in a Hilbert space have
to exist:
Let v, w be of type V
Let r, s of type T
Let a be of type OP.
The following must compile and have the same semantics as in the mathematical
concept of a Hilbert space:
r+s, r-s, r/s, r*s, -r have return type T
v+w, v-w, v*r, r*v, v/r have return type V
a*v has return type V
two_norm(v) and dot(v,w) have return type T
...
4. Error Handling (Exceptions),
Random numbers, and Timing
Error handling – Exceptions
When executing C++ code, different errors can occur:
coding errors made by the programmer, errors due to wrong
input, or other unforeseeable things.
When an error occurs, C++ will normally stop and generate
an error message.
The technical term for this is: C++ will throw an exception
(throw an error).
C++ try and catch
Exception handling in C++ consist of three keywords: try,
throw and catch:
The try statement allows you to define a block of code to be
tested for errors while it is being executed.
The throw keyword throws an exception when a problem is
detected, which lets us create a custom error.
The catch statement allows you to define a block of code to
be executed, if an error occurs in the try block.
The try and catch keywords come in pairs.
Example
try {
// Block of code to try
throw exception; // Throw an exception when a problem arise
}
catch () {
// Block of code to handle errors
}
Code: error_handling/exception.cpp
Example
#include <iostream>
using namespace std;
int main() {
try {
int age = 15;
if (age >= 18) {
cout << "Access granted - you are old enough.";
} else {
throw (age);
}
}
catch (int myNum) {
cout << "Access denied - You must be at least 18 years old.\n";
cout << "Age is: " << myNum;
}
return 0;
}
Code: error_handling/exception.cpp
Example explained
We use the try block to test some code: If the age variable is
less than 18, we will throw an exception, and handle it in our
catch block.
In the catch block, we catch the error and do something
about it.
The catch statement takes a parameter: in our example we
use an int variable (myNum) (because we are throwing an
exception of int type in the try block (age)), to output the
value of age.
If no error occurs (e.g. if age is 20 instead of 15, meaning it
will be be greater than 18), the catch block is skipped.
int age = 20;
Example
#include <iostream>
using namespace std;
int main() {
try {
int age = 20;
if (age >= 18) {
cout << "Access granted - you are old enough.";
} else {
throw (age);
}
}
catch (int myNum) {
cout << "Access denied - You must be at least 18 years old.\n";
cout << "Age is: " << myNum;
}
return 0;
}
Code: error_handling/exception2.cpp
Example
You can also use the throw keyword to output a reference number, like
a custom error number/code for organizing purposes:
try {
int age = 15;
if (age >= 18) {
cout << "Access granted - you are old enough.";
} else {
throw 505;
}
}
catch (int myNum) {
cout << "Access denied - You must be at least 18 years old.\n";
cout << "Error number: " << myNum;
}
Code: error_handling/exception3.cpp
Handle Any Type of Exceptions (...)
If you do not know the throw type used in the try block, you can use the "three
dots" syntax (...) inside the catch block, which will handle any type of
exception:
try {
int age = 15;
if (age >= 18) {
cout << "Access granted - you are old enough.";
} else {
throw 505;
}
}
catch (...) {
cout << "Access denied - You must be at least 18 years old.\n";
}
Code: error_handling/exception4.cpp
More exception details
Rethrowing exceptions
If a catch() clause decides it cannot deal with the exception it can
re-throw it with throw;
Exceptions and inheritance
A catch(ExceptionType& t) clause also catches exceptions
derived from ExceptionType
More details in text books
Uncaught exceptions
throw() qualifiers
Exceptions thrown while dealing with an exception
Avoid exceptions in destructors
Can be very bad since the destructor is not called!
http://en.cppreference.com/w/cpp/error/exception
http://en.cppreference.com/w/cpp/header/stdexcept
Date and time utilities
Utilities for dealing with date and time.
Discussions about efficiency are meaningless in the absence of
measurements.
The standard library:
Header <ctime>: date & time
Header <chrono> (since C++11): precision time measurements
Before C++11: use boost’s <chrono>
See http://www.boost.org/
Date and time utilities
Let’s look at Example 1: demo3/hello_date.cpp
Let’s look at Example 2: demo3/fibo.cpp
(recursive implementation)
Let’s look at Example 3: demo/time_loop.cpp
Always do several timing runs
(“other activities” on your computer may interfere!)
Monte Carlo Methods
A broad class of computational methods relying random sampling
to obtain numerical results
Used e.g.
Weather forecast
Physics
Finance
Economics
Multi-dimensional integration
…
Heavily rely on random numbers!
Multidimensional Numerical
Integration
Simpson Method with N function evaluations
In one dimension, the error is of order
In d dimensions, the error is of order
In high dimensions, this becomes unacceptable (curse of
dimensionality)
A solution: Monte Carlo Integration
Monte Carlo Integration
Given the integral, we compute the average of a function simply as
On the other hand, we could also pick N random points in the
integration interval and simply
Monte Carlo Integration
The error becomes statistical
In more than 8 dimension, this becomes better that Simpson!
Monte Carlo Integration
demo3/random_pond.cpp
Example: Computing Pi by throwing darts!
Random numbers
True/Real random numbers
Hard to obtain
From physical phenomena supposed to be random, e.g.
Thermal noise
Atmospheric noise
Cosmic radiation
Pseudorandom numbers
Get random numbers from an algorithm
Totally deterministic and therefore not random at all
But maybe good enough for your application
Never trust (just one) (pseudo) random number generator
Linear congruential generators
(Too?) simple random number generator
Have the form
From an initial value, the so-called seed, it computes
(deterministically!) a sequence of “random numbers”
Just a simple example...
Linear congruential generators
Linear congruential generators
Linear congruential generators
Linear congruential generators
Linear congruential generators
Linear congruential generators
Linear congruential generators
Random number utilities
Utilities for dealing with random numbers in standard library since
C++11
Header <random>
Use –std=c++11 (if needed)
For previous C++ versions, use the Boost.Random library
-I/path/to/boost/include
See http://www.boost.org/
Both contain
Random number generators (the engines)
Generates a sequence of unsigned random numbers
Distribution functions
Uses an engine to generate random numbers distributed
according to a particular distribution function
Random number engines
Available engines (in standard library):
default_random_engine (alias for engine good & low cost engine)
linear_congruential_engine (linear congruential generators)
mersenne_twister_engine (Mersenne Twister generators)
subtract_with_carry_engine (lagged Fibonacci type generators)
All engines have members such as:
Useful and good generators (stdlib)
Read the documentation for more generators and details
Example: demo3/random_engines.cpp
Useful and good generators
(Boost.Random)
Read the documentation for more generators and details
Seeding random number generators
Set the seed:
Distributions
Uniform distributions
Integer: std::uniform_int_distribution<int> dist1(a,b)
Floating point:
std::uniform_real_distribution<double> dist2(a,b)
Exponential distribution
std::exponential_distribution<double> dist3(lambda)
Normal distribution
std::normal_distribution<double> dist4(mu,sigma)
Example: random_normal.cpp
Read the documentation for more distributions and details
Good practices with random
numbers
Always seed the random number generator ONCE
fixed constant at compile time
input parameter
using current time (not advised)
Print your seed!
debugging is much easier if you can reproduce the same random
sequence
DO NOT trust your results without trying several generators!