Sei sulla pagina 1di 70

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 class } namespace class } Gocad1 { MainWindow{ }; Gocad2 { MainWindow{ }; ANSI feature

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 cant.

#include <vector>

class Traced { 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

Exercise
traced.cpp int main( int argc, char** argv ) { 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( int num, int denom = 1 ); operator double() const; };

ANSI feature

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?
Car
4

Wheel

Person

Brain

Aggregation is a relationship in which one object is a part of another. A aggregates B = B is part of A, but their lifetimes may be different Ex: cars and wheels, engine, c etc.

Composition is a relationship in which one object is an integral part of another A composes B = B is part of A, and their lifetimes are the same Ex: person and brain, lung, 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


A circle is a shape GeomShape Circle
class GeomShape { // }; class Circle : public GeomShape { // }; file.h

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 Boss

class Employee { public : virtual float income(); // 1000 }; 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 : Boss virtual float income() = 0; // not implemented }; class Boss : public Employee { public : virtual float income(); // implemented };

Public Inheritance Philosophy


Inheritance of the Inheritance of the interface implementation Non virtual function Virtual function Mandatory Mandatory

Mandatory

By default, Possible to redefine Re-definition is mandatory

Pure virtual 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 have... Isolated class
base class pure abstract class Derived (concrete) class virtual pure virtual function function Constructor is Destructor is

No Yes (must) Yes

No Yes/no

public public

public public virtual public virtual public virtual

(must)
Yes

Yes / no

protected

(must)

No

public

Polymorphism Mechanism
Base
vtbl_

Base::vf1 Base::vf2 Base::vf3

Derived Base
vtbl_

Derived::vf1 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 Dont 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 dont 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 { public : List( int nb_items ); ~List(); void void void // }; list.h

append_item( const T& item ); remove_item( const T& item ); remove_all();

or fonctions
/** * Swaps two objects of type T. * T should provide copy constructor * and operator= */

swap.h

template<class T> void swap( T& t1, T& t2 ); template<class T> void swap( T& t1, T& t2 ) { T tmp(t1); t1 = t2; t2 = tmp; } swap.h

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 { // };
/** * Sorts a List of objects of type T. * T must provide order operators < */

list.h

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 Type checking Supports inline functions
Example: Generator Unary Function Binary function Predicates

param]

);

Generic Programming
Idea: Replace virtual functions by mandatory functions of template arguments

Example: the GSTL [N. Rmy A. Shtuka B. Lvy


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; }

STL algorithms
Ordering Permutations Search and replace

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
notify()

Observer
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 masters 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.

Potrebbero piacerti anche