Вы находитесь на странице: 1из 69

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.

Вам также может понравиться