Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une...

42
Cours PRC Jean-Christophe Engel 17 mai 2005

Transcript of Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une...

Page 1: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Cours PRC

Jean-Christophe Engel

17 mai 2005

Page 2: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Table des matieres

1 Syntaxe du langage C++ 3

1.1 Programmer sans classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2 Classes et structures 6

2.1 declaration de classe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.1.1 Exemple de la classe vecteur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2.1.2 methodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.1.3 constructeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.1.4 initialisation d’un objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.1.5 destruction des objets : destructeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.2 types structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.2.1 differences syntaxiques entre C et C++ : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3 Pointeurs 11

3.1 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.1.1 declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.1.2 initialisation : operateur & . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.1.3 indirection : operateurs * et → . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.2 Passage de parametre par pointeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.2.1 dans un but d’efficacite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.2.2 “parametre modifiable” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.2.3 const ou comment avoir le beurre et l’argent du (bas-)beurre . . . . . . . . . . . . . . . . . . . 12

3.2.4 fonction qui renvoie plusieurs valeurs (ne pas en parler ...) . . . . . . . . . . . . . . . . . . . . . 12

3.2.5 fonction qui renvoie un pointeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.3 Pointeurs et tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.3.1 tableau a une dimension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.3.2 tableau a 2 dimensions (ne pas detailler) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.4 passage de tableau en parametre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.4.1 tableau a une dimension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.4.2 tableau a 2 dimensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.5 Allocation dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.5.1 Allocation dynamique en C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.5.2 Allocation dynamique C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4 Les references 19

4.1 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.1.1 initialisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.1.2 variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.1.3 parametre formel de fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

4.1.4 resultat de fonction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

1

Page 3: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

TABLE DES MATIERES TABLE DES MATIERES

5 constructeur de copie et surcharge operateurs 22

5.1 Revue des constructeurs et destructeurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5.2 Constructeur de copie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5.3 Operateur d’affectation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

5.4 Surcharge d’operateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

5.4.1 operateurs binaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

5.4.2 operateurs unaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

5.4.3 Cas des operateurs d’entree/sortie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

6 Heritage 27

6.1 Exemple et syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

6.2 Constructeurs d’une classe derivee . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

6.3 Methodes d’une classe derivee . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

6.4 Melange de classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

6.4.1 Probleme de troncature (slicing) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

6.4.2 Methodes virtuelles et liaison dynamique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

6.4.3 Destructeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

6.4.4 Classe abstraite et methode virtuelle pure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

6.5 Le schema de conception “patron de methode” . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

7 Programmation generique : les modeles 32

7.1 fonction generique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

7.2 classe generique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

8 la bibliotheque standard de conteneurs 36

8.1 Presentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

8.2 conteneur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36

8.2.1 conteneur sequentiel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

8.2.2 conteneur associatif . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

8.3 iterateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

8.4 Algorithmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39

9 Exceptions 40

9.1 declencher une exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

9.2 traiter une exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

9.3 Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

9.3.1 avec un type predefini . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

9.3.2 avec une classe definie par l’utilisateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

9.3.3 solution avec classe derivee d’un type standard . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

Jean-Christophe Engel 2 17 mai 2005

Page 4: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Chapitre 1

Syntaxe du langage C++

1.1 Programmer sans classe

(presenter l’exemple p1/ex1.cc)

1. programmer sans classe : un programme est une collection de fonctions et procedures repartis dans plusieursfichiers ; chaque fichier (ou module) est une unite de compilation independante. Pas de mots-cle public/private.Un seul main par programme dont la signature est legerement differente de Java.

2. types integres : comme en Java, sauf booleen qui s’appelle bool.

3. chaınes de caracteres :

– en C, il n’existe pas de type chaıne integre ; il faut utiliser une convention de representation (tableau de char)et des fonctions de la bibliotheque standard.

– en C++, on dispose d’une classe string.

4. constantes : const

5. operateurs : comme en java

6. structures de controle : comme en java

7. tableau : peut etre cree par declaration ou par allocation dynamique (sera vue plus tard) ; un tableau n’est pasun objet et n’a (donc) pas d’attribut de taille attache. Si on veut le passer en parametre a une fonction, il fauttransmettre aussi la taille du tableau et/ou le nombre d’elements presents.

– exemple :

int tailleTableau = 25 ; // taille du tableau

// creation du tableauint valeurs[tailleTableau] ; // tableau des nombres

remarques :

– attention a la position des crochets : apres l’identificateur et non avant– en C, la dimension doit etre une constante connue a la compilation

8. entrees-sorties (grosses differences entre C et C++)

– definies dans la bibliotheque standard :

#include <iostream>

using namespace std ;

– deux operateurs : ecriture (<<) et lecture (>>) et deux fichiers predefinis : cout (sortie) cin (entree)– ecrire : cout << "Moyenne = " << moyenne << endl ;

– lire : cin >> unnombre ;

9. fonctions procedures et parametres :

– lorsque le compilateur compile un appel de fonction, il doit connaıtre soit la signature, soit la definitioncomplete d’une fonction.

– surcharge (n’existe pas en c) : signature parametrique differentes, resultat non pris en compte pour differencierles fonctions.

– possibilite pour une fonction de specifier des valeurs de parametres par defaut (c++)– mecanisme de passage de parametre par defaut : par valeur (sauf les tableaux : idem qu’en Java).

3

Page 5: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 1. SYNTAXE DU LANGAGE C++ 1.1. PROGRAMMER SANS CLASSE

– parametre tableau (le parametre formel designe le parametre effectif) ; preciser qu’une fonction ne peut jamaisrenvoyer un tableau en resultat : pour obtenir le meme effet, il faut declarer un tableau dans la fonctionappelante (ou l’une des fonctions en amont), le passer en parametre et le modifier dans la fonction ; voirl’exemple de la fonction lireTableau (ex1.c) et son appel par la fonction main.

Jean-Christophe Engel 4 17 mai 2005

Page 6: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Syntaxe du langage C

voir p1/ex1.c et p1/ex1.cc

– structure generale d’un programme– types (char, int, float, double, bool uniquement en c++), declaration, initialisation– operateurs, expressions, priorite, conversion de types (presenter ++ (tres) brievement ; dire a++ = ++a et interdit

utiliser ++ dans une expression)– tableaux a une dimension (declaration, acces elements) (syntaxe des tableaux a plusieurs dimensions)– structures de controle (sauf switch) ; insister sur l’interet, voire la necessite de faire des blocs dans les conditionnelles

et iterations.– fonctions :

– definition,– prototype/signature,– parametres par valeur,– surcharge (uniquement en c++) : signature parametrique differentes, resultat non pris en compte pour differencier

les fonctions– possibilite pour une fonction de specifier des valeurs de parametres par defaut (c++)– parametre tableau (le parametre formel designe le parametre effectif) ; preciser qu’une fonction ne peut jamais

renvoyer un tableau en resultat : pour obtenir le meme effet, il faut declarer un tableau dans la fonction appelante(ou l’une des fonctions en amont), le passer en parametre et le modifier dans la fonction ; voir l’exemple de lafonction lireTableau (ex1.c) et son appel par la fonction main.

– recap tableaux

declaration T v[N] N = nombre d’elementsacces element v[i] i ∈{ 0 .. N-1}

parametre formel T’ f(T w[N])

parametre effectif x = f(v) pas de [ ] ni d’indice

la notation T v[ ] n’a pas de sens, sauf comme parametre formel

– entrees/sorties C/C++

5

Page 7: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Chapitre 2

Classes et structures

Plan

– Declaration de classe : private, public, protected– Methodes y compris :

– constructeurs/destructeurs (NB : pas de ramasse-miettes),– surcharge des methodes et du constructeur,– methodes “en ligne” (bof, je n’en parle pas)

– struct (C et C++)

2.1 declaration de classe

– mot-cle class

– declaration des attributs : tout type de base (y compris tableau), toute classe deja definie, pointeur, reference– signature des methodes– controle d’acces :

public : attribut ou methode accessible depuis toute fonction ou methode ; de preference uniquement des methodes,pour assurer l’encapsulation des donnees

private : attribut ou methode accessible uniquement depuis une instance de la classe (ou des methodes/classeamies, ne pas developper maintenant, en reparler lors de la surcharge des operateurs�et�) ; normalementtous les attributs qui ne seront donc accessibles que par les fonctions (methodes) d’acces publiques ; certainesmethodes de “cuisine interne”

protected : attribut ou methode accessible depuis une instance de la classe ou de classes derivees (ne pas developpermaintenant, en reparler a propos de l’heritage)

2.1.1 Exemple de la classe vecteur

declaration (p2/c++/vecteur.h)

#ifndef __VECTEUR_H // -*- c++ -*-#define __VECTEUR_H

class vecteur{

public://------------------------------------------------------------

// un constructeur ; initialisation avec 0, 1, 2 ou 3 reels

vecteur(double v1 = 0, double v2 = 0, double v3 = 0); // valeurs par defaut

//------------------------------------------------------------

// le destructeur

~vecteur(void);

//------------------------------------------------------------

// Quelques methodes

6

Page 8: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 2. CLASSES ET STRUCTURES 2.1. DECLARATION DE CLASSE

void affiche(void) const; // Afficher un vecteur

double prodscal(vecteur) const; // produit scalaire de vecteursvecteur somme(vecteur) const; // Somme de 2 vecteurs ...

vecteur somme(double) const; // Somme d’un vecteur et d’un nombrestatic vecteur somme(vecteur, vecteur);// Somme de 2 vecteurs ...

void homothetie(double); // Homothetie

//------------------------------------------------------------

// les attributs

private:double x, y, z; // 3 coordonnees

};

#endif

– les attributs (coordonnees) du vecteur sont prives (par defaut) donc accessibles aux seuls membres de la classe, engeneral les methodes de l’interface.

– les methodes sont publiques dont accessibles par toute fonction/methode.– methodes qui ne modifient pas les attributs : const

– (plus tard ?) attribut static (ex : static int nb objets ;) un seul exemplaire pour tous les objets de la classe ; il fautobligatoirement le definir et eventuellement l’initaliser (int classe::nb objets = 10 ;) dans l’implementation.

2.1.2 methodes

implementation (p2/c++/vecteur.cc)

#include <iostream>#include "vecteur.h" // classe vecteurusing std::cin;

using std::cout;

//------------------------------------------------------------// constructeur : initialisation avec 0, 1, 2 ou 3 reels

vecteur::vecteur(double v1, double v2, double v3): x(v1), y(v2), z(v3)

{cout << "construit " ; affiche(); // pour info

}

//------------------------------------------------------------

// destructeur

vecteur::~vecteur(){

cout << "detruit " ; affiche(); // pour info}

//------------------------------------------------------------// Affichage d’un vecteur

void vecteur::affiche(void) const{

cout << " " << "[ " << x << " " << y << " " << z << " ]\n";}

//----------------------------------------

// Produit scalaire de 2 vecteurs

double vecteur::prodscal(vecteur v) const

{return x * v.x + y * v.y + z * v.z;

}

//----------------------------------------

// Somme de 2 vecteurs

vecteur vecteur::somme(vecteur v) const

{return vecteur(x + v.x, y + v.y, z + v.z);

}

//----------------------------------------// Somme d’un vecteur et d’un nombrevecteur vecteur::somme(double n) const

{return vecteur(x + n, y + n, z + n);

}

//----------------------------------------// Somme de 2 vecteurs (methode de classe)

vecteur vecteur::somme(vecteur v1, vecteur v2){

return vecteur(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);}

//----------------------------------------// Homothetie

void vecteur::homothetie(double valeur)

{x *= valeur; y *= valeur; z *= valeur;

}

– classe::methode suivi du corps– acces aux attributs de l’instance courante ou d’une autre (operateur .) : methodes afficher, prodscal– possibilite d’avoir plusieurs methodes de meme nom, differenciees par le nombre ou le type des parametres (mais

pas par le type du resultat) : surcharge ;parler des valeurs de parametres par defaut : les deux versions de somme

– passage d’objet par valeur (methodes prodscal et somme)– methodes qui ne modifient pas les attributs : const ; seule une methode “const” peut s’appliquer sur un objet constant

ex : const vecteur v1(1, 2, 3) ;

vecteur v2(2, 3, 4), v3 = v1.somme(v2) ;

2.1.3 constructeur

– meme nom que la classe, pas de rendu de resultat

Jean-Christophe Engel 7 17 mai 2005

Page 9: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 2. CLASSES ET STRUCTURES 2.1. DECLARATION DE CLASSE

– appele automatiquement apres que l’objet a ete construit ; sert a initialiser l’objet (c’est-a-dire ses attributs) etnon a le construire. La creation d’objet peut se faire de plusieurs manieres :– par declaration : vecteur v1 ; ceci cree un objet v1, instance de la classe vecteur et appelle automatiquement le

constructeur– par allocation dynamique (sera vu plus tard)(ne pas parler maintenant du constructeur par copie)

– deux moyens d’initialiser les attributs :– syntaxe “classique” : dans le corps– initialisations declaratives (a preferer a la syntaxe classique) : seules possibles pour les attributs qui sont :

– constants– des objets– des references (on en reparlera plus tard)

– plusieurs constructeurs, differencies par le nombre et le type des parametres : surcharge ; (idem que n’importe quellemethode)

– si absent (pas recommande), appel d’un constructeur par defaut qui se contente d’appeler les constructeurs sansparametres des objets membres, s’il y en a.

– tableaux d’objets : appel du constructeur sans parametre pour chaque element du tableau ; exemple : vecteurtv[1000] ;

2.1.4 initialisation d’un objet

– syntaxe “classique” ; exemple de la methode somme

vecteur vecteur::somme(vecteur v) const {

vecteur somme ;

somme.x = x + v.x ; somme.y = y + v.y ; somme.z = z + v.z ;

return somme ;}

explication appel du constructeur sans parametre puis modification des attributs ; ne peut etre utilisee dans unedeclaration ;

– syntaxe “fonctionnelle” :

rappel :

vecteur v1(10, 20 30) ; // deja vu

et aussi :

vecteur vecteur::somme(vecteur v) const {

vecteur somme(x + v.x, y + v.y, z + v.z) ;

return somme ;}

vecteur vecteur::somme(vecteur v) const {

return vecteur(x + v.x, y + v.y, z + v.z) ;

}

interet de ces deux constructions par rapport a la premiere : le constructeur est appele d’emblee avec les valeurscorrectes des attributs et peut servir a normaliser l’instance ainsi initialisee ; dans le premier cas, la normalisationse fait sur des valeurs de parametres par defaut (ou pas du tout, s’il n’y a pas de constructeur sans parametre)et la modification a posteriori des parametres n’entraıne pas automatiquement la normalisation de l’instance.

exemple : on peut exiger que la norme d’un vecteur soit toujours egale a 1 ; dans ce cas, c’est le role du constructeurde normaliser les composantes ... afin que cette normalisation soit faite automatiquement lors de la creationd’une instance de la classe vecteur)

2.1.5 destruction des objets : destructeur

Pas de gestion automatique de la memoire par ramasse-miettes (torche-poubelle, in ingliche)

– nom : ˜classe, sans parametre– appele juste avant la destruction de l’objet ; sert a des-initialiser l’objet (c’est-a-dire ses attributs) et non a le

detruire.

Jean-Christophe Engel 8 17 mai 2005

Page 10: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 2. CLASSES ET STRUCTURES 2.2. TYPES STRUCTURES

– pour les objets crees par declaration, l’appel du destructeur est implicite quand l’objet arrive en “fin de vie” : sortiede sa portee (bloc dans lequel l’objet a ete declare), fin du programme ;

– pour les objets crees dynamiquement (par new, voir plus loin), l’appel du destructeur doit etre fait explicitement(par l’operateur delete et pas avec le nom ˜classe)

Il sert principalement a liberer la memoire (ou d’autres ressources externes a l’objet) allouee(s) (dynamiquement)(par exemple, par le constructeur)... sinon, n’est pas indipensable, sauf pour tracer la dynamique des appels construc-teur/destructeur.

programme client (p2/c++/client.cc)

#include <iostream>#include "vecteur.h" // classe vecteur

using std::cin;using std::cout;

// petit programme de test de la classe

int main(void) {

vecteur v1, v2(1, 2, 3); // construit [ 0 0 0 ] : v1

// construit [ 1 2 3 ] : v2

cout << "v1 : "; v1.affiche(); // v1 : [ 0 0 0 ]cout << "v2 : "; v2.affiche(); // v2 : [ 1 2 3 ]

v2.homothetie(2);cout << "v2 *= 2 : "; v2.affiche(); // v2 *= 2 : [ 2 4 6 ]

vecteur v3(3, 2, 1); // construit [ 3 2 1 ]

cout << "v3 : "; v3.affiche(); // v3 : [ 3 2 1 ]

cout << "v2 x v3 : " << v2.prodscal(v3) << "\n"; // v2 x v3 : 20

// detruit [ 3 2 1 ] : parametre de prodscal

vecteur v4 = v2.somme(v3); // construit [ 5 6 7 ]

// detruit [ 3 2 1 ] : parametre de somme

cout << "v2 + v3 : "; v4.affiche(); // v2 + v3 : [ 5 6 7 ]

v4 = v4.somme(10);

// construit [ 15 16 17 ] : objet local de somme// detruit [ 15 16 17 ] : objet local de somme

cout << "v4 + 10 : " ; v4.affiche(); // v4 + 10 : [ 15 16 17 ] : v4

v4 = vecteur::somme(v4, v3);// construit [ 18 18 18 ] : objet local de somme// detruit [ 18 18 18 ] : objet local de somme

// detruit [ 15 16 17 ] : parametre de somme// detruit [ 3 2 1 ] : parametre de somme

cout << "v4 + v3 : "; v4.affiche(); // v4 + v3 : [ 18 18 18 ] : v4

return 0;

/*

Fin du programme : destruction des objets restants--------------------------------------------------

detruit [ 18 18 18 ] : v4detruit [ 3 2 1 ] : v3detruit [ 2 4 6 ] : v2

detruit [ 0 0 0 ] : v1*/

}

2.2 types structures

2.2.1 differences syntaxiques entre C et C++ :

Structures en C++

– declarateur = struct au lieu de class

– attributs et methodes publics par defaut– quasiment aucun interet

Structures en C

– declarateur struct

– le mot struct fait partie du nom du type ; utiliser typedef pour s’en debarrasser– pas de declaration de methode, mais fonctions (hors structure)– attributs publics– structure obligatoirement en parametre des fonctions– outil (quasi-)indispensable a privilegier pour faire des types abstraits en C

exemple

struct vecteur { double x, y, z ; } ;typedef struct vecteur Vecteur ;struct vecteur v1 ;Vecteur v2 ;

typedef struct { double x, y, z ; } Vecteur ;struct vecteur v1 ;Vecteur v2 ;

typedef struct { double x, y, z ; } VecteurVecteur v2 ;

Jean-Christophe Engel 9 17 mai 2005

Page 11: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 2. CLASSES ET STRUCTURES 2.2. TYPES STRUCTURES

exemple du type Vecteur : interface (p2/c/vecteur.h)

#ifndef __VECTEUR_H__ /* Blindage du fichier entete */

#define __VECTEUR_H__

typedef struct {

double x, y, z; /* 3 coordonnees*/} Vecteur;

/* Prototypes des fonctions d’acces */

Vecteur creer(double v1, double v2, double v3); /* constructeur avec valeurs*/void affiche(Vecteur v); /* Afficher un vecteur*/

double prodscal(Vecteur v1, Vecteur v2); /* produit scalaire des vecteurs*/Vecteur somme(Vecteur v1, Vecteur v2); /* Somme de 2 vecteurs ...*/

void homothetie(Vecteur * pv, double x); /* Homothetie*/

#endif /*__VECTEUR_H__*/

exemple du type Vecteur : implementation (p2/c/vecteur.c)

#include <stdio.h>#include "vecteur.h" /* TA vecteur*/

/*----------------------------------------

"constructeur" avec valeurs----------------------------------------*/

Vecteur creer(double v1, double v2, double v3)

{Vecteur v;

v.x = v1; v.y = v2; v.z = v3;return v;

}

/*----------------------------------------

Affichage d’un Vecteur----------------------------------------*/

void affiche(Vecteur v){

printf("[ %.1f %.1f %.1f ]\n", v.x, v.y, v.z);}

/*----------------------------------------Produit scalaire de 2 Vecteurs

----------------------------------------*/

double prodscal(Vecteur v1, Vecteur v2)

{return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;

}

/*----------------------------------------Somme de 2 Vecteurs

----------------------------------------*/

Vecteur somme(Vecteur v1, Vecteur v2){

Vecteur somme; /* Resultat*/

somme.x = v1.x + v2.x;

somme.y = v1.y + v2.y;somme.z = v1.z + v2.z;

return somme;}

/*----------------------------------------Homothetie

----------------------------------------*/void homothetie(Vecteur * pv, double valeur)

{pv->x *= valeur; pv->y *= valeur; pv->z *= valeur;

}

– declaration : comme les variables de type predefini– acces aux attributs : variable.attribut– passage en parametre comme pour les variables scalaires de type predefini : par valeur et resultat de fonction– passage de l’adresse, soit pour modification (fonction homothetie), soit pour economiser la place et le temps de

recopie (grosses structures)

programme client : (p2/c/client.c)

#include <stdio.h>#include "vecteur.h" /* TA vecteur*/

/* petit programme de test du Type Abstrait vecteur */

int main(void) {

Vecteur v1 = creer(0, 0, 0), v2 = creer(1, 2, 3),

v3 = creer(3, 2, 1), v4 = creer(0, 0, 0);

printf("v1 : "); affiche(v1);printf("v2 : "); affiche(v2);

homothetie(&v2, 2);printf("v2 *= 2 : "); affiche(v2);printf("v3 : "); affiche(v3);

printf("v2 x v3 : %.1f\n", prodscal(v2, v3));v4 = somme(v2, v3);

printf("v2 + v3 : ") ;affiche(v4);

return 0;}

/*----------------------------------------Resultats

v1 : [ 0.0 0.0 0.0 ]v2 : [ 1.0 2.0 3.0 ]

v2 *= 2 : [ 2.0 4.0 6.0 ]v3 : [ 3.0 2.0 1.0 ]

v2 x v3 : 20.0v2 + v3 : [ 5.0 6.0 7.0 ]

----------------------------------------*/

Jean-Christophe Engel 10 17 mai 2005

Page 12: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Chapitre 3

Pointeurs

Plan

– definition, initialisation, indirection– passage de parametre par pointeur– pointeurs et tableaux– tableaux en parametres– allocation dynamique

3.1 Definition

pointeur = variable dont la valeur est l’adresse d’une variable ou d’un objet ; un pointeur pointe sur une variable d’untype precis, indique dans la declaration du pointeur.

3.1.1 declaration

int * pi ;

float * pr1, * pr2 ; // pas de factorisation du type (beurk)Point * pp ; // en supposant que Point soit un type (structure ou classe) definivecteur * pv ;

Dans la declaration T * p ; le type du pointeur est : T *

3.1.2 initialisation : operateur &

int i, * pi ;

i

pi

p1

ppx

y

pv

v1

Point p1, * pp ;

vecteur v1(5, 7, -2), * pv ;

pi= & i ; pp = & p1 ; pv = & v1 ;

Faire un joli schema : pointeur pointe sur variable et inciter les etudiants a faire de meme

3.1.3 indirection : operateurs * et →

* p : type et valeur de la variable pointee ...

– * pi : int, valeur de i– * pp : Point, valeur de pt– * pv : vecteur, valeur de v1

On peut donc appliquer sur l’expression * p les operations du type correspondant

exemple : (*pi) += 1 ; (*pp).x = 4 ; (*pv).affiche() ;

cas des structures et objets : (*p).attribut ⇐⇒ p→attribut (idem methodes) exemple : pp→x = 4 ; pv→affiche( ) ;

11

Page 13: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 3. POINTEURS 3.2. PASSAGE DE PARAMETRE PAR POINTEUR

3.2 Passage de parametre par pointeur

3.2.1 dans un but d’efficacite

Quand la variable ou l’objet passe en parametre est complexe, le passage par valeur est penalisant (memoire, temps) :au lieu de passer la valeur de l’objet, on passe (par valeur !) son adresse ⇒ si la variable dont on passe l’adresse est detype T, le parametre effectif est de type T *, donc le parametre formel de la procedure appelee doit etre de type T *

exemple :

1. double vecteur::prodscal(vecteur v) { return x * v.x + y * v.y + z * v.z ; } devient

2. double vecteur::prodscal(vecteur * pv) { return x * pv→x + y * pv→y + z * pv→z ; }

appels :

1. vecteur v1, v2 ; double ps = v1.prodscal(v2) ;

2. vecteur v1, v2 ; double ps = v1.prodscal(& v2) ;

3.2.2 “parametre modifiable”

Pour “modifier la valeur d’un parametre” dans une fonction, il faut lui transmettre son adresse : le parametre effectifest donc l’adresse de la variable a modifier, le parametre formel est un pointeur, initialise a l’appel par l’adresse (valeurdu parametre effectif) et pointe donc sur la variable a modifier dans la fonction appelante.

exercice : ecrire une fonction void permuter(float * p1, float * p2) qui echange les valeurs de deux reels passes enparametre (faire chercher les parametres formels) ; donner un exemple d’appel avec des adresses (operateur &),un exemple avec des pointeurs ; representer graphiquement ce qui se passe.

la fonction

void permuter(float * p1, float * p2)

{

float x ;

x = *p1 ; *p1 = *p2 ; *p2 = x ;

}

appel avec &

float r1, r2 ;

permuter(& r1, & r2) ;

appel avec pointeurs

float * pr1, * pr2 ;

pr1 = & r1 ; pr2 = & r2 ;

permuter(pr1, pr2) ;

Bien insister sur le fait que dans les deux cas, il s’agit de passage par valeur... meme si la valeur transmise est uneadresse ; (faire le parallele avec scanf si on en a parle.)

3.2.3 const ou comment avoir le beurre et l’argent du (bas-)beurre

Pour beneficier a la fois de l’efficacite du passage de l’adresse et de l’invariance du passage par valeur : declarer leparametre formel pointeur avec le qualificateur const.

exemple : la methode prodscal ne doit pas modifier le vecteur parametre, d’ou :

double vecteur::prodscal(const vecteur * pv) const

{ return x * pv→x + y * pv→y + z * pv→z ; }

En C++, toute tentative de modifier l’objet designe par pv est rejetee par le compilateur ; en C, le compilateur signale(avertissement) la tentative mais l’accepte.

remarque facultative : on peut aussi contraindre pv a etre constant en mettant un const devant pv ;double vecteur::prodscal(const vecteur * const pv) const

{ return x * pv→x + y * pv→y + z * pv→z ; }

3.2.4 fonction qui renvoie plusieurs valeurs (ne pas en parler ...)

– soit les mettre dans un struct (bof)

exemple :

Jean-Christophe Engel 12 17 mai 2005

Page 14: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 3. POINTEURS 3.3. POINTEURS ET TABLEAUX

struct {int q, r} quotientreste(int nb1, int nb2) {

struct { int q, r } qr ;

qr.q = nb1 / nb2 ; qr.r = nb1 % nb2 ;

return qr ;

}

appel : struct { int q, r } qr = quotientreste(a, b) ;

– soit passer (par valeur) l’adresse des variables resultat :

exemple :

void quotientreste(int nb1, int nb2, int * pq, int * pr) ;

3.2.5 fonction qui renvoie un pointeur

le pointeur ne peut designer une variable locale ; il doit designer soit une variable dont l’adresse est parametre de lafonction, soit une variable allouee dynamiquement.

exemple : fonction qui renvoie l’adresse du max de deux reels

La fonction

float * pointeurMax(float * p , float * p2) {

float * pmax ;

if (*p1 > *p2) {

pmax = p1 ;

} else {

pmax = p2 ;

}

return pmax ;

}

Appels avec les memes declarations

float * pmax ;

pmax = pointeurMax(& r1, & r2) ;

ou

pmax = pointeurMax(pr1, pr2) ;

expliquer le danger d’avoir un pointeur qui designe une variable locale.

3.3 Pointeurs et tableaux

3.3.1 tableau a une dimension

initialisation : float v[NB], * pv = &v[0] ;

relation 1 : *(pv + i) = v[i]

0 1 i n−1p

p+1p+i

consequence : en branchant un pointeur (type T *) sur le debut d’un tableau (type T [ ]), on peut balayer tous leselements du tableau par simple incrementation du pointeur

Interet : principalement quand on fait de l’allocation dynamique (voir plus loin) ; dans ce cas, on ne peut accederaux elements du tableau qu’a partir d’un pointeur qui designe le premier element.

relation 2 : v ≡ &v[0]

un identificateur de tableau est “un pointeur constant” dont la valeur (invariable) est l’adresse du 1er element dutableau et dont le type est T *, si T est le type des elements du tableau ; ceci permet de simplifier les notations.

relation 3 : v[i] ≡ *(v+i) ≡ *(pv+i) ≡ pv[i]

Jean-Christophe Engel 13 17 mai 2005

Page 15: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 3. POINTEURS 3.4. PASSAGE DE TABLEAU EN PARAMETRE

Il n’en demeure pas moins qu’un pointeur n’est pas un tableau

pointeur tableau

declaration T * p T v[N]

cree un pointeur non initialise ; reserve de lamemoire pour un pointeur

reserve de la memoire pour N elts de type T

initialisation p = & v[0] ou p = v

p doit obligatoirement designer un tableau, creepar declaration ou par allocation dynamique

p = p + 1 v = v + 1

p designe l’element suivant du tableau ; si ondesire eviter de modifier par inadvertance p, ledeclarer avec const : T * const p = &v[0] ;

impossible : v est constant

a retenir : quand on possede un pointeur p (constant, si possible) qui designe le debut d’un tableau, on peut accederaux elements du tableau par p[i] avec 0 ≤i < taille(tableau)

3.3.2 tableau a 2 dimensions (ne pas detailler)

double w[NBL][NBC], * pw = &w[0][0] ;

relation de base : *(pw + i * NBC + j) ≡ w[i][j], car les elements sont ranges en memoire ligne par ligne (sans “trou”entre deux lignes) ;

ATTENTION : on ne peut pas utiliser la notation pw[i][j]

3.4 passage de tableau en parametre

Je ne parle pas du passage en parametre de tableaux de dimension quelconque (i.e. non constante d’un appel a l’autre)

3.4.1 tableau a une dimension

Toujours “par variable” : un tableau n’est jamais passe par valeur (sauf s’il est attribut d’une structure ou d’un objetpasse par valeur) ; dans la realite physique sous-jacente, seule l’adresse du premier element est transmis ; on peut donc :

– declarer le parametre formel comme un tableau (type T [ ]) et le manipuler avec un indice (solution que je preconise)– declarer le parametre formel comme un pointeur (type T *) qui sera initialise a l’appel par l’adresse du debut du

tableau “effectif” et acceder aux elements du tableau a l’aide de ce pointeur.

Dans les deux cas, lors de l’appel, il suffit de mettre le nom du tableau (sans [ ] ni indice) pour indiquer le parametreeffectif.

tableau prototype int sommeTableau(int v[NB], int n) ;

appel int vnb[NB], somme = sommeTableau(vnb, NB) ;

pointeur prototype int sommeTableau(int * pv, int n) ;

appel int vnb[NB], somme = sommeTableau(vnb, NB) ;

3.4.2 tableau a 2 dimensions

Regle generale : (i.e. quel que soit le nombre de dimensions d’un tableau), le compilateur a besoin de connaıtre lenombre d’elements de chaque dimension, sauf la premiere, pour calculer la position d’un element dans le tableau.

– si le parametre formel est declare comme un tableau, notation indicee disponible ; ne permet pas de passer destableaux parametres effectifs dont la largeur declaree est differente de la largeur du parametre formel.

– si le parametre formel est declare comme un pointeur, pas de notation indicee possible ; il faut faire soi-meme lecalcul de l’adresse de chaque element, donc il faut aussi passer en parametre les dimensions du tableau.

Jean-Christophe Engel 14 17 mai 2005

Page 16: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 3. POINTEURS 3.5. ALLOCATION DYNAMIQUE

3.5 Allocation dynamique

Objectif : gerer des structures de donnees de taille variable ; Pas de gestion automatique de la memoire (ramassemiettes) ; l’allocation/creation et la liberation/destruction sont geres manuellement par le programmeur.

Regle : tout objet cree dynamiquement doit etre desalloue ...

3.5.1 Allocation dynamique en C++

creation : operateur new

T * p ;

p = new T(parametres d’initialisation) ;

– cree un objet de type T, appelle le constructeur adequat (en fonction des parametres d’initialisation) et renvoiel’adresse de l’objet cree ;exemple : vecteur * pv = new vecteur(1, 2, 3) ;

– si on ne souhaite pas modifier la valeur de p, on peut le declarer const, auquel cas l’initialisation doit etre faiteobligatoirement dans la declaration :T * const p = new T(parametres d’initialisation) ;exemple : vecteur * const pv = new vecteur(1, 2, 3) ;

destruction : operateur delete

delete p ; appelle le destructeur de la classe T pour “nettoyer” l’objet pointe par p puis le detruit .

exemple :

vecteur * pv = new vecteur(0, 1, 2) ;

delete pv ;

utilisation de l’allocation dynamique dans une fonction/methode :

(voir p3/c++/vecteur.{c,h} et p3/c++/client.cc)

Regle importante (montrer la fonction sommeMAUVAIS)

vecteur vecteur::sommeMAUVAIS(const vecteur * const pv) const {

vecteur * psomme = new vecteur(x + pv -> x, y + pv -> y, z + pv -> z) ;

return * psomme ;

}

appel :

vecteur v1(1, 2, 3), v2(4, 5, 6), v3 = v1.sommeMAUVAIS(& v2) ;

le probleme :

cette methode renvoie une copie de l’objet cree dynamiquement qui est assignee a v3 ; c’est mauvais car l’objet creedynamiquement dans la methode :

– n’est pas detruit– n’est plus accessible car son pointeur disparait a la fin de l’appel

Regle : ne jamais renvoyer une copie d’un objet cree dynamiquement, toujours son adresse. Montrer les deux versionscorrectes :

sans allocation dynamique, resultat = valeur

vecteur vecteur::somme(const vecteur * const pv) const {

return vecteur(x + pv -> x, y + pv -> y, z + pv -> z) ;

}

avec allocation dynamique, resultat = adresse

vecteur * vecteur::psomme(const vecteur * const pv) const {

vecteur * psomme = new vecteur(x + pv -> x, y + pv -> y, z + pv -> z) ;

return psomme ;

}

Jean-Christophe Engel 15 17 mai 2005

Page 17: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 3. POINTEURS 3.5. ALLOCATION DYNAMIQUE

cas d’un tableau :

– creation : T * const pt = new T[n] ; le constructeur sans parametre est appele pour chaque elt ;– destruction : delete [ ] pt ; le destructeur est appele pour chaque elt ; NB : delete pt ne detruit QUE le premier

element

exemple : Rationnel * const trat = new Rationnel[n] ; ... trat[i].affiche() ; ... ; delete [ ] trat ;

Jean-Christophe Engel 16 17 mai 2005

Page 18: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 3. POINTEURS 3.5. ALLOCATION DYNAMIQUE

Autre exemple : classe pileNombres.

tailleelements

sommet

tailleelements

sommet

Cet exemple sert a montrer l’interet et le (vrai) role du destructeur ; il sera repris dans la partie qui traite du construc-teur de copie.

En l’absence de destructeur programme, le tableau de nombres alloue dans le constructeur n’est pas detruit quand lapile est detruite.

class pileNombres {public :

pileNombres (int n = 10) ; // constructeur

~pileNombres(void) ; // destructeur

...

private :

// attributs prives

float * elements ;

// pointe sur le premier elt

// du tableau qui sera cree dynamiquement

int taille ; // taille a la creation

int sommet ; // nombre d’elements presents

} ;

// constructeur

pileNombres::pileNombres(int n) :

// creer un tableau de n elements

elements(new float[n]),

taille(n),

sommet(0)

{}

// destructeur

pileNombres::~pileNombres(void) {// detruire le tableau alloue

delete [ ] elements ;

}

// utilisation

int main(void) {pileNombres t(5) ; // cree un tableau de 5 elts

// etc...

}// appel implicite du destructeur en fin de fonction

ou bien

// avec allocation dynamique de l’objet pile

int main(void) {pileNombres const * pt = new pileNombres(5) ;

// etc...

// destruction explicite de l’objet

delete pt ;

}

Si le TP sur les listes a debute, on peut faire le rapprochement.

3.5.2 Allocation dynamique C

prototypes et constantes : #include <stdlib.h>

allocation : void * malloc (long int nb) ; renvoie NULL en cas d’echec, l’adresse d’un bloc de nb octets consecutifs encas de succes ;

– expliquer void *– en general le nombre d’octets est calcule ainsi :

n * sizeof (T),ou T est le type des objets a placer dans la memoire allouee, n est le nombre d’objets de type T ; sizeof est unoperateur qui determine le nombre d’octets occupe par un objet de type T

– on place l’adresse renvoyee dans un pointeur (type T *) ainsi :T * p = (T *) malloc(n * sizeof (T)) ;

la memoire ainsi allouee peut ensuite etre utilisee a l’aide du pointeur comme une variable de type T ou un tableaudont les elements sont de type T (si n > 1).le “transtypage” (T *) est utile pour eviter les messages d’avertissement du compilateur afin d’eviter de melangerdes messages importants et des messages qui ne le sont pas.

Attention : eviter de modifier le pointeur qui pointe sur le bloc de memoire alloue ...

liberation : void free(void * p) ; libere la memoire designee par le pointeur p

exemple : allocation d’une matrice de nblignes par nbcolonnes de reels

Jean-Christophe Engel 17 17 mai 2005

Page 19: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 3. POINTEURS 3.5. ALLOCATION DYNAMIQUE

float * mp = (float *) malloc(nblignes * nbcolonnes * sizeof(float)) ;

acces a l’element d’indices (i, j) : *(mp + i * nbcolonnes + j)

Rappel : la memoire allouee n’est pas locale a la fonction qui fait l’allocation ; on peut donc sans crainte renvoyerl’adresse de la memoire allouee en fin de fonction.

exemple de fonction d’allocation qui renvoie un pointeur ; cf p3/c/client.c (selon temps)

Jean-Christophe Engel 18 17 mai 2005

Page 20: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Chapitre 4

Les references

4.1 Definition

Une reference est une variable, invariable apres initialisation ( !), qui designe de facon non modifiable une autrevariable (ou un objet). Aucun operateur ne s’applique directement a une reference : il s’appliquera toujours a lavariable referencee (on peut considerer une reference sur une variable comme un “alias” de cette variable).

4.1.1 initialisation

Une reference s’initialise toujours avec une variable ou un objet, jamais une valeur (rappel : un pointeur s’initialiseavec une adresse) ; differents cas d’initialisation :

– reference declaree comme variable : dans la declaration– reference comme parametre formel d’une fonction : lors de l’appel par le parametre effectif (la reference, parametre

formel, designe l’objet parametre effectif)– reference comme attribut d’un objet : dans le constructeur (initialisation declarative obligatoirement)

Utilisations principales :

– designation non modifiable d’objets– parametres (plus simple que pointeurs const) et resultats de fonctions (peu lisible)– surcharges d’operateurs

4.1.2 variable

// declarationint i1, i2 ;

int & r1 = i1, & r2(i2) ; // initialisation obligatoire a la declaration

r1 designe la variable i1, r2 designe la variable i2

r1 = 2 ; // equivaut a i1 = 2r2 = r1 ; // equivaut a i2 = i1

initialisation par creation dynamique : ce n’est, en general, pas une bonne idee de designer les donnees creees dyna-miquement par une reference, car peu lisible ; on designera plutot les donnees creees dynamiquement a l’aide d’unpointeur.

vecteur & rv = * new vecteur(1, 2, 3) ;

// l’indirection ci-dessus est obligatoire, car une reference s’initialise avec un objet et non une adresserv.affiche() ; // appelle la methode affiche() sur l’obet designe par la reference rv

et dans ce cas la destruction se fait ainsi : delete & rv ; // & obligatoire, car delete s’aplique sur une adresse

19

Page 21: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 4. LES REFERENCES 4.1. DEFINITION

4.1.3 parametre formel de fonction

Ceci realise le passage de parametre par variable ou par reference ; c’est equivalent au passage de l’adresse d’unparametre effectif, sauf que justement il n’y a pas besoin de passer explicitement l’adresse du parametre lors del’appel, ni de manipuler de pointeur pour acceder a la valeur du parametre effectif ;

fonctions a parametres modifiables :

l’utilisation des references permet d’alleger la notation mais par ailleurs les fonctions a parametres modifiables nerendent pas les programmes tres lisibles (sauf exception)

exemple :

avec pointeur

void permuter (float * r1, float * r2) {float z ;

z = * r1 ; * r1 = * r2 ; * r2 = z ;

}

appel : float x, y ; permuter( &x, &y) ;

l’avantage du passage explicite de l’adresse est qu’on sedoute que la fonction va modifier “les parametres” ;l’inconvenient est l’obligation de manipuler des pointeurs.

idem avec reference

void permuter (float & r1, float & r2) {float z ;

z = r1 ; r1 = r2 ; r2 = z ;

}

appel : float x, y ; permuter( x, y) ;

l’avantage est la simplicite d’ecriture ; l’inconvenient c’estque l’appel ne montre pas le fait que la fonction modifie“les parametres”.

fonctions a parametres non modifiables :

si on ne ne veut pas passer les parametres par valeur, soit pour des raisons d’economie de temps et de place, soit acause des problemes d’affectation de surface (qu’on verra plus tard) : utilisation de const

exemple : produit scalaire de 2 vecteurs

par valeur

double vecteur : :prodscal(vecteur v) const {return x * v.x + y * v.y + z * v.z ;

}

appel : vecteur v1(1, 2, 3), v2(4, 5, 6) ; double x = v1.prodscal(v2) ;

par pointeur

double vecteur : :prodscal(const vecteur * const pv) const {return x * pv->x + y * pv->y + z * pv->z ;

}

appel : vecteur v1(1, 2, 3), v2(4, 5, 6) ; double x = v1.prodscal(& v2) ;

par reference

double vecteur : :prodscal(const vecteur & v) const {return x * v.x + y * v.y + z * v.z ;

}

appel : vecteur v1(1, 2, 3), v2(4, 5, 6) ; double x = v1.prodscal(v2) ;

Conseil : passer par pointeur les parametres quand on veut les faire modifier par une fonction ; les passer par reference(const) quand la fonction ne doit pas les modifier

Attention : les deux versions de prodscal (par valeur et par reference) ne peuvent exister simultanement avec le memenom, car lors de l’appel, le compilateur ne peut pas decider quelle methode appeler.

Jean-Christophe Engel 20 17 mai 2005

Page 22: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 4. LES REFERENCES 4.1. DEFINITION

4.1.4 resultat de fonction

une fonction peut renvoyer en resultat une reference au meme titre qu’un pointeur (meme contrainte : pas sur unevariable locale)

exemple : somme de 2 vecteurs (resultat par valeur, par pointeur, par reference) ; (fichier p4/c++/ex-somme.cc)

//--------------------------------------------------------

// Somme de 2 vecteurs

//--------------------------------------------------------

// resultat par valeur

vecteur vecteur::somme(const vecteur & v) const

{

vecteur somme(x + v.x, y + v.y, z + v.z);

return somme;

}

//--------------------------------------------------------

// resultat par adresse

vecteur * vecteur::psomme(const vecteur & v) const

{

vecteur * psomme = new vecteur(x + v.x, y + v.y, z + v.z);

return psomme;

}

//--------------------------------------------------------

// resultat par reference

// FAUX car renvoie une reference sur une variable locale

vecteur & vecteur::rsommeFAUX(const vecteur & v) const

{

vecteur & rsomme(x + v.x, y + v.y, z + v.z);

return rsomme;

}

//--------------------------------------------------------

// resultat par reference

// CORRECT, mais a eviter

// il vaut mieux renvoyer un pointeur

vecteur & vecteur::rsomme(const vecteur & v) const

{

vecteur & somme = * new vecteur(x + v.x, y + v.y, z + v.z);

return rsomme;

}

//--------------------------------------------------------

// appels

{

vecteur v4 = v2.somme(v3);

vecteur * pv4 = v2.psomme(v3); delete pv4;

vecteur & rv4 = v2.rsomme(v3); delete & rv4;

}

/*--------------------------------------------------------

NB : pour realiser la surcharge de fonctions,

celles-ci doivent posseder des

parametres en nombres et/ou types differents;

le type du resultat n’intervient pas.

une simple difference de mode de passage (valeur/reference)

ne permet pas non plus de distinguer deux methodes lors de l’appel

--------------------------------------------------------------------*/

Jean-Christophe Engel 21 17 mai 2005

Page 23: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Chapitre 5

constructeur de copie et surchargeoperateurs

5.1 Revue des constructeurs et destructeurs

Rappels facultatifs (il n’est probablement pas indispensable de parler des cas 5. et 6.)

1. objet nomme par une variable automatique : creation chaque fois que la declaration est rencontree, destructionen sortie de portee (sauf avec exit). S’il n’y a pas d’initialisation, appel du constructeur par defaut.

2. creation par new : destruction a la charge du programmeur par delete.

3. objet temporaire cree pour l’evaluation d’un expression et detruit a la fin de l’evaluation.

4. objet attribut (non statique) d’un autre objet : creation et destruction en meme temps que l’objet composite.

5. objet designe par une variable globale : creation au debut du programme et destruction en fin d’execution, ycompris sur sortie par exit (eh oui !).

6. objet designe par une variable locale statique : creation a la premiere rencontre de la declaration et destructionen fin de programme, y compris sur sortie par exit.

5.2 Constructeur de copie

C’est un constructeur appele dans les cas suivants :

– initialisation d’un objet par un objet de la meme classe (dans une declaration, par exemple, ou pour un attribut)exemple : vecteur v1(1, 2, 3), v2(v1) ;

– passage d’un objet par valeur (ou renvoi d’un objet par une fonction)exemple : v1.prodscal(v2), v1.somme(v2)

– gestion des exceptions

Lorsqu’il n’est pas programme, il y a appel d’un constructeur de copie par defaut qui se contente de copier les attributsdes objets (copie de surface, voir schema de gauche). Si ce mecanisme par defaut n’est pas satisfaisant (par exemple,si les objets comportent des attributs qui utilisent de la memoire externe a l’objet), il est necessaire de programmerun constructeur de copie : le parametre est une instance de la classe, obligatoirement passee par reference (si elle estpassee par valeur, il y a appel recursif du constructeur de copie ...)

Le traitement est le suivant (on initialise l’objet courant avec un objet o1) :

1. copier les attributs de o1 dans l’objet courant, sauf les pointeurs de donnees externes

2. creer dynamiquement les donnees externes pour l’objet courant

3. y recopier le contenu des donnees externes de o1

22

Page 24: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 5. CONSTRUCTEUR DE COPIE ET SURCHARGE OPERATEURS5.3. OPERATEUR D’AFFECTATION

elements

taille

sommet

elements

taille

sommet

this

p2

pileNombres p1(25)pileNombres p2(p1);

this

p2

elements

taille

sommet

elements

taille

sommet

signature du cpc pour une classe X donnee : X : :X(const X &)

exemple de la pile : voir p5/{pileNombres.h, pileNombres.cc, testpile.cc}

// constructeur de copie : cree et initialise une pile a partir d’une autre

// appele par pileNombres p2 = p1 ; ou pileNombres p2(p1) ;

pileNombres : :pileNombres(const pileNombres & p1)

{copierPile(p1) ;

}// copier dans l’instance courante le contenu de la pile passee en parametre

void pileNombres : :copierPile(const pileNombres & p1)

{taille = p1.taille ;

sommet = p1.sommet ;

// allouer la "bonne quantite"

elements = new float[taille] ;

// copier dans l’instance courante le contenu de la pile p1

int i ;

for (i = 0 ; i < sommet ; i++) {elements[i] = p1.elements[i] ;

}

}

5.3 Operateur d’affectation

Le meme probleme se pose dans le cas de l’affectation d’un objet a un autre : l’operateur predefini d’affectation secontente d’une affectation de surface (copie des attributs d’un objet a l’autre).

pileNombres p1(25), p2(47);p2 = p1;

elements

taille

sommet

elements

taille

sommet

this

p2

this

p2

elements

taille

sommet

elements

taille

sommet

Le traitement est le suivant (on suppose qu’on effectue o2 = o1) :

1. verifier si l’objet de droite est le meme que celui de gauche (ex : o2 = o2) : dans ce cas, ne rien faire

2. si o2 designe deja une zone de donnees externe, la desallouer au prealable

3. copier les attributs de o1 dans o2, sauf les pointeurs de donnees externes

Jean-Christophe Engel 23 17 mai 2005

Page 25: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 5. CONSTRUCTEUR DE COPIE ET SURCHARGE OPERATEURS5.4. SURCHARGE D’OPERATEURS

4. creer dynamiquement les donnees externes pour l’objet courant

5. y recopier le contenu des donnees externes de o1

les trois dernieres operations sont communes au constructeur de copie et a l’operateur d’affectation : on peut en faireune methode (privee de preference) ...

signature de l’operateur d’affectation :

void X : :operator = (const X &) est une signature incomplete car = est un operateur qui donne une valeur de type X,afin de pouvoir imbriquer les affectations comme dans l’expression : o3 = o2 = o1 ; cette expression s’evalue de droitea gauche et est equivalente a : o2 = o1 ; o3 = o2 ; l’expression o2 = o1, outre son effet de bord qui est de modifier o2, aune valeur qui est celle de o2.

X & X : :operator = (const X &) est la signature correcte

exemple de la pile : voir p5/{pileNombres.h, pileNombres.cc, testpile.cc}

// operateur d’affectation

// appele par l’affectation : p1 = p2

pileNombres & pileNombres : :operator = (const pileNombres & p2) {if (this != & p2) {

// desallouer ce qui etait precedemment alloue a la pile

if (elements != 0) delete[ ] elements ;

copierPile(p2) ;

}return * this ; // pour pouvoir enchaıner les affectations

}

NB : faire remarquer :

– le constructeur/destructeur de tableau (appel du constructeur par defaut pour chaque element, idem pour destruc-teur)

– designation de l’objet courant : pileEntier * this

– l’affectation est un operateur qui doit donner un resultat d’un type precis (ici pileEntier), d’ou l’obligation de renvoyer(par reference) la valeur de l’instance affectee ; ceci permet l’imbrication des affectations dans des expressions

– renvoi d’une reference sur un objet designe par un pointeur : il faut “dereferencer” (indirection) : return * this

5.4 Surcharge d’operateurs

On a vu la surcharge de l’operateur =. On peut aussi surcharger d’autres operateurs. Important : la syntaxe resteinchangee, en particulier les precedences (priorite/associativite) des operateurs. (ex : a = b = c (←), cout << x << y

(→))

(NB : tous les operateur unaires ainsi que l’affectation sont associatifs de droite a gauche ; tous les autres operateurssont associatifs de gauche a droite)

5.4.1 operateurs binaires

un operateur binaire peut etre defini de deux facons :

– comme methode d’une classe : si @ est une methode, obj1 @ obj2 est traduit en obj1.operator @(obj2), donc @ doit etreune methode de la classe de obj1.

– comme fonction independante : si @ est une fonction, obj1 @ obj2 est traduit en operator @(obj1, obj2).

exemples (voir p5/{vecteur.h, vecteur.cc, testvecteur.cc}) :

– somme et produit de 2 vecteurs : methodes de la classe vecteur ;

// Somme de 2 vecteurs (operateur +)

vecteur vecteur : :operator + (const vecteur & v) const {return vecteur(x + v.x, y + v.y, z + v.z) ;

}// vecteur * vecteur (produit scalaire)

double vecteur : :operator * (const vecteur & v) const {return x * v.x + y * v.y + z * v.z ;

}

– on peut aussi les programmer comme fonctions avec deux vecteurs en parametres (par reference) ; noter l’utilisationdes accesseurs.

Jean-Christophe Engel 24 17 mai 2005

Page 26: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 5. CONSTRUCTEUR DE COPIE ET SURCHARGE OPERATEURS5.4. SURCHARGE D’OPERATEURS

// Somme de 2 vecteurs (operateur +) par fonction

vecteur operator + (const vecteur & v1, const vecteur & v2) {return vecteur(v1.getX() + v2.getX(), v1.getY() + v2.getY(), v1.getZ() + v2.getZ()) ;

}

– produit vecteur par reel : il faut penser a la commutativite et programmer :– vecteur * reel : methode de la classe vecteur avec un reel en parametre ou fonction avec un parametre vecteur et un

parametre reel– reel * vecteur : fonction (obligatoirement, car la classe reel n’existe pas) avec un reel et un vecteur en parametres ;

ici, j’ai choisi de la declarer “amie” pour montrer comment ca marche, mais je conseille de se mefier des (faux)amis ...

// vecteur * reel

vecteur vecteur : :operator * (double r) const {return vecteur(x * r, y * r, z * r) ;

}// reel * vecteur avec fonction amie

// declaration dans la classe :

class vecteur {...

friend vecteur operator * (double, const vecteur &) ;

...

} ;

// implementation

vecteur operator * (double r, const vecteur & v) {return vecteur(v.x * r, v.y * r, v.z * r) ;

}

evoquer (pour dire que ca existe et que ca peut etre utile) la surcharge des operateurs : [ ], ( )

5.4.2 operateurs unaires

Meme raisonnement pour les operateurs unaires (++, –, *, &, !, ->)

– operateur @ programme comme methode (a 0 parametre) : @a (ou a@) devient a.operator@(void)

– operateur @ programme comme fonction (a un parametre) : @a (ou a@) devient operator@(a)

Le cas des operateurs ++ et – est particulier : ces operateurs existent en deux variantes (prefixee : ++x et postfixee :x++) ; la definition de la variante prefixee suit la regle ci-dessus, mais pour indiquer au compilateur qu’on definit lavariante postfixee de l’operateur, on declare un parametre bidon supplementaire de type int (qui ne sera pas utilise).

– operateur ++ programme comme methode :– ++a devient a.operator++(void)– a++ devient a.operator++(int) // parametre bidon pour operateur ++ postfixe

– operateur ++ programme comme fonction :– ++a devient operator++(a)– a++ devient operator++(a, int) // parametre bidon pour operateur ++ postfixe

5.4.3 Cas des operateurs d’entree/sortie

Ces operateurs sont definis respectivement dans les classes istream (�) et ostream (�). Deux possibilites :

– deriver une classe de la classe (i/o)stream et y redefinir l’operateur avec en parametre une instance de l’objet alire/ecrire (pas simple du tout).

– programmer une fonction avec en parametre une instance d’(i/o)stream et une instance de l’objet a lire/ecrire (leplus simple et le plus frequent).

Dans les deux cas, ces operateurs renvoient une instance de leur operande gauche (classe (i/o)stream) afin de permettrel’imbrication de leurs appels :

cout � a � b ⇔ cout � a ; cout � b ;cin � a � b ⇔ cin � a ; cin � b ;

Exemple : (voir p5/{vecteur.h, vecteur.cc, testvecteur.cc}) :

Jean-Christophe Engel 25 17 mai 2005

Page 27: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 5. CONSTRUCTEUR DE COPIE ET SURCHARGE OPERATEURS5.4. SURCHARGE D’OPERATEURS

// declaration dans vecteur.h

// surcharge de << et >> avec fonctions (obligatoirement)

std : :ostream & operator << (std : :ostream & , const vecteur &) ;

std : :istream & operator >> (std : :istream & , vecteur &) ;

// programmation dans vecteur.cc

// surcharge de <<

std : :ostream & operator << (std : :ostream & sortie, const vecteur & v) {sortie << "[" << v.getX() << " " << v.getY() << " " << v.getZ() << "]\n" ;return sortie ;

}// surcharge de >>

std : :istream & operator >> (std : :istream & entree, vecteur & v) {double x, y, z ;

entree >> x >> y >> z ;

v.setX(x) ; v.setY(y) ; v.setZ(z) ;

return entree ;

}

Jean-Christophe Engel 26 17 mai 2005

Page 28: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Chapitre 6

Heritage

A retenir de cette partie : l’heritage ne fonctionne bien qu’avec des methodes virtuelles (et en particulier un des-tructeur virtuel).

C’est pourquoi, sauf si on a de bonnes raisons d’empecher le bon fonctionnement de l’heritage, il faut declarer lesmethodes et le destructeur avec le qualificateur virtual. (distribuer le source de l’exemple p6/heritage.{h, cc})

6.1 Exemple et syntaxe

Fig. 6.1 – exemple de hierarchie de classes

Point

afficher

rayonCercle

afficherperimetre

perimetre

Rectanglehauteurlargeur

xxx

Figure

perimetreafficher

x yorigine

// Classe POINT

class Point {public :

Point(double x = 0, double y = 0) ;

virtual ~Point(void) ;

virtual void afficher(void) const ;

private :

double x, y ;

} ;

// Classe FIGURE

class Figure {public :

Figure(Point o) ;

virtual ~Figure(void) ;

virtual double perimetre(void) const ;

virtual void afficher(void) const ;

private :

Point origine ;

} ;

// Classe CERCLE

class Cercle : public Figure {public :

Cercle(Point centre, double r) ;

virtual ~Cercle(void) ;

virtual double perimetre(void) const ;

virtual void afficher (void) const ;

private :

double rayon ;

} ;

// Classe RECTANGLE

class Rectangle : public Figure {public :

Rectangle(Point coin, double h, double l) ;

virtual ~Rectangle(void) ;

virtual double perimetre(void) const ;

private :

double hauteur, largeur ;

} ;

27

Page 29: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 6. HERITAGE 6.2. CONSTRUCTEURS D’UNE CLASSE DERIVEE

6.2 Constructeurs d’une classe derivee

– Les constructeurs ont le nom de la classe => pas d’heritage des constructeurs.– Pour initialiser les attributs herites, il faut appeler un constructeur de la classe de base :

– l’appel peut etre implicite, s’il existe un constructeur sans parametre dans la classe de base ;– si tous les constructeurs de la classe de base ont des parametres, il faut appeler explicitement l’un d’eux ; cet appel

se fait obligatoirement dans la partie initialisations declaratives et non dans le corps : cas des constructeurs desclasses Rectangle et Cercle

– initialisation des attributs herites de la classe de base

Cercle::Cercle(Point centre, double r) : Figure(centre), rayon(r) {}Rectangle::Rectangle(Point coin, double h, double l) : Figure(coin), hauteur(h), largeur(l) {}

dans les deux cas, l’appel explicite du constructeur de la classe Figure est obligatoire, car il n’y a pas deconstructeur sans parametre dans cette classe.

– initialisation d’un attribut qui est un objet (constructeur de la classe Figure)

Figure::Figure(Point o) : origine(o) {}

– exemple de creation par declaration

Cercle c(Point(10, 10), 10) ;

Rectangle r(Point(50, 50), 20, 20) ;

– exemple de creation par allocation

Cercle * pc = new Cercle(Point(10, 10), 10) ;

Rectangle * pr = new Rectangle(Point(50, 50), 20, 20) ;

6.3 Methodes d’une classe derivee

– exemple de methode heritee : afficher dans la classe Rectangle est heritee de la classe Figure.

void Figure::afficher(void) const {cout << "Figure : origine = " ;

origine.afficher() ;

cout << "perimetre = " << perimetre() << endl ;

}

Rectangle r(Point(50, 50), 20, 20) ;

r.afficher() ;

methodes appelees :Figure::afficher : par heritage,. Point::afficher : par l’appel de origine.afficher(),. Rectangle::perimetre : car r est un Rectangle

resultat :Figure : origine = (50, 50) perimetre = 80

– Une methode d’une classe derivee peu surcharger une methode de la classe mere (meme nom mais signature pa-rametrique differente) : l’appel de la methode adequate depend des parametres effectifs.

– On peut egalement redefinir une methode (meme nom, memes parametres, corps different). Cette nouvelle definitionmasque la premiere. Le type de l’objet sur lequel est applique la methode determine la methode appelee (voir exemplede la methode afficher dans la classe Cercle). Pour acceder a la methode de la classe de base dans la classe derivee, ilfaut utiliser le nom complet (classe::methode).

void Cercle::afficher(void) const {Figure::afficher() ;

cout << "Cercle : rayon = " << rayon << endl ;

}

Cercle c(Point(10, 10), 10) ;c.afficher() ;

methodes appelees :Cercle::afficher : car c est un Cercle

. Figure::afficher : appel explicite

. . Point::afficher : appel explicite deorigine.afficher(),. . Cercle::perimetre : car c est un Cercle

resultat :Figure : origine = (10, 10) perimetre = 62.8Cercle : rayon = 10

Jean-Christophe Engel 28 17 mai 2005

Page 30: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 6. HERITAGE 6.4. MELANGE DE CLASSES

6.4 Melange de classes

Quand on veut manipuler des objets appartenant a une hierarchie de classes correspondant a divers niveaux d’abs-traction qu’on ne connait pas forcement tous d’avance ou dont on ne connait pas a la compilation le type precis (casd’une collection), on doit manipuler les instances avec des identificateurs, pointeurs ou references du type “classe debase” :

– par exemple dans une liste dont les elements sont des Cercle, Rectangle, etc... (classes derivees de la classe Figure),les elements de la liste sont designes par un attribut de type Figure (ou reference ou pointeur de Figure), mais lesmethodes devront appliquees sur les instances effectivement presentes dans la collection et pas systematiquementsur des instances de Figure (exemple : afficher un element de liste peut tantot afficher un Rectangle tantot un Cercle).

Le mecanisme qui permet l’appel de la methode de la classe de l’instance effective (et non celle de la classe del’identificateur, du pointeur ou de la reference) s’appelle liaison dynamique.

regle de compatibilite des classes : une instance d’une classe derivee doit pouvoir etre utilisee a tout endroit ouune instance de sa classe de base publique peut etre utilisee. De meme un pointeur (resp. une reference) surune instance d’une classe derivee peut etre utilise a tout endroit ou un pointeur (resp. une reference) sur uneinstance de sa classe de base publique peut etre utilise.

Cette regle est valable si les instances sont designees par pointeur ou reference, mais si les instances sont designees paridentificateur, on rencontre des problemes de troncature qui empechent le fonctionnement de la liaison dynamique.

6.4.1 Probleme de troncature (slicing)

Il y a troncature lors des operations suivantes (voir exemples de p6/mainher v1.cc) :

– initialisation classe de base par classe derivee :

Cercle c(Point(10, 10), 10) ;

Figure f(c) ;

f.afficher() ;

appel : Figure::afficherresultat : Figure : origine = (10, 10) perimetre = -77.77

– affectation classe de base par classe derivee :

Rectangle r(Point(50, 50), 20, 20) ;

f = r ;

f.afficher() ;

appel : Figure::afficherresultat : Figure : origine = (50, 50) perimetre = -77.77

– parametre par valeur : initialisation du parametre formel (classe de base) par parametre effectif (classe derivee)

void afficher(Figure f) { f.afficher() ; }Cercle c(Point(10, 10), 10) ;

afficher(c) ;

appel : Figure::afficherresultat : Figure : origine = (50, 50) perimetre = -77.77

Dans tous les cas, la troncature a pour effet de “supprimer” les informations specifiques des classes derivees pour neconserver que les informations de la classe de base et en particulier empeche le fonctionnement de la liaison dynamique(je sais, je me repete, mais c’est important)

Pour eviter cette perte on designe les instances par pointeur ou reference declare(e) comme pointeur ou referencevers la classe de base.

En particulier, pour gerer une collection d’objets appartenant a une hierarchie de classes, les instances devront etredesignees par pointeur ou reference sur la classe de base.

– exemple :

Cercle c(Point(10, 10), 10) ;

Figure * pf(& c) ;

pf->afficher() ;

methodes appelees :. Cercle::afficher : liaison dynamique. . Figure::afficher : appel explicite. . . Point::afficher : appel explicite. . . Cercle::perimetre : car liaison dynamiqueresultat :Figure : origine = (10, 10) perimetre = 62.8Cercle : rayon = 10

Jean-Christophe Engel 29 17 mai 2005

Page 31: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 6. HERITAGE 6.5. LE SCHEMA DE CONCEPTION “PATRON DE METHODE”

Meme chose avec une reference.

6.4.2 Methodes virtuelles et liaison dynamique

Attention : Le mecanisme de liaison par defaut de C++ est statique, c’est-a-dire que le choix de la methode aappeler sur une instance est fait statiquement par le compilateur en fonction du type declare de l’instance(que l’instance soit designee par identificateur, pointeur ou reference) ; ceci ne permet donc pas de tirer partipleinement de l’heritage.Si on veut avoir la possibilite de deriver des classes, il faut declarer virtuelles les methodes qui doivent beneficierde la liaison dynamique ; en particulier, il est hautement recommande de definir (meme avec un corps vide) ledestructeur et le declarer virtuel. Je rappelle ce que je disais au debut : sauf si on a de bonnes raisons d’empecherle bon fonctionnement de l’heritage, il faut declarer les methodes et le destructeur avec le qualificateur virtual.

Attention : toute methode virtuelle (y compris le destructeur)

– doit etre definie dans la classe de base (sauf si pure, voir plus loin)– peut etre redefinie dans les classes derivees

6.4.3 Destructeur

Attention : S’il existe des methodes virtuelles, le destructeur (facultatif, mais hautement recommande) doit etredeclare virtual et doit etre programme dans la classe de base, meme s’il est vide et meme dans le cas de classe abstraite.L’appel du destructeur d’une classe derivee entraıne dans un deuxieme temps celui de la classe de base. C’est important(vital ! !), car les classes derivees peuvent ajouter des attributs qui font reference a des donnees externes qui devrontetre desallouees proprement.

6.4.4 Classe abstraite et methode virtuelle pure

Dans l’exemple presente ci-dessus, definir la methode Figure::perimetre n’a pas vraiment de sens et d’ailleurs creerune instance de la classe Figure n’a pas non plus reellement d’interet : la classe Figure sert plutot a definir unensemble minimal de proprietes (attributs et methodes) dont on souhaite munir differentes sortes de figures concretes(cercles, rectangles, etc...). Cette classe doit donc demeurer une classe abstraite, dont on creera par heritage des classesconcretes.

mecanisme de definition de classe abstraite

– Une methode virtuelle est dite pure quand elle est initialisee a 0 dans la declaration de la classe (c’est-a-dire qu’onne definit pas son implementation).exemple dans la declaration de la classe Figure : virtual double perimetre (void) const = 0 ;

– Une classe qui comporte (au moins) une methode virtuelle pure est dite abstraite et il n’est pas possible d’instancierd’objet de cette classe : elle ne peut que servir de classe de base pour d’autres classes. L’interet est de fournir uneinterface en reportant et masquant les details d’implementation dans les classes derivees.

– Une methode virtuelle pure qui n’est pas definie dans une classe derivee reste pure, donc la classe derivee reste aussiabstraite : ceci permet de construire une implementation par etapes.

Si l’on decide que la classe Figure est une classe abstraite, les problemes de troncature presentes en 6.4.1 ne se posentplus, car il n’est pas possible de declarer d’instance de Figure, ni de parametre de type Figure ; les instances serontobligatoirement designees par pointeur ou reference.

6.5 Le schema de conception “patron de methode”

Il consiste a definir la structure d’un algorithme dans une classe de base (eventuellement abstraite), a l’aide d’operationsdont certaines peuvent etre abstraites et qui devront etre implementees par les classe concretes derivees.

Exemple : On peut vouloir que l’operation d’affichage d’une Figure respecte obligatoirement la structure suivante :

– affichage des attributs de la classe Figure (Point origine)– affichage eventuel des attributs propres des classes derivees– affichage obligatoire du perimetre

Jean-Christophe Engel 30 17 mai 2005

Page 32: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 6. HERITAGE 6.5. LE SCHEMA DE CONCEPTION “PATRON DE METHODE”

On peut realiser ceci en definissant la methode Figure::afficher ainsi :

class Figure {

public :

// afficher les informations de la classe de base

void afficher(void) const ; // non virtuel

// calcul du perimetre (obligatoire)

virtual double perimetre(void) const = 0 ;

protected :

// afficher les informations propres de chaque classe derivee (facultatif)

virtual void afficherPropre(void) const ;

} ;

Figure::afficher(void) const {// afficher le point origine (obligatoire)

origine.afficher() ;

// faire afficher le perimetre obligatoirement par les classe derivees

cout << ‘‘perimetre = ‘‘ << perimetre() ;

// faire afficher les informations propres de chaque classe derivee (facultatif)

afficherPropre() ;

}

// implementation par defaut pour les classe derivees

Figure::afficherPropre(void) const { }

Ainsi, les classes derivees n’ont ni besoin, ni interet de redefinir la methode afficher, mais peuvent (re)definir la methodeafficherPropre et doivent definir la methode perimetre.

NB : si une classe derivee redefinit la methode afficher, celle-ci ne pourra etre appelee que si les instances de cette classesont designees par identificateur ; des qu’on veut utiliser le polymorphisme (et faire jouer la liaison dynamique),on doit designer les instance par un pointeur (ou une reference) vers la classe Figure : comme la methode afficher

ne beneficie pas de la liaison dynamique (absence de virtual), c’est la methode Figure::afficher qui sera toujoursappelee, ce qui nous garantit l’utilisation de l’algorithme souhaite.

NB2 : pour eviter qu’un client puisse appeler directement afficherPropre, on peut lui donner l’accessibilite protected.

Jean-Christophe Engel 31 17 mai 2005

Page 33: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Chapitre 7

Programmation generique : les modeles

programmation generique : c’est la possibilite de parametrer une fonction ou une classe par un type ou une autreclasse. (Exemple : pile de <int, float, rationnel, etc ...>)

Interet : ne pas programmer n variantes d’une fonction ou d’une classe dont seul le type des parametres change.

7.1 fonction generique

Fonction dont les parametres sont d’un type T quelconque (type integre ou classe definie par le programmeur) :

– definition

template <class T>

T plusgrand(T x, T y) {if (x > y) { return x ; } else { return y ; }

}

template <class T> indique :– que T est un type (ou une classe) parametre de la fonc-

tion– que plusgrand est un modele de fonction, dont il faudra

creer des exemplaires concrets(on dit : instancier un modele)

– utilisation

int i(3), j(5), m = plusgrand(i, j) ;

cout << "le plus grand de " << i << " et " << j << " = " << m << endl ;

// le plus grand de 3 et 5 = 5

Rationnel r1(1, 2), r2(3, 4), rm(plusgrand(r1, r2)) ;

cout << "le plus grand de " << r1 << " et " << r2 << " = " << rm << endl ;

// le plus grand de 1/2 et 3/4 = 3/4

A la rencontre des appels ci-dessus (plusgrand(i, j) et plusgrand(r1, r2)), le compilateur instancie deux exemplairesde la fonction plusgrand :

– plusgrand<int>

– plusgrand<Rationnel>

On peut specifier le nom complet de la fonction : plusgrand<int>(i, j) ou plusgrand<Rationnel>(r1, r2) ou fairecomme dans l’exemple : dans ce cas, le type des parametres d’appel (i,, j, r1, r2) permet au compilateur de determinerquelle instance du modele generer. En cas d’ambiguite (un parametre int et l’autre double, par exemple), il fautspecifier :

plusgrand<double>(i, x).

contraintes sur le parametre de type

Il n’est pas possible de specifier explicitement des contraintes sur les parametre de type ; ce sont les operations qu’onva effectuer dans le corps de la fonction sur les valeurs de type T qui vont definir implicitement des contraintes sur letype T

Dans l’exemple, la seule operation appliquee sur les valeurs du type T est la comparaison (operateur <) : il faut doncque cet operateur soit defini sur le type T.

32

Page 34: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 7. PROGRAMMATION GENERIQUE : LES MODELES 7.2. CLASSE GENERIQUE

specialisation : cas des booleens

Dans certains cas, la fonction generique soit n’est pas correcte, soit peut faire l’objet d’un algorithme specifique ; parexemple, on peut vouloir definir l’ordre suivant sur le type booleen : faux < vrai.

On va donc definir une version specialisee de la fonction plusgrand, pour le cas ou T est le type booleen :

template <>

bool plusgrand<bool>(bool x, bool y) {if (x) { return x ; } else { return y ; }// ou bien : return x || y ;

}

la syntaxe template <> signifie que le modele ne prendplus de parametre et T est remplace par le type effectifpour lequel est definie la fonction (ici bool).

7.2 classe generique

Exemple : pile de <int, float, rationnel, etc ...>

interface voir p7/pile.h

// -*- c++ -*-

#ifndef PILEGENERIQUE

#define PILEGENERIQUE

// Classe pour gerer une pile d’elements de type T quelconque

// $Id : pile.h,v 1.1 2003/05/19 08 :43 :42 engel Exp engel $

#include <iostream>

template <class T>

class Pile {public :

// constructeurs et destructeurs

Pile (int n = tailledefaut) ; // constructeur par defaut

Pile (const Pile & p) ; // constructeur de copie

~Pile(void) ; // destructeur

// methodes

bool estPleine(void) const ;

bool estVide (void) const ;

void empiler (const T & e) ; // empiler e sur la pile

T getSommet(void) const ; // renvoyer le sommet

void depiler (void) ; // depiler le sommet

void afficher (std : :ostream &) const ; // afficher la pile

// operateurs

Pile & operator = (const Pile<T> & p) ;

private :

// attributs et methodes prives

void copierPile(const Pile<T> & p) ; // copie les elts de p sur la pile courante

T * elements ; // pointeur sur le tableau des elements

int taille ; // nombre max d’elements

int nbelt ; // nombre courant d’elements

static const int tailledefaut = 20 ; // taille max de la pile par defaut

} ;// operateur d’affichage <<

template <class T>

std : :ostream & operator << (std : :ostream & sortie, const Pile<T> & p) ;

#endif

Le nom du modele de classe (ici Pile) suivi d’un type entre <> represente le nom d’une classe (par exemple : Pile<int>,Pile<double>, Pile<Rationnel>).

Dans la portee definie par Pile<T>, le nom de type Pile est synonyme de Pile<T>.

Jean-Christophe Engel 33 17 mai 2005

Page 35: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 7. PROGRAMMATION GENERIQUE : LES MODELES 7.2. CLASSE GENERIQUE

implementation voir p7/pile.cc

#include "pile.h"

// constructeur

template <class T> Pile<T> : :Pile(void) :

elements(new T[n]), // constructeur sans parametre de la classe T

taille(n),

nbelt(0)

{}

// empiler un element (type T) dans une pile NON PLEINE

template <class T> void Pile<T> : :empiler(const T & elt) {elements[nbelt] = elt ; // operateur = de la classe T++nbelt ;

}

// operateur d’affichage

template <class T> ostream & operator << (ostream & sortie, const Pile<T> & p) {p.afficher(sortie) ;

return sortie ;

}

// afficher le contenu de la pile sans la modifier ...

template <class T> void Pile<T> : :afficher(ostream & sortie) const

{sortie << "<<" ;

for (int i = nbelt - 1 ; i >= 0 ; --i) {sortie << elements[i] << " " ; // operateur << de la classe T

}sortie << ">>" ;

}

utilisation voir p7/testpile.cc

#include "pile.cc" // ben oui, je sais c’est pas classe

// fonction generique

template <class T> void initialiser(Pile<T> & p)

{T v ;

while ( ! p.estPleine()) {cin >> v ; // operateur >> de la classe Tp.empiler(v) ;

}}

// Creer une pile de 10 reels

Pile<float> pf(10) ;

// et l’initialiser

initialiser(pf) ;

cout << "Pile de reels : " << pf << endl ;

Pile de reels : <<3.14 4.93 6.72 8.51 10.3 12.09 13.88 15.67 17.46 19.25 >>

// Creer une pile de 15 rationnels

Pile<Rationnel> pr(15) ;

// et l’initialiser

initialiser(pr) ;

cout << "Pile de Rationnels : " << pr << endl ;

Pile de Rationnels : <<-13/4 -13/4 9/2 0 14/5 -5 -34/67 999 120 -34/67 88/23 5/4 13/5 -4/7 4 >>

Pour chaque declaration differente de pile (Pile<float>, Pile<Rationnel>), le compilateur cree un nouveau type (unenouvelle classe) de pile ; il a donc besoin de (re)compiler sa definition, d’ou la necessite d’inclure l’implementationpile.cc (et pas uniquement pile.h)

Jean-Christophe Engel 34 17 mai 2005

Page 36: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 7. PROGRAMMATION GENERIQUE : LES MODELES 7.2. CLASSE GENERIQUE

contraintes sur le parametre de type

– constructeur : new T[n] ⇒ constructeur sans parametre dans la classe T– empiler : elements[nbelt] = elt ;⇒ operateur = dans la classe T– afficher : sortie << elements[i]⇒ operateur << dans la classe T– getSommet : return elements[nbelt - 1] ; renvoi de valeur ⇒ constructeur de copie dans la classe T– initialiser : cin >> v ;⇒ operateur >> dans la classe T

Jean-Christophe Engel 35 17 mai 2005

Page 37: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Chapitre 8

la bibliotheque standard de conteneurs

8.1 Presentation

bibliotheque de structures de donnees generiques et d’algorithmes classiques.

conteneur

– structure de donnees (collection) de taille variable qui contient des objets. Un conteneur est parametre par la classedes objets qu’il contient (template).

– chaque type de conteneur a des avantages et des inconvenients (ajout, retrait, recherche) : la variete de conteneurspermet au programmeur de choisir les outils adaptes a son probleme.– exemples : vecteur, liste, file, tableaux associatifs, ensemble, multi-ensemble– operations : ajout, suppression, parcours, recherche, ...

iterateur

objet qui permet d’acceder aux elements d’un conteneur donne ; possede une interface commune a tous les conteneurset proche de celle des pointeurs (*, ++, ...)

algorithmes

servent a traiter les elements d’un conteneur : recherche, tri, fusion, modification ; independants des conteneurs, liaisonfaite par iterateur

8.2 conteneur

deux categories :

multimapmap

list

deque

vector multisetset

1. conteneur sequentiel : les elements sont places de facon sequentielle dans le conteneur ; la position des elementsne depend pas de leur valeur ; la recherche est en general sequentielle. (vector, deque, list).

36

Page 38: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 8. LA BIBLIOTHEQUE STANDARD DE CONTENEURS 8.2. CONTENEUR

2. conteneur associatif : collection triee d’elements : la position de chaque element depend d’un critere de tri et nonde l’ordre d’insertion. (set, multiset, map, multimap)

NB : il est possible de trier un conteneur sequentiel mais un conteneur associatif offre de meilleures performances enrecherche

8.2.1 conteneur sequentiel

tableau (vector)

– gere ses elements dans un tableau dynamique ; semantique proche de celle des tableaux :– acces direct aux elements par operateur [ ] et indice– ajout/retrait en fin tres rapide(push back, pop back)– ajout/retrait ailleurs oblige a deplacer les elements qui suivent : lent

exemple

#include <vector>

using namespace std ;

...

vector<Rationnel> vecteurRat ;for (int i = 1 ; i <= 5 ; ++i) {

vecteurRat.push back(Rationnel(i, 7)) ; // ajout en fin du conteneur

}for (int i = 0 ; i < vecteurRat.size() ; ++i) { // afficher les elements

cout << vecteurRat[i] << ’ ’ ;

}cout << endl ;

liste (list)

– liste doublement chaınee d’elements– pas d’acces direct aux elements (donc pas d’operateur [ ]) :

– acces au ieme element requiert un parcours sequentiel des i-1 premiers (long)– passage d’un element au suivant ou au precedent en temps constant

– ajout/retrait en toute position tres rapide (insert, remove)

exemple :

#include <list>

using namespace std ;

list<Rationnel> listeRat;for (int i = 1 ; i <= 5 ; ++i) {

listeRat.push back(Rationnel(i, 7)) ; // ajout en fin du conteneur

}while ( ! listeRat.empty()) {

cout << listeRat.front() << ’ ’ ; // afficher l’element de tete

listeRat.pop front() ; // et l’oter du conteneur

}

file a deux tetes (deque)

– tableau dynamique qui peut grandir aux deux extremites– acces direct aux elements par operateur [ ] et indice– ajout/retrait en debut et en fin tres rapide(push front, push back, pop xxx)– ajout/retrait ailleurs oblige a deplacer les elements : lent

exemple :

Jean-Christophe Engel 37 17 mai 2005

Page 39: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 8. LA BIBLIOTHEQUE STANDARD DE CONTENEURS 8.3. ITERATEUR

#include <deque>

using namespace std ;

...

deque<Rationnel> dequeRat ;for (int i = 1 ; i <= 5 ; ++i) {

dequeRat.push front(Rationnel(i, 7)) ; // ajout en debut du conteneur

}for (int i = 0 ; i < dequeRat.size() ; ++i) { // afficher les elements

cout << dequeRat[i] << ’ ’ ;

} cout << endl ;

8.2.2 conteneur associatif

– un conteneur associatif trie automatiquement ses elements selon un critere d’ordre : fonction qui compare soit lavaleur de deux elements soit celle de cles.

– par defaut, la fonction de comparaison est l’operateur <.– acces aux elements requiert des iterateurs– implementation : arbre binaire trie

– temps d’acces proportionnel a la hauteur de l’arbre (log2(n))– techniques tres efficaces pour equilibrer des arbres

– ensemble (set) : collection dont les elements sont tries selon leur valeur ; chaque element n’apparait qu’une fois.– multi-ensemble (multiset) : idem, mais occurrences multiples autorisees (1, 1, 3, 5, 5, 7, 10)– tableau associatif (map) : contient des paires <cle, valeur> ; la cle sert de critere de tri et doit etre unique (<”jacques”,

10>, <”paul”, 5> ...)– dictionnaire (multimap) : idem mais les cles multiples sont autorisees

8.3 iterateur

C’est un objet qui designe la position d’un element dans un conteneur (qui est donc parametre par le conteneur auquelil permet d’acceder) et qui permet de manipuler l’element designe (en consultation et/ou en modification), ainsi que deparcourir les elements d’un conteneur, a l’aide d’une interface standardisee. On peut le voir comme une generalisationdes pointeurs : ils ont une semantique similaire

– operations fondamentales

* : donne la valeur de l’element courant (celui designe par l’iterateur)

→ : acces aux attributs et methodes de l’element courant, s’il en a

++ : fait progresser l’iterateur a l’element suivant (quel que soit le type du conteneur auquel est associe l’iterateur) ;la plupart des conteneurs permettent aussi –.

== != : comparaisons de deux iterateurs

= : affectation de deux iterateurs

– Quelques methodes de base communes aux conteneurs :– begin() : iterateur qui designe le premier element d’un conteneur– end() : iterateur situe apres la fin d’un conteneur

exemple :

#include <list>

using namespace std ;

typedef set<Rationnel> ensRat ; // ne pas hesiter a definir des types synonymes

ensRat E ;

ensRat : :const iterator iratio ; // permet de designer des elements dans un ensemble de rationnels

ensRat : :const iterator ifin = E.end() ; // iterateur de fin

iratio = E.begin() ;while (iratio != ifin) {

cout << * iratio << " " ;

++iratio ;}cout << endl ;

– chaque conteneur definit (au moins) deux types d’iterateurs :– conteneur : :iterator : acces aux elements en consultation/modification– conteneur : :const iterator : acces aux elements en consultation seule

Jean-Christophe Engel 38 17 mai 2005

Page 40: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 8. LA BIBLIOTHEQUE STANDARD DE CONTENEURS 8.4. ALGORITHMES

8.4 Algorithmes

la STL dispose de nombreux algorithmes standards :

– algorithmes qui ne modifient ni les conteneurs ni leurs elements :– comptage, min, max, recherche...

– algorithmes qui modifient les elements :– remplacement, suppression

– algorithmes qui modifient l’ordre des elements sans modifier leur valeur :– permutation (circulaire ou non), tri, fusion, copie

– algorithmes numeriques– combinaison des elts par differents operateurs arithmetiques (+, *, - ...)

Ces algorithmes ne sont pas des methodes de chaque classe de conteneurs, mais des fonctions independantes desconteneurs auxquels ils accedent par l’intermediaire des iterateurs.

avantage : implementation unique d’un algorithme, independance par rapport au conteneur, genericite de la pro-grammation

inconvenient : ne tirent pas parti des caracteristiques propres de chaque conteneur : certains algorithmes sont doublesde methodes qui operent plus efficacement

exemple :

Trier les elements d’un vecteur (de rationnels) compris entre le debut et la fin

sort(vrat.begin(), vrat.end()) ;

Exemple “complet”

voir p8/stlrat.cc et commenter selon temps disponible

notamment :

– declaration des types de conteneurs utilises :

typedef vector<Rationnel> tableauRationnel ;

typedef list<Rationnel> listeRationnel ;

typedef multiset<Rationnel> ensembleRationnel ;

– declaration d’un conteneur avc n elements initialises a une valeur donnee :

tableauRationnel trat(5, Rationnel(1, 3)) ;

– ajout en fin avec la methode push back : trat.push back(rat) ;

– acces aux elements d’un vecteur avec la notation indicee :

for (unsigned int i = 0 ; i < trat.size() ; ++i) { cout << trat[i] << " " ; }

– copie des elements dans un multi-ensemble avec un iterateur simple et parcours standard– declaration d’un iterateur de tableau : tableauRationnel : :iterator itab ;

ensembleRationnel ensrat ;

itab = trat.begin() ;

while (itab != trat.end()) { ensrat.insert(* itab) ; ++itab ; }

– affichage du contenu d’un conteneur avec l’algorithme copy et l’iterateur de sortie ostream iterator

copy(ensrat.begin(), ensrat.end(),ostream iterator<Rationnel>(cout, " ")) ;

– copie des elements (en ordre inverse) dans une liste avec l’algorithme copy et l’iterateur d’insertion front inserter

listeRationnel lrat ; // liste vide

copy(ensrat.begin(), ensrat.end(), front inserter(lrat)) ;

– declaration d’un iterateur de liste : listeRationnel : :iterator iliste ;

– algorithme de recherche find

iliste = find(lrat.begin(), lrat.end(), rat) ;

– suppression d’un element (methode erase) : lrat.erase(iliste) ;

– insertion d’un element (methode insert) : lrat.insert(iliste, rat) ;

c’est tout pour aujourd’hui

Jean-Christophe Engel 39 17 mai 2005

Page 41: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

Chapitre 9

Exceptions

but : traiter les cas exceptionnels sans alourdir les cas normaux

9.1 declencher une exception

throw e ;

e est une expression de type quelconque :

– type predefini : int(23), string("Division par zero ! !")

– type defini par l’utilisateur : class Erreur { ... } ; ... throw new Erreur(...) ;

– type derive d’un type standard : class Erreur : public domain error { ... } ; ... throw new Erreur(...) ;

9.2 traiter une exception

try {instructions qui peuvent declencher une exception

}catch (typeException1 e1) {

traitement de e1

}catch (typeException2 e2) {

traitement de e2

}...

catch (typeExceptionn en)

{traitement de en

}

9.3 Exemples

voir les fichiers p9/{erreur.h, erreur-std.h, rationnel.cc, exception.cc}

9.3.1 avec un type predefini

Rationnel : :Rationnel(int num, int den) :

numerateur(num), denominateur(den) {if (denominateur == 0) {

throw string("Denominateur nul ! !") ;

}normaliser() ;

}

Rationnel nb, inverse ;

try {inverse = nb.inverse() ;

// traitement normal de inverse

...

}catch (string & msg) {

cerr << msg << endl ;

}

40

Page 42: Cours PRC - perso.univ-rennes1.fr+/prc.pdf · 6.4 M elange de classes ... un programme est une collection de fonctions et proc edures r epartis dans plusieurs chiers ... attribut

CHAPITRE 9. EXCEPTIONS 9.3. EXEMPLES

le mecanisme est simple a mettre en oeuvre, mais ne permet pas de distinguer les exceptions provoquees par differentescauses : on ne peut donc leur appliquer un traitement differencie : a reserver pour des petits programmes de test.

9.3.2 avec une classe definie par l’utilisateur

class Erreur {public :

Erreur(const char * msg) : cause(msg) { } ;const string & getCause(void) { return cause ; } ;

private :

string cause ;

} ;

Rationnel : :Rationnel(int num, int den) :

numerateur(num), denominateur(den) {if (denominateur == 0) {

throw Erreur("Denominateur nul ! !") ;

}normaliser() ;

}

try {inverse = nb.inverse() ;

// traitement normal de inverse

...

}catch (Erreur & e) {

cerr << e.getCause() << endl ;

}

avantage sur la solution precedente : meilleure classification des differentes erreurs gerees par exception.

9.3.3 solution avec classe derivee d’un type standard

#include <stdexcept>

class Erreur : public domain error {public :

Erreur(const char * msg) : domain error(msg) { } ;} ;

Rationnel : :Rationnel(int num, int den) :

numerateur(num), denominateur(den) {if (denominateur == 0) {

throw Erreur("Denominateur nul ! !") ;

}normaliser() ;

}

try {inverse = nb.inverse() ;

// traitement normal de inverse

...

}catch (Erreur & e) {

cerr << e.what() << endl ;

}

avantage : utilise une classification normalisee et predefinie des erreurs ; rien n’empeche, comme sur l’exemple, de creersa propre classe d’erreur judicieusement placee dans la hierarchie standard.

Jean-Christophe Engel 41 17 mai 2005