1.02k likes | 1.77k Views
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, .
E N D
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...
Maintenance: • Bug fixes • User requests • Backward compatibility Conception Development Testing Release Software development Energy = Cost 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 ANSI feature namespace Gocad1 { 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& varinstead ofT 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 { public: 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 traced.h
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” }
class String { public: String( const char* value ); String( const String& rhs ); ~String(); String& operator=( const String& rhs); private: char* data_; }; file.h Constructor and assignment 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? 4 Person Brain Car Wheel 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. 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, 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
GeomShape class GeomShape { //… }; class Circle : public GeomShape { //… }; file.h Circle Inheritance: quick review “A circle is a shape”
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
Employee Boss Polymorphism Mechanism that allows a derived class to modify the behavior of a member declared in a base class class Employee { public : virtualfloat income(); // 1000 }; class Boss : public Employee { public : virtual float income(); // 10000 };
Employee Boss Polymorphism A pure virtual function just defines the interface, and leaves the implementation to derived classes class Employee { public : virtualfloat income() = 0; // not implemented }; class Boss : public Employee { public : virtual float income(); // implemented };
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... No No public public Yes (must) public virtual Yes/no public Yes (must) public virtual Yes / no protected Yes (must) public virtual No public
Base::vf1 Base Base::vf2 vtbl_ Base::vf3 Derived Derived::vf1 Base Derived::vf3 vtbl_ Polymorphism Mechanism
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
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
template<class T> class List { public : List( int nb_items ); ~List(); void append_item( const T& item ); void remove_item( const T& item ); void remove_all(); //… }; list.h Parameterize classes… Case of most container classes: store data of arbitrary types.
/** * Swaps two objects of type T. * T should provide copy constructor * and operator= */ template<class T> void swap( T& t1, T& t2 ); swap.h template<class T> void swap( T& t1, T& t2 ) { T tmp(t1); t1 = t2; t2 = tmp; } swap.h … or fonctions
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
template <class T> class List { //… }; /** * 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_; }; list.h Example
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]