Cours-Gratuit
  • Accueil
  • Blog
  • Cours informatique
home icon Cours gratuits » Cours informatique » Cours programmation » Cours C++

Initiation à a programmation orienté objet avec le langage C++

Initiation à a programmation orienté objet avec le langage C++
Participez au vote ☆☆☆☆☆★★★★★

Formation C++ avancée

ou comment être les stars du C++

Raffi Enficiaud

INRIA

16-18 février 2009

INRIA - IMEDIA

Formation C++ avancée

Organisation générale

A qui s’adresse-t-elle ?`

Thèmes de la formation

 vous permettre d’être le plus autonome possible avec le C++

I Mieux comprendre certaines ”subtilités”du langage

I Mieux comprendre ce que génère le compilateur à partir du code que vous

écrivez

 vous rendre plus productif avec les mécanismes du C++

I Utiliser le compilateur pour faire des évolutions incrémentales

I Travailler en équipe

I Identifier plus précisément les sources potentielles de bug

I Mettre en place des mécanismes de controˆle de fonctionnalité et d’évolution

I S’intégrer au mieux à un ”monde extérieur”à votre (partie de) programme

Déroulement

Formation C++ avancée

J1 : C++ avancé


J1 : C++ avancé

Contenu

Quelques difficultés du langage

Rappels sur les instructions

Classes

Héritage

Exceptions structurées

Etude de cas

Quelques difficultés du langage

Subtilité ?

Exemples

Syntaxe pas trop difficile

Langage difficile

 Types

struct union

POD

typedef extern

Rappels sur lesContrôle instructionsPointeurs

Référence

Garantie de non modification : const

Espace de nom

Pointeurs sur fonction Références sur fonction volatile


Chaˆ?nes de caractère

Même là-dessus il faut revenir

Plusieurs types de chaˆ?ne de caractère : Déclaration :

Chaˆ?ne de type ”classique”: char

Chaˆ?ne de type étendu ”international”: wchar t

Le type international est souvent utile pour ce qui concerne le système de fichier, les messages utilisateurs, etc

1                       char ?s constant = "blablabla" ; 2  wchar t? s wide = L"wide blablabla" ;

Préprocesseur :

Concatène automatiquement les chaˆ?nes de caractères qui se suivent :

1char? toto = "One big string split " "among small strings"

2" and among many lines"

3" " ;

Mot clef ”enum”

Structures

Par compatibilité avec le C, l’accès par défaut est public (cf. partie sur les classes)


Union

Types POD

Les types ”natifs”ou agrégation de type natif

Plus précisément, tout type ayant un équivalent direct avec le C, concernant l’initialisation, la taille, et l’adressage :

toute valeur numérique (bool, int, double ) les enums et les unions les structures sans aucun constructeur, opérateur d’attribution, classe de base, méthode virtuelle, variable non-statique non publique, etc.

Mot clef ”typedef”

Généralement, les typedefs permettent d’avoir un code plus concis, et dans certains cas d’accélérer les temps de compilation (template).

Nous verrons quelques utilisations du mot clef ”typedef”pendant la partie sur les templates.

Mot clef ”extern”

Controˆle

Boucles ”for”

1          for ( int        i = 0;         i < 100;        i++)

La variable ”i”est interne au bloc for. Les ”;”définissent les champs : il est possible de ne pas les initialiser :

1int               i = 0;

2for ( ;                i < 100;        i++)

Pointeurs

Définition

Référence


const”

Définition

Nous verrons plus tard que, pour un objet, seules des méthodes assurant la constance de l’objet peuvent être appelées.

const”

Utilisation des pointeurs

namespace”

Définition & syntaxe

namespace”

Usage


Pointeurs sur fonction

Rappels

Definition (et usage)

Permet de stocker l’adresse d’une fonction dans une variable (l’attribution de cette variable pouvant être donné par une fonction).

La variable peut ensuite (bien-suˆr) être utilisée pour appeler la fonction sur laquelle elle pointe.

1bool                func2 ( const       double&,      int ,    f l o a t &) ;

2void                func1 ( bool       (? toto ) ( const        double&,      int ,     f l o a t &)) {

3//                     Déclaration      de   la    v a r i a b l e    toto ,    comme    étant    un     pointeur

4//                  sur    une      fonction      de    type     bool     ( const       double&,      int ,    f l o a t &)

5              f l o a t     returne d  f ;

6

7       i f ( toto (0.39 ,         42 ,                  re turned  f ) )

8       std : : cout << "function returned true and the float is = to" << returne d  f << std : : endl ;

9else

10std : : cout << "function returned false" << std : : endl ;

11}

12func1 (0) ;                //    e rreu r    à     l ’ exécution      ( l o r s    de     l ’ appel    à    toto ) :       fonction     n u l l e

13func1(&func2 ) ;                  //    a t t r i b u t i o n   de   toto    à   func2

Références sur fonction

Definition (et usage)

Permet de stocker la référence d’une fonction dans une variable (l’attribution de cette variable pouvant être donné par une fonction).

Contrairement au pointeur, la référence sur fonction est non-mutable (ré-assignation de la référence impossible).

1bool                func2 ( const       double&,      int ,    f l o a t &) ;

2void                func1 ( bool       (&var ) ( const        double&,      int ,     f l o a t &)) {

3//                     Déclaration      de   la    v a r i a b l e    toto ,    comme    étant    une    ré fé re nc e

4//                  sur    une      fonction      de    type     bool     ( const       double&,      int ,    f l o a t &)

5              f l o a t     returne d  f ;

6

7       i f ( var (0.39 ,           42 ,                  returned  f ) )

8       std : : cout << "function returned true and the float is = to" << returne d  f << std : : endl ;

9else

10std : : cout << "function returned false" << std : : endl ;

11}

12func1 (0) ;                //    e rreu r    à   la      compilation :         fonction     n u l l e   n ’ e x i s t e    pas

13func1 ( func2 ) ;                 //    a t t r i b u t i o n   de    toto    à   func2

Mot clef ”volatile”

Ce modificateur a été initialement utilisé pour permettre des accès à des variables externes au processeur sur lequel le programme s’exécute (chip annexe par exemple). Il indique au compilateur, en outre, que la variable en question ne participe pas à des optimisations (inférences sur sa valeur).

Pour ceux/celles qui utilisent les threads, ce modificateur doit être utilisé pour les variables dont l’accès se fait via des threads concurrents.


Exercice

Fonction retournant un répertoire temporaire

On souhaite avoir une fonction, temporary path, qui retourne une chaˆ?ne représentant le chemin complet d’un répertoire temporaire.

On souhaite en outre :

 que le chemin temporaire soit lu à partir de la variable d’environnement ”TEMP”. Pour cela, on utilisera la fonction C/ANSI ”std :: getenv”(dans ”<cstdlib>”) qui prend en paramètre le nom de la variable d’environnement et qui retourne ”char ?”. Ce retour peut valoir ”NULL”si la variable d’environnement n’existe pas.

on souhaite que cette variable ne soit lue qu’une seule fois, de manière à réduire l’accès ”couˆteux”à l’environnement.

si cette variable n’existe pas, on ne veut pas que notre programme plante. On initialisera la variable au répertoire contenant le fichier compilé (c’est super laid, mais c’est juste à titre d’exemple)1. Pour cela on se servira de la macro C/ANSI ” FILE       ”, de ”std :: string ”et de ”std: string :: rfind ”(on cherchera le ’.’ à partir de la fin). on veut pouvoir modifier ce répertoire temporaire de la manière suivante :

temporary path () = "mon_nouveau_repertoire" ;

Indice : il s’agit d’une initialisation de type statique, à l’aide d’une fonction retournant la chaˆ?ne qu’on souhaite.   

               Enficiaud (INRIA)                                                                   C++                                                                        16-18/02/2009

Attributs membres

Méthodes membres Attributs statiques

Méthodes statiques

Attributs mutables Champs membres this

ClassesPointeur sur membres

Constructeurs de classe

Destructeurs

Surcharge d’opérateurs

Opérateurs arithmétiques

Opérateur d’attribution

Opérateur de conversion

Opérateur de fonction

Définition

Definition (Classe)

Une classe déclare des propriétés communes à un ensemble d’objets. Elle définie des attributs (variables membres) représentant l’état des objets, et des méthodes définissant leurs comportements.

Definition (Instance)

Une instance de classe est un objet suivant la définition de la classe, et dont les variables membres ont une valeur.


Définition (2)

La classe est l’élément central de la programmation orientée objet. L’objet est une instance de classe.

Déclaration & Syntaxe

La déclaration est donné par la directive ”class”ou ”struct”

Elle est suivie éventuellement du corps de la classe (accolades) Elle se termine par un ”;”

Si le corps est défini, il est possible de déclarer des objets :

1cl a s s              A {} a ,      b ,   c ;

2struct                  B {/? . . . ?/} d , e , f ;

Si le corps n’est pas défini, la directive déclare une classe dont le corps est inconnu. Puisque le corps est inconnu, il est impossible de déclarer des objets de cette classe :

1cl a s s           A;

2A a ;             //     erre ur :     la    t a i l l e    de   a    est     inconnue

3A ?a ;              //   ok :    la    t a i l l e    du     pointeur     et    son    type     sont     connus

Le corps de la classe définie ses membres. Une fois le corps terminé, il n’est pas possible d’ajouter des membres.

Attributs membres

Il est possible de définir les méthodes membres à l’intérieur du corps de la classe :

il deviennent alors ”inline ”(déclarable dans un header par exemple).

Attributs static

Definition (”static”)

Variable membre non liée à une instance particulière, et partagée par toutes les instances d’une classe.

Il faut l’initialiser de la même manière qu’une variable statique normale, et en dehors du corps de la classe

Sauf pour un entier constant, ou un enum, qui peut être initialisé à l’intérieur du corps de la classe

La syntaxe impose la déclaration dans le corps de la classe, et l’attribution/initialisation à l’extérieur du corps de la classe. Attention aux accès concurrentiels.


Attributs ”mutable”

La modification d’une variable mutable est autorisée à partir d’une méthode ”const”.

Les variables ”mutable”permettent de cacher certains détails d’implémentation (ex. mécanisme de cache).

Controˆle d’accès

Dans le corps de la classe, on attribut à chaque membre une restriction sur l’accès, parmi :

public : le membre est visible de ”tout le monde” protégé (protected) : le membre n’est visible que d’une classe fille (cf. héritages)

privé (private) : le membre n’est visible que d’une instance strictement du même type (qui peut être l’instance courante)

Par défaut, une ”class”a une visibilité privée, alors qu’une ”struct”a une visibilité publique (compatibilité avec le C).

1     cl a s s    A {

2     int          i                       p r i v a t e ;       //                     privé 3             void                  method private () ; 4                   public :

5      int       i                       p u b l i c ;

6      void    method () ;

7      } ;

Champs membres

Les champs de type sont valides à l’intérieur de l’espace de nom de la classe. Rappel : les champs de type ne prennent aucune place en mémoire, et donc ne participent pas à la taille occupée en mémoire par la classe.

Mot clef ”this”

Nous verrons ce dernier point pendant les constructeurs.


Exercice

Décomposition en nombres premiers

Objectif

Avoir une classe, ”decomposed primes”, décomposant un entier en puissances de nombres premiers.

1      un champ ”return type”précisera le type de stockage. Il sera du type ”map”

2      la méthode ”decompose”prendra un entier en entrée, et retournera ”return type”

3      une liste initiale de nombre premiers sera définie au lancement du programme (note : variable statique)

4      la classe aura une ”mémoire”cachée, lui permettant d’ajouter des nombres premiers à sa liste initiale

5      pour plus de performances, la classe stockera la décomposition en nombre premiers des nombres inférieurs à 100.

On se servira du type ”caché”, ”std :: map<int,int>”(tableau associatif), pour stocker une décomposition. On se servira du type ”caché”, ”std :: set<int>” (ensemble ordonné), pour stocker les nombres premiers dans l’ordre croissant.

On s’aidera des fichiers ”decomposed primes.[h|cpp]”.

Exercice

Syntaxe

Les pointeurs sur membre non static doivent indiquer qu’ils pointent à l’intérieur d’une classe. Il doivent donc spécifier le bon espace de nom.

1struct               A {

2int                 i ,    j ,    k ,     l , m,    n ;

3int                 methode ( f l o a t ) ;

4int                 methode2 ( f l o a t ) ;

5};

6int             A: : ? ;     //     pointeur      sur    un   e n t i e r    de A

7         int          A::? p i = &A : : i ;          //      pointeur        ”p  i ” sur      un   e n t i e r     dans A,      i n i t i a l i s é    sur       l ’ adresse     de   i

8int                (A::?) (f l o a t ) ;        //     pointeur      sur    une    méthode        de A ( retournant         int ,    et     prenant     un    argument    f l o a t )

9         int           (A::? p methode ) ( f l o a t ) = &A : : methode2 ;           //      pointeur       p methode     i n i t i a l i s é       sur A : : methode2

S’ils pointent sur un membre static, ils sont absolus, n’ont pas besoin de spécifier l’espace de nom, et n’ont pas besoin d’une instance pour être déférencés.

 S’ils pointent sur un membre non static, ils sont valident naturellement à travers une instance (nécessaire pour déférencer le pointeur).


Syntaxe (2)

Syntaxe (3)


Rappel


Construction des variables membres

Rappel : les méthodes ne sont pas des variables (et donc n’influencent pas la taille de la classe).

Construction des variables membres (2)

Il existe bien des manières d’initialiser un objet

Controˆle d’accès

Les mêmes règles d’accès s’appliquent au constructeur :


Exercice

Exercice

”Copy constructible”(2)

Variable membres const


Variable membres référence

Les références doivent être initialisées dans la liste d’initialisation.

Les ”variables”références étant constantes, elles doivent être initialisées dans la liste constructeur également.

Restriction des conversions : mot clef ”explicit”

Aucun ”trans-typage”(conversion implicite) n’est effectué pour les constructeurs explicites.

1struct               A {

2e x p l i c i t                 A( int ,      const    f l o a t &) ;

3e x p l i c i t                 A( long ,      const     double&) ;

4};


Destructeurs

Il porte le même nom que la classe, et n’a jamais de paramètres en entrée. Il faut l’appeler avec l’opérateur ”delete”et (pratiquement) jamais directement.

Les objets sont détruits dans l’ordre inverse de leur déclaration lors de l’accolade fermante du destructeur.

Exercice

Classe de matrice

Enoncé

Ecrire une classe de matrice ”matrice d”de type double. Le constructeur de la classe aura pour paramètres deux entiers indiquant la taille (allocation dynamique).

Introduction

Lorsqu’on défini l’opérateur d’une classe, on dit généralement qu’on surcharge cet opérateur. Ce sous-entend que si ces opérateurs ne sont pas définis, le compilateur essaie de les générer automatiquement. Plusieurs opérateur d’intérêt, exemple :

1operator= ;                   //   a t t r i b u t i o n

2operator== ;                     operator!=      ;      operator <;         operator> ;       . . .     //     comparaison

3operator++ ;                      operator?? ;       //    auto       incrémentation       et      décrémentation      ( pré   ou    post )

4operator+ ;                         operator+=; operator? ;              operator?=;       . . .    //      arithmétique

5operator? ;                   //     déférencement     ou  m u l t i p l i c a t i o n

6operator !               ;    //     négation

Classes & surcharge d’opérateurs

Exercice

Opérateurs arithmétiques

Enoncé

Pour une matrice :

Ecrire les opérateurs d’addition, de multiplication avec un double

Ecrire l’opérateur de multiplication avec une autre matrice

Enoncé

Ecrire l’opérateur d’addition entre deux décompositions en nombre premier (classe ”decomposed primes”).

Ecrire l’opérateur d’addition entre une décomposition et un entier

Ecrire l’opérateur de multiplication (deux ”decomposed primes”et un entier)

Ecrire l’opérateur de division (deux ”decomposed primes”et un entier) : on se permet d’avoir des exposants négatifs


Définition & syntaxe

Cet opérateur est utile lorsque la copie n’est pas triviale (ie. typiquement avec membres de type pointeurs et/ou références). Souvent, lorsque l’opérateur ne peut être généré automatiquement, le compilateur émet un avertissement (warning).

Transitivité

Remarque : auto-attribution (1)


Opérateur d’attribution : remarques

Auto-attribution (2)

Donc

gérer explicitement le cas a = a par ”this”

)

std : : s t r i n g ( r

changer l’ordre des opérations pour avoir une gestion implicite

)

                                     s tmp = new        std : : s t r i n g ( r   . s ) ;

Opérateur d’attribution

Code auto-généré

Le compilateur ”sait”faire des choses :

Si les types contenus dans la structure/classe sont de type triviaux ou possèdent tous un constructeur par recopie (défini ou trivial), alors il n’est pas nécessaire de définir l’opérateur d’attribution (même remarque s’applique au constructeur par recopie).

Donc :

N’écrire l’opérateur d’attribution seulement quand vous le jugez nécessaire (membres pointeur/référence/const).

Opérateur d’attribution

Classe de matrice

Enoncé

Ecrire l’opérateur d’attribution de la matrice avec une autre matrice : si les matrices ne sont pas de même taille, alors on fait la recopie.

Ecrire l’opérateur d’attribution entre une matrice et une autre, entre une matrice et un entier.

Opérateur de conversion

Définition

Cet opérateur peut être très pratique dans certains cas. Il ne définit pas de type de retour, puisque ce type est implicite par le cast.

Classes : surcharge d’opérateur

Introduction

Contrôle d’accès

Visibilité & désambigu¨?sation

Construction et destruction

HéritageConversions

Héritage multiple

Classes virtuelles

Classes virtuelles pures

Interfaces


Définition

Que signifie héritage?

Controˆle d’accès

Controˆle d’accès (2)

Il est possible de se référer explicitement à une méthode ou un attribut de l’une des classes mères (si les contrôles d’accès le permettent), par la syntaxe suivante :

1      B : : value type              doSthg ()           const {

2      A : : doSthg () ;              //                     appelle              A : : doSthg       au                     l i e u                de A : : doSthg

3      //        do                    some                other                 things

4      }

Constructeurs

Une classe fille contient la classe mère (c’en est une extension). Donc, si la classe fille est construite, il faut que la classe mère le soit également.

Constructeur : règle

Le constructeur par défaut de la classe mère est appelé avant l’initialisation du premier (ie. de tous) membre de la classe fille.

La classe fille peut appeler un constructeur particulier de la classe mère, dans sa liste d’initialisation

En fait, la classe mère peut être vue (en terme d’implémentation) comme une variable membre avant toutes les autres.

Les contrôles d’accès du constructeur de la classe mère est identique à n’importe quelle autre membre

Constructeurs : exercice

Soit la classe mère suivante, ouvrant un fichier dans le répertoire courant :

1cl a s s            F i l e U t i l        { 2public :

3//                 i n i t i a l i s e     la   c l a s s e    avec    l e    f i c h i e r    non    ouvert       filename

4F i l e U t i l ( const                          std : : s t r i n g &filename ) {/? . . . ?/}

5

6/? . . . ?/

7};

Une fois le fichier ouvert, la classe propose des manipulations de ce fichier (or de propos ici).

Nous souhaitons une nouvelle classe héritant ces méthodes de manipulation, mais ouvrant les fichiers de manière différente.

Note : Nous considérerons que la classe mère stocke un handle de fichier dans la variable membre ”FILE ?m file handler”. Elle offrirait à ses classes filles un constructeur particulier

Destructeurs

Destructeur : règle

Le destructeur de la classe mère est appelé après la destruction de tous les membre de la classe fille (lors de l’exécution de l’accolade fermante de la classe fille).

C’est à dire, dans l’ordre inverse de la création des éléments.

Conversions

Il est possible de couper une classe, mais il n’est pas possible d’inventer des extensions de classe.

Une classe fille est une extension de sa classe mère. Il est donc possible de présenter la partie concernant sa classe mère.

1    // A          f i l l e               de B

2    A a ;

3    B ?p b = &a ;                     //                     ”du                   plus                  p ré c i s           au                     plus                  général ”

4    B& r e f b = a ;

L’inverse n’est pas vrai, car il faudrait inventer des données manquantes :

1    // B          f i l l e               de A

2    A a ;

3    B ?p b = &a ;                     //                     e rreu r              de                     compilation :     l e                     compilateur       ne                     s a i t                pas

4    B& r e f b = a ; //                s i                     ces                    i n s t r u c t i o n s                      sont                  v a l i d e s         (a                      p r i o r i            e l l e s              ne                     l e                     sont                  pas ) .

Conversions & dynamic cast

Il est possible de tester à l’exécution(et parfois à la compilation), à partir d’un pointeur ou d’une référence sur une classe, si une classe dérive ou est une classe mère d’une autre.

Plus précisément, le compilateur sait si une classe est mère d’une autre à la compilation, mais l’inverse n’est pas vrai. Le test doit être fait en runtime.


Héritage multiple

Définition

Le graphe d’héritage étant plus complexe, le compilateur peut se plaindre parfois de ne pas pouvoir résoudre un appel (deux classes mères ayant une méthode de même nom/paramètres). Il faut alors faire la désambigu¨?sation de la même manière que pour l’héritage simple.

Héritage multiple

Constructeurs & destructeurs

De la même manière que pour l’héritage simple :

Les classes mères sont toutes construites avant n’importe quel attribut membre de la classe.

Si aucun constructeur de classe mère n’est appelé dans la liste d’initialisation, le constructeur par défaut est appelé.

Les classes mères sont construites de gauche à droite dans la liste des classes de base (ex. précédent : A puis B).

et

Les classes mères sont détruites après destruction du dernier membre de la classe fille

Elles sont détruites dans l’ordre inverse de leur construction (de droite à gauche dans la liste des classes de base).

Classes virtuelles

Définition

Classe virtuelle - Définition

Une classe virtuelle est classe contenant au moins une méthode virtuelle

Méthode virtuelle - Définition

Méthode dont l’implémentation peut être (re)définie par une classe dérivée.

Le mot clef ”virtual ”indique si la méthode en question est virtuelle (ou pas). Une méthode virtuelle participe à la taille occupée en mémoire par la classe.

Méthode virtuelle

La méthode appelée par défaut (ie. sans spécification d’espace de nom) est toujours la plus profonde dans la hiérarchie des classes.

Classe virtuelle

Classes virtuelles

Utilisation

Classes virtuelles pures

Définition

Classes virtuelles pures

Propriétés

Une classe mère peut donc ”forcer”l’implémentation (dans les classes filles) des méthodes qu’elle déclare pures


Destructeurs

Héritage

Que se passe-t-il si delete est appelé à partir d’une la classe mère?

Interfaces

Définition

Concept général indiquant les méthodes pour accéder à un objet (au moins en exploiter certaines fonctionnalités) : l’interface définie la signature pour l’utilisation de ces fonctionnalités. La particularité importante de l’interface est le découplage de l’accès d’un objet et de son implémentation. Cette particularité fait en sorte que :

Les clients ne connaissent pas les détails d’implémentation, et ne sont donc pas soumis aux variations de celle-ci (concept objet).

L’interface est supposée stable en termes de fonctionnalités et signatures, notamment par rapport à l’évolution d’un composant logiciel.

L’approche par interfaces est souvent moins performante, puisque les signatures ne sont pas optimales pour une implémentation particulière (et que l’accès aux variables membres se fait par le biais de méthodes d’accès).

En C++, une interface est une classe ne définissant aucune variable membre et dont toutes les méthodes sont publiques et virtuelles pures.

Interfaces

Exemple

Même s’il s’agit d’un artefact du langage


Définition & utilisation

ExceptionsDétailsExceptions & constructeurs structuréesExceptions & destructeurs

Exceptions standards

Définition

Definition (exception)

Une exception est un mécanisme spécial du C++, lancé par le mot clef ”throw”, et permettant d’arrêter l’exécution courante du programme jusqu’à une instruction de récupération d’exception (bloc ”try”/ ”catch”).

L’instruction ”throw”peut être suivie de n’importe quelle classe, structure, entier.

L’instruction ”catch”intercepte la même classe que celle du ”throw”.

Interceptions & relancement

Interception générale

Attention : puisque l’exception n’est pas nommée, il n’est pas possible d’avoir plus de précision sur l’exception.

Mais : Il est possible de relancer l’exception

Déroulement des interceptions

Déroulement des interceptions : détails

Voici ce qu’il se passe lorsqu’une exception est levée :

On arrête l’exécution du programme au point du ”throw”

Le programme teste s’il se trouve dans un bloc ”try”(si ce n’est pas le cas, fin de programme)

On cherche un bloc ”catch”compatible avec le type de l’exception levée (si aucun, fin de programme)

On crée une sorte de ”tunnel/point de connexion”avec ce bloc ”catch”, et on se place dans un mode privilégié

Dans ce tunnel, on appelle les destructeurs de tous les objets crées sur la pile, dans l’ordre inverse de leur création

On retrouve l’exécution du programme au niveau du bloc ”catch”

On appelle le processus de destruction des objets temporaires : le déroulement de pile (stack unwinding).


Utilisation

Deux philosophies

1      Retour de fonction (comme en Java)

2      Arrêt d’exécution sur faute ”grave”. Exemple : on appelle une fonction définie par certains critères sur les données qu’elle traite, et ces critères ne sont pas respectés.

Performances

Plus d’information sur la pile pour la gestion de l’exception, donc ralentissement des appels.

1      Possibilité de marquer le type d’exception possible levée dans une fonction

2      Rapprochement du catch de là ou` l’exception est potentiellement levée

3      Utilisation d’un mot clef pour indiquer qu’un appel ne lève pas d’exception ou ne lève une exception que d’un certain type (non supporté par tous les compilateurs).

1void                function1 ()        throw () ;

2void                function2 ()          throw ( Exception3 ) ;

3void                function2 ()        throw ( . . . ) ;

Cohabitation avec les constructeurs

En effet, les constructeurs n’ont pas de valeur de retour (résultat = objet construit). Lever une exception dans le constructeur en cas de problème permet :

Lorsqu’une exception est levée dans le constructeur, la classe n’est pas créée.

Les destructeurs des variables membres initialisés sont appelés

Le destructeur de la classe en cours de construction n’est pas appelé (la classe n’a pas été construite).

Les destructeurs des classes mères instanciées sont appelées (l’ordre d’instanciation joue un rôle important).

Cohabitation avec les destructeurs

Rappel : (quelques diapositives précédentes) le processus de stack unwinding est exécuté dans un mode particulier.

En fait, ce mode est particulièrement fragile, et ne supporte absolument pas un lancement d’exception.

Standard ”STL”

Le standard (fichier ”<stdexcept>”de la STL) défini quelques exceptions ”type”.

La classe de base est ”std :: exception”, qui implémente la méthode ”virtual     const char ? std :: exception :: what() const”, donnant des renseignements ”utilisateurs”sur le message de l’exception.

1     std : : runtime error ;         //                     reporte              l e s                  e r r e u r s         à                      l ’ exécution

2     std : : l o g i c                    e r r o r ;            //                     reporte              l e s                  e r r e u r s         de                     logique             d ’ appel

3     std : : invalid argument ;     //                     argument           i n v a l i d e       d ’un                appel

4     std : : out or range ;           //                     reporte              l e s                  dépassements   :                       u t i l i s é         dans                 c e r t a i n s       appels               STL ( substring ,  vector

. . . )

5     std : : bad alloc ;                //                     reporte              l e s                  problèmes        d ’ a l l o c a t i o n                      mémoire          ( dans               header <new>)

Il faut généralement se reporter à la documentation pour connaˆ?tre les exceptions qu’on doit récupérer. Les exceptions font partie du comportement du service demandé : elles affectent le fonctionnement de VOTRE programme.

Exercice

But

Soit une classe ”ContenuFichier”qui contient le contenu d’un fichier. A son` instanciation, il ouvre le fichier, lit son contenu et le met dans un buffer, puis ferme le fichier.

Il ouvre le fichier à l’aide de la fonction ”fopen”.

Si le fichier n’existe pas, il lance une exception ”file not found ”. Cette exception est à définir. Elle doit contenir le nom du fichier recherché.

Si le fichier existe, le constructeur alloue une zone mémoire de type ”char ?” d’une taille de 1024 octet (1Ko). Il lit ensuite le contenu du fichier à l’aide de la fonction C ”fread”(”#include <cstdio>”).

tant qu’il n’a pas terminé la lecture du fichier, il place le bloc précédemment lu à la fin d’un tableau. Pour cela, on utilisera un vecteur STL ”std :: vector” (”#include <vector>”) et sa méthode ”std :: vector :: push back”.

à la fin de lecture, il doit allouer un gros bloc mémoire, dans lequel il va copier les blocs précédemment lus et placés en mémoire.

Exercice (suite)


Etude de cas

Etude de cas

Chaˆ?ne algorithmique

On veut un programme qui applique des algorithmes à la chaˆ?ne à une structure de donnée initiale. Chaque algorithme à une interface identique à tous les autres algorithmes. Il sera identifié dans la chaˆ?ne par son nom. Pour chaque chaˆ?ne algorithmique, chaque algorithme à un nom unique. Chaque algorithme ”A”peut créer une nouvelle donnée, qui pourra être utilisée dans la chaˆ?ne par les algorithmes en aval de ”A”. La donnée sera par exemple identifiée par son type exact et le nom de l’algorithme qui l’a créé. L’exercice est en deux phases :

 Phase de réflexion/étude/design : à l’aide de mécanismes d’héritage et d’interfaces, définissez les intervenants informatiques. On fera un point sur le design.

Phase de développement : vous retroussez vos manches.

En 4 équipes.

Définition de l’interface d’un algorithme

Définition de la structure de donnée

Création de deux algorithmes

Formation C++ avancée

J2 : Programmation générique

J2 : Programmation générique

Contenu

Les templates

STL

Patrons de conception (Design patterns) Introduction

Présentation

Mécanismes

Déclaration & définition

Les templatesEspace de nom

Inférence de type

Spécialisation

Programmation incrémentale

Synthèse




Egalement appelés ”patrons”

Definition (Template)

Les templates permettent de définir des familles de classes, structures ou fonctions.

Mécanisme très puissant!

1   permet de rapidement (à partir d’un code complètement typé) définir une famille de classe ou de fonction

2   permet d’ajouter des mécanismes logiques dans le choix des structures/fonctions invoquées (méta-programmation)

3   permet d’avoir un code facilement extensible à des nouveaux cas d’utilisation

Difficile à prendre en main

Certains compilateurs sont en retard dans le support des templates (il faut les bannir)


Exemple

Un exemple trivial de l’abstraction du type pour des fonctions

Exemple & remarque

La fonction maximum sera alors appelable pour tout type ”T”, MAIS avec les conditions suivantes :

l’algorithme est valide si l’opérateur ”>”existe pour le type ”T” la fonction prend des copies des objets et retourne une copie, ce qui n’est pas valide pour tout type ”T”:

1   l’opérateur par recopie est correct pour l’algorithme (il retient l’information permettant de faire la comparaison)

2   l’opérateur par recopie est correct pour la partie appelante (”return”ne retourne pas une référence, mais une copie temporaire)

On commence à comprendre pourquoi c’est si séduisant, mais un peu délicat?

Exemple (2)

Un exemple trivial de l’abstraction du type pour des structures

Détails

Il existe plusieurs mécanismes autour de l’utilisation des templates :

la déclaration et la définition la spécialisation totale ou partielle la déduction (inférence) de type pour les fonctions templates

Déclaration & définition

La déclaration commence par le mot clef ”template”, suivi de :

Les déclarations, classiquement, indiquent au compilateur que ces fonctions et ces structures templates existent.

Elles indiquent également le nombre d’arguments abstraits dans le template (utile pour la spécialisation ou pour les arguments templates).

Liste des types admissibles

Les types admissibles dans la liste des type sont :

des types ”abstraits”: ”class”ou ”typename”suivi de leur nom des valeurs constantes connues au moment de la compilation, et du type : entier, enum, pointeur, référence ou pointeur sur membre (le pointeur inclut son naturellement le type pointé)  des classes templates

Il est possible d’avoir, pour les classes et structures templates, des arguments templates par défaut (et qui peuvent référer à des types précédents)

1template <cl a s s                    T,    cl a s s      U = int ,       cl a s s     V = T>

2struct                      ss t r u c t ;

3

4// même             p r i n c i p e   que    pour    la      surcharge ,       V non       optionnel      ne    peut   s u i v r e   un    argument       optionnel

5template <cl a s s                    T,    cl a s s      U = int ,       cl a s s   V>

6struct                      ss t r u c t 2 ;

7

8template <cl a s s                    T,    cl a s s      U = int >

9T func1 (T,                       U) ;       //    i n t e r d i t   !    pas    de    défaut     sur    l e s      fonctions        templates

Liste des types admissibles : valeurs

Liste des types admissibles : valeurs (2) Suite

Liste des types admissibles : template

Il est également possible de mettre parmi les arguments, un autre template. Il doit être déclaré précisément avec la même liste template que sa définition/déclaration.

Espace de noms & champs de type

Un peu le même principe que les namespace : utilisation de l’opérateur :: .

Definition (”types complets”)

Dans un template, les types complets sont les types connus : soit parce que ce ne sont pas des types abstraits (constantes) soit parce qu’ils font partie de la liste d’arguments template a

a. les arguments templates sont également des types

Definition (”noms dépendants”)

Un nom dépendant est un type qu’il n’est possible de connaˆ?tre qu’une fois le type template complètement résolu.

Règle

Les noms dépendants doivent être accédés via l’opérateur ”typename”. Ce mot clef indique au compilateur que le nom dépendant est un type.

Il n’est valide qu’à l’intérieur des templates.

Espace de noms & champs de type (2)

Exercice 1

S’abstraire des opérations de recopies dans le template suivant :

                  template <cl a ss        T>

                      T maximum( const T i1 ,            const T i2 )

{

                    return        i1 > i2      ?    i1   :    i2 ;

}

En faire un functor, qui sache renseigner ses types d’entrée et de sortie.

Exercice 2

Ecrire une structure template give me square, présentant une interface de tableau indicé ”operator []”, et retournant le carré de l’opérateur de fonction template (”functor”- ”operator()”) suivant :

1      template <

2      cl a s s storage type ,

3      int       s i z e = 10 ,

4      cl a s s return type         = float >

5      struct   s my struct t {

6      storage type                  value1 [ s i z e ] ;

7      typedef                        return type         return type ;

8

9       return type         operator () () {

10        storage type       ret = 0;

11        for ( int             i = 0;                 i < s i z e ;          i                       ++)

12        ret += value1 [ i ] ;

13        return                ret                     /                       s i z e ;

14        }

15        };

Ce mini exercice doit montrer comment interfacer correctement deux templates.


Inférence de types


Inférence de types : exercice

Enoncé

Ecrire une fonction template qui écrit dans ”std :: cout”le type passé en argument (la valeur ne nous intéresse pas ici). On se servira le mot clef C++ ”typeid(T)”et la méthode ”name”de l’objet retourné ”typeid(T).name()”.

Attention : ”typeid”

La sortie de ”typeid”est compilateur dépendant! Il est utilisé pour tester l’égalité de types, mais non pour une sortie textuelle véritablement informative. ”typeid” retourne un objet CONSTANT ”type info”(inclure le fichier #include <typeinfo>).

Spécialisation

Spécialisation totale/partielle

Notion FONDAMENTALE !

La spécialisation est un mécanisme puissant qui relègue au compilateur les choix des structures lors de la découverte des paramètres templates.

Notion FONDAMENTALE ! (bis)

La spécialisation définit des NOUVEAUX TYPES. Une classe template et ses spécialisations n’ont RIEN DE COMMUN a.

a. en termes de contenu

Cf. exemples précédents : les classes ”s my template<U,V>”et ” s my template<int, int>”sont des TYPES DIFFERENTS.

Definition (Spécialisation totale)

C’est un type de spécialisation ou` tous les types templates sont renseignés.

Cf. exemples précédents.

Spécialisation totale/partielle


Spécialisation & programmation incrémentale

Supposons que nous avons un functor ”s do very weird things ”qui travaille sur un ensemble de types ”Ti”

Supposons que maintenant, nous avons une version ”améliorée”de ce functor, pour une combinaison C de ces types

Alors, il est possible d’ajouter cette version ”améliorée”dans notre programme, sans ”rupture de contrat”pour les appels existants. Le compilateur utilise alors la version améliorée ou générale automatiquement, selon la combinaison de type.

On a appelle ce mécanisme la programmation incrémentale : on commence par une version très générale, et ensuite on ajoute le particulier, sans retoucher le code client existant.

a. enfin je

C’est donc le compilateur qui travaille plus que nous.


141 / 201



Functors - objets fonctionnels

<functional>

STL & conteneurs

STL<<vectormap>>

STL & algorithmes génériques

<limits>

<algorithm>

STL

Présentation

La STL (Standard Template Library) est une librairie standard, distribuée avec (pratiquement?) tous les compilateurs C++.

Elle fournit des services minimaux, comme des structures template de ”container” (éléments contenant), des algorithmes d’exploitation et des classes utilitaires de renseignement. Elle participe beaucoup à la création d’algorithmes templates.

Il faut très souvent faire appel à la STL

Utiliser la STL est bon pour votre productivité

Il s’agit donc d’un objet, qui est par définition plus riche qu’une fonction, et qu’il est possible d’appeler comme une fonction.

1     struct       s functor dumb limited {

2     // ! Surcharge de l ’ opérateur operator () , induisant l e comportement dé s i ré 3 bool operator () ( const bool b) const throw () { return ! b ;}

4        };

5

6     s functor dumb limited      op ;

7     a s s e r t (! op( true ) ) ;

Plus généralement, un functor permet de faire des choses plus puissantes que des fonctions.



<functional> (2)

”std :: bind1st”/ ”std :: bind2nd”: fonctions mettant un des deux paramètres d’une fonction binaire à une constante

Le functor résultant devient unaire.

<vector>

Tableau dynamique

Bon, tout le monde connaˆ?t?


<map>

Tableau associatifs

Definition (”map”)

Une ”map”est un tableau associatif : à chaque clef, elle associe une valeur.

Le type de la clef et celui de la valeur sont les deux paramètres templates obligatoires.

Un troisième paramètre très utile donne la fonction d’ordre sur la clef. Il vaut par défaut le functor ”std :: less ”(inférant un ordre ”<”sur l’espace des clefs).

L’ordre est donné par un functor, il est donc très facile d’en définir un sur une structure de clef complexe.

Si la relation d’ordre n’est pas correctement construite, on peut avoir des mauvaises surprises.

<map>

Exercice

Enoncé

<limits>

Renseignements numériques

<limits>

”<limits>”contient des informations relatives aux types numériques. Elle contient principalement une classe template ”T”, ”<numeric limits>”qui donne des informations sur le type numérique ”T”.

Soit un type ”T”, ”std :: numeric limit<T>”définie des méthodes statiques (accessible sans instance) permettant d’accéder à un ensemble de fonctions renseignant ”T”:

1std : : numericlimit <T>::max() ;                                      //      fonction        statique        retournant      l e    maximum   p o s s i b l e   pour    l e      type T

2std : : numericlimit <T>:: min () ;                                     //    l e    minimum    p o s s i b l e    pour    l e     type T

3                std : : numeric limit <T>:: i s          i n t e g e r () ;       //    booléen       indiquant         que T est       bien    un    type    e n t i e r

4std : : numericlimit <T>:: e p s i l o n () ;                 //                     valeur                indiquant          la                      l i m i t e           numérique       d ’ i n d i s t i n c t i o n                 entre                 deux T               s u c c e s s i f s

<limits>

Exemple d’utilisation

Initialisation correcte de l’algorithme en cas de liste vide :

Algorithmes génériques

Utilisation de <algorithm>

La partie <algorithm> de la STL propose une suite d’algorithmes fréquemment utilisées : tri, partition, ordre lexicographique, etc.

Les algorithmes se déclinent généralement en plusieurs variantes qui se déclinent suivant les types en entrée.

Patrons de

Itérateurs conception (Design

Singletons patterns)


Définition

Definition (Patron de conception)

Les patrons de conception (en anglais Design Patterns) sont des propositions de conception informatiques largement éprouvées dans la résolution de problèmes génie logiciel types.

De nombreux patrons sont définis (cf. Wikipedia), nous en verrons quelques uns

La STL utilise abondamment la notion d’itérateur qui est un patron de conception

Le C++ se prête bien à l’utilisation de patrons

Améliore grandement le travail collaboratif (non intrusion de notions complémentaires, séparation fonctionnelle )

Mais les patrons de conception restent des propositions (pouvant vous donner des idées) et suivre ces modèles n’est pas une fin en soi.


Itérateurs

Itérateur : interface

1      test de fin d’itération : pour un itérateur en C++, un couple d’itérateur est nécessaire : le premier itère, le second marque la fin. Un test d’égalité permet alors de déterminer la fin de l’itération

2      incrémentation ou décrémentation : avance ou recule l’itérateur d’un (ou plusieurs) élément(s)

le test de fin est implémenté en surchargeant l’”operator!=”de la classe de l’itérateur

l’incrémentation est implémentée en surchargeant l’”operator++”de la classe de l’itérateur

Il existe deux versions de l’”operator++”: le préfixé (”++it”) et le post-fixé (”it++”). Le premier ne prend pas d’argument et retourne une référence sur lui-même (”return ?this”). Le second prend un argument entier (non utilisé) et retourne une copie de lui-même avant incrémentation.


Patrons

Singletons

Utile dans certains contextes : par exemple une classe de synchronisation de ressources, une classe de mappage d’évènements, etc.

Ecrire la classe ”ExempleSingleton”(utilisation minimale du singleton).

159 / 201

Formation C++ avancée

J3 : Librairies externes et organisation de code

J3 : Librairies externes et organisation de code

Contenu

10  Librairies externes

11  Extension C++ avec Boost

12  Optimisation

Compilation et édition de liens

Librairies externes

Visibilité du code & interfaces

Librairies externes

Interfa¸cage avec le ”C”

Il faut pour cela que les types C soit correctement renseignés (au compilateur C++) comme étant C.

Lorsque l’on compile une unité de code en C++ :

1      la macro        cplusplus est toujours définie (tout compilateur confondu)

2      la directive extern ”C”permet alors de renseigner le type (structure, fonction)

Librairies externes

Interfa¸cage avec le C : exemple

Remarques sur les inclusions

”private/protected/public”ne sont que des directives destinées au compilateur, et indiquant approximativement l’usage des membres d’une classe.

Pour qu’un utilisateur externe puisse utiliser une classe, il faut qu’il manipule tous les types des méthodes que cette classe appelle, ainsi que tous les types utilisés pour l’implémentation.

Rappel

Interfaces

Les interfaces permettent d’arriver à un résultat correct pour les deux parties, pour un surcouˆt négligeable.

Elles laissent le soin au fournisseur de service de modifier l’implémentation, tant que l’interface du service reste la même

Elles empêchent la divulgation de détails d’implémentation

Elles évitent au client le fardeau de la gestion de type liés à l’implémentation du fournisseur

Elles sont compatibles avec les templates et potentiellement la présence de beaucoup de types

Elles restent souples à manipuler pour le client

Présentation Modes d’utilisation & compilation

Extension C++

filesystem avec Boost

Tests unitaires

Thread


Présentation

Boost est un Consortium de développeur C++, travaillant sur des extensions standards du C++.

Licence

La licence d’utilisation est d’exploitation de Boost est permissive et non intrusive (redistribution sans restriction dans des programmes à but commercial ou non, sans obligation de mention ni redistribution des sources).

Il faut utiliser Boost.

Utiliser Boost est une bonne chose, car Boost vous veut du bien.

Il faut convaincre tout le monde d’utiliser Boost.

Boost est le prochain standard.

Modes d’utilisation

Deux manière d’utiliser Boost :

version ”headers”: à inclure dans l’unité de compilation concernée, et c’est tout (ex. : graph, mpl, proto, fusion, asio ) version compilée : certaines (peu) parties de Boost nécessitent une compilation, et (donc) une liaison avec le programme client (ex. : filesystem, regex, mpi, )

Il faut utiliser Boost, c’est simple à installer.

Compilation et installation

Unises (Unix, Linux, Mac, Cygwin, )

$ cd path/to/boost_1_37_0

$ ./configure --prefix=path/to/installation/prefix $ make install

Windows avec MS Visual Studio

Démarrer la ligne de commande Visual (normalement dans le menu démarrer et dans le répertoire de Visual)

$ cd path/to/boost_1_37_0/tools/jam

$

$ cd path/to/boost_1_37_0 $ copy path/to/boost_1_37_0/tools/jam/src/bin.Quelquechose/bjam .

$ bjam --build-dir=D:\temporary_dir --toolset=msvc --buildtype=complete stage


<boost/filesystem>

Ex. : nom des fichiers, gestion de l’arborescence, gestion des droits d’accès, existence, etc.


<boost/test>

Les tests ne démarreront qu’au retour de cette fonction de de déclaration.


<boost/test> : exemple


: exemple d’appel


Questions générales

OptimisationConteneurs

Optimisations logicielles

”Premature optimization is the root of all evil”(Donald Knuth)

On optimise toujours en fonction d’une ou plusieurs contraintes Ex. : temps de calcul, occupation mémoire, algorithmique

Les contraintes sont parfois contradictoires et/ou conflictuelles.

Definition (Couˆt algorithmique)

Couˆt théorique de l’algorithme exprimé par rapport à la taille N des données traitées et exprimé en O(†(N)), pour N ? N?.

† : 1 (temps constant), identité, polynôme, log


Questions classiques :

 Le C++ est plus lent que le C : vrai ou faux?

Réponse mitigée. D’expérience : vrai pour des parties isolées de code, faux pour des projets d’envergure importante. Même débat entre le C et l’assembleur par exemple

 Ou` et quand optimiser?

Une fois qu’on a enfin un truc qui fonctionne, selon un design relativement correct.

Remarque : les contraintes influencent nécessairement la phase de design, donc inutile de faire de l’optimisation logicielle trop tôt.

 Comment optimiser?

Encore une fois, selon la contrainte.

Nous allons voir quelques éléments d’optimisation algorithmique et logicielle


Choix des conteneurs

Les couˆts d’accès des conteneurs standards sont également standards! Donc garantie multi plate-forme.

Choix des conteneurs (1)

Choix des conteneurs (2)


I Choix du compilateur!!

I Options d’optimisation de plusieurs niveaux (GCC : O1, O2, 03, O45 active un ensemble d’optimisations)

I Choix du processeur cible

I Utilisation automatique de librairies externes (open MP, etc)

I Profondeur des inlines

I Utilisation des fonctions intrinsèques I Optimisations globales à l’édition de lien I

Avantage important : méthode d’optimisation non-intrusive

191 / 201

Comprendre et gérer la bidouille de compilateurs Code rapide (suite) :

Utilisation des inlines

Méthode d’optimisation intrusive!

Pas vraiment une optimisation : le choix de l’inlining reste le choix du compilateur

Utilisation de portions de code assembleur

Très performant (souvent, mais pas toujours)

Très spécifique

Dur

Très dur même

Difficile à maintenir

Plate-forme dépendant

192 / 201

Formation C++ avancée

Synthèse de la formation


Synthèse de la formation

Contenu

Un outil à maˆ?triser

Savoir-faire

La généricité“light”

Merci de votre attention

Release early, release often



. ie. répondant à des spécifications fonctionnelles strictes

Decouvrir ces documents

  • Initiation à la programmation orientée objet avec le langage Python

    Initiation à la programmation orientée objet avec le langage Python

  • Programmation orientée objet Langage C++

    Programmation orientée objet Langage C++

  • Apprendre les bases de la programmation avec le langage Java

    Apprendre les bases de la programmation avec le langage Java

  • Cours programmation objet et JAVA avec exemples

    Cours programmation objet et JAVA avec exemples

  • Mini projet de programmation en langage C

    Mini projet de programmation en langage C

  • Python et la Programmation Orientée Objet

    Python et la Programmation Orientée Objet

  • Cours pour commencer le JAVA

    Cours pour commencer le JAVA

  • Débuter la programmation avec le langage Ada

    Débuter la programmation avec le langage Ada

Articles connexes

  • Série d'exercices Java sur la programmation orienté objet POO
  • Tutoriel Python : Notions de base
  • Python : tout ce que vous devez savoir
  • Exercices sur la programmation web avec le langage PHP
  • Exercice comptabilité : initiation aux techniques de consolidation
  • Comment coder pour les débutants
  • Application Python : gestion des comptes
  • Python : comment programmer les classes
  • Contactez-nous
  • A propos de nous
  • On recrute
  • Rechercher dans le site
  • Politique de confidentialité
  • Droit d'auteur/Copyright
  • Conditions générales d'utilisation
  • Plan du site
  • Accueil
  • Blog
  • Finance et compta.
  • Formations Pro.
  • Logiciels & Apps
  • Organisation
  • Cours informatique
  • Aide à la rédaction
  • Etudes et Metiers
  • Science et Tech
  • Titans de la Tech
id 11354 02