Advanced C++
Programming
Guillaume Caumon, September 2007
Introduction
C++ supports a variety of programming styles:
procedural (as FORTRAN, pascal, C, …)
object-oriented (as Eifel, JAVA, …)
generic
Features interact with each other.
C++ is very flexible, but also very confusing...
Software development
Energy = Cost Maintenance:
- Bug fixes
- User requests
- Backward compatibility
Conception
Development
Testing
Release Time
Introduction
The goal of this course is to provide some keys to
choose the design which:
eases the maintenance process;
optimizes performance;
optimizes memory requirements.
Prerequisites
A working knowledge of C++ and of the basic
object-oriented concepts.
What is a pointer ?
What is a class ?
What is inheritance ?
Outline
• Some Programming Rules
• Optimizing C++ programs
• Generic Programming
• A short STL overview
• Some Design Patterns
I- Some C++ features and
programming rules
Miscellaneous
Object design
Resolving ambiguities: namespace
namespace Gocad1 { ANSI feature
class MainWindow{ … };
}
namespace Gocad2 {
class MainWindow{ … };
}
using namespace Gocad1;
using Gocad2::MainWindow;
Gocad1::MainWindow window(…);
References or pointers ?
Goal: refer to objects
References are as cheap as pointers,
and more convenient
Rules:
• In function parameters, use
const T& var instead of T var
• A nil reference does not exist, so use pointers
whenever you would need ‘nil references’.
• pointers can be reassigned, references can’t.
#include <vector>
class Traced { Exercise
public: traced.h
Traced();
Traced( int parameter );
Traced( const Traced& rhs );
~Traced();
Traced& operator=( const Traced& rhs );
private:
int impl_;
};
Using std::vector;
Class Dummy {
execute_val( vector<Traced> flags );
execute_p( vector<Traced>* flags );
execute_ref( vector<Traced>& flags );
execute_cref( const vector<Traced>& flags );
};
Exercise
int main( int argc, char** argv ) { traced.cpp
cout << "Creating the vector of data" << endl;
vector<Traced> data(2);
data[0] = Traced(0); data[1] = Traced(1);
cout << "---------------------------\n" <<
endl;
Dummy dummy;
dummy.execute_val( data );
dummy.execute_ptr( &data );
dummy.execute_ref( data );
dummy.execute_cref( data );
return 1;
}
g++ -o Traced traced.cpp
./Traced
Exceptions
Goals : improve program safety
How ? Framework to bypass the function call stack
try {
// some code that might cause pbs
}
throw “message”
catch ( “message” ) {
// special processing “Doctor Watson”
}
Constructor and assignment
class String { file.h
public:
String( const char* value );
String( const String& rhs );
~String();
String& operator=( const String& rhs);
private:
char* data_;
};
int main() {
String s1 = “tutu”;
String s2( “toto” );
s2 = s1;
};
Implicit type conversions
class Rational {
Rational( int num, int denom = 1 );
double operator double() const;
};
void print_rational(
ostream& out, const Rational& fraction
) {
out << fraction->num() << “ / ” <<
fraction->denom() << endl;
}
int main( int argc, char** argv ) {
Rational r(1,2);
double d = .5 * r;
print_rational( cout, d );
return 1;
}
Avoiding implicit conversions
class Rational {
explicit Rational( ANSI feature
int num,
int denom = 1
);
operator double() const;
};
Overloading operators (I)
class Rational {
Rational& operator =( const Rational& rhs );
bool operator ==( const Rational& rhs );
Rational operator +( const Rational& rhs );
Rational& operator +=( const Rational& lhs );
Rational& operator++(); // ++a (prefix)
Rational operator++(int); // a++ (postfix)
};
Overloading operators (II)
class Rational {
// …
Rational operator+( int rhs );
friend Rational operator+(
int lhs, const Rational& rhs
);
};
Rational operator+(
int lhs, const Rational& rhs
) { //… }
If the left-hand side of the expression is of a
different type, the operator MUST be a non-member
Modifying non const member in const
member
Downgrade the const member
Non ANSI: use const cast
ANSI: use the mutable keyword
I- C++ main programming
rules
Miscellaneous
Object design
What is OOP?
Object-Oriented Programming is a “philosophy”
where the source code of a program is split into
reusable objects.
What is an object, then?
An object is made of two parts:
- The interface = catalog of the object features
-The implementation = internal machinery
The interface in C++
Usually defined in a header (.h) file:
class Car {
public:
// Members can be accessed from any object
protected:
// Can only be accessed by Car and its derived objects
private:
// Can only be accessed by Car for its own use.
};
Aggregation or Composition?
4
Car Wheel Person Brain
Aggregation is a relationship Composition is a relationship
in which one object is a part in which one object is an
of another. integral part of another
A aggregates B A composes B
= =
B is part of A, but their B is part of A, and their
lifetimes may be different lifetimes are the same
Ex: cars
c and wheels, engine, Ex: person and brain, lung,
etc. etc.
Classes: Basic Design Rules
Hide all member variables
Hide implementation functions and data
Minimize the number of public member functions
Avoid default constructors
Avoid overloading (can be ambiguous)
Use const members whenever possible / needed
Be aware of compiler generated functions
Inheritance: quick review
GeomShape
“A circle is a shape”
Circle
class GeomShape { file.h
//…
};
class Circle : public GeomShape {
//…
};
Public Inheritance Philosophy
Public inheritance ‘is a’
In other words:
What is applies to a base class
applies to its derived classes
Three aspects to consider:
class public interface
class relationship with derived classes
class internal cookware
Polymorphism
Mechanism that allows a derived class to modify the
behavior of a member declared in a base class
Employee
class Employee {
public :
virtual float income(); // 1000 Boss
};
class Boss : public Employee {
public :
virtual float income(); // 10000
};
Polymorphism
A pure virtual function just defines the interface, and
leaves the implementation to derived classes
Employee
class Employee {
public :
virtual float income() = 0; Boss
// not implemented
};
class Boss : public Employee {
public :
virtual float income(); // implemented
};
Public Inheritance Philosophy
Inheritance of Inheritance of the
the interface implementation
Non virtual Mandatory Mandatory
function
Virtual function Mandatory By default,
Possible to
redefine
Pure virtual Mandatory Re-definition is
function mandatory
Private Inheritance Philosophy
Private inheritance is implemented in term of
This is an equivalent variant of agregation:
class Car : private engine {
//…
};
class Car {
private:
Engine engine_;
};
Inheritance and fonctions
can virtual pure virtual Constructor Destructor
have... function function is… is…
Isolated
No No public public
class
base Yes public
Yes/no public
class (must) virtual
pure Yes public
abstract Yes / no protected
class (must) virtual
Derived
Yes public
(concrete) No public
class (must) virtual
Polymorphism Mechanism
Base::vf1
Base Base::vf2
vtbl_
Base::vf3
Derived Derived::vf1
Base
vtbl_
Derived::vf3
Consequences
Never call a virtual function in a constructor
Never declare a virtual function inline
Calling a virtual function is more expensive
than calling a non-virtual function
Be aware of the increased size of classes
with virtual functions
Cast operators
Avoid c-style casting operators.
ANSI C++ provides 4 cast operators :
Type* static_cast<Type>(expression)
Type* const_cast<Type>(expression)
Type* dynamic_cast<Type>(expression)
Type* reinterpret_cast<Type>(expression)
Additional guidelines...
Avoid multiple inheritance: use composition
Forbid default parameters in virtual functions
Don’t redefine (overload) a non virtual function
Differentiate between layering and inheritance
II- Optimization in C++
Optimization
Main issue: algorithm complexity and memory
requirements
Main question: which part of the code should be
optimized ?
20% of the code is used 80% of the time…
Code maintenance and debug vs. optimization.
Lazy evaluation
Compute only when needed
Examples:
Matrix operator +
Gocad association mechanism
Anticipated evaluation
Compute once, and cache information.
Examples:
Statistics manager
Dynamic arrays
Return value optimization
A first try...
class Complex {
//
};
const Complex operator*(
const Complex& a, const Complex b
) {
Complex c;
c.set_real( a.real() * b.real() );
c.set_im( a.im() + b.im() );
return c;
}
Return value optimization
A second try...
class Complex {
//
};
const Complex& operator*(
const Complex& a, const Complex b
) {
Complex c;
c.set_real( a.real() * b.real() );
c.set_im( a.im() + b.im() );
return c;
}
Return value optimization
A last try...
class Complex {
//
};
const Complex operator*(
const Complex& a, const Complex b
) {
return Complex(
a.real() * b.real(),
a.im() + b.im()
);
}
...But don’t alter your code quality for that !!
Some rules...
Overload to avoid implicit type conversions (fine
tuning only)
Prefer operator += to operator +
Prefer generic programming to virtual functions
Use inline functions, but not too much...
Postpone variable declaration
Example: dynamic arrays
Goal: add items dynamically to a set.
Problem:
- dynamic memory allocation is time-consuming
- the number of elements is not known from the start
Proposal:
Allocate N elements, then add items
Allocate 2N elements, then copy existing items, then add items
Allocate 4N elements, then copy existing items, then add items
III- Generic Programming in C++
Parameterize classes…
Case of most container classes: store data
of arbitrary types.
template<class T> class List { list.h
public :
List( int nb_items );
~List();
void append_item( const T& item );
void remove_item( const T& item );
void remove_all();
//…
};
… or fonctions
/** swap.h
* Swaps two objects of type T.
* T should provide copy constructor
* and operator=
*/
template<class T> void swap(
T& t1, T& t2
);
template<class T> void swap( swap.h
T& t1, T& t2
) {
T tmp(t1);
t1 = t2;
t2 = tmp;
}
Templates
Template code is compiled only when it is
used (template instanciation)
Keyword ‘class’ (or ‘typename’) or ‘int’ can
be used to qualify template arguments.
Members can be required from template
arguments
Example
template <class T> class List { list.h
//…
};
/**
* Sorts a List of objects of type T.
* T must provide order operators <
*/
template <class T> class ListSorter {
public :
ListSorter( List<T>& list );
void sort();
private :
List<T>& list_;
};
Functors
Goal: replace pointers to functions
How ?
[return type] operator()( [type param] );
Type checking
Supports inline functions
Example:
Generator
Unary Function
Binary function
Predicates
…
Generic Programming
Idea:
Replace virtual functions by mandatory
functions of template arguments…
Example: the GSTL [N. Rémy – A. Shtuka – B. Lévy –
J. Caers]
Example: Matrix objects (1)
class Matrix {
public:
virtual double operator()( int i, int j ) = 0;
};
class SymmetricMatrix : public Matrix {
Public:
virtual double operator()( int i, int j );
};
class UpperTriangularMatrix : public Matrix {
public:
virtual double operator()( int i, int j );
};
Any problem?
Static Polymorphism
• Replace virtual function by a template
parameter
• Delegate the function to the template argument
Example: Matrix objects (2)
class SymmetricStorage {
public:
double operator()( int i, int j );
};
class UpperTriangularMatrix {
//…
};
template <class STORAGE> class Matrix {
public:
double operator()( int i, int j ) {
return storage_(i, j);
}
private:
STORAGE storage_;
};
Example: Matrix objects (2)
template <class STORAGE> class Matrix {
public:
//…
bool is_invertible();
bool is_sparse();
bool is_symmetric_positive_definite();
//…
};
Delegation to STORAGE:
- Burdens the STORAGE concept
- May end up with inconsistencies
Example: Matrix objects (3)
template <class LEAF> class Matrix {
public:
LEAF& leaf() {
return static_cast<LEAF&>( *this );
}
};
class SymmetricMatrix:
public Matrix<SymmetricMatrix> {
//…
};
Derived type is known at compile-time
Derived classes can define their own functions
Template Specialization
• Redefine the implementation for some
template arguments
Application: metaprograms
template <int N> class Factorial {
public:
enum { value = N * Factorial<N-1>::value };
};
template <> class Factorial<1> {
public:
enum { value = 1 };
};
Void f() {
const int fact4 = Factorial<4>::value;
}
Application: metaprograms
Practical interest:
Specialization for optimizing behavior for small objects
Example :
Expand loops in vector / tensor /matrix calculus
IV- Overview of the STL
Include files
#include <iostream>
#include <utility>
#include <complex>
#include <list>
#include <vector>
#include <map>
….
STL containers
std::vector<int> vect(30);
vect[2] = 3;
//…
for(
std::vector<int>::iterator it(vect.begin());
it != vect.end(); it++
){
int& cur_value = *it;
}
V- Some design patterns
Finite state machine
To solve a linear system of equations…
add equation coefficients one by one, then invert the matrix
Class TransactionBased {
public:
start_task1();
do_action1(…);
end_task1();
start_task2(); // can be started only if task1 was completed
do_action2(…);
end_task2();
private:
enum State{ UNDEF=0, TASK1=2, TASK2 = 4};
};
Composite
Treat a collection of objects as an object
itself
Graphic
Text Line Picture
Singleton
Ensure that an object is instantiated only
once
Singleton
static Singleton* instance()
Observer
Define a dependency between objects
Subject Observer
notify() update()
ConcreteObserver
Factory method
Create the right type of elements in an
abstract framework
Creator
create_product() Product
ConcreteCreator ConcreteProduct
Conclusions
Concrete Applications...
Programming project
Oral presentation of the use of one
particular design pattern in Gocad (Dec 2,
2005)
In your master’s projects
References
• Brokken et Kubat, C++ Annotations,
http://www.icce.rug.nl/docs/cpp.shtml
• Stroustrup, Le langage C++ (3e ed.), Addisson
Wesley, 1996.
• Gamma et al., Design Patterns, Addisson Wesley,
1995.
• Meyers, Effective C++, Addisson Wesley.
• Meyers, More Effective C++, Addisson Wesley.
• Meyers, Effective STL, Addisson Wesley.
• http://www.oonumerics.org/blitz/papers/
• Gautier et al., Cours de Programmation par objets,
Masson.