Vous êtes sur la page 1sur 160

Table des Matires

Notes au lecteur LES BASES DU C++ 1) Les objets et les classes Notions de base L'ide Mise en uvre C + + ' L'intrt par rapport au C Rsum Pour bien dmarrer en C + + Complments Fonctions inline Le mot-cl this lments static dans une classe Dclaration de classe anticipe 2) Les c o n s t r u c t e u r s et les destructeurs Les notions de base L'ide Mise en uvre C + + constructeurs Mise en uvre C + + destructeurs Complments Constructeur copie Constructeurs de conversion 3) L'hritage simple Notions de base L'ide Mise en uvre C + + Redfinition d'une fonction-membre Conversion de Drive* vers Base* Rsum

13 13 13 14 19 20 20 21 21 23 24 25 27 27 27 28 30 32 32 36 37 37 37 38 41 42 43

Pont entre C et C + +

L'intrt par rapport au C Complments Hritage et constructeurs Hritage et destructeurs Exemple complet Listes d'initialisations 4) La surcharge Notions de base L'ide Mise en uvre C + + L'intrt par rapport au C Complments Surcharge d'oprateurs Surcharge de l'oprateur = Oprateurs de conversion de types 5) Les petits + du C++ Les commentaires Ruse de sioux Paramtres par dfaut Les constantes Dclarations RSUM Encapsulation Constructeurs & destructeurs Hritage Surcharge ENCORE PLUS DE C++ 6) La fin du malloc : new et delete Notions de base L'ide Mise en uvre C + + L'intrt par rapport au C 7) La f i n du printf : cout, cin et cerr Notions de base L'ide Mise en uvre C + + L'intrt par rapport au C Complments Surcharger et

43 44 44 44 45 47 51 51 51 52 53 54 54 57 61 63 63 64 65 67 73 75 77 77 78 79

83 83 83 84 86 89 90 90 90 91 93 93

Table des matires

Formater les sorties Tableau rcapitulatif 8) Le p o l y m o r p h i s m e et la virtualit Notions de base L'ide Mise en uvre C + + Rsum L'intrt par rapport au C Complments Destructeurs virtuels Classes abstraites et fonctions virtuelles pures 9) Les rfrences Notions de base L'ide Mise en uvre C + + L'intrt par rapport au C Complments Rfrences et objets provisoires Fonction retournant une rfrence 10) Les templates ou la mise en uvre de la gnricit Notions de base L'ide Mise en uvre C + + Templates de fonctions Templates de classes Complments Attention l'ambigut 11) Les classes et f o n c t i o n s amies Notions de base L'ide Mise en uvre C + + Attention ! 12) L'hritage m u l t i p l e Notions de base L'ide Mise en uvre C + + L'intrt par rapport au C Complments Duplication de donnes d'une classe de base Masquage du polymorphisme

95 100 102 102 102 104 105 105 107 107 108 114 114 114 115 115 116 116 117 118 119 119 119 119 121 124 124 125 126 126 126 127 130 130 130 131 133 134 134 136

Pont entre C et C++

Hritage virtuel 13) Les e x c e p t i o n s Notions de base L'ide Mise en uvre C + + L'intrt par rapport au C Rsum Exemple complet Complments Spcifier des exceptions Exception non intercepte Exceptions en cascade 14) La c o m p i l a t i o n spare Notions de base L'ide Mise en uvre Quoi mettre, et dans quels fichiers ? Comment lancer la compilation ? Complments Comment viter les redclarations ? Static, inline et porte Travailler avec d'autres langages GUIDE DE SURVIE 15) Conseils Les tapes d'un projet Conception Trouver les classes Architecturer les classes Dtailler les classes valuer l'ensemble Codage C + + Quels outils ? Mise en uvre de la rutilisabilit Implantation des classes 16) Questions-Rponses Index

138 140 140 140 141 145 145 145 147 147 148 150 153 153 153 154 155 159 161 161 162 164

169 169 170 170 171 173 174 174 174 175 177 179 185

16

Pont entre C et C++

d o n n e s d ' u n objet, donnes-membres. P a r opposition, les autres d o n n e s et fonctions sont qualifies de hors-classe. R e p r e n o n s notre e x e m p l e . Voici c o m m e n t n o u s aurions p u dfinir la classe L i v r e :
class Livre { private : char char char public : void void void
} ;

titre[20]; auteur[20]; diteur[20];

N o t e z la prsence du point-virgule aprs l'accolade fermante.

Saisir(); Afficher(); Imprimer();

Le C + + n'aime pas forcer la main des programmeurs. Il est possible de dfinir des donnes public, c'est-dire accessibles directement de l'extrieur de l'objet C'est une pratique dconseiller, car elle viole l'ide m m e d'encapsulation.

C o m m e n t dfinir une f o n c t i o n - m e m b r e C o m m e v o u s p o u v e z le constater, n o u s n ' a v o n s fait q u e dclarer les fonctions S a i s i r , A f f i c h e r e t I m p r i m e r . I l faut m a i n t e n a n t les dfinir c o m p l t e m e n t . La s y n t a x e est identiq u e au l a n g a g e C, ceci prs : il faut indiquer au compilateur quelle classe est rattache u n e fonction. Le format de dfinition d'une fonction-membre (c'est--dire inclue dans u n e classe) est le suivant :
type_retourn NomDeVotreClasse : : f o n c t i o n { p a r a m t r e s ) { // corps de la fonction
}

Ce qui d o n n e d a n s n o t r e e x e m p l e :
void
{

Livre::Saisir()

puts("Entrez le titre du livre : " ) ; gets(titre); puts("Entrez le nom de l'auteur : " ) ; gets(auteur); puts("Entrez l'diteur :"); gets(diteur);
}

void Livre: :Afficher() { printf("Titre : %s par %s (%s)", titre, auteur, diteur);

Les objets et les classes

17

void
{

Livre::Imprimer()

fprintf(stdprn, "Titre : %s par %s (%s)", titre, auteur, diteur);


}

L e s fonctions d ' u n e classe p e u v e n t l i b r e m e n t a c c d e r toutes ses d o n n e s - m e m b r e s (ici : t i t r e , a u t e u r e t d i t e u r ) . N ' i m p o r t e quelle fonction d ' u n e classe p e u t g a l e m e n t a p p e ler u n e autre fonction de la m m e classe. Voil ! N o t r e prem i r e classe est crite ! V o y o n s m a i n t e n a n t de quelle m a n i r e n o u s p o u v o n s l'utiliser. C r e r un objet P o u r utiliser u n e classe, il faut d ' a b o r d crer un objet qui a u r a c o m m e type cette classe. L a s y n t a x e est l a m m e q u e p o u r dclarer une variable en C :
Nom_Classe nom_objet;

D a n s l ' e x e m p l e du livre, cela d o n n e :


Livre mon_livre;

B i e n e n t e n d u , toutes les formes de dclaration du C s o n t acceptes : tableaux, pointeurs, tableaux de p o i n t e u r s , etc. Par exemple :
Livre Livre ma_bibliotheque[20]; // tableau de 20 objets-livres *sur_ma_table_de_chevet; // pointeur sur un objet-livre

A c c d e r la partie p u b l i q u e d'un objet M a i n t e n a n t q u e n o u s s a v o n s c o m m e n t dclarer un objet, il reste dcouvrir c o m m e n t l'utiliser. En clair : c o m m e n t saisir, afficher ou i m p r i m e r un objet livre . V o u s ne serez p a s d p a y s : l'accs a u x f o n c t i o n s - m e m b r e s p u b l i q u e s d ' u n o b jet se fait c o m m e si v o u s accdiez u n e d o n n e d ' u n e structure classique du l a n g a g e C :

18

Pont entre C et C++

objet.fonction_publigue(paramtres); pointeur_sur_objet -> fonction_publique(paramtres);

Il est t e m p s d'crire un petit p r o g r a m m e qui va saisir puis afficher dix livres :


void
{

main() bouquin[10]; i;

Livre int

for (i = 0; i < 10; i++) bouquin[i].Saisir(); for (i = 0; i < 10; i++) bouquin[i].Afficher();
}

C ' e s t aussi s i m p l e que cela. V o u s s a v e z m a i n t e n a n t c o m m e n t crire des classes simples, et c o m m e n t utiliser des objets. Prcision i m p o r t a n t e sur l'accs a u x d o n n e s private V o u s v o u s r a p p e l e z q u e les d o n n e s o u fonctions p r i v a t e d ' u n e classe ne sont p a s visibles partir d e s fonctions d'un autre objet. M a i s u n e petite prcision s ' i m p o s e : u n e fonction m e m b r e d ' u n e classe A peut accder directement toutes les d o n n e s (y c o m p r i s p r i v a t e ) d'autres objets de classe A. C e c i n ' e s t p a s f o r c m e n t vident p u i s q u ' e l l e m a n i p u l e dans ce cas les d o n n e s d ' u n autre objet. Elle p e u t p o u r t a n t accd e r a u x d o n n e s e t fonctions p r i v a t e d e cet autre objet car il est de la m m e classe. E x e m p l e :
class A { private : int a; public :
//

...

void fontion_a();
} ;

class B { private : int b; public :


// }; ...

L e s objets et les classes

19

void
{

A::fonction_a() autre_a; objet_b; // // // // OK : on reste dans cet objet OK : autre_a est de classe A NON : b est private dans une autre classe

A B

a = 1; autre_a.a = 1 ; objet_b.b = 1;
}

L'intrt par rapport au C

V o u s l ' a v e z v u , u n objet r e g r o u p e des d o n n e s e t d e s fonctions qui o p r e n t sur ces d o n n e s . Q u e l est l'intrt de rais o n n e r e n objets plutt q u ' e n fonctions o u e n structures, c o m m e c'est le cas en C ? L e c o d e g a g n e e n scurit. V o u s s a v e z q u e les d o n n e s d ' u n objet ne sont m a n i p u l e s q u e p a r ses p r o p r e s fonctions, les f o n c t i o n s - m e m b r e s . V o u s p o u v e z d o n c contrler l'intgrit des d o n n e s et cibler v o s r e c h e r c h e s en c a s de bogue. L e s p r o g r a m m e s g a g n e n t e n clart. S i l'architecture d e s objets est b i e n conue*, v o u s c o m p r e n e z r a p i d e m e n t le rle de tel ou tel objet. U n e fonction ne se p r o m n e p a s d a n s le vide, elle est rattache un objet, d o n c l ' e n s e m b l e des d o n n e s q u ' e l l e m a n i p u l e . La clart des p r o g r a m m e s et leur c l o i s o n n e m e n t en objets p e r m e t t e n t u n e m a i n t e n a n c e et u n e volutivit p l u s facile. G r c e a u x trois a v a n t a g e s p r c d e n t s , des q u i p e s d e dv e l o p p e u r s p e u v e n t p l u s facilement e n v i s a g e r l'criture de trs gros p r o g r a m m e s . C e r t a i n s p o u r r a i e n t p r t e n d r e q u e l'on p e u t s i m u l e r le principe de classe en C. Il suffirait, aprs tout, de c r e r u n e structure c o n t e n a n t des pointeurs de fonction, p o u r s t o c k e r les fonctions m e m b r e s . C e r t e s , m a i s s'agissant d ' u n e g y m n a s t i q u e prilleuse, v o u s p e r d e z le bnfice de la clart d e s p r o g r a m m e s * . D e plus, rien n e v o u s interdit d ' a c c d e r d i r e c t e m e n t a u x d o n n e s de votre structure, ce qui tord le c o u l'une d e s rgles f o n d a m e n t a l e s de l ' e n c a p s u l a t i o n : cac h e r les d o n n e s . Par ailleurs, v o u s serez d a n s l'incapacit de s i m u l e r les autres caractristiques m a j e u r e s d u C + + , q u e n o u s d c o u v r i r o n s d a n s les chapitres suivants.

* Les pointeurs de fonction ne rendent jamais un programme plus clair...

20

Pont entre C et C++

Rsum

N o u s v e n o n s de p r s e n t e r la n o t i o n la p l u s i m p o r t a n t e du C + + et des l a n g a g e s orients-objets : l'encapsulation. L e C + + p e r m e t d e crer e t m a n i p u l e r d e s objets. U n objet s e c o m p o s e de donnes et de fonctions de traitement qui lui sont p r o p r e s . C e r t a i n e s de ces d o n n e s et de ces fonctions sont c a c h e s , c'est--dire q u ' e l l e s ne sont accessibles q u e de l'intrieur de l'objet, partir de ses fonctions p r o p r e s . P o u r crer u n objet e n C + + , i l faut d ' a b o r d dfinir u n m o u l e , a p p e l classe e n C + + . L e s objets sont e n s u i t e crs c o m m e des variables classiques, partir de leur classe. Les d o n n e s et fonctions dfinies d a n s u n e classe s o n t a p p e les respectivement donnes membres et fonctions membres, par o p p o s i t i o n a u x d o n n e s et fonctions hors-classe. Par e x e m p l e , n ' i m p o r t e quelle fonction de la librairie s t a n d a r d du C ( c o m m e p r i n t f ) est u n e fonction hors-classe.

Pour bien dmarrer en C++

En tant q u e p r o g r a m m e u r en l a n g a g e C, la p h i l o s o p h i e objets v o u s droutera peut-tre a u dbut. V o u s n e d e v e z p l u s p e n ser en t e r m e de fonctions, m a i s en t e r m e d'objets, r e g r o u p a n t leurs p r o p r e s d o n n e s e t leurs p r o p r e s fonctions. Par e x e m ple, si v o u s v o u l e z afficher le p l a t e a u d ' u n j e u , ne p e n s e z p a s quelque chose c o m m e a f f i c h e r _ p l a t e a u (plateau) m a i s plutt p l a t e a u . a f f i c h e r ( ) , o p l a t e a u est u n objet c o n t e n a n t , par e x e m p l e , la p o s i t i o n des p i o n s du j e u . Bref, v o u s partez d ' u n objet et lui e n v o y e z un m e s s a g e (sous forme de fonction). R a s s u r e z - v o u s , c'est un rflexe qui viendra r a p i d e m e n t e t q u e v o u s trouverez d e p l u s e n p l u s agrable. B i e n q u e la p h i l o s o p h i e objet n c e s s i t e u n e c o n c e p tion p l u s s o i g n e q u ' a u p a r a v a n t , elle p r o c u r e un rel plaisir et b e a u c o u p d ' a v a n t a g e s .

Les objets et les classes

21

Complments
R a p p e l o n s q u e v o u s p o u v e z sauter cette section d a n s u n p r e m i e r t e m p s , et p a s s e r d i r e c t e m e n t au chapitre suivant. S i , toutefois, v o u s dsirez e n connatre u n p e u p l u s sur les classes, c o n t i n u e z votre lecture.

Fonctions inline

L e s fonctions inline sont une facilit d u C + + p e r m e t t a n t d'optimiser la vitesse d'excution des programmes. L ' e x c u t i o n d ' u n e fonction inline est en effet p l u s rapide q u e celle d ' u n e fonction dfinie n o r m a l e m e n t : le c o m p i l a t e u r r e m p l a c e les appels de telles fonctions p a r le c o d e de c e s fonctions. Cette r e m a r q u e en e n g e n d r e u n e autre : il v a u t m i e u x q u e les fonctions inline soient trs courtes (une ligne ou d e u x ) p o u r viter d e cloner i n u t i l e m e n t d e n o m b r e u s e s lignes d e c o d e . M i s e en uvre C + + E x a m i n o n s l ' e x e m p l e suivant :
class UnNombre { private : int public : int void void
} ;

nbr; get_nbr (); set_nbr (int); Afficher();

C o m m e v o u s p o u v e z le voir, cette classe dclare trois fonctions p u b l i q u e s , qui ne sont e n c o r e dfinies nulle part. En thorie, il faudrait dfinir c h a c u n e de ces fonctions en d e h o r s de la classe, de cette m a n i r e :
int UnNombre: :get_nbr() { return nbr;
}

Le C + + m e t notre disposition les fonctions inline, p o u r dfinir c o m p l t e m e n t u n e fonction l'intrieur de la dfinition de sa classe. Ainsi, au lieu de dfinir part d e s fonctions trs

22

Pont entre C et C++

c o u r t e s , v o u s p o u v e z les inclure d i r e c t e m e n t d a n s la classe. C ' e s t c e q u e n o u s allons faire p o u r les fonctions s e t _ n b r e t get_nbr:


class UnNombre { private : int public : int void void
} ;

nbr;

get_nbr() set_nbr(int n2 ) Afficher();

{ return nbr; } { nbr = n2; }

C o m m e v o u s p o u v e z l e constater, n o u s a v o n s dfini complt e m e n t les d e u x p r e m i r e s fonctions p u b l i q u e s de cette classe (en gras d a n s l ' e x e m p l e ) . C e s fonctions sont a p p e l e s inline. L a troisime, A f f i c h e r , n ' e s t q u e dclare n o t e z l e pointv i r g u l e aprs la dclaration. Il faudra la dfinir en dehors de la classe, c o m m e n o u s a v i o n s l'habitude de le faire. Q u a n d on dfinit u n e fonction inline, il est inutile d'accoler le n o m de la classe et les caractres : : a v a n t le n o m de la fonction, p u i s q u ' e l l e est dfinie d a n s le c o r p s de sa classe. Le mot-cl inline Il existe un autre m o y e n de bnficier du m c a n i s m e inline, sans dfinir les fonctions d i r e c t e m e n t d a n s la classe : il suffit d'utiliser le mot-cl inline en tte de la dfinition de la fonction. Q u a n d v o u s spcifiez q u ' u n e fonction est inline, v o u s d o n n e z s i m p l e m e n t u n e indication a u c o m p i l a t e u r , qui est libre de la suivre ou p a s . D'ailleurs certains compilateurs refusent de rendre inline d e s fonctions qui c o n t i e n n e n t des mots-cls c o m m e for ou while. Si, par e x e m p l e , n o u s avions v o u l u q u e Afficher soit une fonction inline, sans p o u r autant inclure s o n c o d e d a n s la dclaration de la classe UnNombre, il aurait fallu crire :
inline
{

Ce systme est efficace si vous utilisez la compilation spare. N o u s dtaillerons cela au chapitre 14.

void

UnNombre: : Afficher()

printf("Le nombre est : %d", nbr);


}

Les objets et les classes

23

La dclaration de classe restant identique, m a i s il faut cette fois-ci indiquer au c o m p i l a t e u r le n o m de la classe laquelle appartient la fonction inline (c'est le rle de U n N o m b r e : : ), p u i s q u e cette fonction n ' e s t p a s dclare d i r e c t e m e n t d a n s la classe. Rsum L e C + + p e r m e t a u p r o g r a m m e u r d e dfinir d e s fonctionsm e m b r e inline. C e s fonctions se c o m p o r t e n t c o m m e toutes les autres fonctions d ' u n e classe, la seule e x c e p t i o n q u e lors d e l a compilation, c h a q u e appel u n e fonction i n l i n A e s t r e m p l a c p a r le corps de cette fonction. Il en rsulte un g a i n de t e m p s l'excution. On rserve c e p e n d a n t cet a v a n t a g e a u x fonctions trs courtes, afin q u e la taille finale de l ' e x c u t a b l e ne soit p a s trop n o r m e .

Remarque : des fonctions hors-classes peuvent elles-aussi tres dfinies inline.

Le mot-cl this

C h a q u e classe p o s s d e a u t o m a t i q u e m e n t u n e d o n n e c a c h e u n p e u spciale : l e pointeur t h i s . Utilis d a n s u n e fonction d e classe, t h i s est u n pointeur sur l'objet c o u r a n t partir d u q u e l on a appel la fonction m e m b r e . E x e m p l e :
class A { private: int i ; public : void f ( ) ;
) ;

void A : :f() { this -> i = 1;


}

// quivaut i = 1;

t h i s est de type p o i n t e u r sur l'objet c o u r a n t qui a a p p e l l a fonction . D a n s l ' e x e m p l e , t h i s est d e t y p e p o i n t e u r d e A . V o u s e n dduirez q u e ( * t h i s ) reprsente l'objet c o u rant, et v o u s a u r e z raison. L'utilit d e t h i s est patente p o u r certains oprateurs, o u p o u r tester l'galit de d e u x objets (voir p a g e 6 1 ) .

24

Pont entre C et C++

lments static dans une classe

On ne peut pas utiliser le mot-cl this dans une fonction static, puisque celle-ci ne dpend pas d'un objet prcis.

V o u s p o u v e z dclarer des objets, variables ou fonctions s t a t i c d a n s u n e classe. C e s l m e n t s seront alors c o m m u n s tous les objets de cette classe. Ainsi, u n e variable s t a t i c aura la m m e valeur p o u r tous les objets de cette classe. S i l'un des objets c h a n g e cette variable s t a t i c , elle sera m o d i f i e p o u r tous les autres objets. On distingue ainsi les variables et fonctions d'instance (celles qui sont spcifiques un objet), et les variables et fonctions de classe ( c o m m u n e s tous les objets de cette classe). C e s dernires doivent tre dclares s t a t i c . I l faut initialiser c h a q u e variable s t a t i c e n d e h o r s d e l a dclaration d e classe, sans rpter l e mot-cl s t a t i c (voir l ' e x e m p l e ci-dessous). L e s f o n c t i o n s - m e m b r e s dclares s t a t i c d o i v e n t tre a p p e les partir du n o m de la classe, c o m m e ceci : N o m C l a s s e : : f ( ), sans faire rfrence un objet prcis. Elles p e u v e n t d o n c tre a p p e l e s s a n s q u ' a u c u n objet de la classe n'existe.
#include <iostream.h>

Pour c o m p r e n d r e cet exemple, vous devez d'abord lire le chapitre 2 sur les constructeurs.

class Flam { protected: static int nb_objets; int donne; public : Flam() : donne(0) { nb_objets++; } static affiche_nb_objets() { cout << nb_objets << endl; }
};

int

Flam: :nb_objets = 0;

// initialisation

void main() { // appel de la fonction static partir de la classe Flam: :affiche_nb_objets(); Flam a, b, c; // dclarons 3 objets

// appel de la fonction static partir d'un objet a.affiche_nb_objets();


}

// affichage // 0 // 3

Les objets et les classes

25

D a n s cet e x e m p l e , le constructeur de Flam ajoute 1 la v a riable de classe nb_objets (dclare static au s e n s C + + ) . En crant trois objets a, b et c, n o u s a p p e l o n s trois fois ce constructeur. On p e u t appeler u n e fonction static en la faisant p r c d e r du n o m de la classe suivi de d e u x d o u b l e p o i n t s ou partir d ' u n objet. Il n ' y a p a s de diffrence entre les d e u x . D i f f r e n c e e n t r e le m o t - c l static du C et du C + + A t t e n t i o n : d a n s ce q u e n o u s v e n o n s d ' e x p l i q u e r , le m o t - c l static sert dclarer d e s variables o u fonctions de classe. En C standard, r a p p e l o n s q u e static signifie q u e l ' l m e n t n ' e s t accessible que dans s o n fichier s o u r c e .

Dclaration de classe anticipe

T o u t c o m m e v o u s p o u v e z dclarer u n e structure un endroit et la dfinir c o m p l t e m e n t ailleurs, v o u s a v e z la p o s sibilit de le faire p o u r des classes. La s y n t a x e est s i m p l e : class nom_de_classe ;. I m a g i n o n s q u ' u n e classe A c o n tienne un objet de classe B et r c i p r o q u e m e n t . Il faut recourir u n e dclaration p o u r q u e A ait c o n n a i s s a n c e de B a v a n t q u e B ne soit dfinie :
class B; // dclaration anticipe

class A { protected: B objet_B; public : I I . . .


};

class B { protected: A objet_A; public : // ...


};

Les constructeurs
et les destructeurs

La vie et la mort d'un objet C++ sont rgies et les destructeurs. Les fonctions constructeurs cration d'un objet, les fonctions destructeurs qu'il sort de sa porte de visibilit, c'est--dire

par les constructeurs sont appeles la sont invoques ds ds qu'il meurt.

Les notions de base


L'ide E n C o u e n C + + , q u a n d v o u s dclarez u n e variable s a n s l'initialiser, son c o n t e n u est i n d t e r m i n . E n C + + , q u a n d v o u s dclarez u n objet d ' u n e certaine classe, s o n c o n s t r u c teur sera a p p e l automatiquement. Un c o n s t r u c t e u r est u n e f o n c t i o n - m e m b r e qui p o r t e toujours le n o m de la classe d a n s laquelle elle est dfinie, et qui ne renvoit rien (pas m m e un v o i d ) . S i v o u s n e dfinissez p a s e x p l i c i t e m e n t u n c o n s t r u c teur p o u r u n e classe, l e C + + e n utilise u n d'office, qui v a s e contenter de rserver de la m m o i r e p o u r toutes les variables de v o t r e classe. Attention : q u a n d v o u s dfinissez un p o i n t e u r sur un objet, a u c u n constructeur n ' e s t appel. Si v o u s dsirez e n s u i t e allouer de la m m o i r e et appeler un constructeur, utilisez n e w (voir chapitre 6, p a g e 8 3 ) .

28

Pont entre C et C++

P a r a l l l e m e n t a u x constructeurs, l e C + + n o u s p r o p o s e les destructeurs. Q u a n d l'objet sort de la p o r t e du bloc d a n s lequel il a t dclar, n o t a m m e n t q u a n d l ' e x c u t i o n du prog r a m m e atteint la fin d ' u n bloc o l'objet est dfini, son destructeur est appel automatiquement.

Mise en uvre C++ constructeurs

C o m m e n t dfinir u n e fonction c o n s t r u c t e u r ? N o u s savons q u ' e l l e doit porter l e m m e n o m q u e s a c l a s s e . P r e n o n s l ' e x e m p l e d ' u n e classe Equipage :
class Equipage { private : int

personnel;

public : Equipage(int personnel_initial);


} ;

Equipage::Equipage (int personnel_initial) { personnel = personnel_initial;


}

N o t r e constructeur de la classe Equipage ne r e t o u r n e auc u n e valeur, p a s m m e u n v o i d , p u i s q u e c'est u n constructeur. Il accepte un p a r a m t r e , le n o m b r e de p e r s o n n e s dans l ' q u i p a g e . C e l a signifie q u e l o r s q u e v o u s v o u d r e z dclarer un objet de classe Equipage, il faudra faire suivre l'identifiant de l'objet p a r les p a r a m t r e s effectifs du constructeur, entre p a r e n t h s e s . l ' e x c u t i o n du p r o g r a m m e suivant, le c o n s t r u c t e u r Equipage : : Equipage est appel a u t o m a t i q u e m e n t , avec c o m m e p a r a m t r e l'entier 311 :
Ici, le constructeur est appel, ce qui initialise la d o n n e - m e m b r e personnel 311
)

void
{

main()

Equipage base_alpha( 311) ; // dclaration de l'objet et appel du constructeur

Constructeur sans paramtre Si v o u s dfinissez un constructeur s a n s p a r a m t r e , par e x e m p l e c o m m e ceci :

Les constructeurs et les destructeurs

29

Equipage : : Equipage()
{

nombre = 0 ;
)

il faut faire attention la m a n i r e de l'appeler. L ' e x e m p l e suivant v o u s m o n t r e ce qu'il faut et ne faut pas faire :
void
{

main() mon_equipage(); mon_autre_equipage; // (1) incorrect // (2) correct

Equipage Equipage }

Si v o u s ajoutez des p a r e n t h s e s , c o m m e d a n s le c a s 1, le c o m p i l a t e u r c o m p r e n d q u e v o u s dclarez u n e fonction ! Donc, pour appeler correctement un constructeur sans par a m t r e , il faut o m e t t r e les p a r e n t h s e s , c o m m e d a n s le cas 2. C ' e s t s e u l e m e n t dans ce cas que le c o n s t r u c t e u r sans p a r a m tre est appel. C o n s t r u c t e u r s surchargs Il est tout fait p o s s i b l e de dfinir p l u s i e u r s c o n s t r u c t e u r s h o m o n y m e s , qui s e d i s t i n g u e r o n t alors p a r l e t y p e e t / o u l a quantit d e p a r a m t r e s . R e p r e n o n s l a classe E q u i p a g e e t dfinissons trois c o n s t r u c t e u r s :
class Equipage { private : int

personnel;

public : Equipage(int personnel_initial); Equipage(); Equipage(int nbr_femmes, int nbr_hommes);


} ;

// 1 // 2 // 3

// constructeur 1 Equipage:: Equipage(int personnel_initial) { personnel = personnel_initial;


}

// constructeur 2 Equipage : : Equipage() { personnel = 0;

30

Pont entre C et C++

} // constructeur 3 Equipage::Equipage(int nbr_femmes, int nbr_hommes) { personnel = nbr_femmes + nbr_hommes;


}

V o u s d i s p o s e z m a i n t e n a n t de trois m a n i r e s d'initialiser un objet d e classe E q u i p a g e . L ' e x e m p l e qui suit illustre c e concept :


void
{

main() // appel au constructeur 1 // appel au constructeur 3 // appel au constructeur 2

Equipage base_alpha(311); Equipage mixte(20, 1 ) ; Equipage inconnu;


}

Mise en uvre C++ destructeurs

R a p p e l o n s q u e le destructeur est a p p e l automatiquement q u a n d un objet sort de la p o r t e du b l o c d a n s lequel il est dclar c'est--dire q u a n d il n ' e s t p l u s accessible au prog r a m m e u r . U n e f o n c t i o n - m e m b r e destructeur n e retourne p a s de t y p e (pas m m e un v o i d ) , et n ' a c c e p t e aucun paramtre. C o m m e p o u r les c o n s t r u c t e u r s , le c o m p i l a t e u r g n r e un destructeur p a r dfaut p o u r toutes les classes qui n ' e n sont p a s p o u r v u e s . Ce destructeur p a r dfaut se b o r n e librer la m m o i r e des d o n n e s de la classe. M a i s si v o u s dsirez faire u n e o p r a t i o n particulire p o u r dtruire un objet, et notamm e n t si v o u s a v e z d e s p o i n t e u r s d o n t il faut librer l'espace m m o i r e , v o u s p o u v e z dfinir v o t r e p r o p r e destructeur. Attention ! C o n t r a i r e m e n t a u x c o n s t r u c t e u r s , il ne p e u t exister qu'un seul destructeur par classe ! Le n o m de la fonction destructeur est de la forme -NomDeClasse . Le p r e m i e r s i g n e est un tilde . Exemple I m a g i n o n s u n e classe qui c o n t i e n n e u n e d o n n e - m e m b r e p o i n t a n t v e r s u n e c h a n e de caractres :
#include <stdio.h> #include <string.h> class Capitaine

Les constructeurs et les destructeurs

31

private : char *nom; public : // constructeur Capitaine) char *nom_initial ); // destructeur -Capitaine();
} ;

// constructeur Capitaine::Capitaine( char *nom_initial ) { nom = (char*) malloc(strlen(nom_initial) + 1 ) ; strcpy(nom, nom_initial);


}

// destructeur Capitaine:: -Capitaine() { free( nom ); }

Complments
Constructeur copie Lorsqu'il faut initialiser un objet avec un autre objet de m m e classe, le constructeur copie est appel a u t o m a t i q u e m e n t . Voici u n petit e x e m p l e d e c o d e a p p e l a n t l e c o n s t r u c t e u r c o pie de la classe B e t a :
class Beta

// le contenu ne nous intresse pas ici

void

fonction_interessante(Beta beta)

// traitement sur beta

void

main()

32

Pont entre C et C++

Beta

premier, deuxime (premier), troisime = premier;

// (1) // (2) // (3)

fonction_interessante(premier);
}

* voir le paragraphe sur les listes d'initialisations, page 47,

* Dans ce cas, l'oprateur = n'est pas appel puisque ce n'est pas une affectation.

La ligne (1) signifie q u e l'objet d e u x i m e doit tre initialis a v e c l'objet p r e m i e r * . La ligne (2) initialise elle aussi l'objet t r o i s i m e avec l'objet p r e m i e r . C e n ' e s t p a s u n e affectation, c'est une initialisation !* Enfin, la ligne (3) fait appel u n e fonction qui va recopier le p a r a m t r e effectif, p r e m i e r , d a n s s o n p a r a m t r e formel, b e t a . Bref, ces trois lignes font implicitement a p p e l au constructeur c o p i e . C o m m e p o u r les c o n s t r u c t e u r s o r d i n a i r e s ou les destructeurs, l e C + + e n g n r e u n p a r dfaut s i v o u s n e l'avez p a s fait v o u s - m m e . Ce c o n s t r u c t e u r c o p i e p a r dfaut effectue u n e c o p i e b t e , m e m b r e m e m b r e , d e s d o n n e s de la classe. D a n s les cas o v o s classes c o n t i e n n e n t d e s pointeurs, cela p o s e un p r o b l m e : en ne c o p i a n t q u e le pointeur, l'objet c o p i et l'objet c o p i a n t pointeraient sur la m m e zone mm o i r e . Et ce serait fcheux ! C e l a signifierait q u ' e n modifiant l ' u n d e s d e u x objets, l'autre serait g a l e m e n t modifi de m a n i r e invisible ! L e s c h m a suivant illustre c e p r o b l m e .

Schma illustrant le problme d'une copie membre membre

Les constructeurs et les destructeurs

33

P o u r r s o u d r e c e p r o b l m e , v o u s d e v e z d o n c crire votre propre constructeur copie, qui effectuera u n e c o p i e p r o p r e avec, par e x e m p l e , u n e nouvelle allocation p o u r c h a q u e pointeur d e l'objet copi. N o u s o b t e n o n s l e s c h m a suivant, aprs u n e c o p i e p r o p r e .

Schma illustrant le rsultat d'une copie propre.

C o m m e n t dfinir son p r o p r e c o n s t r u c t e u r copie ? La f o r m e d ' u n c o n s t r u c t e u r copie est toujours la m m e . Si votre classe s'appelle Gogol, s o n c o n s t r u c t e u r c o p i e

s'appellera Gogol: : Gogol (const Gogol&). Le p a r a m * les rfrences sont traites au chapitre 9, page I 13.

tre est u n e rfrence* un objet de classe Gogol. Ici const n ' e s t p a s n c e s s a i r e m a i s fortement r e c o m m a n d , p u i s q u e v o u s ne modifierez pas l'objet copier. c r i v o n s la classe Gogol, p o s s d a n t un p o i n t e u r de char, et efforons-nous d'crire son c o n s t r u c t e u r de c o p i e :
#include <string.h> #include <iostream.h> class Gogol { private: char public : // constructeur normal Gogol(char * c ) ; // constructeur de copie Gogol(const Gogol &a_copier); *pt;

*cout est un mot cl C++ utilis pour l'affichage de caractres. Voir chaptre 7, page 89.

// pour cout*

34

Pont entre C et C++

// accs aux donnes membre void set_pt(char * c ) ; char *get_pt(); // autres fonctions void Afficher();:
>;

// constructeur normal Gogol::Gogol(char *c) { pt = new char [strlen(c) + 1 ] ; strcpy(pt, c ) ;


}

// constructeur de copie Gogol:: Gogol(const Gogol &a_copier)


{

Il faut vrifier que l'objet source et l'objet destination sont diffrents, car s'ils taient identiques, delete pt effacerait la fois les pointeurs source et destination !

if (this != &a_copier) // voir dans la marge


{

// effaons l'ancien pt delete pt; // allouons de la place pour le nouveau pt pt = new char [strlen(a_copier.pt) + 1 ] ; // donnons une valeur au nouveau pt strcpy(pt, a_copier.pt);
} }

void Gogol::set_pt(char *c) { delete pt; pt = new char [strlen(c) + 1 ] ; strcpy(pt, c ) ;


}

char *Gogol: :get_pt() { return pt;


}

void Gogol::Afficher()
{

cout << pt << endl;


}

void {

maint) Gogol gog("Zut"), bis = gog; // appel du constructeur copie // Zut // Zut

gog.Afficher() ; bis.Afficher();

Les constructeurs et les destructeurs

35

// on modifie la chaine de gog gog.set_pt("Mince alors"); gog.Afficher(); bis.Afficher();


}

// Mince alors // Zut

C o n c l u s i o n : les objets gog et bis p o s s d e n t b i e n d e u x p o i n teurs d s i g n a n t d e u x z o n e s m m o i r e s diffrentes. C ' e s t parfait. M a i s si n o u s n ' a v i o n s p a s crit notre p r o p r e constructeur copie, celui g n r p a r dfaut aurait c o p i les d o n n e s m e m b r e m e m b r e . A i n s i , d a n s gog et bis, pt aurait t le m m e . D o n c , le fait de dtruire le p o i n t e u r de gog aurait g a l e m e n t dtruit celui de bis p a r effet de b o r d . Inutile de prciser les c o n s q u e n c e s d s a s t r e u s e s d ' u n tel v nement.

Constructeurs de conversion

Si vous avez besoin de l'opration rciproque (convertir un objet de votre classe vers une variable de type T), allez voir p a g e 61 o l'on vous dira que c'est possible.

Un c o n s t r u c t e u r n ' a y a n t q u ' u n seul a r g u m e n t de t y p e T spcifie u n e c o n v e r s i o n d ' u n e variable de t y p e T v e r s un o b jet de la classe du constructeur. Ce c o n s t r u c t e u r sera a p p e l a u t o m a t i q u e m e n t c h a q u e fois q u ' u n objet de t y p e T sera rencontr l o il faut un objet de la classe de constructeur. P a r e x e m p l e , i m a g i n o n s q u ' o n veuille u n e c o n v e r s i o n autom a t i q u e d ' u n entier vers un objet de classe Prisonnier, o l'entier r e p r s e n t e s o n n u m r o matricule. A i n s i , q u a n d l e c o m p i l a t e u r attend un objet Prisonnier et qu'il n ' a q u ' u n entier la p l a c e , il effectue la c o n v e r s i o n a u t o m a t i q u e m e n t en crant l'objet Prisonnier l'aide du c o n s t r u c t e u r de conversion. Exemple :
#include <iostream.h> class Prisonnier { protected: int numro; char nom [50] ; public :
// ...

Prisonnier(int n) : numro(n) { cout << "Conversion de " << n << endl; nom[0] = 0 ; }
} ;

void {

fonction(Prisonnier p)
// . . .

36

Pont entre C et C++

void {

main()

Prisonnier

p = 2;

// donne p = Prisonnier(2)

fonction(6); // donne fonction(Prisonnier(6)) } // affiche : // Conversion de 2 // Conversion de 6

L'hritage simple

L'hritage vous permet de modliser le monde rel avec souplesse, d'organiser vos classes et de rutiliser celles qui existent dj. C'est un concept fondamental en programmation oriente-objets. Nous parlons ici d'hritage simple, par opposition l'hritage multiple qui sera dvelopp dans la deuxime partie du livre.

Notions de base
L'ide L e s informaticiens se feraient b e a u c o u p m o i n s de soucis s'ils p o u v a i e n t r g u l i r e m e n t rutiliser des p o r t i o n s d e c o d e qu'ils ont dj crit p o u r des projets antrieurs. G r c e l'hritage, u n e part du r v e devient ralit : v o u s p o u v e z profiter des classes dj existantes p o u r en crer d'autres. M a i s l o l'hritage est intressant, c'est q u e v o u s p o u v e z ne garder q u ' u n e partie de la classe initiale, modifier certaines de ses fonctions ou en ajouter d'autres. L'hritage s'avre trs a d a p t a u x cas o v o u s d e v e z m a n i puler des objets qui ont des p o i n t s c o m m u n s , m a i s qui diffrent l g r e m e n t .

38

Pont entre C et C++

* N o u s aurions pu parler de la classificat i o n des cornichons, des jeunes filles ou des flippers, mais un exemple classique est sans doute mieux adapt un large public.

Le principe Le p r i n c i p e initial de l'hritage est p r o c h e de celui de la classification en catgories : on part du c o n c e p t le p l u s gnral p o u r se spcialiser d a n s les cas particulier. A d m e t t o n s que n o u s v o u l i o n s parler de vhicules*, et e s s a y o n s de tisser des liens d'hritage entre q u e l q u e s v h i c u l e s b i e n c o n n u s :

Le sens des flches d'hritage pourrait tre : p o s s d e les caractristiques de ou est un , au s e n s large du terme. Ainsi, voiture est un vhicule. De m m e p o u r le bateau. Le camion, lui, p o s s d e les caractritiques de voiture et, indirectem e n t , de vhicule. O n dit q u e v o i t u r e hrite de v h i c u l e . D e m m e , bateau hrite de v h i c u l e , et c a m i o n de voiture. D ' u n cas gnral, r e p r s e n t par vhicule, n o u s a v o n s tir d e u x cas particuliers : voiture et bateau. Camion est lui-mme un cas particulier de voiture, car il en p o s s d e grosso modo toutes les caractristiques, et en ajoute d'autres q u i lui sont propres.

Mise en uvre C++

D a n s l ' e x e m p l e ci-dessus, c h a q u e l m e n t du s c h m a est rep r s e n t p a r u n e classe. U n e r e m a r q u e de vocabulaire : q u a n d u n e classe A hrite d ' u n e classe B, on dit q u e A est la classe de base et B la classe drive. P o u r y voir p l u s clair, criv o n s les classes v h i c u l e et voiture, et e x a m i n o n s ensuite ce q u e cela signifie a u n i v e a u d u C + + :
class Vhicule { protected : int // ...

Le m o t cl protected est expliqu dans les pages qui suivent.

nombre_de_places;

L'hritage simple

39

public : // constructeurs Vhicule(); // ... // fonctions de traitement void Afficher));


// . . .
} ;

Remarque : nous n'avons pas dtaill ces classes afin de nous concentrer sur le mcanisme d'hritage. Vous retrouverez les classes compltes en fin de chapitre.

class Voiture : public Vhicule


{

protected : int chevaux_fiscaux; // . . . public : // constructeurs Voiture(); // fonctions de traitement void CalculVignette();
} ;

R i e n ne d i s t i n g u e ces classes d'autres classes ordinaires, si ce

n ' e s t la c h a n e : public Vhicule la suite du n o m


de la classe Voiture. C ' e s t bel et b i e n cette c h a n e qui indiq u e au compilateur q u e la classe Voiture hrite de la classe

Vhicule. Le mot-cl public qualifie la spcification d'accs


d e l'hritage. N o u s v e r r o n s ci-dessous q u e l e t y p e d ' h r i t a g e d t e r m i n e quelles d o n n e s de la classe de b a s e sont accessib l e s d a n s la classe drive. V o i l , n o u s a v o n s dclar q u e la classe Voiture hritait de la classe Vhicule. Q u e cela signifie-t-il c o n c r t e m e n t ? Q u e d a n s d e s objets de classe drive (Voiture), v o u s p o u v e z a c c d e r des d o n n e s ou fonctions m e m b r e s d'objets de la classe de b a s e (Vhicule). Accs a u x m e m b r e s de la classe de b a s e P o u r savoir p r c i s m e n t quelles d o n n e s s o n t accessibles d a n s la classe drive, il faut considrer la spcification d'accs* ainsi q u e le t y p e des d o n n e s (public, protected ou private) de la classe de b a s e . Le tableau s u i v a n t d o n n e les c o m b i n a i s o n s possibles :

* Rappelons que la spcification d'accs est dtermine par le mot-cl qui suit les deux-points (:) aprs la ligne class

NomClasseDrive.

40

Pont entre C et C++

A u t r e m e n t dit, le mot-cl protected est identique private si vous n'utilisez pas l'hritage.

L e c o n t e n u d u tableau i n d i q u e l e type d u m e m b r e ( p u b l i c , p r o t e c t e d , p r i v a t e o u inaccessible) d a n s l a classe drive. V o u s d c o u v r e z d a n s c e tableau u n n o u v e a u spcificateur d ' a c c s : p r o t e c t e d . C e spcificateur est i d e n t i q u e p r i v a t e l'intrieur de la classe. Il diffre u n i q u e m e n t en cas d'hritage, p o u r d t e r m i n e r s i les l m e n t s p r o t e c t e d sont accessibles ou n o n d a n s les classes drives. P o u r m i e u x c o m p r e n d r e ce tableau, voici trois petits exemples de drivation :
class Base { private : int protected : int public : int
} ;

detective_prive; acces_protege; domaine_publicpe;

class Deriveel : public Base { // detective_prive est inaccessible ici // acces_protege est considr comme protected // domaine_public est considr comme public
} ;

class Derivee2 : protected Base { // detective_prive est inaccessible ici // acces_protege est considr comme protected // domaine_public est considr comme protected
>;

class { // // //
};

Derivee2 : private Base detective_prive est inaccessible ici acces_protege est considr comme private domaine_public est considr comme private

L'hritage simple

41

Ces exemples amnent plusieurs remarques : U n e d o n n e o u fonction m e m b r e p r i v a t e est toujours inaccessible d a n s ses classes drives L a spcification d ' a c c s d ' u n e d o n n e ( p u b l i c , p r o t e c t e d o u p r i v a t e ) d a n s u n e classe drive est e n fait l a spcification d ' a c c s la p l u s forte. Par e x e m p l e , si u n e f o n c t i o n - m e m b r e est p r o t e c t e d d a n s l a classe d e b a s e , e t q u e l'hritage est p u b l i c , elle devient p r o t e c t e d d a n s la classe drive. E n rgle gnrale, o n utilise l'hritage p u b l i c p o u r m o dliser des relations du m o n d e rel . V o i r le chapitre 15 sur les conseils.

Redfinition d'une fonctionmembre

V o u s p o u v e z dfinir des fonctions a u x enttes i d e n t i q u e s d a n s diffrentes classes a p p a r e n t e s p a r u n e relation d'hritage. La fonction a p p e l e d p e n d r a de la classe de l'objet qui l'appelle, ce qui est d t e r m i n s t a t i q u e m e n t la compilation. R e p r e n o n s : u n e f o n c t i o n - m e m b r e d ' u n e classe drive p e u t d o n c avoir le m m e n o m (et les mmes p a r a m tres) q u e celle d ' u n e classe de b a s e . P r e n o n s l ' e x e m p l e d ' u n e gestion de textes qui d i s t i n g u e les textes b r u t s des textes m i s en forme, et dfinissons d e u x fonctions a u x enttes identiques (mais au c o r p s diffrent) :
#include <stdio.h> class TexteBrut public : void void

Imprimer(int nb) ;

TexteBrut::Imprimer(int nb)

printf("Imprimer de la classe TexteBrutXn");

class TexteMisEnForme : public TexteBrut public : void void

// hritage

Imprimer(int n b ) ;

TexteMisEnForme::Imprimer(int nb)

printf("Imprimer de la classe TexteMisEnFormeXn");

42

Pont entre C et C++

void main() { TexteBrut TexteMisEnForme

txt; joli_txt;

txt.Imprimer( 1 ) ; joli_txt.Imprimer(1);
}

// affichage : Imprimer de la classe TexteBrut Imprimer de la classe TexteMisEnForme

Conversion de Drive* vers Base*

D a n s un cas o u n e classe Drive hrite p u b l i q u e m e n t d ' u n e classe Base, les c o n v e r s i o n s de p o i n t e u r s de Drive v e r s des p o i n t e u r s de Base est faite a u t o m a t i q u e m e n t aux endroits o le c o m p i l a t e u r attend un p o i n t e u r sur Base :
class Base { >; class Drive : public Base { }; void main() { Base *base; Drive *derivee; base = drive;
}

// OK

Attention : cette c o n v e r s i o n n ' e s t p a s autorise si l'hritage

est protected ou private ! Si v o u s r e m p l a c e z public


p a r protected d a n s le listing ci-dessus, la d e r n i r e ligne du main p r o v o q u e r a u n e erreur de c o m p i l a t i o n . P o u r q u o i ? P a r c e q u e p o u r q u ' u n p o i n t e u r sur u n e classe drive puisse tre converti sur u n e classe de b a s e , il faut q u e cette dernire soit accessible, c'est--dire q u ' e l l e p o s s d e des m e m b r e s public accessibles. Or ce n ' e s t p a s le cas d ' u n h r i t a g e pro-

tected ou private. Rsum


E n C + + , u n e classe p e u t hriter d ' u n e autre. O n dit alors que la classe qui hrite est u n e classe drive, l'autre classe tant a p p e l e classe de base. Q u a n d u n e classe A hrite d'une classe B, elle p e u t a c c d e r certaines d o n n e s ou fonctions

L'hritage simple

43

m e m b r e s d e l a classe B . C ' e s t u n petit p e u c o m m e s i u n e partie de la classe B avait t recopie d a n s le c o r p s de la classe A. Pour savoir ce qui est accessible d a n s A, il faut c o n s i d r e r la spcification d ' a c c s ( p u b l i c , p r o t e c t e d o u p r i v a t e ) , l e type d ' a c c s des d o n n e s d e B ( p u b l i c , p r o t e c t e d o u p r i v a t e ) e t consulter l e tableau d e l a p a g e 4 0 .

L'intrt par rapport au C

L, le C est c o m p l t e m e n t battu. G r c e l'hritage, v o u s allez enfin p o u v o i r rutiliser facilement le c o d e q u e v o u s a v e z dj crit. V o u s p o u r r e z enfin factoriser les p o i n t s c o m m u n s d ' u n e structure d'objets. Il n ' e x i s t e rien, d a n s le l a n g a g e C , qui p e r m e t t e d e mettre e n u v r e facilement u n m c a n i s m e similaire. P r e n o n s un e x e m p l e : v o u s d e v e z grer les e m p l o y s d ' u n e centrale nuclaire. partir d ' u n e classe de b a s e E m p l o y , v o u s faites hriter d'autres classes c o r r e s p o n d a n t a u x catgories de p e r s o n n e l : Ouvrier, C a d r e , A g e n t S c u r i t , etc. Si l'avenir d'autres p o s t e s sont crs (par e x e m p l e P o r t e P a r o l e ou A v o c a t ) , v o u s p o u r r e z crer la classe c o r r e s p o n d a n t e et la faire hriter de E m p l o y ou de classes spcifiques. D a n s n o tre e x e m p l e , un P o r t e P a r o l e pourrait hriter de la classe C a dre. L ' a v a n t a g e : v o u s n ' a v e z p a s r-crire tout ce q u e les portes-paroles et les c a d r e s ont en c o m m u n s .

Complments
Hritage et constructeurs
L e s c o n s t r u c t e u r s n e sont j a m a i s hrits. Q u a n d v o u s dclarez un objet d ' u n e classe drive, les c o n s t r u c t e u r s p a r d faut d e s classes de b a s e s v o n t tre a p p e l s a u t o m a t i q u e m e n t avant q u e le c o n s t r u c t e u r spcifique de la classe drive ne le soit. Voici un e x e m p l e :
class Base { public : Base();

44

Pont entre C et C++

} ;

class Deriveel : public Base { public : Deriveel();


} ;

class Derivee2 : public Deriveel { public : Derivee2();


};

void
{

main()

Base b; // appel Base() Deriveel dl; // appels Base() puis Deriveel() Derivee2 d2 ; // appels Base() , Deriveel().. . // puis Derivee2() // corps de la fonction }

P o u r ne p a s a p p e l e r les c o n s t r u c t e u r s p a r dfauts m a i s des c o n s t r u c t e u r s avec des p a r a m t r e s , v o u s d e v e z recourir aux listes d'initialisations, e x p l i q u e s p l u s loin d a n s ce chapitre.

Hritage et destructeurs

A l o r s q u e les constructeurs sont a p p e l s d a n s l'ordre desc e n d a n t (de la classe de b a s e la classe d r i v e ) , c'est l'inverse p o u r les destructeurs : celui de la classe d r i v e est a p p e l en p r e m i e r , p u i s celui de la classe de b a s e immdiat e m e n t suprieure, et ainsi de suite j u s q u ' la classe de base la plus haute. C o m p l t o n s l ' e x e m p l e p r c d e n t :
void
{

main()

Base b; // appel Base() Deriveel dl ; // appels Base{) puis DeriveelO Derivee2 d2 ; // appels Base(), Deriveel().. . // ...puis Derivee2() // corps de la fonction
}

// mort de b : appel -Base() // trpas de dl : appels -Deriveel() puis -Base() // radication de d2 : appels ~Derivee2(), // -Deriveel() puis -Base()

L'hritage simple

45

Exemple complet

R e p r e n o n s notre p r o b l m e d e vhicules. Voici l e c o d e c o m plet des classes Vhicule et Voiture, et d e s e x e m p l e s d'accs a u x d o n n e s de la classe de b a s e , savoir Vhicul.
class Vhicule { private : char buffer_interne[128]; // uniquement utilis par les fonctions de // la classe Vhicule. Inaccessible aux // classes drives. protected : char nom_vehicule[20]; int nombre_de_places; // donnes protges, transmises aux classes // drives mais toujours inaccessibles / / l'extrieur de l'objet public : // fonctions qui resteront publiques // dans les classes drives ( part les // constructeurs) // constructeur Vhicule(char *nom, int places); // fonctions d'accs aux donnes membre char *get_nom_vehicule(); int get_nombre_de_places(); void set_nom_vehicule(char *nom); int set_nombre_de_place(int p ) ; // fonctions de traitement void Afficher();;
};

// dfinition des fonctions Vhicule ::Vhicule (char *nom, int places) { sprintf(nom_vehicule, "%20s", n o m ) ; // on crit dans la chaine nom_vehicule les // premiers caractres de la chaine nom. nombre_de_places = places;
}

20

char
{

*Vehicule::get_nom_vehicule()

return nom_vehicule;
}

int
{

Vhicule::qet nombre de places!) return nombre_de_places;

46

Pont entre C et C++

void Vhicule::set nomvehicule (char *nom) { sprintf(nom_vehicule, "%20s", nom); // on crit dans la chaine nom_vehicule les 20 // premiers caractres de la chaine nom.
}

int {
}
//

Vhicule::set_nombre_de_place(int nombre_de_places = p;

p)

Voici maintenant la classe drive

class Voiture : public Vhicule


{

private : float SavantCalcul (); protected : int chevaux_fiscaux; public : // constructeurs Voiture(char *nom, int places, int chevaux); // fonctions d'accs int get_chevaux_fiscaux(); void set_chevaux_fiscaux(int c h ) ; // fonctions de traitement float CalculVignette();
} ;

Voiture:: Voiture (char *nom, int places, int chevaux)


{

sprintf(nom_vehicule, "%20s", n o m ) ; nombre_de_places = places; chevaux_fiscaux = chevaux; } int Voiture : :get_chevaux_fiscaux()


{

return chevaux_fiscaux;
}

void Voiture:: set_chevaux_fiscaux (int ch) { chevaux_fiscaux = ch;


}

float Voiture:: SavantCalcul (int code) { float savant_calcul; // on fait un savant calcul avec "code" return savant_calcul;

L'hritage simple

47

La fonction CalculVignette fait n'importe quoi, soyez certain que l'auteur en a parfaitement conscience.

float
{

Voiture:: CalculVignette()

int code; if (chevaux_fiscaux < 2) code = 0 ; else code = chevaux_fiscaux - 1; return (SavantCalcul(code));
}

Listes d'initialisations

L e s lecteurs les p l u s alertes n e m a n q u e r o n t p a s d e c o n s t a t e r q u e l q u e s l o u r d e u r s d a n s l ' e x e m p l e p r c d e n t . E x a m i n e z les c o n s t r u c t e u r s de la classe Voiture. E x a m i n e z m a i n t e n a n t les c o n s t r u c t e u r s de la classe Vhicule. Je rcapitule p o u r v o u s les d e u x c o n s t r u c t e u r s ci-dessous. N e r e m a r q u e z - v o u s pas une certaine ressemblance ?
Vhicule::Vehicule(char *nom, int places) { sprintf(nomvehicule, "%20s", nom); nombre_de_places = places;
}

Voiture::Voiture(char *nom, int places, int chevaux) { sprintf(nom_vehicule, %20s", nom); nombre de_places = places; chevaux_fiscaux = chevaux; }
n

O h , m a i s c'est b i e n sr ! L e s d e u x p r e m i r e s lignes s o n t i d e n t i q u e s ! N ' e s t - c e p a s l le s i g n e p r o v o c a t e u r d ' u n e red o n d a n c e inutile et d a n g e r e u s e p r o p r e susciter u n e colre aussi v i v e q u e justifie ? Le p r o b l m e vient du fait q u e les c o n s t r u c t e u r s (ainsi q u e les d e s t r u c t e u r s , m a i s c e n ' e s t p a s l e p r o p o s ici) n e s o n t p a s hrits. C e l a signifie q u e , q u e l q u e part d a n s le c o n s t r u c t e u r de Voiture, v o u s d e v e z initialiser les v a r i a b l e s - m e m b r e s dfin i e s d a n s Vhicule, et d o n t Voiture a hrit. Ici, il s'agit d e s v a r i a b l e s nom et places. Le p r o b l m e est en fait s i m p l e : c o m m e n t faire p o u r appeler un c o n s t r u c t e u r de la c l a s s e de B a s e , d e p u i s un c o n s t r u c t e u r d ' u n e classe d r i v e ? D e u x solutions :

48

Pont entre C et C++

Vous tes en train de paniquer ? Vous avez l'impression d'tre noy sous les n o u veauts syntaxiques du C + + ? Ne tremblez plus, car l'aidem m o i r e C + + est arriv ! Il est joli et il vous attend sur le rabat de la couverture !

Faire c o m m e d a n s l ' e x e m p l e , c'est--dire recopier les lig n e s de c o d e du constructeur de la c l a s s e de b a s e qui v o u s intresse. C ' e s t trs laid : d u p l i q u e r b t e m e n t du c o d e , c'est alourdir le p r o g r a m m e , et si j a m a i s ce c o d e doit tre modifi, v o u s devrez retrouver tous les e n d r o i t s o v o u s l ' a v e z recopi. I m a g i n e z u n p e u c e q u e cela peut d o n n e r d a n s u n e hirarchie de p l u s i e u r s c e n t a i n e s de classes ! La m e i l l e u r e solution : utiliser u n e liste d'initialisation. Q u ' e s t - c e d o n c ? C ' e s t un m o y e n p r a t i q u e d ' a p p e l e r direct e m e n t des constructeurs p o u r des d o n n e s - m e m b r e qu'il faut initialiser. Voici un e x e m p l e d'utilisation de la liste d'initialisation :
// constructeur de la classe Drive, amlior Voiture ::Voiture(char *nom, int places, int chevaux) : Vhicule(nom, places)
{

chevaux_fiscaux = chevaux;
)

J u s t e aprs les p a r a m t r e s du constructeur, tapez le caractre : (deux points) et a p p e l e z e n s u i t e les constructeurs d e s classes de b a s e s q u e v o u s a v e z c h o i s i s , a v e c leurs par a m t r e s b i e n e n t e n d u . Si v o u s a p p e l e z p l u s i e u r s constructeurs, sparez-les p a r u n e virgule. D a n s l ' e x e m p l e ci-dessus, les p a r a m t r e s nom e t p l a c e s utiliss p o u r appeler l e c o n s t r u c t e u r d e V h i c u l e sont ceux que vous avez passs au constructeur de V o i t u r e . V o u s p o u v e z g a l e m e n t utiliser u n e liste d'initialisation p o u r v o s variables s i m p l e s (entier, caractre, etc.) Exemple :
// constructeur de la classe Drive, encore amlior Voiture::Voiture(char *nom, int places, int chevaux) : Vhicule(nom, places), chevaux_fiscaux(chevaux) { }

L e s diffrents l m e n t s de la liste d'initialisation sont sp a r s p a r u n e virgule. V o u s r e m a r q u e z q u e m a i n t e n a n t , il n ' y a plus d'instruction d a n s le corps de la fonction ! La liste d'initialisation est le seul m o y e n d'initialiser une d o n n e - m e m b r e spcifie constante. R a p p e l o n s qu'une

L'hritage simple

49

c o n s t a n t e ne p e u t tre qu'' initialise (et j a m a i s affecte), voir p a g e 67. Un autre a v a n t a g e d e s listes d'initialisation : elles n e crent p a s d'objets t e m p o r a i r e s c o m m e u n e fonction ordinaire. L e s objets sont initialiss d i r e c t e m e n t , d ' o gain de t e m p s certain. Rsum U n e liste d'initialisation p e u t appeler d e s c o n s t r u c t e u r s d e s classes d e b a s e o u initialiser des variables s i m p l e s . R s u m de la s y n t a x e :
Classe::Classe(paramtres) : liste_ini { // corps du constructeur de Classe
}

O l i s t e _ i n i contient u n o u p l u s i e u r s l m e n t s s u i v a n t s , spars p a r d e s virgules :


ConstructeurClasseDeBase(paramtres) ou Variable(valeur_initiale)

La surcharge

Le C++ offre aux programmeurs la possibilit de dfinir des fonctions homonymes. Ce principe est appel surcharge de fonction .

Notions de base
L'ide crire d e u x fonctions qui portent l e m m e n o m est u n e c h o s e impensable en langage C. Pas en C + + . Imaginez que vous v o u l i e z crire u n e fonction qui trie des tableaux d'entiers. L ' e n t t e de cette fonction ressemblerait ceci :
void tri(int tab[], int taille_tableau);

Si p a r la suite v o u s a v e z b e s o i n d ' u n e fonction q u i trie des tableaux de rels, v o u s d e v e z rcrire la fonction, en lui donn a n t un autre n o m :
void tri_reels(float tab[], int taille_tableau);

S i v o u s tes logiques avec v o u s - m m e , v o u s d e v r e z galem e n t c h a n g e r le n o m de la fonction initiale :


void tri_entiers(int tab[], int taille_tableau);

52

Pont entre C et C++

Ce qui v o u s oblige modifier tous les appels cette fonction. G r c e au m c a n i s m e de surcharge de fonction, v o u s pouvez d o n n e r le n o m tri a u x d e u x fonctions, le compilateur pouv a n t les diffrencier grce a u x types d ' a r g u m e n t s . N o t e z q u e le l a n g a g e C p r o p o s e dj un type de surcharge p o u r les o p r a t e u r s arithmtiques : le m c a n i s m e mis en u v r e d p e n d du type des objets sur l e s q u e l s on invoque l'oprateur.

Mise en uvre C++

R e p r e n o n s notre e x e m p l e de tri, et appliquons-lui le principe de s u r c h a r g e . L e s d e u x enttes d e v i e n n e n t :


void void tri(int tab[], int taille_tableau); triffloat tab[], int taille_tableau);

La seule diffrence tant le type du p r e m i e r paramtre. E x a m i n o n s m a i n t e n a n t les appels de fonctions suivants :


void main() { float int

tab_f[] = { 1.5, 1.2, 7.2, 0.07 } ; tab_i[] = { 3, 2, 1, 0 };

tri(tab_f, 4 ) ; tri(tab_i, 4 ) ;
}

Le p r e m i e r a p p e l tri fait a u t o m a t i q u e m e n t rfrence la d e u x i m e fonction tri, qui accepte un tableau de flottants en p r e m i e r p a r a m t r e . Le d e u x i m e a p p e l tri se fait natur e l l e m e n t avec la fonction tri qui s ' o c c u p e d e s entiers. C o m m e n t le c o m p i l a t e u r fait-il la diffrence e n t r e plusieurs fonctions h o m o n y m e s ? T o u t d ' a b o r d , seules les fonctions situes d a n s la porte de l'appel c'est--dire visibles sont p r i s e s en compte. E n s u i t e , les critres suivants sont e x a m i n s : 1. Le type ou le n o m b r e de p a r a m t r e s . D s q u e d e u x fonctions h o m o n y m e s diffrent p a r le t y p e ou le n o m b r e de l'un ou de p l u s i e u r s de leurs p a r a m t r e s , le compilateur reconnat la b o n n e fonction s e l o n les types rels passs l'appel. C ' e s t l ' e x e m p l e q u e n o u s v e n o n s d e voir.

La surcharge

53

Attention ! Le type de retour de la fonction n ' e s t j a m a i s pris en c o m p t e ! Par e x e m p l e , il est illgal de dclarer les d e u x fonctions suivantes :
int float truc(int); truc(int);

Le c o m p i l a t e u r ne pourrait faire la diffrence. En revanc h e , il est toujours possible d'crire :


int float truc (int); truc (float);

P u i s q u e , cette fois, le type des p a r a m t r e s est diffrent. 2. Si l'tape 1 ne p e r m e t pas d'identifier la fonction a p p e ler, le c o m p i l a t e u r essaie de convertir les p a r a m t r e s rels vers les p a r a m t r e s des fonctions h o m o n y m e s . A i n s i , u n c h a r o u u n s h o r t seront convertis v e r s u n int, les float seront convertis v e r s des double, les p o i n t e u r s de classes drives seront convertis en p o i n t e u r s de classes de base*, etc. En cas d ' a m b i g u t insoluble, le c o m p i l a t e u r signale u n e erreur. D e toutes m a n i r e s , m i e u x v a u t viter les cas a m b i g u s qui sont s o u r c e d'affres ternelles.

* Les classes de base et les classes drives seront expliques au chaprtre 3, sur l'hritage.

L'intrt par rapport au C

O n pourrait croire q u e les fonctions h o m o n y m e s r e n d e n t les p r o g r a m m e s m o i n s lisibles et plus confus. C e r t e s , si v o u s dfinissez plusieurs fonctions traite_donnees , qui font des c h o s e s t o t a l e m e n t diffrentes, le gain en clart sera plutt discutable. E n r e v a n c h e , d a n s les cas o v o u s d i s p o s e z d ' u n j e u d e fonctions qui fait p r a t i q u e m e n t la m m e c h o s e , m a i s d o n t certains p a r a m t r e s diffrent, v o u s a v e z intrt utiliser la s u r c h a r g e . Bref, c h a q u e fois q u e le sens des fonctions est s e n s i b l e m e n t identique, d o n n e z - l e u r l e m m e n o m . D a n s tous les c a s , le l a n g a g e C v o u s oblige d o n n e r d e s n o m s diffrents, s i m p l e m e n t p o u r q u e l e c o m p i l a t e u r reconnaisse ses petits. R e m a r q u o n s q u e d a n s l ' e x e m p l e du tri,

54

Pont entre C et C + +

n o u s p o u r r o n s aussi n o u s servir du m c a n i s m e de la gnricit (mis en u v r e par les templates), e x p o s au c h a p i t r e 10.

Complments
Surcharge d'oprateurs
E n C + + , les o p r a t e u r s c o u r a n t s ( + , - , = = , etc.) p e u v e n t tre s u r c h a r g s . C o m m e n t est-ce p o s s i b l e ? En fait, q u a n d vous utilisez un oprateur op sur un objet, le c o m p i l a t e u r gnre un appel la fonction operator op. L e s trois critures suiv a n t e s sont q u i v a l e n t e s :
objet_l = objet_2 + objet_3; objet_l = operatort (objet2, objet3); objet_l = objet_2. (operator+ (objet_3));

// (1) // (2)

o p e r a t o r op , o op est un o p r a t e u r s t a n d a r d (ici +) est en ralit u n n o m d e fonction c o m m e u n autre, qui est dfini p a r dfaut p o u r les types d e b a s e d u C + + . D o n c , p o u r personnaliser le c o m p o r t e m e n t des o p r a t e u r s , il v o u s suffit de s u r c h a r g e r la fonction o p e r a t o r op correspondante, c o m m e v o u s s a v e z dj le faire p o u r v o s p r o p r e s fonctions. Attention ! Q u a n d v o u s s u r c h a r g e z un o p r a t e u r , il faut veiller choisir l'une ou l'autre des d e u x m t h o d e s de l ' e x e m p l e ci-dessus : D a n s le cas (1), il s'agit de la fonction operator+ globale, dfinie en d e h o r s de toute classe. Elle p r e n d d e u x paramtres, c o r r e s p o n d a n t aux d e u x m e m b r e s d e l'addition. D a n s le cas ( 2 ) , il s'agit de la f o n c t i o n - m e m b r e operator+, dfinie d a n s la classe d'objet_2, q u i s u p p o s e que l'objet sur lequel elle est a p p e l e est le p r e m i e r paramtre de l'opration d'addition (ici ob j et _2). Si v o u s utilisiez la fois ces d e u x m t h o d e s de surcharge p o u r la m m e fonction, le c o m p i l a t e u r ne pourrait p a s faire la diffrence. V o u s p o u v e z s u r c h a r g e r les o p r a t e u r s s u i v a n t s :

La surcharge

55

%
A

&

<

>

+ = - = *= / = ! = < = >= &&

%= = || ++

&= --

|= ,

<< > > = < < = ->* -> () []

==

V o y o n s l ' e x e m p l e d ' u n p r o g r a m m e c o m p l e t redfinissant l'oprateur + p o u r des objets de classe C o l o r :


#include <stdio.h> class Color { private : int rouge; int vert; int bleu; public : // constructeurs (voir chapitre 2) Color(int r, int v, int b ) ; Color(); void Afficher)); Color operator+ (Color Sdroite);
};

Color;:Color() { rouge = vert = bleu = 0 ;


}

Color::Color(int r, int v, int b) { rouge = r; vert = v; bleu = b;


)

void {

Color::Afficher() printf("rouge=%d vert=%d bleu=%d\n", rouge, vert, bleu);

* Le passage par rfrence est une exclusivit C++ ! Vous en saurez plus en lisant le chapitre 9, page 113.

Color Color::operator+ (Color Sdroite) // droite est pass par rfrence *


{

// fonction redfinissant l'oprateur + : // RESULTAT = A + B / / A est l'objet Color courant // B est le paramtre "droite" // RESULTAT est la valeur retourne Color rsultat) (rouge + droite.rouge) / 2, (vert + droite.vert) / 2, (bleu + droite.bleu) / 2 ) ;

56

Pont entre C et C++

return rsultat;
}

void {

main() Color rouge_pur(10, 0, 0 ) , bleu_pur(0, 0, 10), mixage ;

// //

mixage = rouge_pur + bleu_pur; cette ligne qruivaut : mixage = rouge_pur.operator+ (bleu_pur) mixage.Afficher(); // affiche "rouge=5 vert=0 bleu=5"

Plusieurs r e m a r q u e s : Le p r e m i e r p a r a m t r e d ' u n e fonction o p r a t e u r (Color &droite d a n s l ' e x e m p l e ) est p a s s p a r rfrence (voir chapitre 9, p a g e 1 1 3 ) . En d e u x m o t s : q u a n d v o u s p a s s e z un p a r a m t r e p a r rfrence, cela q u i v a u t p a s s e r l'objet original. Ainsi, modifier un p a r a m t r e p a s s p a r rfrence revient modifier le p a r a m t r e rel, utilis l'appel de la fonction. U n o p r a t e u r r e t o u r n e u n objet d e l a m m e classe q u e ses p a r a m t r e s (ici, un objet de classe Color). V o u s devrez d o n c crer un objet de cette classe d a n s la fonction oprateur, l'initialiser en fonction des d e u x p a r a m t r e s de l'oprateur, et le retourner.

P o u r q u o i passer droite par rfrence dans o p e r a t o r + ? E x p l i q u o n s plutt p o u r q u o i un p a s s a g e p a r valeur habituel serait p r o b l m a t i q u e . A d m e t t o n s q u e n o u s a y o n s dfini operator+ c o m m e suit :
Color Color::operator+ (Color droite) // droite est pass par valeur
{

// . . .
}

L e p a r a m t r e d r o i t e tant p a s s p a r v a l e u r , u n e copie s ' o p r e entre le p a r a m t r e effectif (l'objet b l e u _ p u r ) et le p a r a m t r e formel utilis d a n s l a fonction (l'objet d r o i t e ) .

La surcharge

57

D a n s c e cas, l a c o p i e n e p o s e p a s d e p r o b l m e , m a i s si, p a r e x e m p l e , votre classe c o m p o r t a i t un p o i n t e u r allou, la c o p i e d'objet p a r dfaut serait m a u v a i s e . A v e c un p a s s a g e p a r rfrence, il n ' y a p a s de c o p i e , p u i s q u e l a fonction o p e r a t o r + agit d i r e c t e m e n t sur l e p a r a m t r e effectif, b l e u _ p u r . E n d'autres t e r m e s , m a n i p u l e r l'objet d r o i t e q u i v a u t m a n i p u l e r l'objet b l e u _ p u r . Rcapitulons : Soit v o u s utilisez u n p a s s a g e p a r rfrence e t v o u s n ' a v e z p a s b e s o i n de v o u s soucier de la c o p i e de v o t r e objet, Soit v o u s utilisez u n p a s s a g e par v a l e u r e t v o u s d e v e z vrifier q u e la copie s'effectue bien. Si n c e s s a i r e , dfinissez un c o n s t r u c t e u r de c o p i e (voir p a g e 3 1 ) . Inutile de prciser m a i s je le fais q u a n d m m e q u e la p r e m i r e solution est plus intressante q u e la s e c o n d e .

Surcharge de l'oprateur =

P o u r q u o i redfinir l'oprateur = ? L'affectation d ' u n objet, via l'oprateur = , est u n e o p r a t i o n sensible qui mrite q u ' o n s'y attarde. Si v o u s ne dfinissez p a s d'oprateur = p o u r u n e classe, le c o m p i l a t e u r en g n r e un p a r dfaut qui effectue u n e affectation b t e , d o n n e s membre donnes membre. Bien que ce genre de comport e m e n t soit satisfaisant d a n s le cas de classes s i m p l e s , il devient parfois d a n g e r e u x si v o u s utilisez des p o i n t e u r s . Le p r o b l m e est similaire celui du constructeur-copie (voir page 31). C o n s i d r o n s l ' e x e m p l e suivant :
Sinclude <stdio.h> tinclude <string.h> class Exclamation { private: char *cri ; public : Exclamation(char *c) { cri = new char[strlen(c)+1]; strcpy(cri, c ) ; } void set_cri(char * n c ) ; void Affiche() { printf("%s\n", cri); )
};

Le constructeur Exclamation ainsi que la fonction Affiche sont dfinis inline (voir page 21 ). new et delete remplacent malloc et free (voir chapitre 6 page 83).

58

Pont entre C et C++

void Exclamation::set_cri(char *nc) { // vrifions si nous n'avons pas affaire aux // mme pointeurs if (ne == cri) return; // librons la mmoire de l'ancienne valeur de cri delete cri; // allouons de la mmoire pour la nouvelle valeur cri = new char[strlen(nc)+1]; // copions le paramtre dans la donne cri. strcpy(cri, n e ) ;
}

void main() { Exclamation

beurk("Beurk"), bof("Bof"); // affiche "Beurk" // affiche "Bof" // operateur= dfini par dfaut // affiche "Beurk" // affiche "Beurk" / / o n modifie bof et beurk

beurk.Affiche(); bof.Affiche(); bof = beurk; beurk.Affiche(); bof.Affiche();

bof.set_cri("Ecoeurant" ); beurk.Affiche(); bof.Affiche();


}

// affiche n'importe quoi // affiche "Ecoeurant"

La c o p i e p a r dfaut a ici des effets d s a s t r e u x : en copiant u n i q u e m e n t le p o i n t e u r cri et n o n ce qui est p o i n t , n o u s n o u s r e t r o u v o n s avec d e u x objets d o n t la d o n n e cri pointe sur la m m e z o n e m m o i r e :

La surcharge

59

Le rsultat de cette d c a d e n c e ne se fait p a s attendre : il suffit d e modifier l'un des d e u x objets p o u r q u e s o n j u m e a u soit affect. C ' e s t ce q u e n o u s faisons dans la ligne bof . s e t _ c r i ( " E c o e u r a n t ") ; Cette instruction libre l ' a n c i e n n e v a l e u r d e c r i e n a p p e l a n t d e l e t e . O r , cette anc i e n n e valeur est g a l e m e n t p a r t a g e p a r l'objet b e u r k . S a n s le savoir, n o u s a v o n s d o n c effac d e u x cris au lieu d'un. C o m m e n t redfinir l'oprateur = ? C e s constatations affreuses n o u s p o u s s e n t crire notre propre oprateur d'affectation, qui va se c h a r g e r de copier le c o n t e n u d e s p o i n t e u r s cris. R a p p e l o n s q u e l ' u s a g e de = appelle l a fonction o p r t o r = . Voici l'exemple complet :
tinclude <stdio.h> #include <string.h> class Exclamation { private: char *cri ; public: Exclamation(char *c) { cri = new char[strlen(c)+1]; strcpy(cri, c ) ; } void set_cri(char * n c ) ; void Affiche() { printf("%s\n", cri); ) Exclamation
};

&operator= (Exclamation &source);

60

Pont entre C et C++

void Exclamation::set_cri(char *nc) { if (ne == cri) return; delete cri; cri = new char(strlen(ne)+1]; strcpy(cri, n e ) ; }
Cette fonction, qui retourne une rfrence, est explique la suite du listing.

Exclamation &Exclamation::operator =(Exclamation Ssource) { // vitons d'affecter deux objets identiques if (this == &source) return *this; // retournons l'objet courant delete cri; cri = new char[ strlen(source.cri)+1 ]; strcpy(cri, source.cri); return *this; // retournons l'objet courant
}

void main() { Exclamation

beurk("Beurk"), bof("Bof"); // affiche "Beurk" // affiche "Bof" // operateur= dfini par nos soins // affiche "Beurk" // affiche "Beurk" // on ne modifie que bof

beurk.Affiche(); bof.Affiche(); bof = beurk; beurk.Affiche(); bof.Affiche();

bof.set_cri("Ecoeurant"); beurk.Affiche() ; bof.Affiche();


}

// affiche "Beurk" : parfait ! // affiche "Ecoeurant"

P o u r q u o i ' r e t u r n *this'? L ' o p r a t e u r = doit retourner un objet de type Exclamation. P l u s e x a c t e m e n t , i l doit retourner l'objet sur lequel o p e r a tor+ a t a p p e l , a p r s q u e l'affectation a t faite. En ret o u r n a n t *this, n o u s r e t o u r n o n s b i e n l'objet c o u r a n t q u e n o u s v e n o n s d e modifier. M a i s p o u r q u o i retourner u n e rfrence plutt q u ' u n objet Exclamation tout s i m p l e ? P o u r r p o n d r e c o r r e c t e m e n t au cas de figure suivant :
(bof = beurk).Affiche();

La surcharge

61

Si n o u s r e t o u r n i o n s u n e s i m p l e copie du rsultat, l ' o p r a t i o n A f f i c h e n e s'effectuerait p a s sur b o f m a i s sur s a c o p i e . P o u r q u o i faire le test 'if (this == & s o u r c e ) ' ? Ce test a p o u r b u t de vrifier si n o u s ne v o u l o n s p a s affecter l'objet l u i - m m e (ce serait le cas de l'instruction a = a ; ). Si c e test n'tait p a s ralis, n o u s n o u s r e t r o u v e r i o n s d a n s u n e situation trs dsagrable. R e g a r d e z un instant la p r e m i r e ligne aprs l e test. O u i , c'est b i e n d e l e t e c r i . I m a g i n e z m a i n t e n a n t q u ' u n c o q u i n ait crit b e u r k = b e u r k . E n faisant d e l e t e c r i , n o u s effacerions l e c r i d e l'objet s o u r c e mais aussi, sans le vouloir, celui de l'objet destination. D s lors, rien d ' t o n n a n t ce q u e n o u s n o u s e x p o s i o n s l ' i m p r v u e n a c c d a n t a u c r i d e l'objet source.

Oprateurs de conversion de types

L'opration rciproque, savoir la conversion d'un type quelconque en un objet de la classe, est tout fait possible ! Il suffit d'utiliser un constructeur spcial (voir page 35),

P o u r autoriser et contrler la c o n v e r s i o n d ' u n e classe A v e r s un type T q u e l c o n q u e , v o u s p o u v e z dfinir un o p r a t e u r de c o n v e r s i o n de n o m operator T ( ). C h a q u e fois q u ' u n objet de classe A doit tre c o n s i d r c o m m e u n e v a r i a b l e de t y p e T, l'appel cet o p r a t e u r se fera a u t o m a t i q u e m e n t . Attention ! Ce g e n r e d ' o p r a t e u r ne doit pas avoir de type de retour, m a i s doit tout de m m e r e t o u r n e r u n e variable de t y p e T (on vite ainsi l a r e d o n d a n c e d u n o m d u t y p e e t d e s r i s q u e s d ' i n c o h r e n c e lis cette r e d o n d a n c e superflue). A d m e t t o n s q u e v o u s v o u l i e z m o d l i s e r des articles d a n s u n m a g a s i n . La classe Article, qui contient diverses informations, doit p o u v o i r tre utilise d a n s d e s e x p r e s s i o n s de calcul de prix. Il faut d o n c dfinir operator float ( ) ;
#include <string.h> #include <stdio.h> class Article { protected: char nom[50]; float prix; int nb_disponible; public: Article(char *n, float p, int nb) : prix(p), nb_disponible(nb) { strcpy(nom, n ) ; } operator float(); };

On ne devrait pas spcifier la taille du tableau directement dans la classe, mais plutt utiliser une constante. Toutes mes excuses p o u r cette hrsie honte.

62

Pont entre C et C++

Article : :operator float()


{

return prix; } void main() { float total; Article b("Bonne bire", 12., 3 0 ) ; total = 5 * b; // conversion de b en float printf("5 bonnes bires valent %.2f F", total);
}

// affichage : 5 bonnes bires valent 60.00 F

Les petits + du C++

Pour conclure cette premire partie, attardons-nous sur les petits changements qui rendent la vie du programmeur plus agrable. Contrairement aux autres chapitres, celui-ci ne se dcompose pas en deux parties (notions de base et complments). Chaque point est expliqu en une seule fois, sans distinction de niveau de difficult .

Les commentaires
V o u s tes u n p r o g r a m m e u r c o n s c i e n c i e u x , v o u s c o m m e n t e z donc abondamment vos programmes. Le C + + a pens v o u s e t p r o p o s e u n e n o u v e l l e m a n i r e d'introduire d e s c o m m e n t a i r e s d a n s les c o d e s - s o u r c e s :
void
{

main()

// commentaire pertinent jusqu' la fin de la ligne


}

L e s d e u x caractres / / i n d i q u e n t au p r - p r o c e s s e u r le d b u t d ' u n c o m m e n t a i r e , qui s'arrte a u t o m a t i q u e m e n t la fin de la ligne. V o u s n ' a v e z d o n c p a s b e s o i n de r p t e r / / p o u r m e t t r e fin au c o m m e n t a i r e .

64

Pont entre C et C++

B i e n e n t e n d u , v o u s p o u v e z toujours utiliser l ' a n c i e n style de c o m m e n t a i r e (entre / * e t * / ) , trs p r a t i q u e p o u r les prog r a m m e u r s qui ont b e a u c o u p de c h o s e s r a c o n t e r sur p l u s i e u r s lignes. L e s d e u x styles d e c o m m e n t a i r e s peuvent tre i m b r i q u s : u n e section de c o d e entre / * et * / p e u t contenir d e s c o m m e n t a i r e s / / , et r c i p r o q u e m e n t .

Ruse de sioux

Il existe un autre m o y e n de m e t t r e en c o m m e n t a i r e de nomb r e u s e s lignes, trs utile si ces lignes c o n t i e n n e n t dj des c o m m e n t a i r e s avec / * et * /. P e n s e z au p r - p r o c e s s e u r et utilisez # i f d e f e t # e n d i f . Voici l e listing initial :
void
{

fonction_boguee() *pointeur; // pointeur sur un truc

char

/* dbut de la fonction */ strcpy(pointeur, "Plantage"); }

V o u s v o u l e z m e t t r e en c o m m e n t a i r e tout le c o n t e n u de la fonction. A m o i n s de s u p p r i m e r le c o m m e n t a i r e intrieur, v o u s ne p o u v e z p a s utiliser / * et * / , p u i s q u e le p r e m i e r / * serait ferm par le * / du c o m m e n t a i r e intrieur. La solution :


void { #if 0 char fonction_boguee()

*pointeur;

// pointeur sur un truc

/* dbut de la fonction */ strcpy(pointeur, "Plantage"); #endif }

Rappelons que # i f expression est v a l u p a r l e prp r o c e s s e u r . Si expression est vrai, le c o d e qui suit (jusqu' # e n d i f ) est c o m p i l . Il suffit d o n c de d o n n e r u n e expression fausse p o u r m e t t r e en c o m m e n t a i r e s la z o n e de code v o u l u e . C ' e s t p o u r q u o i n o u s utilisons # i f 0 qui n ' e s t jam a i s vrai.

L e s petits + du C++

65

Paramtres par dfaut


L aussi, u n e ide s i m p l e et efficace : v o u s p o u v e z d o n n e r u n e v a l e u r p a r dfaut p o u r u n o u p l u s i e u r s p a r a m t r e s d e fonction, d a n s la dclaration de cette fonction. A i n s i , l'appel, v o u s p o u v e z o m e t t r e les p a r a m t r e s qui p o s s d e n t u n e v a leur p a r dfaut. E x e m p l e :
/* fichier fonction.h */ float calcul_tva(float montant, float tva = 18.6);

Ici, on donne une valeur par dfaut (18.6) au deuxime paramtre de la fonction calcul_tva.

/* fichier fonction.cpp */ float calcul_tva(float montant, float tva) { if (tva > 0. && tva < 100.) return montant * (tva / 100.); else { /* traitement d'erreur... */ }
}

/* fichier main.cpp */ #include "fonction.h" void


{

main() res;

float

res = calcul_tva(100.); // 2me argument vaut 18.6 res = calcul_tva(100., 5.5); res = calcul_tva(100., 18.6);
}

I l existe d e u x possibilits d ' a p p e l e r l a fonction c a l cul_tva : A v e c d e u x p a r a m t e s : c'est la solution c l a s s i q u e . D a n s ce cas, le p a r a m t r e p a r dfaut est c r a s p a r la v a l e u r d'appel du deuxime paramtre. A v e c un seul p a r a m t r e : d a n s ce c a s , le c o m p i l a t e u r s u p p o s e q u e l e d e u x i m e p a r a m t r e p r e n d l a v a l e u r p a r dfaut (dans l ' e x e m p l e , 1 8 . 6 ) . Attention : t o u s les p a r a m t r e s qui d i s p o s e n t d ' u n e v a l e u r p a r dfaut d o i v e n t tre dclars aprs les p a r a m t r e s norm a u x , c'est--dire la fin de la liste des p a r a m t r e s .

66

Pont entre C et C++

P l u s i e u r s p a r a m t r e s par dfaut Il est p o s s i b l e de dfinir u n e v a l e u r par dfaut p o u r plusieurs p a r a m t r e s d'une fonction. Veillez c e p e n d a n t les dclarer aprs les p a r a m t r e s n o r m a u x . L ' e x e m p l e suivant est i m p o s s i b l e car a m b i g u :
void phonquession(int a = 1, int b, int c = 3); // erreur !

C e t t e dclaration est i m p o s s i b l e : le p r e m i e r p a r a m t r e a une v a l e u r p a r dfaut, m a i s p a s le d e u x i m e . P o u r q u o i n'est-ce p a s autoris ? C ' e s t a s s e z l o g i q u e . I m a g i n e z q u e v o u s appeliez la fonction c o m m e ceci :
phonquession(12 , 13);

M e t t e z - v o u s un instant d a n s la p e a u du p a u v r e compilateur : q u e v e u t dire cet appel ? Q u e le p r e m i e r p a r a m t r e vaut 12, et le d e u x i m e 13 ? Ou b i e n le d e u x i m e 12 et le troisime 13 ? A l l o n s , s o y o n s r a i s o n n a b l e s , et c o n t e n t o n s - n o u s de dclarer les v a l e u r s par dfaut la file, en fin d ' e n t t e :
void phonquession(int // mieux. b, int a = 1, int c = 3 ) ;

A i n s i , n o u s l e v o n s toute a m b i g u t . Voici c o m m e n t seraient interprts diffrents appels p h o n q u e s s i o n :


phonquession(10); // b:10 a:l phonquession(10, 11); // b:10 a:ll phonquession(10, 11, 1 2 ) ; // b:10 a:ll c:3 c:3 c:12

Les petits + du C++

67

Les constantes
Habitu(e) du l a n g a g e C, v o u s d e v e z c e r t a i n e m e n t utiliser # d e f ine tour de b r a s p o u r dfinir v o s c o n s t a n t e s . L e s p l u s a u d a c i e u x d'entre v o u s a u r o n t peut-tre a d o p t le mot-cl const ( d i s p o n i b l e d e p u i s la n o r m e ANSI du l a n g a g e C ) . Le C + + v a p l u s loin e n t e n d a n t les possibilits d e const. N o u s allons voir tout cela au travers des diffrents m o y e n s d'utiliser des c o n s t a n t e s e n C + + . U n e c o n s t a n t e s i m p l e , visible d a n s un b l o c ou un seul fichier s o u r c e C ' e s t la m a n i r e la plus s i m p l e de dclarer u n e c o n s t a n t e :
const float TVA = 0.186;

Il est i n d i s p e n s a b l e d'initialiser la c o n s t a n t e au m o m e n t de sa dclaration. On ne p e u t p a s s i m p l e m e n t dclarer const float TVA, puis, q u e l q u e s lignes en d e s s o u s , affecter u n e valeur TVA. T o u t c o m p i l a t e u r C + + v o u s insultera si v o u s agissez de la sorte. D a n s le cas d ' u n tableau constant, ce sont tous ses l m e n t s qui sont c o n s t a n t s :
const float tab[2] = 1.1; tab[3] = { 2.2, 3.3, 4.4 } ; // impossible, refus par le compilateur

C o m m e toutes les autres variables, la p o r t e d ' u n e c o n s t a n t e d p e n d de l'endroit o elle a t dclare. C o n s t a n t e s et p o i n t e u r s Il faut b i e n distinguer ce qui est point du pointeur l u i - m m e . G r c e const, n o u s s o m m e s en m e s u r e de spcifier au c o m p i l a t e u r si n o u s v o u l o n s q u e le pointeur soit c o n s t a n t , ou b i e n q u e les donnes pointes soient c o n s t a n t e s , ou e n c o r e q u e les d e u x soient figs. Dtaillons ces trois c a s :

68

Pont entre C et C++

char char char

chainel[] = "baratin"; chaine2 [ ] = "billevei/ses " ; chaine3[] = "balivernes";

// ce qui est en gras est constant const char * p = chainel; char * const p = chaine2; const char * const p = chaine3 ; // (1) // (2) // (3)

D a n s (1), la c h a n e " b a r a t i n " est c o n s t a n t e , m a i s pas le p o i n t e u r p. V o u s p o u v e z d o n c crire p = 0 (aprs v o u s tre assur q u ' u n autre p o i n t e u r d s i g n e " b a r a t i n " , sans quoi v o u s ne p o u v e z p l u s y a c c d e r ) . D a n s (2), c'est le p o i n t e u r p qui est constant. Si v o u s utilisez u n autre p o i n t e u r p o u r a c c d e r " b i l l e v e u s e s " , v o u s p o u r r e z la modifier librement. D a n s (3), tout est verrouill. Ni p ni la chane " b a l i v e r n e s " n e p e u v e n t tre modifis. Illustrons u n e dernire fois ce qui est p o s s i b l e et ce qui ne l'est p o i n t :
void
{

main()

char chainel[] = "baratin"; char chaine2[] = "billeveuses"; char chaine3[] = "balivernes"; const char * p = chainel; // (1) char * const p = chaine2; // (2) const char * const p = chaine3 ; // (3) pl[2] = 'X'; pl = new char[10]; p2[2] = 'X; p2 = new char[10]; p3[2] = 'X'; p3 = new char[10];
}

// impossible // OK / / OK // impossible // impossible // impossible

Pourquoi ne pas avoir dclar les chanes comme ceci :


const char * p = "baratin";

En c o d a n t la c h a n e " b a r a t i n " en d u r c o m m e ci-dessus, n o u s n ' a u r i o n s p a s pu la modifier. En effet, u n e chane d-

Les petits + du C++

69

clare ainsi est s t o c k e d a n s u n e z o n e d e m m o i r e p r o t g e . T o u t accs cette z o n e e n g e n d r e un p l a n t a g e p l u s ou m o i n s i m m d i a t . N o u s a v o n s d o n c t contraints d'utiliser d e s v a riables intermdiaires, qui stockent les d o n n e s d a n s u n e z o n e d e m m o i r e modifiable. U n e c o n s t a n t e s i m p l e , v i s i b l e d a n s p l u s i e u r s f i c h i e r s sources Un petit rappel de C s t a n d a r d s ' i m p o s e . Si v o u s v o u l e z dclarer u n e variable globale visible d a n s p l u s i e u r s fichiers sources, v o u s p r o c d e z ainsi :

C o m m e v o u s l e v o y e z , l a variable g l o b a l e n ' e s t dfinie q u ' u n e seule fois, d a n s l e fichier m a i n . c p p . C h a q u e fois q u ' u n autre fichier v e u t p o u v o i r l'utiliser, il doit i n d i q u e r q u e cette variable existe et est dfinie ailleurs , d a n s un autre fichier (c'est l e rle d u m o t - c l e x t e r n ) .

Les petits + du C++

71

D c l a r e r q u ' u n e fonction r e t o u r n e u n e c o n s t a n t e N e lisez p a s c e p a r a g r a p h e avant d e c o n n a t r e les rfrences, e x p o s e s a u chapitre 9 , p a g e 113. P o u r q u ' u n e fonction retourne u n e rfrence c o n s t a n t e , i l suffit d ' i n d i q u e r c o n s t a v a n t le type de retour de la fonction. L'intrt de cette t e c h n i q u e est d'viter la c o p i e d ' u n objet entre la fonction a p p e l e et la fonction appelante, tout en p r s e r v a n t l'intgrit de l'objet. E x e m p l e :
const int &f() { static big_objet big; // manipulations sur big return big;
}

void main() { f() = 0; // dclenche une erreur de compilation // cause du const.


}

D c l a r e r u n e c o n s t a n t e interne u n e classe C ' e s t un p r o b l m e intressant : c o m m e n t dclarer u n e c o n s tante qui ne sera visible et utilisable q u e d a n s sa classe ? V o u s seriez peut-ter tent de p r o c d e r ainsi :
class MaClasse { private: const int MAX = 100; public :
// . . .
} ;

// incorrect, hlas

C ' e s t oublier q u e l'on ne p e u t p a s affecter de v a l e u r directem e n t d a n s l a dclaration d ' u n e classe. V o u s n e p o u v e z q u e dclarer d e s variables, p a s les initialiser. M a l i n , v o u s v o u s dites q u e v o u s aller initialiser la c o n s t a n t e en d e h o r s de la classe :
class MaClasse { private: const int MAX; public :
// ...

// toujours incorrect

72

Pont entre C et C++

} ;

MaClasse::MAX = 100;

// toujours incorrect

C ' e s t e n c o r e i m p o s s i b l e , car v o u s n e p o u v e z affecter a u c u n e v a l e u r u n e c o n s t a n t e , en d e h o r s de s o n initialisation. Et ce n ' e s t p a s u n e initialisation ici, car MAX est u n e variable d ' i n s t a n c e (lie un objet prcis) et n o n de classe. Elle ne p e u t d o n c p a s tre initialise sans tre s t a t i c . Q u e faire, m e direz-vous ? I l existe d e u x solutions. U n e avec s t a t i c c o n s t , l'autre avec enum. La static const solution. U n e c o n s t a n t e p r o p r e u n e classe doit tre l o g i q u e m e n t dclare s t a t i c c o n s t . Elle doit tre s t a t i c car c'est u n e variable de classe, d o n t la valeur est c o m m u n e tous les objets de cette classe ; et elle doit tre c o n s t car on v e u t interdire les modifications de sa valeur. L a seule m a n i r e d'initialiser u n e variable s t a t i c c o n s t dclare d a n s u n e classe, c'est de l'initialiser en d e h o r s de la classe. En voici la d m o n s t r a t i o n :
class MaClasse { private: static const int MAX; public : MaClasse();
} ;

// constante de classe

const int MaClasse: :MAX = 100;

L ' e n u m solution Si v o s c o n s t a n t e s sont de t y p e entier, v o u s p o u v e z utiliser e n u m p o u r les dclarer et les initialiser l'intrieur d ' u n e classe. B i e n q u e cela ne m a r c h e q u e sur les entiers, cette solution p r s e n t e l ' a v a n t a g e d'initialiser la c o n s t a n t e immdiatement. C ' e s t la seule m t h o d e q u e je c o n n a i s s e pour initaliser u n e c o n s t a n t e de classe d a n s le corps de la c l a s s e , et d o n c qui p e r m e t t e de l'utiliser p o u r d i m e n s i o n n e r un tableau d a n s la classe. V o i c i un e x e m p l e :

Les petits + du C++

73

class MaClasse { private: enum { MAX = 100 }; int tab[MAX]; public :


// ...

// MAX est de type int // OK

>;

Dclarations
E n C + + v o u s p o u v e z dclarer v o s variables o u objets n ' i m p o r t e o d a n s le c o d e , y c o m p r i s a p r s u n e srie d'instructions. La p o r t e de telles variables va de l'endroit de leur dclaration la fin du bloc c o u r a n t ( l ' a c c o l a d e f e r m a n t e en gnral).
void
{

main()

int i; for (i=0; i<100; i++) { int j ; // traitement


}

// ici j est inconnu


}

V o u s p o u v e z dclarer d a n s u n sous-bloc u n e variable portant l e m m e n o m q u ' u n e autre variable d c l a r e d a n s u n bloc suprieur. D a n s c e c a s , l a variable l a p l u s p r o c h e d e l'endroit de l'instruction est utilise. E x e m p l e :
void
{

main()

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

74

Pont entre C et C++

// ici i vaut 100 et pas 1


}

Variable de boucle Voici u n e construction intressante : v o u s p o u v e z dclarer u n e variable de b o u c l e d i r e c t e m e n t dans l'instruction f or ( , , ), ce qui v o u s garantit q u e p e r s o n n e n'utilisera cette variable en d e h o r s de la b o u c l e :
void main() { for (int i = 0; i < 100; i++) { // traitement
}
)

Rsum

La dcouverte du C++ est un peu droutante, tant les termes et concepts indits sont nombreux. C'est pourquoi nous vous proposons ici un rsum rapide de la premire partie du livre. Il vous permettra d'avoir les ides claires avant d'aborder la suite, riche en rebondissements...

Rsum premire partie

77

Encapsulation

Le p r i n c i p e fondateur de la p r o g r a m m a t i o n oriente-objet est l'encapsulation. Au lieu de r s o u d r e un p r o b l m e p a r d e s structures e t des fonctions s a n s lien explicite ( c o m m e e n C ) , l ' e n c a p s u l a t i o n p r o p o s e d'utiliser des objets, entits inform a t i q u e s r e g r o u p a n t d o n n e s e t fonctions i n t i m e m e n t lies. L e s d o n n e s d ' u n objet n e p e u v e n t tre m a n i p u l e s q u e p a r les fonctions cres s p c i a l e m e n t p o u r cela. E n C + + , u n objet est u n e i n s t a n c e de classe, q u e l'utilisateur p e u t dfinir c o m m e il le veut. L e s fonctions qui m a n i p u l e n t les d o n n e s d ' u n objet s o n t appeles fonctions-membres. De m u l t i p l e s objets p e u v e n t tre crs partir de la m m e classe, c h a c u n c o n t e n a n t des v a l e u r s diffrentes. L e s objets sont issus d ' u n e classe.
class NomClasse { private: // variables et objets public : // fonctions de manipulation de ces variables / / e t objets
} ;

void
{

main() objetl; objet2;

NomClasse NomClasse

objetl.nom_de_fonction_public();
}

Constructeurs & destructeurs


* qu'il sort simplement dclar sttiquement ou allou dynamiquement par l'oprateur new.

C h a q u e classe doit p o s s d e r au m o i n s u n e fonction constructeur et u n e fonction destructeur. Si le p r o g r a m m e u r n ' e n dfinit pas, le c o m p i l a t e u r en g n r e p a r dfaut. Le c o n s t r u c t e u r est appel c h a q u e cration d'objet*, et p e r m e t a u p r o g r a m m e u r d'initialiser c o r r e c t e m e n t l'objet. L e destructeur est a p p e l ds q u ' u n objet sort de sa p r o p r e p o r te o u est d s a l l o u e x p l i c i t e m e n t p a r u n a p p e l d e l e t e .

78

Pont entre C et C++

Hritage

L'hritage p e r m e t de mettre en relation d e s classes. Q u a n d u n e classe B hrite d ' u n e classe A, A est a p p e l e classe de b a s e et B classe drive. Au n i v e a u s m a n t i q u e , B est u n e sorte de A (voir e x e m ple p a g e suivante). Au n i v e a u du C + + , la classe B recopie (en q u e l q u e sorte) les d o n n e s et fonctions de la classe A. Un objet de classe B intgre donc les d o n n e s et fonctions accessibles de A.

La classe drive B p e u t redfinir des fonctions de la classe de b a s e A. On parle alors de redfinition de fonction membre, ce qui signifie q u ' u n m m e entte de fonction est partag p a r toute u n e hirarchie de classes.

Rsum premire partie

79

Surcharge

La surcharge de fonction p e r m e t au p r o g r a m m e u r de d o n n e r l e m m e n o m d e s fonctions situes d a n s l a m m e c l a s s e , o u e n d e h o r s d e toute classe. L a seule c o n t r a i n t e est q u e l e c o m p i l a t e u r p u i s s e faire la distinction g r c e au n o m b r e ou au t y p e des p a r a m t r e s de ces fonctions s u r c h a r g e s .
int
{

carre(int a) return (a * a) ;

float carre(float a) { return (a * a ) ;


}

Encore plus de C++


Vous connaissez maintenant les grands principes du C++. Mais votre voyage au pays des objets n'est pas fini : il vous reste dcouvrir d'autres concepts qui enrichissent encore plus le langage. Une bonne connaissance des lments prsents dans la premire partie facilitera votre lecture.

La fin du malloc
new et delete

Nous sommes au regret de vous annoncer le dcs de malloc, survenu aprs de nombreuses annes de bons et loyaux services... En ralit, bien que vous puissiez toujours utiliser malloc, calloc et free, le C++ met votre disposition new et delete, leurs talentueux remplaants.

Notions de base
L'ide L e C + + propose deux nouveaux mots-cls, new e t d e l e t e , p o u r r e m p l a c e r leurs ans m a l l o c e t f r e e . P o u r q u o i u n tel c h a n g e m e n t ? P o u r s'adapter a u x n o u v e l l e s caractristiques d u C + + q u e sont l'hritage, les c o n s t r u c t e u r s e t les destructeurs. E n effet, alors q u e m a l l o c e t f r e e s o n t d e u x fonctions d e l a b i b l i o t h q u e standard d u C , n e w e t d e l e t e s o n t c o m p l t e m e n t intgrs au l a n g a g e C + + . Ce sont la fois d e s m o t s - c l s (rservs) et d e s oprateurs. Le rle de n e w est d o u b l e : il r s e r v e l ' e s p a c e m m o i r e q u ' o n lui d e m a n d e , p u i s l'initialise en a p p e l l a n t les c o n s t r u c t e u r s des objets crs.

84

Pont entre C et C++

L e rle d e d e l e t e est s y m t r i q u e : i l a p p e l l e les d e s t r u c t e u r s des objets crs p a r new, p u i s libre l ' e s p a c e m m o i r e rserv.

Mise en uvre C++

L'oprateur new C o m m e m a l l o c , l'oprateur n e w r e t o u r n e soit l ' a d r e s s e m m o i r e n o u v e l l e m e n t alloue, soit 0 (zro) si l'opration a chou. On p e u t utiliser n e w de trois m a n i r e s diffrentes :

Dans les cas cicontre, la variable pt dsigne toujours un pointeur de T (dclar avec T *pt).

pt = new T;
L'oprateur new recherche un espace mmoire pour un seul l m e n t de type T. Ici, T p e u t d s i g n e r un t y p e standard ( c h a r , i n t , etc.) o u u n e classe. D a n s c e dernier c a s , n e w appellera le c o n s t r u c t e u r par dfaut de la c l a s s e T. pt = new T {arguments d'un constructeur de classe T) -, Ici, au lieu d ' i n v o q u e r le c o n s t r u c t e u r p a r dfaut de la classe T, n e w appelera le c o n s t r u c t e u r c o r r e s p o n d a n t a u x paramtres indiqus. pt = new T [taille du tableau] ; C e t t e s y n t a x e d e m a n d e n e w de c h e r c h e r un e s p a c e m m o i r e p o u v a n t contenir t a i 1 2 e _ t a b l e a u l m e n t s conscutifs de type T (T p o u v a n t tre un t y p e s t a n d a r d ou u n e classe). Le c o n s t r u c t e u r p a r dfaut de la c l a s s e T est invoqu pour chaque lment. L'oprateur delete I l est fortement r e c o m m a n d d ' a p p e l e r d e l e t e p o u r librer l ' e s p a c e - m m o i r e allou p a r new. L a s y n t a x e d e d e l e t e est simple : d e l e t e pt; A p p e l l e le destructeur de pt (si pt est un p o i n t e u r v e r s u n objet) p u i s libre l ' e s p a c e m m o i r e p r a l a b l e m e n t allou p a r u n new. d e l e t e [ ] pt; M m e c h o s e , m a i s p o u r librer u n tableau allou a v e c new T [ taille tableau ].

A t t e n t i o n ! Dtruire un objet individuel avec l'instruction delete [ ] p r o v o q u e quelque chose d'indfini mais de gnralement nuisible (loi de Murphy). De mme, utiliser delete p o u r dtruire un tableau entraine des consquences t o u t aussi alatoires !

La fin du malloc

85

L e s spcifications officielles d u C + + v o u s g a r a n t i s s e n t q u e rien d e m a u v a i s n'arrivera s i v o u s a p p e l e z d e l e t e s u r u n p o i n t e u r nul. Exemple R e g a r d o n s m a i n t e n a n t c o m m e n t utiliser n e w e t d e l e t e p o u r allouer et librer des c h a n e s de caractres. P r e n o n s c o m m e e x e m p l e la classe CDRom, dfinie ci-dessous :
class CDRom { protected : char * titre; public : CDRom(); CDRom(char *tit); -CDRom();
} ;

// dfinition des constructeurs CDRom: :CDRom()


{

titre = new char[8]; if (titre == 0) badaboum(); strcpy(titre, "<Aucun>");


}

CDRom::CDRom(char *tit) { titre = new char[ strlen(tit) + 1 ]; if (titre == 0) badaboum(); strcpy(titre, tit); } // dfinition du destructeur CDRom::-CDRom()
{

delete [] titre;
}

Vous constarerez que nous avons bien respect la symtrie n e w / d e l e t e . L a fonction b a d a b o u m s'occupe d e traiter les erreurs, l o r s q u e n e w n ' e s t p a s p a r v e n u allouer la m m o i r e demande. E x a m i n o n s m a i n t e n a n t c o m m e n t utiliser n e w e t d e l e t e a v e c d e s objets. P u i s q u e n o u s l ' a v o n s s o u s l a m a i n , g a r d o n s

la classe CDRom :

86

Pont entre C et C++

void
{

main() *pt_inconnu, *pt_cd_X; rebel_assault("Rebel Assault"); // (1) // (2) // (3) // // // // // // (4) (5) (6) (7) (8) (9)

CDRom CDRom

pt_inconnu = new CDRom; pt_cd_X = new CDRom("Virtual Escort"); // blabla delete pt_inconnu; delete pt_cd_X; }

L e s instructions (1) et (2) n ' a p p e l l e n t a u c u n c o n s t r u c t e u r , et ne r s e r v e n t de la p l a c e q u e p o u r les p o i n t e u r s e u x - m m e s . La ligne (3) appelle le d e u x i m e c o n s t r u c t e u r de la c l a s s e CDRom, et initialise l'objet rebel_assault. R i e n de n e u f ici. En r e v a n c h e , l'instruction (4) va trouver de la p l a c e en m m o i r e p o u r u n objet de classe CDRom, i n i t i a l i s e p a r le p r e m i e r c o n s t r u c t e u r (le constructeur p a r dfaut, p u i s q u ' i l ne p r e n d aucun paramtre). Vous pouvez maintenant utiliser pt_inconnu p o u r appeler d e s fonctions p u b l i q u e s de la classe CDRom. L'instruction (5) va crer un objet initialise p a r le d e u x i m e constructeur, et r e t o u r n e r l ' a d r e s s e de cet objet. Enfin, les lignes (7) et (8) appellent le destructeur de la c l a s s e CDRom p o u r dtruire les objets p o i n t s p a r pt_inconnu et pt_cd_X. C e s destructeurs v o n t librer les c h a n e s de caractres alloues.

L'intrt par rapport au C

C o m p a r o n s les d e u x lignes s u i v a n t e s :
tab = (char *) malloc (TAILLE * sizeof(char)); tab = new char [TAILLE] ; // C / , /C++

* C'est une possibilit rserve aux experts...

L'intrt d u n e w devrait v o u s frapper d e plein fouet. C ' e s t vrai : n o n s e u l e m e n t n e w est p l u s s i m p l e utiliser, m a i s en plus, il fait partie i n t g r a n t e du C + + , d o n c il est p o r t a b l e et n e n c e s s i t e p a s l'adjonction d ' u n e librairie o u l'inclusion d ' u n fichier d'entte. Par ailleurs, l'utilisation du n e w p e r m e t la vrification de type (ce q u e ne p e r m e t p a s malloc), d o n c u n e p l u s g r a n d e sret d u c o d e . E n outre, n e w a p p e l l e l e c o n s t r u c t e u r v o u l u , c'est un oprateur, il p e u t tre redfini p a r v o s soins* et il peut tre hrit. Q u e v o u l e z - v o u s de p l u s ?

La fin du malloc

87

Que celui qui n'a pas pest la premire fois qu'il a dcouvert le profil de malloc se lve.

C e r t e s , v o u s p o u v e z toujours utiliser m a l l o c e n C + + . M a i s d a n s c e cas, v o u s d e v r e z appeler v o u s - m m e les c o n s t r u c teurs v o u l u s , et, ce qui est n e t t e m e n t p l u s f c h e u x , v o u s risq u e r e z de p a s s e r au travers de n e w spcifiques certaines c l a s s e s . Je m ' e x p l i q u e : si un gentil p r o g r a m m e u r rcrit un joli n e w p o u r s a classe e t q u e v o u s i g n o r e z s u p e r b e m e n t ses efforts e n utilisant m a l l o c , n o n s e u l e m e n t v o t r e objet risq u e r a d'tre m a l initialise, m a i s , p l u s g r a v e , v o u s v o u s attirerez i n v i t a b l e m e n t son inimiti. Un conseil : m e t t e z - v o u s au new, v o u s ne le r e g r e t t e r e z p a s .

La fin du printf
cout, cin et cerr

Nous venons famille printf. nration : la jets issus de rutilisabilit.

d'enterrer la famille malloc, passons maintenant Et donnons maintenant sa chance la nouvelle famille cout. Notons que cout, cin et cerr sont des classes C++, et qu'ils illustrent trs bien le concept

la gobde

Notions de base
L e C + + intgre d a n s s a librairie s t a n d a r d diffrents m c a n i s m e s d ' e n t r e s e t d e sorties. I l s'agit d e s flux c o u t , c i n e t c e r r , c o u p l s a u x o p r a t e u r s > > e t < < . L ' o p r a t e u r > > perm e t de lire un flux, alors q u e v o u s a u t o r i s e crire dedans. V o i c i le rle des trois flux s t a n d a r d s du C + + * :

90

Pont entre C et C++

t r e a m , e t c i n u n objet d e classe i s t r e a m . T o u t c e b e a u m o n d e est dfini d a n s l e fichier i o s t r e a m . h . N o u s allons voir c o m m e n t utiliser les flux C + + e t p o u r q u o i ils s o n t p l u s intressants q u e c e u x d e C .

Mise en uvre C++

P r e n o n s un e x e m p l e s i m p l e : n o u s allons crire un petit p r o g r a m m e qui calcule l a m o y e n n e d e s n o t e s d ' u n tudiant.


#include "iostream.h" void { char float float int maint) nom[50]; notes[3]; cumul = 0 ; i;

cout << "Entrez le nom de l'tudiant : cin >> nom; for (i=0; i<3; i++)
{

N o t e z que, t o u t c o m m e p o u r scanf, vous ne pouvez pas entrer d'espace(s) pendant la saisie d'une chane. En fait, p o u r tre exact, vous pouvez le faire, mais vous rendrez v o t r e programme compltement fou, et vous avec.
}

cout << "Entrez la note n" << i+1 << ' '; // (1) cin >> notes[ i ] ; cumul += notes[i]; cout << nom << " a pour moyenne " << cumul/3. << endl; } / / C e programme affiche ceci : // (les caractres gras sont taps par l'utilisateur) Entrez le Entrez la Entrez la Entrez la GaryMad a nom de l'tudiant : GaryMad note nl 0.5 note n2 7 note n3 19.5 pour moyenne 9

Remarque V o u s p o u v e z afficher p l u s i e u r s variables o u c o n s t a n t e s d a n s l a m m e instruction, d u m o m e n t q u e v o u s les s p a r e z p a r < < . C ' e s t l e cas e n ( 1 ) , d a n s l a p r e m i r e instruction d e l a b o u c l e , o n o u s affichons u n e c h a n e , p u i s l'entier i , p u i s l e caractre e s p a c e . C e t t e r e m a r q u e est g a l e m e n t v a l a b l e p o u r les lectures a v e c c i n e t > > .

La fin du printf

91

M i s e en forme automatique V o u s n o t e r e z q u e v o u s n ' a v e z pas b e s o i n d e prciser l e form a t d'affichage, celui tant dfini p a r dfaut p o u r les types d e b a s e d e C . Si, n a n m o i n s , v o u s s o u h a i t e z a p p l i q u e r d e s contraintes sur l'affichage ( n o m b r e de chiffres d c i m a u x d ' u n rel par e x e m p l e ) , inutile de revenir printf, cout sait le faire trs bien. R e p o r t e z v o u s a u x c o m p l m e n t s de ce chapitre p o u r savoir c o m m e n t . Sans adresse V o u s a v e z not l'abscence de & d a n s la s y n t a x e du cin. Ce dernier n ' a p a s b e s o i n de connatre l'adresse de la variable lire, c o m m e c'tait l e cas p o u r s c a n f .

L'intrt par rapport au C

L'intrt principal de cout, cin, cerr, << et >> est la vrification de type. En effet, c h a q u e objet est affich en fonction de sa classe ; il n ' y a p a s de r i s q u e de confusion ou de quip r o q u o . L e s fonctions printf et c o m p a g n i e s o n t i n c a p a b l e s de vrifier le type des d o n n e s q u ' e l l e s traitent (ce qui accrot les r i s q u e s d'erreurs). P l u s j a m a i s les c h a n e s de c a r a c t r e s ne seront affiches c o m m e d e v u l g a i r e s entiers, j e v o u s l e p r o m e t s ... Par ailleurs, u n e s i m p l e c o m p a r a i s o n de lignes suffit p o u r q u e la vrit a p p a r a i s s e de m a n i r e clatante l ' h o n n t e h o m m e : cout et ses frres sont r e m a r q u a b l e m e n t p l u s s i m p l e s utiliser q u e leurs a i n e s d e la famille printf.
// C standard (infme, n'est-ce pas ?) printf("%s%d%c%f\n", "Voici i:", entier, '+', rel); scanf("%f", &reel); // C++ (mieux, tout de mme !) cout << "Voici i:" << entier << '+' << rel << endl; cin >> rel;

M a i s l'intrt ne se limite p a s l : v o u s p o u v e z surtout redfinir les oprateur << et p o u r c h a c u n e de v o s c l a s s e s ! A i n s i , les o p r a t i o n s d ' e n t r e s / s o r t i e s g a r d e r o n t toujours la m m e s y n t a x e , i n d p e n d a m m e n t d e s classes utilises ! Par e x e m p l e , u n e classe FilmHorreur p o u r r a afficher le n o m du film et s o n ralisateur, opration ralise de la m m e m a n i r e q u e p o u r un entier :

92

Pont entre C et C++

Utilisateurs de MSD O S , prenez garde ! Le n o m de fichier utilis dans l'include fait plus de huit caractres !

#include "FilmHorreur.h" void


{

// voir dans la marge

main()

FilmHorreur cout << film;


}

film("Doume", "Adi software");

On s u p p o s e b i e n e n t e n d u q u e la classe est dfinie, et que l'on a fait ce qu'il faut p o u r q u e l ' o p r a t e u r prenne en c o m p t e l'affichage de la classe F i l m H o r r e u r . V o u s allez a p p r e n d r e c o m m e n t raliser ce petit m i r a c l e d a n s les comp l m e n t s d e c e chapitre.

Complments
Surcharger et
Avant de lire ce complment, vous devez connatre les fonctions amies (page 123), ainsi que les principes de la surcharge, dvelopps page 5 1 .

V o u s p o u v e z s u r c h a r g e r les o p r a t e u r s < < e t > > p o u r affic h e r v o s p r o p r e s objets. En d'autres t e r m e s , il faut que les fonctions operator<< e t o p e r a t o r s a c h e n t quoi faire l o r s q u ' e l l e s sont a p p l i q u e s des objets de v o t r e classe. A v a n t de p o u r s u i v r e les e x p l i c a t i o n s , un petit rappel s ' i m p o s e : q u a n d on utilise un o p r a t e u r @, cela revient app e l e r la fonction operator c o r r e s p o n d a n t e . V o i l ce que cela d o n n e p o u r + :
int rsultat, a = 1, b = 1; rsultat = a + b; // quivaut rsultat = operatort (a, b ) ;

T r a n s p o s o n s l ' e x e m p l e ci-dessus :
NomClasse mon_objet;

cout << mon_objet; // quivaut operator<< (cout, mon__objet);

Il s'agit ici de l'oprateur << global. R a p p e l o n s q u e cout et cin sont r e s p e c t i v e m e n t d e s objets de classe ostream et

La fin du printf

93

istream. P o u r s u r c h a r g e r , i l suffit d o n c d'crire l a fonction hors-classe suivante :


ostream operator<< (ostream, NomClasse)

La fonction s u r c h a r g e r est identifie. Il ne reste p l u s q u ' l'crire. Un p r o b l m e se p o s e alors : c o m m e n t a c c d e r a u x d o n n e s de l'objet de classe NomClasse ? D e u x solutions : p a s s e r p a r les fonctions m e m b r e s public d ' a c c s a u x d o n n e s , ou b i e n dclarer d a n s la classe NomClasse q u e la fonction operator<< est u n e amie. D a n s ce dernier c a s , o p e r a t o r p e u t a c c d e r l i b r e m e n t a u x d o n n e s private e t

protected des objets de NomClasse. Ce qui d o n n e :


Vous trouverez page 176 un exemple de surcharge de << qui ne dclare pas de fonction amie.

#include <iostream.h> class NomClasse { private: int n; public : NomClasse(int i) : n(i) {} int get_n() const { return n; } // dclarons que l'oprateur global << est // un ami de notre classe (NomClasse) : friend ostreamS operator<< (ostream& out, const NomClasse& obj);
};

Nous utilisons ici des passages par rfrence (voir chapitre 9, page I 13).

// surchargeons l'oprateur global << pour qu'il // sache traiter des objets de classe NomClasse : ostream& operator<< (ostreamk out, const NomClasse& obj)
{

// out est l'objet comparable cout // obj est l'objet afficher return out << '[' << obj.n << ' ] ' << endl;
}

void {

maint)

NomClasse

objet(1);

cout << objet;


}
/*

Pour redfinir >>, utilisez : istream& operator>> (istreamk in, NomClasse& obj);

94

Pont entre C et C++

*/

// affichage: // [1]

L a fonction o p e r a t o r < < r e t o u r n e u n e rfrence sur u n objet d e classe o s t r e a m , p o u r q u e v o u s p u i s s i e z utiliser d e s instructions la file :
cout << objl << obj2; // quivaut : // operator<< (operator<<( cout, objl), obj2);

D a n s l a ligne ci-dessus, o p e r a t o r < < ( c o u t , o b j l ) doit tre l u i - m m e l'objet d e classe o s t r e a m qui v i e n t d'tre appel ; c'est p o u r cela q u e n o u s r e t o u r n o n s u n e rfrence v e r s cet objet.

Formater les sorties


Ces formatages o n t t vrifis sur le compilateur T u r b o C + + p o u r PC, mais devraient fonct i o n n e r sur tous les autres compilateurs. * sauf contre indicat i o n dans le texte.

L e s indications de formatage (prsentation) d e s sorties doiv e n t tre p a s s e s c o u t d e l a m m e m a n i r e q u e les d o n n e s afficher, savoir avec l'oprateur < < . Q u a n d v o u s utilisez u n e instruction d e f o r m a t a g e , v o u s dev e z inclure au d b u t de votre c o d e le fichier i o m a n i p . h en plus d u fichier i o s t r e a m . h . L e s p a r a g r a p h e s qui suivent d o n n e n t des e x e m p l e s p o u r c h a q u e type d e f o r m a t a g e diffrent. V o u s t r o u v e r e z e n s u i t e un tableau rcapitulatif de toutes ces fonctions. Attention : toutes ces instructions de formatage* restent v a lables p o u r tous les c o u t qui les suivent d a n s l e p r o g r a m m e . A f f i c h e r au m o i n s n caractres L'identificateur s e t w ( n ) force c o u t afficher a u m o i n s n caractres, et plus si ncessaire, s e t w est l'abbrviation de set zuidth ( spcifie la largeur ). Par dfaut les affichages s o n t c a d r s droite. E x e m p l e :
#include "iostream.h" #include "iomanip.h" void
{

main()

cout << setw(3) << 1 << endl; cout << 1.2345 << endl;
}

La fin du printf

95

// affiche : 1 1.2345

P o u r q u e c o u t affiche autant d e caractres q u e n c e s s a i r e , spcifiez u n e l o n g u e u r m i n i m a l e d e 0 ( s e t w ( 0 ) ) . C ' e s t d'ailleurs la valeur par dfaut. A l i g n e r les sorties g a u c h e Utilisez ici l e barbare s e t i o s f l a g s ( i o s : : l e f t ) . E x e m ple :
#include "iostream.h" finclude "iomanip.h" void
{

main()

cout << setw(5) << "Wax" << endl; //(l) cout << setiosflags(ios::left ) << "Wax" << endl; 11(2) II setw(5) est toujours valable ici !
}

// affiche : Wax Wax

La l i g n e (1) i n d i q u e qu'il faut afficher au m o i n s cinq caractres. Par dfaut, l ' a l i g n e m e n t se fait droite, d ' o la p r e m i r e ligne affiche (deux e s p a c e s et les trois lettres). La ligne (2) p r c i s e qu'il faut aligner le texte g a u c h e . R a p p e l e z - v o u s q u e la c o n s i g n e d'afficher au m o i n s cinq caractres est toujours valable. Cette fois-ci, les trois lettres de W a x sont affiches, suivies de d e u x e s p a c e s . A l i g n e r les s o r t i e s droite C ' e s t l e c o m p o r t e m e n t p a r dfaut d e c o u t . S i v o u s v o u l e z l e rtablir, utilisez s e t i o s f l a g s ( i o s : : r i g h t ) v o i r p a r a g r a p h e prcdent. A f f i c h e r n chiffres aprs la v i r g u l e Utilisez s e t p r e c i s i o n ( n ) :
#include "iostream.h" #include "iomanip.h" void main()

96

Pont entre C et C++

cout << setprecision(2 ) << 1.23456 << endl;


}

// affiche : 1.23

Afficher les zros aprs la virgule Utilisez s e t i o s f l a g s ( i o s : : s h o w p o i n t ) .


#include "iostream.h" #include "iomanip.h" void
{

main()

cout << setw(5) << setiosflags(ios:: showpoint) << 1.2 << endl; } // affiche : 1.200

Ne pas afficher les zros aprs la virgule C ' e s t l a fonction r c i p r o q u e d e l a p r c d e n t e . Utilisez s e tiosf lags(ios::showbase) :
#include "iostream.h" #include "iomanip.h" void
{

main()

cout << setw(5) << setiosflags(ios::showpoint) << 1.2 << endl; cout << setiosflags(ios::showbase) << 1.2 << endl;
}

// affiche : 1.200 1.2

Afficher u n ' + ' p o u r les n o m b r e s positifs Utilisez s e t i o s f l a g s ( i o s : : s h o w p o s ) :


#include "iostream.h" #include "iomanip.h" void
{

maint)

La fin du printf

97

cout << setiosflags(ios::showpos) << 1.2 << endl; } // affiche : + 1.2

R e m p l a c e r le caractre blanc par un autre caractre N o u s p a r l o n s ici d e s e s p a c e s affichs p o u r c o m b l e r les v i d e s . P a r e x e m p l e , si v o u s a v e z spcifi s e t w ( 5 ) et q u e v o u s affic h e z le chiffre 1, quatre e s p a c e s seront affichs en p l u s . Si v o u s n ' a i m e z p a s les e s p a c e s , utilisez s e t f i l l ( c a r ) p o u r les r e m p l a c e r p a r un caractre de votre c h o i x :
#include "iostream.h" #include "iomanip.h" void main() { cout << setfill ('-') << setw(5) << 1 << endl;
}

// affiche : 1

A f f i c h e r les n o m b r e s d a n s u n e autre b a s e S o n t dfinies trois b a s e s : l'octal (base 8 ) , le d c i m a l (par dfaut) et l ' h e x a d c i m a l (base 16). il suffit de spcifier l'abrviation ( o c t , d e c , h e x ) p o u r q u e tous les affichages suivants se fassent d a n s cette b a s e :
#include "iostream.h" #include "iomanip.h" void
{

main()

cout << hex << 12 << endl;


}

// affiche : c

A f f i c h e r les n o m b r e s e n n o t a t i o n s c i e n t i f i q u e Un rappel p o u r les n o n - m a t h e u x s ' i m p o s e : la n o t a t i o n scientifique est de la forme n Ee, o n est un n o m b r e rel entre 0 et 1 n o n inclus, et o e est g a l e m e n t un n o m b r e rel.

98

Pont entre C et C++

U n e telle n o t a t i o n r e p r s e n t e le n o m b r e n m u l t i p l i p a r 10 p u i s s a n c e e. P o u r formater l'affichage en n o t a t i o n scientifiq u e , utilisez s e t i o s f l a g s ( i o s : : scientific) . E x e m ple :


#include "iostream.h" #include "iomanip.h" void
{

main()

cout << setiosflags(ios::scientific) << 12.3 << endl;


}

// affiche : .123 e2

A f f i c h e r l e s n o m b r e s e n n o t a t i o n fixe C ' e s t l'affichage p a r dfaut d e s n o m b r e s , il s'obtient en utilisant s e t i o s f l a g s ( i o s : : fixed) . E x e m p l e :


#include "iostream.h" #include "iomanip.h" void
{

main()

cout << setiosflags(ios:: scientific) << 12.3 << endl; cout << setiosflags(ios::fixed) << 12.3 << endl; } // affiche : .123 e2 12 .3

La fin du printf

99

Tableau rcapitulatif

Ce tableau r e p r e n d les diffrentes options d'affichage valables pour c o u t .

Effet voulu
au moins n caractres aligner gauche aligner droite n caractres aprs la virgule afficher les 0 aprs la virgule ne pas afficher les 0 aprs la virgule '+' devant les nombres positifs changer le caractre de remplissage afficher en dcimal, octal ou hexadcimal notation scientifique notation virgule fixe

cout ...
setw(n) setiosflags(ios::left) setiosflags(ios::right) setprecision(n) setiosf lags(ios: :showpoint) setiosflags(ios::showbase) setiosf lags(ios: :showpos) setfill(c); // c est un char dec, oct ou hex setiosf lags(ios::scientific) setiosflags(ios::fixed)

Le polymorphisme
et la virtualit

Vous connaissez dj les vertus de l'hritage. Dcouvrez maintenant celles du polymorphisme, mis en uvre par la virtualit en C++. Si vos notions sur l'hritage sont encore un peu floues, vous gagnerez relire le chapitre 3 avant d'aborder celui-ci.

Notions de base
L'ide Le p r i n c i p e de p o l y m o r p h i s m e p e u t s ' a p p a r e n t e r u n e surc h a r g e e t u n e redfinition d e f o n c t i o n - m e m b r e t e n d u e . R a p p e l o n s q u e la surcharge consiste p o u v o i r d o n n e r le m m e n o m p l u s i e u r s fonctions ; le c o m p i l a t e u r faisant la diffrence grce aux p a r a m t r e s . La redfinition de fonctionmembre a p p l i q u e ce m m e p r i n c i p e travers u n e arboresc e n c e d'hritage, e t p e r m e t e n p l u s d e d o n n e r e x a c t e m e n t l e m m e entte d e fonction. Le p o l y m o r p h i s m e va p l u s loin : il p e r m e t de trouver dynamiquement quel objet s ' a d r e s s e u n e fonction, p e n d a n t l ' e x c u t i o n d u p r o g r a m m e . A l o r s q u e l a redfinition d e f o n c t i o n - m e m b r e d t e r m i n a i t cela la c o m p i l a t i o n , c'est-dire de m a n i r e statique, le p o l y m o r p h i s m e m n e s o n en-

202

Pont entre C et C++

qute et va jusqu' retrouver la classe relle d'un objet point. Limites de la redfinition de fonction-membre Reprenons l'exemple d'une gestion de textes bruts et mis en forme. Si l'on utilise un pointeur de T e x t e B r u t qui pointe rellement vers un T e x t e M i s E n F o r m e , la redfinition de fonction-membre seule montre ses limites :
#include <iostream.h> class TexteBrut

{
public: void
} ;

|
j Imprimer(int n b ) ;

void TexteBrut::Imprimer(int nb) { cout << "Imprimer de la classe TexteBrut" << endl;
}

class TexteMisEnForme : public TexteBrut

// hritage

{
public: void
} ;

I
! Imprimer(int n b ) ;

void TexteMisEnForme::Imprimer(int nb) { cout << "Imprimer de la classe TexteMisEnForme" < < endl;

i i

}
void main() { TexteBrut TexteMisEnForme

|
i i *txt; joli_t;xt; j

txt = &joli_txt; txt -> Imprimer(1 );

i
i

// affichage : // Imprimer de la classe TexteBrut

Ce n'est pas le rsultat espr : il faudrait que la fonction Imp r i m e r dfinie dans la classe T e x t e M i s E n F o r m e soit appele, car l'objet rellement point appartient cette classe. Le polymorphisme permet justement de rsoudre ce problme.

Le polymorphisme et la virtualit

103

Mise en uvre C++

L e p o l y m o r p h i s m e est m i s e n u v r e p a r l a virtualit e n C + + . R e p r e n o n s l ' e x e m p l e p r c d e n t . Il suffit d'ajouter le m o t - c l virtual d e v a n t l'entte de la fonction Imprimer, d a n s la classe TexteBrut. Ce qui d o n n e :
#include <iostream.h> class TexteBrut { public : virtual void
} ;

Imprimer(int n b ) ;

// ... le reste est identique l'exemple prcdent ... void main() { TexteBrut TexteMisEnForme

*txt; joli_txt;

txt = &joli_txt; // txt pointe sur un TexteMisEnForme txt -> Imprimer(1); } // affichage : // Imprimer de la classe TexteMisEnForme

E x a m i n o n s le rle du mot-cl virtual d a n s n o t r e petite architecture de classes. Le rle d'une fonction virtuelle Q u a n d l e c o m p i l a t e u r r e n c o n t r e d e s f o n c t i o n s - m e m b r e s virtuelles, il sait qu'il faut attendre l ' e x c u t i o n du p r o g r a m m e p o u r savoir quelle fonction appeler. Il d t e r m i n e r a en t e m p s v o u l u la bonne fonction, c'est--dire celle qui est dfinie d a n s la classe de l'objet rel a u q u e l la fonction est a p p l i q u e . M a i s r e v e n o n s n o t r e e x e m p l e p o u r illustrer c e s p r o p o s : le petit b o u t de m a i n ( ) a b e a u tre petit, il n ' e n est p a s m o i n s trs riche en e n s e i g n e m e n t s . L'utilisation d ' u n p o i n t e u r sur classe TexteBrut m r i t e q u e l q u e s c l a i r c i s s e m e n t s .

204

Pont entre C et C++

P o i n t e u r s sur u n e c l a s s e d e b a s e
* O u i oui, en C c'est possible, car le C est un grand laxiste. Mais en C + + , le contrle des types est plus serr, et vos incartades ne passeront plus : finies les convertions automatiques hasardeuses de truc_machin vers bidule_chose. De la discipline, que diable !

En temps normal, quand vous dclarez un pointeur de type A, vous ne pouvez pas le f a i r e pointer sur une donne de type B*. En tous cas, cela devrait tre contraire votre thique. M a i s i c i , nous sommes dans une situation spciale : nous dclarons un pointeur sur une classe de base. D a n s ce cas prcis, et le C + + nous y autorise, vous pouvez faire point e r ce pointeur vers une classe drive de cette classe de base. C'est exactement ce que nous faisons dans l'avant-dernire ligne du l i s t i n g . La ligne qui suit (txt -> Imprimer ( 1 ) ) devrait donc f a i r e appel la fonction Imprimer dfinie dans la classe TexteBrut, puisque txt est un pointeur sur TexteBrut. M a i s NON, puisque le C + + aura vu que txt pointe maintenant sur joli_txt, objet de classe TexteMisEnForme. C e t t e ligne appelera donc la fonction Imprimer de la classe TexteMisEnForme.
D a n s u n e hirarchie de classe, p o u r dclarer des fonctions qui o n t l e m m e n o m e t les m m e s a r g u m e n t s * e t d o n t les appels sont rsolus d y n a m i q u e m e n t , il faut qu'elles soient virtuelles. Il suffit de dclarer u n e fonction virtuelle d a n s la classe de b a s e p o u r q u e toutes les fonctions i d e n t i q u e s des classes drives soient elles aussi virtuelles*. P e n d a n t l'excution, la fonction appele d p e n d de la classe de l'objet qui l'appelle (et n o n du type de variable qui pointe sur l'objet). D a n s le cas d'un pointeur sur u n e classe qui appelle u n e fonction virtuelle, c'est la classe de l'objet point rellement par ce pointeur qui d t e r m i n e la fonction a p p e l e . C e m c a n i s m e c o m m u n d e n o m b r e u x l a n g a g e s orientsobjets est appel polymorphisme. La virtualit est sa m i s e en uvre C + + . N o u s avions dj v u q u ' a v e c l'hritage, l e C + + proposait q u e l q u e c h o s e de trs intressant, totalement a b s e n t du C. G r c e a u p o l y m o r p h i s m e , v o u s d i s p o s e z d ' u n m o y e n nouv e a u , d ' u n e s o u p l e s s e et d ' u n e p u i s s a n c e t o n n a n t e : utiliser des fonctions h o m o n y m e s , d o n t les a r g u m e n t s ont l e m m e profil, tout au l o n g d ' u n e hirarchie de classe, en bnficiant de la r s o l u t i o n d y n a m i q u e des appels.

Rsum
* C'est--dire m m e n o m b r e et mmes types d'arguments.

* Pour la clart du listing, on pourra rpter le mot-cl virtual devant chaque fonction concerne, t o u t au long de la hirarchie.

L'intrt par rapport au C

Le polymorphisme et la virtualit

105

Rions ensemble. Un utilisateur de Macintosh dclarait rcemment : Windows p o u r PC, c'est mettre du rouge lvre un poulet

E x e m p l e : v o u s d v e l o p p e z un e n v i r o n n e m e n t g r a p h i q u e . V o u s le baptisez Fentres pour Groupes de Travail 96*. G r c e la virtualit, c h a q u e objet g r a p h i q u e de v o t r e s y s t m e (bouton, m e n u droulant, liste, etc.) p o s s d e r a u n e fonctionm e m b r e Afficher, a v e c les m m e s p a r a m t r e s . Il v o u s sera p o s s i b l e d'crire q u e l q u e c h o s e c o m m e :
ObjetGraphiqueGenerique *obj[10]; // tableau de 10 pointeurs // ... affectation d'objets divers aux pointeurs du tableau // (nous ne dtaillons pas cette partie) for (i = 0; i < 10; i++) { obj[i] -> Afficher(paramtres);
}

V o u s n ' a v e z plus v o u s soucier du t y p e e x a c t de l'objet grap h i q u e p o i n t p a r obj [ i ] , p u i s q u e l a b o n n e fonction Aff i c h e r sera a p p e l e a u t o m a t i q u e m e n t . En C, il aurait fallu p a s s e r p a r d e s p o i n t e u r s sur void*, dfinir p l u s i e u r s structures figes c o n t e n a n t d e s p o i n t e u r s sur d e s fonctions, e t autres bidouilles p e u c a t h o l i q u e s . U n e autre solution consisterait utiliser des s w i t c h , m a i s cela rendrait l ' e x t e n s i o n du c o d e existant trs p i n e u s e . Le C n ' t a n t p a s c o n u p o u r cela, v o u s v o u s e x p o s e r i e z de m u l t i p l e s prob l m e s de portabilit, de fiabilit, de m a i n t e n a n c e . C a r la v ritable force d e frappe d u C + + , c'est l'hritage. S e u l l'hritage, et s o n c o m p l i c e la virtualit, p e r m e t t e n t de m e t t r e e n u v r e des s y s t m e s c o m p l e x e s e t r e l a t i v e m e n t facile modifier.

Le polymorphisme et la virtualit

105

Rions ensemble. Un utilisateur de Macintosh dclarait rcemment : Windows p o u r PC, c'est mettre du rouge lvre un poulet

E x e m p l e : v o u s d v e l o p p e z un e n v i r o n n e m e n t g r a p h i q u e . V o u s le baptisez Fentres pour Groupes de Travail 96*. G r c e la virtualit, c h a q u e objet g r a p h i q u e de v o t r e s y s t m e (bouton, m e n u droulant, liste, etc.) p o s s d e r a u n e fonctionm e m b r e A f f i c h e r , a v e c les m m e s p a r a m t r e s . I l v o u s sera p o s s i b l e d'crire q u e l q u e c h o s e c o m m e :
ObjetGraphiqueGenerique *obj[10]; // tableau de 10 pointeurs // ... affectation d'objets divers aux pointeurs du tableau // (nous ne dtaillons pas cette partie) for (i = 0; i < 10; i++) { obj[i] -> Afficher(paramtres) ;
)

V o u s n ' a v e z plus v o u s soucier du t y p e e x a c t de l'objet grap h i q u e p o i n t p a r o b j [ i ] , p u i s q u e l a b o n n e fonction A f f i c h e r sera a p p e l e a u t o m a t i q u e m e n t . E n C , i l aurait fallu p a s s e r p a r d e s p o i n t e u r s sur v o i d * , dfinir p l u s i e u r s structures figes c o n t e n a n t d e s p o i n t e u r s sur d e s fonctions, e t autres bidouilles p e u c a t h o l i q u e s . U n e autre solution consisterait utiliser des s w i t c h , m a i s cela rendrait l ' e x t e n s i o n du c o d e existant trs p i n e u s e . Le C n ' t a n t p a s c o n u p o u r cela, v o u s v o u s e x p o s e r i e z de m u l t i p l e s p r o b l m e s de portabilit, de fiabilit, de m a i n t e n a n c e . C a r la v ritable force d e frappe d u C + + , c'est l'hritage. S e u l l'hritage, et s o n c o m p l i c e la virtualit, p e r m e t t e n t de m e t t r e e n u v r e des s y s t m e s c o m p l e x e s e t r e l a t i v e m e n t facile modifier.

106

Pont entre C et C++

Complments
Destructeurs virtuel
Il est tout fait p o s s i b l e de dfinir un d e s t r u c t e u r virtuel p o u r u n e classe. A quoi sert un d e s t r u c t e u r virtuel ? A dtruire l'objet r e l l e m e n t point, et n o n un objet i m a g i n a i r e qui aurait l e m m e type q u e celui d u p o i n t e u r . U n petit exemple :
#include <iostream.h> class Vhicule { public : virtual -Vhicule!) { cout << "-Vhicule" << endl; }
>;

class Camion : public Vhicule { public : virtual -Camion() { cout << "-Camion" << endl; }
};

void main() { Vhicule *pt_inconnu; Camion *mon_beau_camion = new Camion; pt_inconnu = mon_beau_camion; delete pt_inconnu;
}

// affichage : / / -Camion // -Vhicule

Le delete en dernire ligne appelle le d e s t r u c t e u r de Camion (puis le d e s t r u c t e u r de Vhicule, car les d e s t r u c t e u r s s o n t a p p e l s en srie de la classe d r i v e la classe de b a s e ) . Si les d e s t r u c t e u r s n ' t a i e n t p a s virtuels, seul le d e s t r u c t e u r de Vhicule aurait t a p p e l . P o u r e x p l i q u e r cela a u t r e m e n t , pt_inconnu est d c l a r c o m m e un p o i n t e u r sur Vhicule, m a i s p o i n t e en ralit sur un objet de classe Camion. C o m m e les d e s t r u c t e u r s s o n t ici virtuels, le p r o g r a m m e a p p e l l e le destructeur qui la classe de l'objet r e l l e m e n t point, c'est--dire Camion.

Le polymorphisme et la virtualit

107

Classes abstraites et fonctions virtuelles pures

Le concept de classe abstraite (ou classe abstraite de base) est c o m m u n de n o m b r e u x l a n g a g e s orients-objets. De quoi s'agit-il ? U n e classe est dite abstraite si elle est c o n u e d a n s le b u t de servir de c a d r e g n r i q u e ses classes drives. De p l u s , u n e classe abstraite ne p e u t e n g e n d r e r aucun objet. C ' e s t un p o i n t capital, qui p e r m e t de s o u l i g n e r q u e la raison d'tre d ' u n e classe abstraite rside d a n s ses classes drives. Fonctions virtuelles pures L e C + + autorise l a m i s e e n u v r e des classes abstraites, p a r l e biais d e f o n c t i o n s - m e m b r e s virtuelles p u r e s . E n C + + , u n e classe est dclare abstraite q u a n d elle contient au moins une fonction virtuelle pure. U n e fonction virtuelle p u r e est u n e f o n c t i o n - m e m b r e d o n t seul le profil (valeur retourne, n o m et p a r a m t r e s ) est dfini. On dclare u n e fonction virtuelle en ajoutant = 0; aprs l'entte d e l a fonction, e n plus d u m o t - c l v i r t u a l . Consquences Q u a n d v o u s dclarez u n e fonction virtuelle p u r e d a n s u n e classe, voil ce qui se p a s s e : la classe est dcrte classe abstraite : elle ne p e u t d o n c pas tre utilise p o u r crer un objet. la fonction virtuelle p u r e ne peut tre dfinie : elle n ' a q u ' u n profil, u n entte, m a i s pas d e c o r p s les classes futures qui hriteront de cette classe abstraite devront dfinir les f o n c t i o n s - m e m b r e s virtuelles p u r e s de la classe abstraite. I n t r t du v i r t u e l p u r L'intrt d e s fonctions virtuelles p u r e s et d e s c l a s s e s abstraites de b a s e , c'est de s'assurer q u e les classes d r i v e s r e s p e c teront l'interface d e votre classe. V o u s i m p o s e z u n e n t t e p o u r u n e fonction g n r i q u e , et v o u s forcez les classes driv e s redfinir cette fonction. C ' e s t un m o y e n s u p p l m e n taire d e garantir u n e b o n n e h o m o g n i t d e votre architecture de classes.

108

Pont entre C et C++

E x e m p l e court D a n s le listing qui suit, n o u s dclarons la f o n c t i o n - m e m b r e a f f i c h e r virtuelle p u r e , e n ajoutant = 0 aprs s o n entte. N o u s n e p o u v o n s d o n c p a s crer d'objet d e cette classe. E n r e v a n c h e , n o u s p o u v o n s crer d e s objets de la classe Driv e qui dfinit l a fonction a f f i c h e r .
class AbstraiteBase { protected : // blabla public : virtual void afficher(int x, int y) = 0; // c'est ce = 0 qui rend la fonction // virtuelle pure
} ;

class Drive : { protected : public : virtual //


} ;

public AbstraiteBase // blabla void afficher(int x, int y ) ; (virtuelle simple)

void
{

Drive::afficher(int x, int y)

// traitement } void
{

main() // impossible !!! // possible.

AbstraiteBase toto; Drive tata;


}

tata.afficher(15, 1 2 ) ; // possible.

Exemple complet Voici u n petit e x e m p l e c o m p l e t m e t t a n t e n v i d e n c e l'intrt de la virtualit p u r e . A v a n t d ' e n d c o u v r i r le listing, quelques c l a i r c i s s e m e n t s : D a n s l e c a d r e d u d v e l o p p e m e n t d ' u n e interface g r a p h i q u e , n o u s a v o n s choisi de dfinir u n e classe Obj etGraphique, qui contient le m i n i m u m i n d i s p e n s a b l e tous les objets grap h i q u e s de notre interface. Par objet g r a p h i q u e , n o u s entend o n s tout ce qui est utilis d a n s l'interface (bouton, a s c e n s e u r s , m e n u s , m a i s aussi z o n e p o u v a n t contenir d'autres objets, z o n e de dessin, etc.) D a n s un souci de sim-

Le polymorphisme et la virtualit

109

plification, n o u s retiendrons trois caractristiques : l'objet g r a p h i q u e parent, les c o o r d o n n e s en x et en y de l'objet d a n s l ' e s p a c e de c o o r d o n n e s a s s o c i e s l'objet parent. A q u o i sert l'objet p a r e n t ? A savoir d a n s quel autre objet est situ l'objet courant. P a r e x e m p l e , u n b o u t o n aura c o m m e o b jet p a r e n t u n e fentre. D a n s l e c a d r e d e l ' e x e m p l e , n o u s dcrirons g a l e m e n t l a classe B o u t o n O n O f f , c o r r e s p o n d a n t l'objet c a s e coc h e r . P o u r offrir un m i n i m u m de r a l i s m e s a n s c o m p l i q u e r , n o u s s u p p o s e r o n s q u e n o u s travaillons e n m o d e texte, e t utiliserons les primitives d e T u r b o C + + sur P C p o u r p o s i tionner le c u r s e u r l'cran. R a s s u r e z - v o u s , il n ' y a rien de sorcier l dedans. Voici le listing :
#include <conio.h> // propre a Turbo C++, // permet de positionner le curseur

enum Boolean { False = (1==0), True = (1==1) } ; // type boolen


//

classe ObjetGraphique

class ObjetGraphique { protected : ObjetGraphique *pere; int x, y;

// Objet pere // coord. par rapport // au pere

Nous utilisons ici plusieurs techniques propres au C + + et indpendantes des fonctions virtuelles : les paramtres par dfaut (voir page 65), la liste d'initialisation (voir page 47), et la dfinrtion inline des fonctions (voir page 21),

public : // constructeur ObjetGraphique(ObjetGraphique *pi = 0, int xi = 0, int yi = 0) : pere(pi), x(xi), y(yi) { } // fonctions d'accs ObjetGraphique *get_pere() { return pere; ) int get_x() { return x; } int get_y() { return y; } // fonctions de modification virtual Boolean set_x(int me) = 0; virtual Boolean sety(int ny) = 0; // fonctions de haut niveau virtual void Redessine() = 0;
} ;

110

Pont entre C et C++

//

classe BoutonOnOff

Les constantes sont traites page 67.

class BoutonOnOff : public ObjetGraphique { private : const char CAR_ON; //caractre affich si ON const char CAR_OFF; //caractre affich si OFF Boolean tat; //bouton ON (True) ou OFF public : // constructeur BoutonOnOff(ObjetGraphique *pi = 0, int xi = 0, int yi = 0, Boolean ei = False) : ObjetGraphique(pi, xi, y i ) , etat(ei), CAR_ON('x'), CAR_OFF('0') { } // fonctions virtuelles pures a dfinir // fonctions de modification virtual Boolean set_x(int nx) ; virtual Boolean set_y(int n y ) ; // fonctions de haut niveau virtual void Redessine!); // fonctions nouvelles de BoutonOnOff Boolean get_etat() { return tat; } void set_etat(Boolean ne) { tat = ne; Redessine(); }
} ;

// dfinitions des fonctions de BoutonOnOff Boolean BoutonOnOff::setx(int nx)


{

i f (nx != x) { x = nx ; Redessine(); } return True;


}

Boolean
{

BoutonOnOff;:set v(int ny)

if (ny != y) { y = ny; Redessine( ) ;


}

return True;
}

void
{

BoutonOnOff::Redessine()

// positionnons le curseur en x, y gotoxy(x, y ) ; // affichons un caractre l'endroit du curseur

Le polymorphisme et la virtualit

111

putch( tat ? CAR_ON : CAR_OFF );


}

// void
{

programme principal main()

BoutonOnOff

bouton(0, 10, 1 0 ) ;

bouton.Redessine(); getch(); // attend qu'une touche soit frappe bouton.set_etat(True);


)

Ce programme met en vidence l'intrt de la virtualit pure. D a n s la classe de base O b j e t G r a p h i q u e , trois fonctions sont dclares virtuelles pures : s e t _ x , s e t _ y , et R e d e s s i n e . Pourquoi ? D a n s l'ide, p o u r fournir un c a d r e g n r a l i d e n t i q u e tous les objets g r a p h i q u e s . En forant les classes d r i v e s dfinir ces trois fonctions, on s'assure q u e leurs e n t t e s d e v r o n t se c o n f o r m e r un certain profil. Un p r o g r a m m e u r sachant utiliser u n objet g r a p h i q u e s a u r a a u t o m a t i q u e m e n t utiliser tous les autres, y c o m p r i s c e u x qui ne sont pas e n c o r e crits. s e t _ x e t s e t _ y d p e n d e n t bel e t b i e n d e c h a q u e objet g r a p h i q u e , car il c o n v i e n t de s'assurer q u e les n o u v e l l e s v a l e u r s sont correctes, n o t a m m e n t , o n p e u t l ' i m a g i n e r , p a r r a p p o r t l'objet p r e qui contient n o t r e objet graphique. R e d e s s i n e d p e n d aussi d e c h a q u e objet g r a p h i q u e . I l n e p r e n d a u c u n p a r a m t r e car u n objet doit tre c a p a b l e de se r e d e s s i n e r en fonction de ses d o n n e s i n t e r n e s (ici ses c o o r d o n n e s en x et y ) .

Les rfrences

Seul le passage par valeur est autoris en C standard. Mme lorsque vous passez un pointeur une fonction, vous passez la valeur de ce pointeur. Le C++ introduit une nouvelle possibilit : le passage par rfrence (connu depuis longtemps en Pascal). Par ailleurs, ce mme mcanisme de rfrence vous permet de dclarer des synonymes de variables : il s'agit des rfrences libres.

Notions de base
L'ide Q u a n d v o u s p a s s e z u n e fonction un p a r a m t r e p a r rfrence, cette fonction reoit un s y n o n y m e du p a r a m t r e rel. En o p r a n t sur le p a r a m t r e p a s s p a r rfrence, la fonction p e u t d o n c modifier d i r e c t e m e n t l e p a r a m t r e rel. V o u s n ' a v e z p a s b e s o i n d'utiliser l'adresse d u p a r a m t r e p o u r l e modifier, c o m m e v o u s d e v r i e z le faire en C s t a n d a r d . Le caractre & est utilis d a n s l'entte de la fonction p o u r sig n a l e r q u ' u n p a s s a g e p a r rfrence est d c l a r :
void plus_l(int Snombre) // passage par rfrence attendu { nombre++;

Mise en uvre C++

114

Pont entre C et C++

void {

main()

int i = 3 ; plus_l(i); // i vaut maintenant 4 }

C o m m e v o u s le constatez, l'appel se fait de m a n i r e trs s i m p l e . L a fonction p l u s _ l sait q u e l e p a r a m t r e entier q u ' e l l e attend est u n e rfrence vers l e p a r a m t r e rel i . E n c o n s q u e n c e , toute modification d e n o m b r e d a n s l a fonction p l u s _ l s e rpercute sur l a variable i d u p r o g r a m m e principal. Dclarer une rfrence libre V o u s p o u v e z utiliser les rfrences en d e h o r s de toute fonction : en dclarant q u ' u n e variable est s y n o n y m e d'une autre, c'est--dire q u e l ' u n e est u n e rfrence v e r s l'autre. D s lors, u n e modification sur l ' u n e q u e l c o n q u e de ces variables affectera le c o n t e n u de l'autre. P a r e x e m p l e :
void main() { int nombre = 1 ; int &bis = nombre; // bis est une rfrence nombre int *pointeur; bis = 1 0 ; nombre = 20; pointeur = &bis; (*pointeur) = 3 0 ;
}

// bis et nombre valent 10 // bis et nombre valent 20 // bis et nombre valent 3 0

L'intrt par rapport au C

* les constructeurs copie sont vus au chapitre 2, page 3 I.

Le fait d'utiliser le caractre & p o u r autre c h o s e q u e ce qu'il signifie en l a n g a g e C doit v o u s paratre trange. S o y e z rassur : v o u s p o u v e z p r e s q u e t o t a l e m e n t v o u s p a s s e r d'utiliser les rfrences, tout en bnficiant des substanciels a v a n t a g e s du C + + . P o u r q u o i presque totalement ? Eh b i e n , p o u r tre franc, il faut a v o u e r q u e les c o n s t r u c t e u r s de copie* ont b e soin de types p a s s s p a r rfrence. V o u s r e m a r q u e r e z tout d e m m e q u e , p a s s e l a p r e m i r e a n g o i s s e , les rfrences se rvlent trs p r a t i q u e s !

Les rfrences

115

Complments
Rfrences et objets provisoires
* Petite prcision de vocabulaire : dans la ligne int &ref = ini; ref dsigne l'objet rfrence et ini l'objet initial.

P o u r q u ' u n e rfrence soit c o r r e c t e m e n t utilise, il faut q u e l'objet rfrence et l'objet initial* soient de m m e type. S i n o n , plusieurs cas p e u v e n t se prsenter : L'objet initial est u n e c o n s t a n t e
int &ref = 1 ; const int &ref = 1 ; // (1) NON (voir ci-dessous) // (2) OUI

L'objet initial peut tre converti v e r s l'objet r f r e n c e


int const initial = 5; float &ref = initial; float &ref = initial;

// (1) NON // (2) OUI

L e s d e u x cas ci-dessus e n g e n d r e n t les m m e s r e m a r q u e s : (1) La rfrence n ' e s t p a s constante. V o t r e c o m p i l a t e u r doit n o r m a l e m e n t gnrer u n e erreur d e type. (2) La rfrence est constante. Un objet t e m p o r a i r e est cr ; la rfrence le dsigne.

Fonction retournant une rfrence

D a n s s o n dsir infini de r e p o u s s e r toujours plus loin les limites d u C , l e C + + p e r m e t , grce a u x rfrences, d'utiliser les fonctions c o m m e des variables ordinaires a u x q u e l l e s o n p e u t affecter u n e valeur. Ainsi, q u a n d u n e fonction r e t o u r n e u n e v a l e u r par rfrence, on p e u t d i r e c t e m e n t agir s u r cette v a l e u r d e retour. M a i s u n petit e x e m p l e v a u t m i e u x q u ' u n e explication o b s c u r e :
// variables globales int t a b [ ] = { 0 , 1, 2, 4, 8 } ; const int nb_elem = 5 ; // nbr d'lments de tab int &fonction(int j) // retourne une rfrence { if (j >= 0 && j < nb_elem) return tab[j]; else return tab[0];
}

void main()

116

Pont entre C et C++

fonction (0) = 0 ; // tab[0] vaut -b fonction(l) = fonction(4); // tab[l] vaut 8


}

La p r e m i r e ligne du m a i n est e x c u t e c o m m e ceci : a p p e l f o n c t i o n , qui retourne u n e rfrence vers t a b [ 0 ] . D o n c , d a n s l ' e x p r e s s i o n f o n c t i o n (0) = 10;, tout se p a s s e c o m m e si f o n c t i o n ( 0 ) tait r e m p l a c p a r t a b [ 0 ]. On affecte d o n c b i e n la valeur 10 t a b [ 0 ]. L a d e u x i m e ligne fonctionne selon l e m m e principe.

Les templates
ou la mise en uvre de la gnricit

Toujours plus, toujours : dans sa C + + nous offre les templates, qui les fonctions ou les classes par des sants, mais souvent dlicats mettre teurs relativement rcents autorisent et fiable.

dbauche d'effets spciaux, le vous permettent de paramtrer types. Les templates sont puisen uvre : seuls les compilaleur usage de manire simple

Notions de base
L'ide
Juste une remarque de vocabulaire : la

gnricit est le
concept orientobjet, alors que les

templates sont la
solution apporte par le C + + pour mettre en uvre ce concept.

J u s q u ' prsent, les fonctions p o u v a i e n t avoir c o m m e p a r a m t r e s des variables o u des objets. M a i s j a m a i s v o u s n ' a v e z eu la possibilit de les p a r a m t r e r p a r d e s types. C ' e s t j u s t e m e n t le rle et l'intrt de la gnricit et des t e m p l a t e s . D e s classes p e u v e n t g a l e m e n t tre dclares t e m p l a t e s , si v o u s j u g e z q u ' e l l e s doivent d p e n d r e d ' u n type. Par e x e m p l e , u n e classe qui gre u n e liste d'objets q u e l c o n q u e s n ' a p a s se soucier du type rel de ces l m e n t s : elle ne doit s ' o c c u p e r q u e de les classer, d'en ajouter ou d ' e n s u p p r i m e r sa liste, etc. U n e telle classe a d o n c intrt tre p a r a m t r e

118

Pont entre C et C++

p a r le type des l m e n t s q u ' e l l e s t o c k e . Il serait j u d i c i e u x d'en faire u n e classe t e m p l a t e .

Mise en uvre C++

Il est i m p o r t a n t de distinguer d e u x types de t e m p l a t e s : celles qui c o n c e r n e n t les fonctions et celles qui c o n c e r n e n t les classes. E x a m i n o n s d ' a b o r d les t e m p l a t e s de fonctions. Exemple P r e n o n s l ' e x e m p l e classique de la fonction qui r e t o u r n e le m i n i m u m de d e u x chiffres. Il s e m b l e l g i t i m e de v o u l o i r r e n d r e cette fonction i n d p e n d a n t e du t y p e d e s objets. V o y o n s la m i s e en u v r e d ' u n e telle fonction, g r c e a u x templates :
tinclude <iostream.h> / / pour l'affichage avec cout template <class TYPE> TYPE min(TYPE a, TYPE b) { return ( a < b ) ? a : b; } main() { int char

Templates de fonctions

il = 10, i2 = 20; cl = 'x', c2 = ' a ;


1

cout << "min entier : " << min(il, i2) << endl; cout << "min char : " << min(cl, c2) << endl;
}

Commentaires Observez bien la dclaration de A v a n t le type de retour, v o u s d e v e z

la fonction. prciser tem-

piate <class NomType> o NomType est un identificateur de votre cru, q u e v o u s utiliserez e n s u i t e p o u r d s i g n e r le type p a r a m t r e de votre fonction. V o u s tes obligs d'utiliser ce n o m de type d a n s au moins un p a r a m t r e de votre fonction. Ici, les d e u x l m e n t s c o m p a r e r sont de t y p e MonType, et la fonction r e t o u r n e g a l e m e n t un objet de type MonType. l'appel, le c o m p i l a t e u r reconnat le type des p a r a m t r e s et r e m p l a c e d a n s la fonction toutes les o c c u r e n c e s de

Les templates

119

MonType par le t y p e v o u l u (ici, int d a n s le p r e m i e r appel e t c h a r d a n s l e d e u x i m e ) . T e m p l a t e s de f o n c t i o n s : cas g n r a l

Templates de classes

* Il faudrait bien entendu grer un nombre variable d'objets, avec allocations et dsallocations la demande . Cela compliquerait la classe et nuirait l'explication des templates. Par ailleurs, la gestion d'erreurs est rduite ici sa plus simple expression. N o u s verrons au chapitre I 3, page I 37, comment implanter une vritable gestion des erreurs.

Exemple L e s t e m p l a t e s v o u s seront g a l e m e n t utiles d a n s d e s c l a s s e s . R e p r e n o n s l ' e x e m p l e de la classe qui gre u n e liste d'objets de type (ou de classe) q u e l c o n q u e . P o u r simplifier le p r o p o s , n o u s utiliserons u n tableau p o u r stocker les objets*. C o m m e n t la dclarer ?
tinclude <iostream.h> // pour affichage avec cout #include <stdlib.h> // pour la fonction exit template <class TYPE> class Liste { private: enum { MAX = 3 ,} ; // constante TYPE elem[MAX]; public : TYPE void void
} ;

Liste() { } get_elem(int n ) ; set_elem(int n, TYPE e ) ; affiche();

template <class TYPE> TYPE Liste<TYPE> ::get_elem(int n) // retourne le nime lment class
{

if (n >= 1 && n <= MAX) return elem[ n-1 ]; else { cerr <<"Liste::get_elem - Indice hors limite :" << n << endl;

120

Pont entre C et C++

exit(1); }
}

template <class T Y P E > void Liste<TYPE>: :set_elem(int n, TYPE e) // stocke e la nime place dans la liste { if ( n >= 1 && n <= MAX) elem[n-l] = e; else { cerr <<"Liste : :set_elem - Indice hors limite :" << n << endl; exit(1);
}
}

template <class TYPE> { int i;

void

Liste<TYPE> ::affiche()

for (i = 0; i < MAX ; i + +) cout << "Num " << i+1 << ':' << elem[i] << endl;
}

main()
{

Liste<int>

liste;

liste.set_elem(1, 1 0 ) ; liste.set_elem(3, 3 0 ) ; liste.set_elem(2, 2 0 ) ; liste.affiche(); // affichage : // Num 1 : 10 // Num 2 : 20 // Num 3 : 30


}

Q u e l q u e s e x p l i c a t i o n s s u r cet e x e m p l e N o u s a v o n s choisi d e stocker les l m e n t s d a n s u n tableau de taille MAX, MAX tant u n e c o n s t a n t e spcifique la classe Liste, dfinie l'aide d ' u n enum (voir p a g e 7 1 ) . La dclaration de la classe i n d i q u e q u e c'est u n e t e m p l a t e par a m t r e p a r le type TYPE :
template <class TYPE> class Liste

Les templates

121

N o u s utilisons d o n c le type g n r i q u e TYPE d a n s la c l a s s e , p o u r i n d i q u e r ce q u e n o u s s t o c k o n s d a n s le t a b l e a u e l e m :


TYPE elem[MAX];

N o t e z g a l e m e n t la dfinition des f o n c t i o n s - m e m b r e s en deh o r s de la classe :


template <class TYPE> TYPE Liste<TYPE> : :get_elem(int n)

TYPE est le type de retour de la fonction, d o n t le n o m de classe est Liste<TYPE>. La seule c h o s e qui c h a n g e vraim e n t par r a p p o r t u n e f o n c t i o n - m e m b r e n o r m a l e , c'est le

prfixe template <class TYPE>.


Utilisation d'une classe t e m p l a t e L'utilisation d ' u n e classe t e m p l a t e se fait en d o n n a n t le n o m d u type entre c h e v r o n s . D a n s l ' e x e m p l e ci-dessus, cela donne :
Liste<int> liste;

o int aurait pu tre r e m p l a c p a r n ' i m p o r t e quel autre type o u classe. T e m p l a t e s de classes : cas g n r a l Voici c o m m e n t l'on dclare u n e classe t e m p l a t e :

122

Pont entre C et C++

Et voici c o m m e n t l'on dfinit u n e f o n c t i o n - m e m b r e d ' u n e classe t e m p l a t e :

Complments
Attention l'ambigut
I l p e u t v o u s arriver d'avoir b e s o i n d ' i m b r i q u e r d e s t e m p l a tes. Par e x e m p l e , v o u s v o u l e z grer u n e liste d'arbres, o la classe Liste et la classe A r b r e sont des t e m p l a t e s . Il n ' y a en thorie p a s de p r o b l m e , m a i s je tiens attirer v o t r e attention sur la m a n i r e de dclarer un objet Liste :
Liste<Arbre<int>> ma_liste_d_arbres; // NON

D a n s cette dclaration, l e type d e v o t r e liste t e m p l a t e n ' e s t autre q u ' u n e classe A r b r e , e l l e - m m e t e m p l a t e , d o n t l e t y p e est i n t . C e t t e dclaration p r o v o q u e i n s t a n t a n m e n t u n e erreur de c o m p i l a t i o n , b i e n q u e , p r e m i r e v u e , elle s e m b l e correcte. La seule c h o s e qui c l o c h e , c'est la p r s e n c e d e . E n C + + , les d e u x caractres s u p r i e u r s r e p r s e n t e n t un oprateur. Il y a d o n c m p r i s e . P o u r y r e m d i e r , t a p e z un e s p a c e entre les d e u x c h e v r o n s . L a b o n n e dclaration d e v i e n t donc :
Liste< Arbre<int> > ma_liste_d_arbres; // OUI

Les classes et fonctions amies

L'amiti n'est pas une vertu rserve aux humains. Les classes et les fonctions C++ peuvent, elles aussi, se lier d'amiti. Heureusement, vous seul pouvez les autoriser s'enticher ainsi les unes des autres.

Notions de base
L'ide
* Rappelons que l'encapsulation c o n siste manipuler des objets composs de donnes et de fonctions de manipulation de ces donnes. Les donnes sont caches au monde extrieur.

L o r s q u ' u n e fonction est dclare a m i e ( f r i e n d ) d ' u n e c l a s s e , elle p e u t a c c d e r directement toutes les d o n n e s p r i v a t e o u p r o t e c t e d d e cette classe. L o r s q u ' u n e classe est dclare a m i e d ' u n e autre c l a s s e , ce s o n t toutes les fonctions de la p r e m i r e classe qui p e u v e n t a c c d e r a u x d o n n e s p r i v e s d e l a d e u x i m e classe. C'est bien beau, mais si vous avez bien compris le premier chapitre de ce livre, v o u s devriez b o n d i r de v o t r e sige, et v o u s crier M a i s c'est u n e entorse i n s u p p o r t a b l e la sacrosainte rgle de l'encapsulation* ! Et v o u s auriez raison. A u s s i s o m m e s - n o u s en droit de n o u s d e m a n d e r pourquoi le crateur du C + + a p e r m i s q u e l'on viole le m o t n ' e s t p a s

224

Pont entre C et C++

trop fort l ' e n c a p s u l a t i o n ? E s s e n t i e l l e m e n t p o u r d e s rais o n s d'architecture, d u e s a u fait q u e l e C + + n ' e s t p a s u n lang a g e e n t i r e m e n t orient objet (il co-existe a v e c le C ) , m a i s aussi p o u r p e r m e t t r e un p r o g r a m m e u r d ' a c c o r d e r des droits entre ses classes internes, et v e n t u e l l e m e n t p o u r des raisons d ' o p t i m i s a t i o n (voir aussi les q u e s t i o n s - r p o n s e s , a u chapitre 16). V o u s t r o u v e r e z l'utilit d e s classes a m i e s d a n s l ' e x e m p l e d e s u r c h a r g e d e l'oprateur , p a g e 92. C e l a dit, mfiez-vous et n'utilisez les a m i e s q u e si v o u s y tes contraint...

Mise en uvre C++

La dclaration de l'amiti d ' u n e classe A p o u r u n e autre entit se fait n ' i m p o r t e o d a n s la classe A, g r c e au m o t - c l

friend:
class A { friend void fonction_B(); friend class C;
//
> ;

// (1) pour une fonction 1/(2) pour une classe

...

La l i g n e (1) signifie q u e fonction_B p e u t a c c d e r a u x d o n n e s private ou protected de la classe A. R e m a r q u e z q u e si f onction_B tait u n e fonction m e m b r e d ' u n e autre c l a s s e , il faudrait spcifier de quelle classe elle est m e m b r e , avec u n e ligne du style :
friend void AutreClasse : :fonction_B();

La l i g n e (2) signifie q u e toutes les fonctions de la c l a s s e C ont le droit d ' a c c d e r d i r e c t e m e n t a u x d o n n e s private ou

protected de la classe A.
C o m m e v o u s l'avez constat, la dclaration f riend... se fait d a n s la classe qui accorde le droit a u x autres de v e n i r tripatouiller ses p r o p r e s d o n n e s . R c a p i t u l o n s :

Classes et fonctions amies

125

Attention !

Utilisez avec la p l u s g r a n d e p r c a u t i o n les fonctions f r i e n d : elles tordent le c o u l ' e n c a p s u l a t i o n , et sont d o n c d a n g e r e u s e s p o u r la stabilit de votre application. Elles ne sont intressantes q u e p o u r o p t i m i s e r des c l a s s e s , o u p o u r accorder d e s droits entre classes internes qui n ' i n t r e s s e r o n t p a s d'autres p r o g r a m m e u r s . E n r s u m , bidouillez v o s c l a s ses v o u s , m a i s faites du travail p r o p r e sur les classes qui seront u t i l i s e s / r e p r i s e s p a r d'autres. G a r d e z b i e n p r s e n t l'esprit q u ' u n e modification d a n s les d o n n e s p r i v e s d ' u n e classe qui a dclar d'autres c l a s s e s a m i e s doit se rpercuter sur ces autres classes ! A u s s i , m fiez-vous et i n d i q u e z toujours c l a i r e m e n t d a n s la d o c u m e n tation quelles classes sont a m i e s d e qui, p o u r q u e c e u x qui s e p e n c h e r o n t sur v o t r e travail d a n s q u e l q u e s m o i s ( v o u s y c o m p r i s ) ne s'arrachent p a s les c h e v e u x en se c o g n a n t la tte c o n t r e les m u r s .

L'hritage multiple

L'hritage dj pour diffrence classes ! tors...

simple vous permet d'utiliser des classes qui existent en crer d'autres. L'hritage multiple est identique, la prs que vous pouvez hriter simultanment de plusieurs Ce qui n'est pas sans engendrer quelques problmes re-

Notions de base
L'ide L ' h r i t a g e m u l t i p l e v o u s p e r m e t d e crer des classes d r i v e s qui hritent e n m m e t e m p s d e p l u s i e u r s classes d e b a s e . I l n e s'agit pas d ' u n hritage s i m p l e e n c h a n e m a i s bel e t b i e n d'un hritage m u l t i p l e s i m u l t a n :

128

Pont entre C et C++

D a n s le cas p r s e n t droite du s c h m a ci-dessus, la classe C hrite du c o n t e n u des classes A et B (mais cela d p e n d touj o u r s d e s spcificateurs p r o t e c t e d , p r i v a t e e t p u b l i c ) .

Mise en uvre C++

U n hritage multiple s e spcifie c o m m e u n hritage s i m p l e , en sparant p a r d e s virgules les classes de b a s e s dsires. P o u r dclarer l'hritage multiple du s c h m a p r c d e n t , il faudrait crire :
class A { // blabla public : void
} ;

fonction_a();

class B { // blabla public : void


} ;

fonction_b();

class C : public A, public B


{

// blabla public : void


} ;

fonction_c();

E n admettant que f o n c t i o n _ a , f o n c t i o n _ b e t f o n c t i o n ^ ont t dfinies, v o u s pourriez crire q u e l q u e chose comme :


void
{

main() objet_c;

objet_c.fonction_a(); // appel A::fonction_a() objet_c.fonction_b() ; // appel B: :fonction_b() objet_c.fonction_c(); // appel C: :fonction_c()


}

Il n ' y a l rien de b i e n surprenant. On pourrait m m e croire q u e c'est simple. M a i s c o m m e d e n o m b r e u x c o n c e p t s C + + , l e d e s s u s d e l'iceberg c a c h e u n certain n o m b r e d e complications difficiles dceler au p r e m i e r a b o r d . . .

L'hritage multiple

129

O r d r e d'appel des constructeurs D a n s le cas d ' u n hritage m u l t i p l e , la cration d ' u n objet de la classe drive appelle les c o n s t r u c t e u r s des classes de b a s e d a n s l'ordre de dclaration de l'hritage. P a r e x e m p l e :
class A {
} ;

class B {
} ;

class C : public B, public A // la ligne ci-dessus dtermine l'ordre d'appel // des constructeurs
{
} ;

void
{

main()

C objet_c; // appel B ( ) , puis A ( ) , puis C()


}

Les listes d'initialisations sont prsentes page 47.

Par ailleurs, si v o u s p r c i s e z u n e liste d'initialisation*, l'ordre d ' a p p e l des c o n s t r u c t e u r s d p e n d r a toujours de l'ordre de d c l a r a t i o n des classes de b a s e s . P a r e x e m p l e :
class C : public B, public A { public : C() ;
} ;

C::C() : A(param), B(param) // liste d'initialisation


{ }

void
{

main() objet_c; // appel B(param), A(param) puis C()

C
}

L'intrt par rapport au C

Le l a n g a g e C ne p r o p o s e p a s d ' q u i v a l e n t de la n o t i o n d'hritage s i m p l e , il est d o n c v a i n de c h e r c h e r un q u i v a l e n t d'hritage m u l t i p l e . Le principal atout de ce dernier est q u ' i l p e r m e t d e m o d l i s e r d e faon p l u s juste l e m o n d e rel.

730

Pont entre C et C++

Par e x e m p l e , si v o u s l a b o r e z u n e classification des a n i m a u x , v o u s p o u v e z tre a m e n considrer la b a l e i n e c o m m e un m a m m i f r e m a i s aussi c o m m e u n a n i m a l v i v a n t s o u s l'eau. L'hritage multiple vous permettra de garder un schma cohrent :

exemple

d'hritage multiple

Il faut n a n m o i n s t e m p r e r cette excitation : l'hritage m u l tiple est c o m p l e x e m a n i p u l e r ; n o u s a v o n s vu q u e l point il faut faire attention a u x a m b i g u t s difficiles dceler. E s s a y e z autant q u e p o s s i b l e d'viter l'hritage multiple. N ' o u b l i e z p a s q u e m m e s i v o u s l e m a t r i s e z parfaitement, c e n ' e s t peut-tre p a s le cas d e s futurs utilisateurs de v o s classes. Or la c o m p r h e n s i o n d ' u n e hirarchie de classes est vitale p o u r c o n c e v o i r du c o d e fiable et c o m p r h e n s i b l e .

L'hritage multiple

131

Complments
Duplication de donnes d'une classe de base A t t a r d o n s - n o u s sur c e qui s e p a s s e e n m m o i r e d a n s certains cas d ' h r i t a g e s multiples. A d m e t t o n s q u e les d e u x classes d e b a s e d ' u n hritage m u l t i p l e hritent e l l e s - m m e s d ' u n e m m e classe de b a s e :

hritage multiple double

Traduisons ce schma en C + + :
class Base { int variable_base; // blabla
} ;

class A : public Base { int variable_A; // blabla


};

class B : public Base { int variable_B; // blabla


} ;

class C : public A, public B { int variable_C; // blabla


};

C o n s q u e n c e s : un objet de classe C c o n t i e n d r a d e u x fois les d o n n e s hrites de la classe Base. P o u r q u o i ? P a r c e q u e la fois l'objet A et l'objet B c o n t i e n n e n t les d o n n e s hrites de la classe Base. Or la classe C hrite de la c l a s s e A et de la

132

Pont entre C et C++

classe B. C bnficie donc de d e u x copies distinctes des donn e s hrites de la classe Base. En m m o i r e , cela r e s s e m b l e ceci :

C o m m e n t , ds lors, a c c d e r l ' u n e ou l'autre c o p i e d e s donn e s de la classe Base ? T o u t s i m p l e m e n t en utilisant l'oprateur de rsolution de p o r t e : :. Il suffit de spcifier le n o m de la classe laquelle appartient la d o n n e v o u l u e . A i n s i , p o u r a c c d e r variable_base hrit d a n s la classe B, il faut utiliser B: :variable_base. De m m e , p o u r acc d e r la c o p i e de variable_base s t o c k e d a n s la classe A, il faut utiliser A : : va r i ab 1 e_ba s e. Voici un e x e m p l e illustrant la duplicit d e s d o n n e s et leur utilisation :
class Base public : int variable_base; // dsol *

* Pour un exemple simple et court, la variable est dclare public. C'est un exemple ne pas suivre (en temps normal). Il faudrait bien entendu la dclarer private ou protected et dfinir des fonctions public d'accs et de modification.

class A : public Base // blabla

class B : public Base // blabla

class C : public B, public A // blabla

void main()

L'hritage multiple

133

objet_c; // (1) erreur!!! // (2) mieux // (3) mieux

objet_c.variable_base = 1; objet_c. B : :variable_base = 1; objet_c .A: :variable_base = 2;


}

L a ligne (1) n e prcise p a s quelle v a r i a b l e _ b a s e elle recourt. L e c o m p i l a t e u r n e c o m p r e n d p a s e t v o u s d e m a n d e d e lever l ' a m b i g u t . En r e v a n c h e , les lignes (2) et (3) sont s a n s q u i v o q u e . R a p p e lons que les variables B : :variable_base et A : : v a r i a b l e _ b a s e s o n t diffrentes e t b i e n distinctes e n mmoire. Masquage du polymorphisme S o u s c e titre s e c a c h e u n p r o b l m e p i n e u x e n g e n d r par l'hritage multiple. Le fait d'tre oblig de p r c i s e r e x a c t e m e n t quelle fonction on fait rfrence va l ' e n c o n t r e m m e du p r i n c i p e de virtualit. P r e n o n s un e x e m p l e o cette faiblesse est m i s e en v i d e n c e :

N o u s considrons qu'un priphrique est la fois une ressource et un objet rel, physique. N o u s dfinissons une fonction virtuelle I n f o pour les classes R e s s o u r c e , O b j e t R e e l e t P r i p h r i q u e . Voici l e listing C + + qui correspond cette architecture :
class Ressource { public : virtual void
};

Info();

void

Ressource::Info() { }

class ObjetReel

134

Pont entre C et C++

{ public : virtual void Info();


} ;

void

ObjetReel: : Info() { }

class Priphrique : public Ressource, public ObjetReel


{

} ;

class Imprimante : public Priphrique


{

public : virtual void Info();


};

void

Imprimante::Info() { }

void main() { Priphrique Imprimante

* inconnu; hp5 00; // (1) // (2) inattendu // (3) OK

inconnu = &hp500; inconnu -> Info(); inconnu -> Ressource ::Info();


)

Le problme

* En effet inconnu est un pointeur de Priphrique, mais il pointe en ralit sur un objet de classe Imprimante.

Il existe un moyen d'viter ce problme : c'est l'hritage virtuel, dtaill ci-aprs.

Voici le problme : en temps normal, grce au mcanisme de virtualit, la ligne (2) du m a i n devrait appeler la fonction I n f o dfinie dans la classe I m p r i m a n t e * . M a i s hlas, cause de l'hritage multiple, c'est la fonction-membre dfinie par la classe P r i p h r i q u e qui est appele, notre grand dam ! P o u r plus de clart, je vais reprendre l'explication d'une autre manire. Quand une classe, ici P r i p h r i q u e , utilise l'hritage multiple, cela peut poser un problme d'ambiguit : il suffit que les classes de base hrites (ici R e s s o u r c e et O b j e t R e e l ) possdent des fonctions homonymes (ici I n f o ) et toc, le compilateur C + + renonce au polymorphisme. Il appelle donc la fonction correspondant au type statique du pointeur (ici P r i p h r i q u e ) . O r , la souplesse de la virtualit rside justement dans cette espce de flou permis au pro-

L'hritage multiple

135

g r a m m e u r , qui p e u t utiliser des p o i n t e u r s d e classes p o u r a p p e l e r d e s fonctions virtuelles. L a b o n n e fonction tant app e l e p e n d a n t l'excution d u p r o g r a m m e , s e l o n l'objet rell e m e n t point.

Hritage virtuel

Il vaut mieux lire les paragraphes prcdents avant de s'aventurer dans celui-ci.

C o m m e n o u s l ' a v o n s v u p r c d e m m e n t , les d o n n e s d ' u n e classe d e b a s e p e u v e n t tre d u p l i q u e s d a n s certains cas d ' h r i t a g e multiple. M a l h e u r e u s e m e n t , tel n ' e s t p a s forcm e n t le dsir du p r o g r a m m e u r . R e m d i a n t ce p r o b l m e , l'hritage virtuel p e r m e t de n'avoir qu'une seule occurrence d e s d o n n e s hrites d ' u n e classe d e b a s e .

hritage multiple en diamant

P o u r q u e la classe C ne p o s s d e q u ' u n seul e x e m p l a i r e d e s d o n n e s de la classe Base, il faut q u e les classes A et B hritent v i r t u e l l e m e n t de la classe Base. Voici l ' e x e m p l e C + + qui c o r r e s p o n d cette explication (c'est le m m e e x e m p l e q u e le p a r a g r a p h e p r c d e n t , l ' e x c e p t i o n des m o t s - c l s virtual h a b i l e m e n t placs) :
class Base { public : int variable_base; // blabla
};

class A : virtual public Base { int variable_A; // blabla


};

class B : virtual public Base { int variable_B; // blabla


} ;

136

Pont entre C et C++

class C : public A, public B { int variable_C; // blabla


} ;

O b s e r v e z m a i n t e n a n t sur c e s c h m a l'tat d e l a m m o i r e q u a n d un objet de classe C est cr :

Voil, il n ' y a p l u s q u ' u n e seule o c c u r r e n c e d e s d o n n e s de B a s e . C o n t r a i r e m e n t a u p a r a g r a p h e p r c d e n t o v o u s tiez oblig de spcifier le c h e m i n de v o s d o n n e s , v o u s n ' a v e z p l u s v o u s en soucier ici. Illustrons cette j o v i a l e c o n c l u s i o n p a r un b o u t de p r o g r a m m e :
void main() { C objet_c; ,objet_c.variable_base = 1 ;
>

// parfait !

Les exceptions

Le traitement des erreurs est depuis toujours une vraie plaie pour les programmeurs. Le C++ nous propose le mcanisme des exceptions pour coordonner la lutte contre ces erreurs indsirables. Attention cependant : les exceptions sont encore peu rpandues, tant au niveau des compilateurs que des programmeurs C++. C'est donc en quelque sorte un secteur en chantier ...

Notions de base
L'ide U n p r o g r a m m e , m m e b i e n crit, p e u t rencontrer d e s erreurs d ' e x c u t i o n s : disque plein, saturation de la m m o i r e , etc. C o m m e n t traiter ces p r o b l m e s ? L e C + + r p o n d s i m p l e m e n t par le c o n c e p t d'exception. Qu'est-ce qu'une exception ? U n e e x c e p t i o n est u n e variable, d e n ' i m p o r t e quel type, qui s y m b o l i s e u n e erreur. Q u a n d u n e partie d e p r o g r a m m e rencontre un p r o b l m e , elle p e u t lancer u n e e x c e p t i o n , qui p o u r ra tre intercepte p a r un autre b o u t de p r o g r a m m e . L'intrt de ce s y s t m e est de sparer la dtection d'erreur d e s t r a i t e m e n t s associs. P a r e x e m p l e , q u a n d v o u s c r i v e z u n e hirarchie de classes, il est j u d i c i e u x de dfinir les e x -

138

Pont entre C et C++

ceptions q u e v o s classes p o u r r o n t lancer e n cas d e problme. De s o n ct, l'utilisateur de v o s classes aura c o n n a i s s a n c e de la liste de ces e x c e p t i o n s , et p o u r r a e n v i s a g e r diffrents trait e m e n t s p o u r r a d i q u e r le mal.

Mise en uvre C++

A t t e n t i o n : les exceptions f o n t uniquement rfrence aux vnements internes au programme, et n o n aux vnements extrieurs c o m m e par exemple un clic souris ou l'appui sur une t o u c h e du clavier.

Utiliser des exceptions existantes P o u r ce faire, il faut b i e n distinguer d e u x b l o c s de c o d e : celui qui ralisera les oprations d a n g e r e u s e s , c'est--dire susceptibles de gnrer des e x c e p t i o n s , et celui q u i traitera ces e x c e p t i o n s si elles surviennent. Le p r e m i e r bloc est prcd d u mot-cl t r y , l e s e c o n d d u mot-cl c a t c h . V o y o n s u n petit e x e m p l e :
#include <iostream.h> #include <except.h> void main() { try
{

int *p = new int[1E99]; // difficile ! cout << "Fin normale" << endl;
}

catch (xalloc)
{

cout << "Fin chaotique" << endl;


L 'exemple de xalloc fonctionne avec T u r b o C + + sur PC. Consultez la documentation de votre compilateur p o u r obtenir la liste des exceptions dfinies. ) }

// affichage : // Fin chaotique

A l l o u e r 1 9 9 entiers est u n e opration dlicate, en tout cas p o u r m o n ordinateur. Il faut savoir g a l e m e n t q u ' e n cas d ' c h e c d'allocation m m o i r e , u n e e x c e p t i o n d e t y p e x a l l o c est g n r e . A p r s l e bloc t r y , n o u s dtaillons u n bloc c a t c h qui intercepte j u s t e m e n t les e x c e p t i o n s x a l l o c . L ' e x c u t i o n m o n t r e b i e n q u e l'exception a t g n r e et traite. F o n c t i o n n e m e n t de try et catch Q u a n d u n e e x c e p t i o n est dtecte d a n s u n bloc t r y , l ' e x c u t i o n de ce dernier est s t o p p e , p u i s d r o u t e s u r le bloc c a t c h c o r r e s p o n d a n t . A l a fin d u bloc c a t c h , o n n e retourne pas d a n s le bloc t r y fautif. En r e v a n c h e , le pro-

Les exceptions

139

g r a m m e suit son cours comme si le bloc t r y avait t excut. Q u a n d u n e e x c e p t i o n est r e n c o n t r e , les d e s t r u c t e u r s d e s objets du bloc t r y sont appels avant d'entrer d a n s le bloc catch. U n b l o c t r y doit o b l i g a t o i r e m e n t tre suivi d ' a u m o i n s u n bloc c a t c h . Plusieurs b l o c s c a t c h p e u v e n t tre dcrits la file, si chac u n i n t e r c e p t e u n type d ' e x c e p t i o n diffrent. S i a u c u n bloc c a t c h n'intercepte l'exception m i s e , l a fonction t e r m i n a t e est appele. Par dfaut, elle fait appel l a fonction a b o r t qui m e t fin a u p r o g r a m m e . C e p o i n t est a b o r d p l u s e n dtail d a n s les c o m p l m e n t s d e ce chapitre. Prcisions sur catch U n bloc c a t c h doit tre p r c d d ' u n bloc t r y . L a s y n t a x e d e c a t c h p e u t tre l ' u n e des trois s u i v a n t e s :
catch (Type) Intercepte les exceptions de type Type, ainsi que les exceptions de classes drives si Type est une classe. Idem que ci-dessus, mais lxception est reprsente dans le bloc catch par un objet rel : obj. Intercepte toutes les exceptions non-traites par les blocs catch prcdents.

catch (Type obj) catch (...)

Intercepter plusieurs exceptions I l suffit p o u r cela d'crire plusieurs b l o c s c a t c h aprs l e bloc t r y . C o m m e n o u s l ' a v o n s v u d a n s l e tableau c i - d e s s u s , v o u s p o u v e z intercepter toutes les e x c e p t i o n s , s a n s distinction, e n utilisant c a t c h ( . . . ) .
tinclude <iostreain.h> #include <except.h> void main() { try
{

// quelque chose qui va dclencher // Exceptionl, Exception2 ou une autre exception } catch (Exceptionl) {

140

Pont entre C et C++

cout << "Fin chaotique 1" << endl;


}

catch (Exception2) { cout << "Fin chaotique 2" << endl;


}

catch (...) { cout << "Fin trs chaotique" << endl;


}
}

Crer et lancer ses propres exceptions I n t e r c e p t e r les erreurs des a u t r e s , c'est b i e n , m a i s p r v o i r les s i e n n e s , c'est m i e u x . N o u s a v o n s v u q u ' u n e e x c e p t i o n p o u v a i t tre de n ' i m p o r t e q u e l type : u n e v a r i a b l e ou un objet. La p r e m i r e t a p e c o n s i s t e d o n c crer les t y p e s c o r r e s p o n d a n t v o s e x c e p t i o n s , c'est--dire a u x e r r e u r s q u e v o u s p o u r r e z d c l e n c h e r . E n s u i t e , q u a n d v o u s c r i v e z u n e fonction, v o u s p o u r r e z lancer u n e de v o s e x c e p t i o n s g r c e au m o t - c l

throw:
throw obj; throw; Lance l'exception obj. Relance la dernire exception lance.

throw s a n s p a r a m t r e p e u t s'utiliser q u a n d v o u s n ' a r r i v e z p a s r s o u d r e le p r o b l m e d a n s un b l o c catch. V o u s relancez donc la dernire exception, en esprant qu'il existe un autre b l o c catch d a n s la ou les fonctions a p p e l a n t e s . Exemple D a n s l ' e x e m p l e qui suit, la classe MaClasse va l a n c e r u n e

e x c e p t i o n de classe MonErreur, ds q u e la fonction FonctionBoguee est a p p e l e .


#include <iostream.h> #include <except.h> // dfinissons une classe exception (en fait une classe // comme une autre) class MonErreur { public : MonErreur() {cout << "Constructeur de MonErreur" << endl;} };

Les exceptions

141

// dfinissons une classe qui va lancer des exceptions class MaClasse


{

public : void FonctionBoguee!);


};

* Dans la ligne cicontre, MonErreurO cre un objet temporaire de classe MonErreur. C'est donc bel et bien un objet qui est spcifi aprs t h r o w .

void MaClasse::FonctionBoguee() { // admettons que cette fonction soit bogue; // elle gnrera donc une exception : throw MonErreur(); // * voir dans la marge } void main() { try
{

MaClasse obj; obj.FonctionBoguee();


}

catch (MonErreur) { cout << "Panique" << endl;


}
}

// affichage : // Constructeur de MonErreur // Panique

C e t exemple nous montre que l'exception gnre dans F o n c t i o n B o g u e e , de la classe M a C l a s s e , est intercepte dans le m a i n par l'instruction c a t c h ( M o n E r r e u r ) . N o u s avons donc mis en vidence le fait que c'est le concepteur de la classe M a C l a s s e qui dtecte les erreurs et lance une exception ; alors que c'est l'utilisateur de cette classe, ici la fonction main, qui envisage les solutions selon l'exception rencontre. L'intrt par rapport au C
Le C ne p r o p o s e a u c u n s y s t m e de g e s t i o n des erreurs. La p r a t i q u e la p l u s c o u r a n t e c o n s i s t e r e n v o y e r u n e v a l e u r signifiant q u ' u n e erreur est i n t e r v e n u e . C e l a oblige tester cette valeur c h a q u e appel, ce qui alourdit et ralentit c o n s i d r a b l e m e n t l e p r o g r a m m e . Par ailleurs, q u e faire q u a n d u n e telle erreur est dtecte ? G r c e a u x e x c e p t i o n s , les erreurs sont d e s objets p a r t entire, d c l e n c h a b l e s puis rcuprables en c a s c a d e , du b l o c le

142

Pont entre C et C++

p l u s p r o c h e en fait le p l u s c o m p t e n t p o u r traiter l'erreur a u x b l o c s les p l u s g n r a u x . M a i s p l u s e n c o r e , c'est u n e tentative de formaliser les t r a i t e m e n t s d'erreur, un c a d r e gnral qui p e r m e t a u x p r o g r a m m e u r s d e m i e u x s'y retrouver.

Rsum

L e C + + p r o p o s e d e grer les erreurs p a r l e m c a n i s m e d e s e x c e p t i o n s . U n e e x c e p t i o n n ' e s t rien d'autre q u ' u n e v a r i a b l e qui r e p r s e n t e u n e erreur. T r o i s m o t s - c l s p e r m e t t e n t d e les mettre en uvre : t r y , c a t c h et t h r o w . Q u a n d une exception est l a n c e p a r t h r o w q u e l q u e part d a n s u n b l o c t r y , l ' e x c u t i o n de ce bloc est s t o p p e et d r o u t e v e r s le b l o c c a t c h correspondant. S i aucun bloc c a t c h n'intercepte l'exception, l a fonction t e r m i n a t e est a p p e l e (voir c o m p l m e n t s ) . P a r dfaut, elle m e t s o b r e m e n t fin a u p r o g r a m m e par un appel a b o r t ( ). N o u s allons m a i n t e n a n t dtailler u n c a s c o n c r e t d'utilisation d e s e x c e p t i o n s : u n e classe tableau qui n ' a c c e p t e p a s q u ' o n c r i v e en d e h o r s de ses b o r n e s .
#include <iostream.h> #include <except.h> class ErreurBorne { protected: int borne_fautive; public: ErreurBorne(int b) { borne_fautive = b; } int get_borne_fautive() { return borne_fautive; }
} ;

Exemple complet

class Tableau { protected: enum { MAX = 10 }; // constante int tab[MAX]; public : int get_MAX() { return MAX; } int &operator[](int i ) ;
} ;

Les exceptions

143

* L'oprateur [ ] retourne ici une rfrence (un synonyme) du tableau pour que l'utilisateur puisse consulter mais aussi modifier sa valeur dans une expression du style t[n] = i. V o i r le chapitre sur les rfrences, page I I 3.

int &Tableau: :operator[] (int i) { if (i<0 || i>=MAX) throw ErreurBorne(i); else return tab[i];
}

// * voir dans la marge

void main() { Tableau t; int i; try { t[9] = 1; t[99] = 2; // instruction fautive

i = t[9]; // ligne jamais excute } catch (ErreurBorne err) { cout << "Erreur de borne : " << err.get_borne_fautive() << endl;
}
}

// affichage : // Erreur de borne : 99

Complments
Spcifier des exceptions
V o u s p o u v e z i n d i q u e r a u c o m p i l a t e u r e t a u x futurs utilisateurs d e v o s classes quelles e x c e p t i o n s v o s fonctions s o n t susceptibles de lancer. P o u r ce faire, il faut n o m m e r c e s e x c e p t i o n s d a n s un throw, a p r s l'entte n o r m a l . E x e m p l e :
void int void void fl(int a) throw (MonErreur); f 2 ( ) throw (MonErreur, MaCrainte); f3(char *s) throw(); // aucune exception f4(float f ) ; // toutes les exceptions

Attention : il faut que le throw soit spcifi dans l'entte de dclaration et dans l'entte de dfinition de la fonction.

V o i l la signification de ces trois lignes : f 1 ne p e u t lancer q u e d e s e x c e p t i o n s de classe MonErreur ou de c l a s s e s driv e s . f2 p e u t lancer des e x c e p t i o n s MonErreur ou Ma-

144

Pont entre C et C++

C r a i n t e ou de classes drives, f 3 ne p e u t l a n c e r a u c u n e exception, f 4 p e u t se p e r m e t t r e de lancer toutes les e x c e p tions, c a r a u c u n e limitation n ' e s t e x p r i m e . Fonction unexpected S i , m a l g r tout, u n e fonction lance u n e e x c e p t i o n qui ne fig u r e p a s d a n s l e t h r o w d e son entte, l a fonction u n e x p e c t e d (inattendue) est appele. Par dfaut, cette fonction fait appel la fonction t e r m i n t e qui fait e l l e - m m e appel a b o r t . V o u s p o u v e z , s i v o u s l e dsirez, r e m p l a c e r l a fonction u n e x p e c t e d p a r dfaut p a r u n e fonction d e v o t r e cru. I l suffit d'utiliser s e t _ u n e x p e c t e d avec c o m m e seul param t r e un n o m de fonction respectant l'entte s u i v a n t :
PFV VotreNomDeFonction(void);

o P F V est un type dfini p a r :


typedef void(*PFV) () ;

Ce qui c o r r e s p o n d un p o i n t e u r sur u n e fonction qui ne p r e n d a u c u n p a r a m t r e e t qui r e t o u r n e v o i d . Exemple complet :


#include <iostream.h> #include <except.h> // si PFV est inconnu, dfinissons-le #ifndef PFV typedef void( *PFV) (); #endif class MonErreur {}; class MonDilemne {}; class MaClasse { public : void FonctionBoguee)) throw(MonDilemne);
};

void MaClasse::FonctionBoguee() throw(MonDilemne) { throw MonErreur(); //n'est pas autoris par l'entte
}

PFV
{

Monlnconnue()

Les exceptions

145

/ / n e doit pas retourner son appelant // doit sortir du programme cout << "Je suis dans l'inconnue..." << endl; exit(1);
}

void main() { try { MaClasse ob j ; set_unexpected( (PFV)Monlnconnue); obj.FonctionBoguee();


}

catch (MonErreur) { cout << "Panique" << endl;


}
}

// affichage : / / J e suis dans l'inconnue

Exception non intercepte

Il p e u t arriver q u ' u n e e x c e p t i o n l a n c e ne c o r r e s p o n d e auc u n bloc c a t c h qui suit l e bloc t r y . D a n s c e c a s , l a fonction t e r m i n a t e est a p p e l e . Par dfaut, elle m e t fin a u p r o g r a m m e p a r u n appel a b o r t ( ) . V o u s p o u v e z c e p e n d a n t dfinir v o t r e p r o p r e fonction terminate, l'aide de s e t _ t e r m i n a t e . V o t r e fonction doit c o r r e s p o n d r e l'entte suivant (o P F V est dfini c o m m e i n d i q u ci-dessus) :
PFV VotreNomDeFonction(void);

V o i c i u n e x e m p l e d e fonction t e r m i n a t e p e r s o n n e l l e :
#include <iostream.h> #include <except.h> // si PFV est inconnu, dfinissons-le #ifndef PFV typedef void( *PFV ) (); #endif class MonErreur {}; class MonDilemne {}; class MaClasse { public : void FonctionBoguee() throw(MonErreur);

146

Pont entre C et C++

) ;

void MaClasse::FonctionBoguee() { throw MonErreur();


}

throw(MonErreur)

PFV
{

MonTerminator() // ne doit pas lancer d'exception // ne doit pas retourner son appelant cout << "On dirait qu'il y a un problme" << endl; exit(1);

void main() { try { MaClasse obj; setterminate ((terminate_function)MonTerminator) ; obj.FonctionBoguee();


}

catch (MonDilemne.) { cout << "Panique" << endl;


}

// affichage : // On dirait qu'il y a un problme

Exceptions en cascade

Q u e s e passe-t-il q u a n d u n e e x c e p t i o n est l a n c e d a n s u n b l o c catch ? R p o n s e en d e u x t e m p s : Le b l o c A c o n t e n a n t le b l o c catch est s t o p p . Si ce bloc A tait l u i - m m e d a n s un bloc try, l ' e x c e p t i o n serait traite p a r un d e s blocs catch c o r r e s p o n d a n t au bloc try qui contient A. L a b r u m e d e ces explications sera peut-tre d i s s i p e p a r l ' e x e m p l e s u i v a n t : u n e e x c e p t i o n de classe MonDilemne est l a n c e d a n s un b l o c catch.
#include <iostream.h> #include <except.h> class MonErreur { ) ; class MonDilemne {}; class MaClasse {

Les exceptions

147

public : void FonctionBoguee() throw(MonErreur);


} ;

void MaClasse::FonctionBoguee() throw(MonErreur) { throw MonErreur(); // autoris par l'entte } void fonction!) { try { MaClasse obj ; obj.FonctionBoguee(); } catch (MonErreur) { cout << "Erreur" << endl; throw MonDilemne(); } catch (MonDilemne) { // ce catch n'est pas appel cout << "Dilemne dans fonction" << endl;
}

} void { try {
}

main()

fonction!); catch (MonDilemne)


{

// dclenchera un MonDilemne

// ce catch est appel cout << "Dilemne dans le main" << endl;
} }

// affichage : // Erreur // Dilemne dans le main

La compilation spare

Bien que la compilation spare ne constitue pas une nouveaut du C++, un chapitre lui est consacr car beaucoup de programmeurs C en ignorent les principes. Par ailleurs, le C++ diffre lgrement du C dans ce domaine.

Notions de base
L'ide La c o m p i l a t i o n spare p e r m e t de rpartir le c o d e - s o u r c e d ' u n e application d a n s plusieurs fichiers. C e t t e t e c h n i q u e p r o c u r e u n e meilleure clart d a n s l'organisation du projet. V o u s g a g n e z aussi du t e m p s : seuls les fichiers m o d i f i s d e puis la dernire c o m p i l a t i o n sont r e c o m p i l s . A v a n t d ' a b o r d e r la m i s e en u v r e de la c o m p i l a t i o n s p a r e , rappelons brivement le fonctionnement d'un compilateur. P r i n c i p e d'un c o m p i l a t e u r D e u x g r a n d e s tapes sont n c e s s a i r e s p o u r transformer votre c o d e - s o u r c e en e x c u t a b l e : la c o m p i l a t i o n et l'dition de liens (parfois appel l i n k a g e p a r certains fous qui refusent d ' e m p l o y e r les t e r m e s franais officiels mea culpa...). La compilation d ' u n fichier C ou C + + d o n n e un fichier objet,

250

Pont entre C et C++

U n e librairie est en quelque sorte un ensemble de fichiers .o que vous incluez l'dition des liens.

en gnral suffixe p a r . o. C e s fichiers . o sont e n s u i t e lis entre e u x , v e n t u e l l e m e n t avec d e s librairies*, p o u r former l ' e x c u t a b l e p r o p r e m e n t dit. D a n s l ' e x e m p l e ci-dessous, d e u x fichiers s o u r c e s suffixes p a r . c p p s o n t c o m p i l s s p a r m e n t puis links a v e c u n e librairie i n d p e n d a n t e p o u r d o n n e r l ' e x c u t a b l e m a i n . e x e . S i , aprs u n e p r e m i r e compilation, v o u s n e modifiez q u e l e fichier o u t i l s . c p p , v o u s n ' a u r e z b e s o i n d e r e c o m p i l e r q u e o u t i l s . c p p ; v o u s p o u r r e z rutiliser l'ancien m a i n . o p o u r l ' d i t i o n des liens.

Mise en uvre

P o u r raliser effectivement u n e c o m p i l a t i o n s p a r e , il faut savoir d e u x c h o s e s : quoi m e t t r e , et d a n s quels fichiers c o m m e n t lancer la c o m p i l a t i o n U n projet C + + s e d c o m p o s e g n r a l e m e n t e n b e a u c o u p d ' l m e n t s de natures diffrentes : des c l a s s e s , d e s fonctions globales (hors-classes), des c o n s t a n t e s , d e s structures, etc. C o m m e n t savoir o placer c h a q u e l m e n t ?

Quoi mettre, et dans quels fichiers ?

La compilation spare

151

On parlera ici de fichiers .cpp pour dsigner les fichiers contenant du codesource C + + . Votre compilateur utilise peut-tre une autre extension (.cxx ou .C par exemple).

Principe gnral D a n s la majorit d e s c a s , les fichiers fonctionnent p a r c o u ple : un fichier s u f f i x e p a r . h p o u r dclarer u n e classe ou d e s enttes de fonctions, et un fichier s u f f i x e p a r . cpp p o u r la dfinition d e ces fonctions ( m e m b r e s o u n o n ) . A d m e t t o n s q u e v o u s criviez d a n s un fichier A q u e l c o n q u e . Si v o u s a v e z b e s o i n d'utiliser u n e classe ou u n e fonction dfinie ailleurs, il faut inclure au d b u t de A le fichier . h qui dclare ce d o n t v o u s a v e z b e s o i n . P o u r utiliser u n e autre i m a g e , le . h est l ' a m b a s s a d e u r de v o tre travail, il p r s e n t e c o m m e n t utiliser ce q u e v o u s a v e z fait, alors q u e le . cpp dcrit c o m m e n t c'est implant. Conseils La dclaration d ' u n e classe se fait g n r a l e m e n t d a n s un fichier . h qui p o r t e s o n n o m . La dfinition d e s f o n c t i o n s - m e m b r e s d ' u n e c l a s s e , ainsi q u e la dfinition des c o n s t a n t e s et variables de classe (static) se fait d a n s un fichier . cpp p o r t a n t le n o m de la classe. L e s objets, variables ou fonctions globales (c'est--dire accessibles d e p u i s n ' i m p o r t e quelle fonction d u p r o g r a m m e ) sont dfinis d a n s un fichier d o n t le n o m r e s s e m b l e glo-

bal . cpp
La dclaration de ces objets g l o b a u x se fait d a n s un fichier d o n t le n o m r e s s e m b l e extern.h, o c h a c u n de ces objets est p r c d du m o t - c l extern. L e s fonctions hors-classe, C standard, figurent d a n s d e s fichiers . c La dclaration de ces fonctions se fait d a n s des fichiers . h Si v o u s utilisez de n o m b r e u s e s classes, v o u s g a g n e r e z reg r o u p e r plusieurs classes d a n s un seul fichier . h. Il est cep e n d a n t conseill de g a r d e r un seul fichier . cpp p a r classe les corps des fonctions p r e n n e n t en effet plus de p l a c e q u e les dclarations de classes.

V o u s trouverez ci-dessous u n s c h m a qui r s u m e ces c o n seils travers un e x e m p l e . L e s listings c o m p l e t s sont situs u n p e u plus loin d a n s c e chapitre.

252

Pont entre C et C++

Compilation

spare

exemple

Remarques A v e c l a c o m p i l a t i o n spare, l e p r o g r a m m e u r est contraint de respecter certaines rgles : C h a q u e fichier . c p p utilisant d e s l m e n t s d ' u n autre fichier doit inclure le fichier . h dclarant les l m e n t s en question. Ici, m a i n . c p p inclut les fichiers e x t e r n . h , f o n c _ c . h e t C l a s s i . h , car i l v a utiliser d e s variables e t c o n s t a n t e s

La compilation spare

153

globales dclares d a n s e x t e r n . h , des fonctions C dclares d a n s f o n c _ c . h , e t des classes d c l a r e s d a n s

Classl . h .
Q u a n d v o u s modifiez l'entte d ' u n e fonction d a n s u n fichier . cpp, il faut veiller mettre j o u r le fichier . h correspondant. L ' o r d r e d'inclusion des fichiers . h est i m p o r t a n t . S i , p a r e x e m p l e , v o u s utilisez un objet global de classe Classl dclar d a n s e x t e r n , h, il faut q u e p a r t o u t Classl. h soit inclus a v a n t e x t e r n . h (sinon l e t y p e Classl sera inconnu dans e x t e r n . h). Listing Voici le listing qui dtaille l'architecture p r s e n t e d a n s le s c h m a de la p a g e p r c d e n t e :
// fichier main.cpp #include "extern.h" extern "C" { // voir complments #include "fonc_c.h"
}

#include "Classl.h" void main() { Classl objetl; _statut = 0;


}

// fichier global.cpp // constantes globales const float PI = 3.1415; // variables globales int _statut;

Cette manire d'oprer (avec #ifndef) est explique dans les complments, page I 57.

// fichier extern.h tifndef _EXTERN_H #define _EXTERN_H // constantes globales extern const float // variables globales extern int _statut;

PI;

754

Pont entre C et C++

#endif

// fichier Classl.h #ifndef _CLASS1_H #define _CLASS1_H class Classl { protected: // ... public : Classl ( ) ; void FoncMemb ( ) ;
} ;

#endif

// fichier Classl.cpp #include "Classl.h" // constructeurs Classl::Classl() {


}

// autres fonctions-membre de Classl void Classl::FoncMemb()


{
}

// fichier fonc_c.h #ifndef _FONC_C_H #define _FONC_C_H #define CONST_C void #endif 100

fonction_c(int a ) ;

// fichier fonc c.c #include "fonc_c.h" int fonction_c(int a) { return a;


}

La compilation spare

155

Comment lancer la compilation ?

L e s m t h o d e s d e c o m p i l a t i o n varient b e a u c o u p selon les s y s t m e s . C e r t a i n s c o m p i l a t e u r s s ' o c c u p e n t de p r e s q u e tout : il v o u s suffit d'indiquer quels fichiers font partie de v o t r e p r o jet. D ' a u t r e s en r e v a n c h e , r e p o s e n t sur l'utilitaire m a k e . Make C e t utilitaire v o u s p e r m e t lancer u n e c o m p i l a t i o n s p a r e d ' u n e seule instruction. P o u r cela, il est n c e s s a i r e d'crire un script de compilation, a p p e l makefile. Ce script idendifie les fichiers c o m p i l e r , ainsi q u e les d p e n d e n c e s entre ces fichiers. Si v o t r e makefile est crit c o r r e c t e m e n t , seuls les fichiers n c e s s a i r e s seront r e c o m p i l s . Le format d ' u n e paire de lignes d ' u n fichier makefile est le suivant (ici, les crochets i n d i q u e n t des l m e n t s o p t i o n n e l s ) :
fichier_cible : fichier_l [fichier_2 ...] instruction excuter

Si une ligne de votre Makefile est t r o p longue, vous pouvez la couper en deux condition que la premire ligne se termine par un backslash (\).

C e qui signifie : p o u r obtenir f i c h i e r _ c i b l e , j ' a i b e s o i n d e f i c h i e r _ l , f i c h i e r _ 2 , etc. e t d ' e x c u t e r l'instruction de la d e u x i m e ligne. M a k e en dduit q u e si l'un d e s fichiers f i c h i e r _ l , f i c h i e r _ 2 , etc. est plus rcent (au n i v e a u d e s dates d e dernire modification) q u e f i c h i e r _ c i b l e , i l faut e x c u t e r l'instruction de la d e u x i m e ligne. Ce format sert p r i n c i p a l e m e n t d e u x g e n r e s d'instructions (on s u p p o s e r a q u e v o t r e c o m p i l a t e u r C + + s'appelle CC) : c o m p i l e r un . c p o u r obtenir un . o
fichier.o : fichier.c fichier.h autre_fichier.h CC -c fichier.c

C e s lignes signifient q u e s i f i c h i e r . c , f i c h i e r . h o u a u t r e _ f i c h i e r . h ont t modifis u n e date p o s t r i e u r e d e celle d e f i c h i e r . o , i l faut r e c o m p i l e r f i c h i e r . c p o u r o b tenir un n o u v e a u f i c h i e r , o. L ' o r d r e de c o m p i l a t i o n dp e n d b i e n e n t e n d u d e votre c o m p i l a t e u r . En fait, on i n d i q u e aprs les : le n o m du fichier s o u r c e c o n cern, ainsi q u e la liste d e s fichiers . h qu'il utilise.

156

Pont entre C et C++

lier les . o et les librairies p o u r former l ' e x c u t a b l e


excutable : fichier.o autre_fichier.o lib.a CC -o excutable fichier.o autre_fichier.o -llib.a

C e qui v e u t d i r e : s i f i c h i e r , o , a u t r e _ f i c h i e r . o o u l i b . a s o n t p l u s r c e n t s q u e e x c u t a b l e , i l faut refaire u n e dition d e s liens. N o u s utilisons ici u n e librairie titre d ' e x e m p l e ; il est tout fait p o s s i b l e q u e v o u s n ' e n n ' a y e z p a s b e s o i n . Il faudrait d a n s ce cas crire :
excutable : fichier.o autre_fichier.o CC -o excutable fichier.o autre_fichier.o

Exemple Voici l'allure du fichier makefile l ' e x e m p l e p r s e n t p a g e 152.

permettant

de

compiler

# fichier makefile appli : main.o global.o classl.o fonc_c.o CC -o appli main.o global.o classl.o fonc_c.o main.o : main.cpp extern.h fonc_c.h classl.h CC -c main.cpp global.o : global.cpp CC -c global.cpp classl.o : classl.cpp classl.h CC -c global.cpp fonc_c.o : fonc_c.c fonc_c.h CC -c fonc_c.c

L a n c e r la c o m p i l a t i o n (enfin!) A s s u r e z - v o u s q u e le fichier makefile est d a n s le r p e r t o i r e o sont situs les fichiers-sources de v o t r e projet, et t a p e z


make -f nom_fichier_makefile

Si v o u s n o m m e z le fichier makefile M a k e f i l e (avec un M m a j u s c u l e ) , il v o u s suffira de taper m a k e p o u r lancer la c o m p i l a t i o n s p a r e de votre projet.

La compilation spare

157

N o t e z q u e certains s y s t m e s a c c e p t e n t i n d i f f r e m m e n t m a k e f i l e o u M a k e f i l e (avec o u s a n s m a j u s c u l e ) , d o n n a n t l a prfrence au fichier qui c o m m e n c e p a r u n e m a j u s c u l e si les d e u x sont p r s e n t s d a n s le rpertoire courant.

Complments
Comment viter les redclarations ?
L e c o m p i l a t e u r n ' a i m e p a s les dclarations m u l t i p l e s d ' u n m m e l m e n t (objet, variable, c o n s t a n t e , fonction, etc.) Il faut d o n c veiller ne p a s inclure d e u x fois le m m e fichier . h d a n s le m m e fichier. C e l a arrive facilement d a n s un c a s de t y p e p o u p e s r u s s e s :

E n traitant a p p l i . c p p , l e p r - p r o c e s s e u r r e m p l a c e l a ligne #include " e x t e r n . h " p a r l e c o n t e n u d u fichier e x tern.h. On obtient d o n c deux lignes #include " toto . h", qui inclueront d e u x fois le fichier toto . h, ce qui va g n r e r des erreurs de redclaration. Il existe un m o y e n s i m p l e d'viter ces r e d c l a r a t i o n s , tout en p e r m e t t a n t l'inclusion m u l t i p l e du m m e . h : utiliser les directives d e p r c o m p i l a t i o n # i f n d e f , # d e f i n e e t # e n d i f . Le truc consiste ne p r e n d r e en c o m p t e le c o n t e n u du . h q u e la p r e m i r e fois q u e le p r c o m p i l a t e u r le r e n c o n t r e . Voici un fichier . h qui utilise cette t e c h n i q u e :
// dbut du fichier extern.h #ifndef _EXTERN_H #define EXTERN_H // corps complet du fichier #endif // fin du fichier extern.h

158

Pont entre C et C++

En fait, on associe c h a q u e fichier . h un s y m b o l e p o r t a n t s o n n o m (ici _EXTERN_H). A U dbut, c e s y m b o l e n ' e x i s t e pas. La p r e m i r e fois q u e le p r c o m p i l a t e u r traite le fichier, il r e n c o n t r e la ligne #ifndef _EXTERN_H. C o m m e le s y m b o l e _EXTERN_H n ' e x i s t e p a s , le p r c o m p i l a t e u r traite le fichier j u s q u ' rencontrer u n # e n d i f (ou u n #elif d'ailleurs). Le p r - p r o c e s s e u r traite d o n c tout le c o r p s du fichier j u s q u ' n o t r e #endif final. D a n s cette partie d e c o d e prise e n c o m p t e , l a p r e m i r e ligne est # de fi ne _EXTERN_H, qui dfinit le symbole _EXTERN_H, ce qui servira au p r o c h a i n p a s s a g e r e n d r e la c o n d i t i o n # i f n d e f _EXTERN_H fausse, et viter d'inclure u n e n o u v e l l e fois l e fichier e x t e r n . h .

Static, inline et porte

Le g a i n de t e m p s et de clart n ' e s t p a s le seul intrt de la c o m p i l a t i o n s p a r e . L a d c o m p o s i t i o n e n p l u s i e u r s fichiers a aussi d e s c o n s q u e n c e s sur la p o r t e de certaines v a r i a b l e s et fonctions : Static global T o u t e fonction dclare s t a t i c ainsi q u e toute v a r i a b l e dclare s t a t i c e n global*, n ' e s t accessible q u e d a n s l e fichier o elle est dfinie. E x e m p l e : PORTE DE VAR ET FONCI

* C'est--dire en dehors de t o u t e fonction

On ne p e u t a c c d e r ni var, ni foncl d e p u i s f i c h 2 . cpp, car ils ont t dclars static d a n s f i c h l . c p p . Ils ne sont a c c e s s i b l e s q u ' l'intrieur de f ichl. cpp.

La compilation spare

159

Attention ! Ici, s t a t i c n ' a p a s l e m m e sens q u e lorsqu'il qualifie les v a r i a b l e s - m e m b r e s ou f o n c t i o n s - m e m b r e s ! R a p p e l o n s q u e d a n s ces derniers c a s , s t a t i c signifie qu'il s'agit de variables ou fonctions de classe, c o m m u n e s tous les o b jets issus de la classe. F o n c t i o n s inlines S i v o u s dclarez u n e fonction i n l i n e e n d e h o r s d ' u n e classe, la p o r t e de celle-ci est a u t o m a t i q u e m e n t r d u i t e au fichier d a n s lequel elle est dclare. Constantes globales U n e c o n s t a n t e dclare en global n ' e s t visible q u e d a n s le fichier o elle est dfinie, m o i n s de spcifier e x p l i c i t e m e n t c o n s t d a n s l a dcla'ration externe. E x e m p l e 1, o PI est dclar l o c a l e m e n t f i c h l . c p p :

E x e m p l e 2, o PI est visible la fois d a n s f i c h l . c p p et fich2.cpp :

7 60

Pont entre C et C++

Travailler avec d'autres langages

L e C + + p e r m e t l ' u s a g e d e fonctions p r o v e n a n t d ' a u t r e s lang a g e s : le C b i e n e n t e n d u , m a i s aussi le P a s c a l , le Fortran, etc. C o m m e c h a q u e l a n g a g e a d o p t e s a p r o p r e l o g i q u e d'dition d e s liens ( n o t a m m e n t e n c e qui c o n c e r n e l a m a n i r e d e stocker les p a r a m t r e s de fonctions sur la pile d ' e x c u t i o n ) , il faut l'indiquer d ' u n e m a n i r e ou d ' u n e autre d a n s le c o d e C + + . V o u s p o u v e z l e faire grce l a c l a u s e e x t e r n " n o m " , o nom reprsente un type d'dition de liens ( c o n s u l t e z la d o c u m e n t a t i o n d e votre c o m p i l a t e u r ) . Il existe trois m a n i r e s d'utiliser e x t e r n "nom" :
extern "nom" lment; dclare que l'dition des liens de lment (un entte de fonction ou une variable) se fait selon la convention nom. Idem que ci-dessus, mais tous les lments dclars dans bloc sont concerns. Idem que ci-dessus, mais toutes les dclarations faites dans fichier.h sont concernes.

extern

"nom"{

bloc de dclarations; } extern "nom" { #include "fichier.h"

) Prenons l'exemple d'un programme C + + ayant besoin de fonctions C. On utilise e x t e r n " C " :
// fichier sriai.h (C standard) int int serial_output(int, unsigned char); serial_input(int, unsigned char*);

// fichier main.cpp (C++) possibilit 1 extern "C" int serial_output(int, unsigned char); extern "C" int serial_input(int, unsigned char*); void main { /*...*/ }

Utilisons la d e u x i m e possibilit p o u r dclarer les d e u x fonctions sans rpter e x t e r n "C" :


// fichier main.cpp (C++) possibilit 2 extern "C" { int int
}

serial_output(int, unsigned char); serial_input(int, unsigned char*);

La compilation spare

161

void main { /*...*/ }

Enfin, a d o p t o n s la dernire solution, la p l u s l g a n t e , la p l u s c o n o m i q u e , bref, la m e i l l e u r e d a n s n o t r e e x e m p l e :


// fichier main.cpp (C++) possibilit 3 extern "C" { #include "sriai.h" } void main { /*...*/ }

U t i l i s e r du c o d e C + + partir du C P o u r utiliser d u c o d e C + + partir d ' u n c o d e C , s u i v e z l'exemple suivant :


// fichier C extern "C" double racine(double d) { // code C++ Math monObjetMath; return monObjetMath.racine(d); } void main() { double res; res = racine(22.);
}

Guide de survie
Les concepts prsents dans les deux premires parties vous laissent peut-tre perplexe : comment utiliser un tel arsenal pour venir bout d'un problme rel ? Cette partie expose quelques conseils de base pour concevoir une application en C++, ainsi qu'une srie de questions/rponses sur le langage.

Conseils

Ce chapitre aborde quelques conseils de programmation orienteobjets en C++. Il ne s'agit pas d'noncer des rgles d'or, mais plutt de donner quelques pistes pour dbuter. Par la suite, l'exprience aidant, vous pourrez concevoir et coder dans un style qui ne vous sera dict par personne...

Les tapes d'un projet


U n projet i n f o r m a t i q u e p e u t s e d c o m p o s e r e n q u a t r e p h a s e s grossires : 1. 2. 3. 4. D t e r m i n a t i o n du cahier des c h a r g e s fonctionnel Conception Codage Maintenance

M a l g r tout le soin p o u r les sparer, ces p h a s e s ont t e n d a n c e se m l a n g e r de m a n i r e subtile. C ' e s t p o u r q u o i les tentativ e s d e formalisation d e l a p r o g r a m m a t i o n ont s o u v e n t c h o u , car il s'agit l d ' u n e activit p r o f o n d m e n t h u m a i n e , v o i r e artistique.

166

Pont entre C et C++

L a p r e m i r e p h a s e consiste d t e r m i n e r c e q u e v o u s (ou v o s clients) a t t e n d e n t de l'application d v e l o p p e r . Il est en effet capital d e n e j a m a i s p e r d r e d e v u e les b e s o i n s a u x q u e l s l e p r o g r a m m e doit r p o n d r e . M a l h e u r e u s e m e n t (ou h e u r e u s e m e n t ) , c e s b e s o i n s s o n t susceptibles d ' v o l u e r tout a u l o n g d u projet, m e t t a n t e n c a u s e c e qui tait c o n s i d r c o m m e acquis d s l e dbut. N o u s n e n o u s p e n c h e r o n s p a s p l u s sur cette p h a s e qui n e d p e n d p a s d u C + + . A t t a r d o n s - n o u s m a i n t e n a n t sur l a c o n c e p t i o n p r o p r e m e n t dite.

Conception
La c o n c e p t i o n oriente-objets consiste e s s e n t i e l l e m e n t trouver les classes qui m o d l i s e n t v o t r e p r o b l m e et les relations qui se tissent entre elles.

Trouver les classes

C ' e s t u n e p h a s e essentielle. Pourtant, il est difficile de d o n n e r d e s c o n s e i l s g n r a u x tant les solutions d p e n d e n t d u p r o b l m e . O n isole p l u s facilement les classes q u a n d o n p r e s s e n t (du v e r b e pressentir) les relations qui les u n i r o n t ; aussi la lecture de la section suivante pourra-t-elle v o u s aider. Q u e l q u e s r e m a r q u e s : u n e classe p e u t tre v u e c o m m e u n e entit, u n e ide a u n i v e a u d e votre projet. R a p p e l o n s q u ' u n e classe est un tout, form de d o n n e s et de fonctions qui traitent ces d o n n e s . P o u r crer u n e c l a s s e , il faut d o n c q u e d e s liens troits existent entre ses c o m p o s a n t s . Si cela n ' e s t p a s le cas, il se p e u t q u ' e l l e se d c o m p o s e en s o u s - c l a s s e s . Un autre m o y e n consiste formuler le p r o b l m e en franais. V o u s a u r e z alors d e b o n n e s c h a n c e s p o u r q u e les n o m s c o m m u n s r e p r s e n t e n t des objets, e t q u e les v e r b e s s y m b o l i sent d e s f o n c t i o n s - m e m b r e s (c'est l a m t h o d e d e B o o c h ) . ce n i v e a u de c o n c e p t i o n , ne p r e n e z p a s en c o m p t e les classes lies l ' i m p l m e n t a t i o n , m a i s c o n c e n t r e z - v o u s sur celles qui r p r s e n t e n t d i r e c t e m e n t la ralit du p r o b l m e . Inutile,

Conseils

167

p a r e x e m p l e , de p e n s e r des listes ou des tableaux ce niveau.

Architecturer les classes

C ' e s t p r a t i q u e m e n t la p h a s e la p l u s difficile. Il existe princip a l e m e n t quatre sortes de relations entre d e u x c l a s s e s A et B: 1. 2. 3. 4. A A A A est u n e sorte de B est c o m p o s de B utilise B n ' a a u c u n lien avec B (eh oui!)

La diffrence entre les points d e u x et trois n ' e s t p a s toujours v i d e n t e , m a i s n o u s allons voir cela d a n s les p a r a g r a p h e s qui suivent. N o u s ne dtaillerons p a s le dernier point, d o n t le rle est s i m p l e m e n t d e v o u s rappeler q u ' i l n e faut p a s a b s o l u m e n t s ' a c h a r n e r trouver un lien entre d e u x c l a s s e s . A est u n e s o r t e de B Il s'agit d ' u n e relation d'hritage. La f o r m u l a t i o n est u n e sorte de fonctionne trs b i e n p o u r l'identifier. Si m a l g r tout cela ne suffit p a s , il faut p e n s e r q u e la classe d r i v e (ici A) p e u t tre u n e spcialisation de la classe de b a s e . D a n s certains cas, u n e relation n e p e u t p a s s e m o d l i s e r s o u s forme est u n e sorte de , b i e n q u ' i n t u i t i v e m e n t v o u s sentiez q u ' i l existe u n e relation d'hritage entre les d e u x . Il est alors p o s s i b l e q u e les d e u x entits soient s u r s , c'est-dire q u ' e l l e s aient des p o i n t s c o m m u n s et hritent toutes d e u x d ' u n e m m e classe d e b a s e . Exemple Q u e l l e s sont les relations entre camion et voiture ? U n e v o i t u r e est-elle u n e sorte de c a m i o n ou est-ce l'inverse ? D a n s ce c a s , s e l o n l e p r o b l m e , o n p e u t p e n c h e r p l u t t p o u r l a solution un c a m i o n est u n e sorte de v o i t u r e , car les v o i t u r e s s o n t p l u s frquentes, et elles ont t i n v e n t e s a v a n t les c a m i o n s . L e s c a m i o n s sont d o n c u n e spcialisation d e s v o i t u res. On p e u t aussi crer u n e classe vhicule, ou v h i c u l e terrestre, qui servira de classe de b a s e v o i t u r e et c a m i o n .

168

Pont entre C et C++

A est c o m p o s de un ou p l u s i e u r s B C e t t e relation r e p r s e n t e u n objet qui p e u t s e d c o m p o s e r e n d'autres objets, qui p o s s d e n t c h a c u n leur vie p r o p r e . D e tels objets B sont dclars c o m m e d o n n e s - m e m b r e s de l'objet principal A . Exemple U n e v o i t u r e est c o m p o s e d ' u n m o t e u r e t d e q u a t r e p n e u s . C e m o t e u r e t ces p n e u s sont des objets qui p e u v e n t e x i s t e r indpendamment. C e n ' e s t v i s i b l e m e n t p a s u n e relation d ' h r i t a g e , car u n p n e u (ou un m o t e u r ) n ' e s t p a s u n e sorte de v o i t u r e .

A utilise B Il existe d e u x m a n i r e s de reprsenter cette relation en t e r m e de l a n g a g e orient-objet. Soit l'objet B est dfini c o m m e donn e - m e m b r e de l'objet A, soit A utilise d e s objets B g l o b a u x o u p a s s s c o m m e p a r a m t r e s d e fonctions. P a r r a p p o r t la relation p r c d e n t e (A est c o m p o s de B ) , la n u a n c e se situe au n i v e a u du s e n s : ici, les objets B ne font

Conseils

169

p a s partie d e l'objet A . Ils sont c o m p l t e m e n t i n d p e n d a n t s , m a i s s o n t utiliss p a r A p o u r r s o u d r e un p r o b l m e . Exemple Dans un environnement graphique, le gestionnaire d ' v n e m e n t s utilise les informations en p r o v e n a n c e de la souris. Il n ' e s t ni c o m p o s d ' u n e souris, p a s p l u s qu'il n ' e s t l u i - m m e u n e sorte d e souris.

B i e n e n t e n d u , en t e r m e d ' i m p l m e n t a t i o n , il se p e u t q u e la classe g e s t i o n n a i r e d ' v n e m e n t ait c o m m e d o n n e - m e m b r e un objet de classe souris. M a i s il se p e u t g a l e m e n t qu'il c o m m u n i q u e a u t r e m e n t avec l'objet souris, p a r e x e m p l e par l'intermdiaire d ' u n e autre classe qui centralise les entres.

Dtailler les classes

L e s classes sont c o n n u e s et leurs relations identifies. Il faut m a i n t e n a n t dtailler les f o n c t i o n s - m e m b r e s q u ' e l l e s contiennent. Ce qu'il est b o n de g a r d e r l'esprit : M i n i m i s e r les c h a n g e s e n t r e les c l a s s e s P l u s v o s classes sont i n d p e n d a n t e s , m o i n s elles c h a n g e n t d'informations a v e c les autres, et p l u s facile sera la m a i n t e n a n c e d e v o t r e projet. R d u i s e z autant q u e p o s s i b l e l e n o m bre de p a r a m t r e s d a n s les f o n c t i o n s - m e m b r e s . En dvoiler un m i n i m u m D a n s l e m m e ordre d'ides, u n e classe doit s'efforcer d e cac h e r un m a x i m u m de ses p r o p r e s d o n n e s , et surtout la manire d o n t ces d o n n e s sont s t o c k e s . I m a g i n e z toujours ce qui se passerait si v o u s c h a n g i e z la m a n i r e de r e p r s e n t e r ces d o n n e s . L'interface avec l'extrieur, c'est--dire les fonctions p u b l i c , n e doit p a s c h a n g e r e n tout c a s aussi p e u que possible.

2 70

Pont entre C et C++

valuer l'ensemble

C o n f r o n t e z plusieurs scnarios votre architecture de class e s , e t vrifiez q u e tout p e u t tre m i s e n u v r e . V o u s g a g n e rez p a s s e r en r e v u e les fonctionnalits a t t e n d u e s ( p h a s e 1). Si v o t r e c o n c e p t i o n s'avre trop c o m p l e x e utiliser, r e m e t tez-l en c a u s e et e s s a y e r d ' e n i m a g i n e r u n e autre. Il faut savoir q u e s i v o u s a v e z des p r o b l m e s d e c o m p r h e n s i o n d e l'architecture ce n i v e a u , ils ne feront q u e s'amplifier l'tape suivante.

Codage C++
* C'est une faon de parler.

V o t r e projet est dtaill sur le papier, il ne reste plus* q u ' le p r o g r a m m e r . A v e c l e C + + , v o u s d i s p o s e z d e n o m b r e u x outils d o n t l'utilisation s i m u l t a n e n ' e s t p a s toujours v i d e n t e . Ce p a r a g r a p h e va tenter de v o u s faciliter la tche. C e t t e t a p e consiste choisir les classes-outils ou les bibliot h q u e s q u e v o u s utiliserez p o u r m e t t r e e n u v r e v o t r e c o n ception. C e n e s o n t p a s d e s classes c o n c e p t u e l l e s , m a i s d e s classes p r o p r e s a u x t e c h n i q u e s d e p r o g r a m m a t i o n . Par e x e m p l e , toutes les classes c o n t e n e u r (tableaux, listes, arb r e s , g r a p h e s ) en font partie. Elles servent s t o k e r d'autres objets. Si v o u s d e v e z c o n c e v o i r d e s classes-outils p o u r votre projet, p e n s e z la rutilisabilit, et faites en sorte q u ' e l l e s soient aussi universelles q u e possible. V o u s a u r e z peut-tre p l u s d e m a l , m a i s v o u s g a g n e r e z d u t e m p s sur v o t r e p r o c h a i n projet. B i e n e n t e n d u , c e g e n r e d e classe-outils n c e s s i t e u n e d o c u m e n t a t i o n : c o m m e n t e z - l e s ou dcrivez leur f o n c t i o n n e m e n t part, si p o s s i b l e a v e c des e x e m p l e s d'utilisation. Factoriser des classes R e p r e n e z votre g r a p h e d'hritage. V o u s p o u v e z e s s a y e r d e trouver d e s p o i n t s c o m m u n s entre c l a s s e s , s u f f i s a m m e n t n o m b r e u x p o u r former u n e classe d e b a s e . L ' a v a n t a g e d ' u n e

Quels outils ?

Conseils

171

telle factorisation rside d a n s la m i n i m i s a t i o n d e s r e d o n d a n c e s . D o n c , les modifications ventuelles n ' a u r o n t se faire q u ' un endroit si elles c o n c e r n e n t la classe de b a s e . C e t t e t a p e ne figure p a s d a n s la c o n c e p t i o n , car il s'agit l d ' u n e m t h o d e d e p r o g r a m m a t i o n qui n ' a p a s f o r c m e n t d e s e n s conceptuel. Exemple Vous concevez un environnement graphique compos de divers objets : fentres, b o u t o n s , etc. Si c h a q u e objet c o m p o r t e u n identifiant, u n e rfrence vers l'objet p a r e n t , e t j e n e sais q u o i d'autre, il e s t j u d i c i e u x de c r e r u n e classe ObjetG e n e r i q u e qui r e g r o u p e r a tous ces p o i n t s c o m m u n s . Ici n o u s bnficions d ' u n autre atout : tous les objets fentres, b o u tons et autres p o u r r o n t tres d s i g n s p a r un p o i n t e u r s u r ObjetGenerique*.

* Rappelons que le C+ + fart la conversion de Drive* vers Base*.

Mise en uvre de la rutilisabilit

L ' u n d e s c h e v a u x d e bataille d e s l a n g a g e s orients-objets est l a rutilisabilit d u c o d e produit. L e C + + est p a r t i c u l i r e m e n t b i e n a r m p o u r m e n e r b i e n cette croisade. O u t r e l'hritage q u e n o u s a v o n s dtaill d a n s la partie c o n c e p t i o n , p a r l o n s ici du p o l y m o r p h i s m e , de la gnricit et des e x c e p t i o n s . L e p o l y m o r p h i s m e e t les f o n c t i o n s v i r t u e l l e s U n e fonction virtuelle doit garder le m m e s e n s d a n s toute la b r a n c h e d'hritage. U n e x e m p l e parfait serait u n e fonction d e rotation virtuelle dfinie p o u r un objet g r a p h i q u e de b a s e . U n b o n m o y e n d ' i m p o s e r u n m o u l e p o u r u n e srie d e c l a s s e s consiste crer u n e classe de b a s e abstraite, o des fonctions virtuelles p u r e s sont dclares. C e g e n r e d e c l a s s e fixe u n c a dre gnral d'utilisation : les classes drives d o i v e n t dfinir les fonctions virtuelles p u r e s , en r e s p e c t a n t leur entte. N o t r e e x e m p l e s'appliquerait b i e n cela : u n e classe abstraite ObjetVirtuel, qui ne crerait d o n c a u c u n objet, m a i s qui dfinirait les enttes des fonctions affichage, rotation, etc. On i m a g i n e facilement q u e des classes R e c t a n g l e o u C e r c l e hritent d'ObjetVirtuel (voir s c h m a p a g e suivante).

172

Pont entre C et C++

La gnricit et les templates Les templates permettent de paramtrer des classes par des types de d o n n e s , ces derniers p o u v a n t tre d'autres classes. L e s c o n t e n e u r s (les classes de s t o c k a g e ) sont tout i n d i q u s p o u r utiliser les templates. U n b o n exercice serait d e c o n c e voir u n e classe Liste template, qui accepterait c o m m e param t r e le type des objets q u ' e l l e stocke. Les exceptions Q u a n d v o u s c o n c e v e z u n e classe, v o u s n e s a v e z p a s forcm e n t qui v a l'utiliser, n i q u a n d o n v a l'utiliser. E n a d o p t a n t l e m c a n i s m e d ' e x c e p t i o n , v o u s p o u v e z signaler des erreurs ( t h r o w ) l'utilisateur de la classe qui agira en c o n s q u e n c e s ( t r y / c a t c h ) . D o c u m e n t e z e t signalez c l a i r e m e n t les c l a s s e s d ' e x c e p t i o n s q u e v o u s p o u v e z lancer. L ' u n d e s m o y e n s c o n siste faire suivre l'entte d ' u n e fonction p a r les e x c e p t i o n s q u ' e l l e est susceptible de lancer (voir p a g e 1 4 3 ) .

Implantation des classes

E n c o n c e v a n t e t c o d a n t u n e classe C + + , i l est intressant d e c o n s i d r e r q u e l q u e s points : Respectez la cohrence des fonctions d'accs U n e classe c o m p t e trs s o u v e n t d e s fonctions d ' a c c s p o u r consulter ou modifier d e s d o n n e s - m e m b r e s ( m o i n s q u e

Conseils

173

v o u s n ' a y e z dclar des d o n n e s - m e m b r e s public, m a i s v o u s devriez carter d ' e m b l e cette possibilit). P e n s e z garder u n e c o h r e n c e p o u r distinguer ces fonctions d e s a u tres. Faites p a r e x e m p l e p r c d e r les fonctions de c o n s u l t a tion p a r g e t _ e t les fonctions d e modification p a r s e t _ , e n les faisant suivre du libell exact des d o n n e s - m e m b r e s . Prat i q u e m e n t tous les e x e m p l e s de ce livre r e s p e c t e n t ce format. R e n d e z const les fonctions qui ne m o d i f i e n t rien S i u n e f o n c t i o n - m e m b r e d ' u n e classe n e modifie a u c u n e d o n n e - m e m b r e , p e n s e z l a r e n d r e c o n s t . V o u s garantissez ainsi a u x utilisateurs de votre classe q u e l'objet ne sera p a s modifi en faisant appel cette fonction.
Tous les exemples de ce livre ne respectent pas cette convention, par souci d'allgement et de clart.

class Awesome { protected: int a; public : int get_a( ) const { return a; };


} ;

Prfrez protected private R a p p e l o n s q u e des d o n n e s private ne s o n t p a s hrites. L e s d o n n e s protected, en r e v a n c h e , s o n t accessibles a u x classes drives m a i s restent inaccessibles a u x autres c l a s s e s . A u s s i est-il g n r a l e m e n t plus intressant d'utiliser pro-

tected.
Respectez la symtrie des constructeurs et destructeurs Si un c o n s t r u c t e u r alloue de la m m o i r e ou d e s r e s s o u r c e s diverses, faites en sorte q u e le destructeur les libre. Attention : r a p p e l e z - v o u s q u e la s y n t a x e de delete est diffrente p o u r u n e s i m p l e variable (delete v a r ) q u e p o u r u n ta-

b l e a u (delete [ ] t a b ) .
I n i t i a l i s e z t o u t e s les d o n n e s - m e m b r e s , d a n s c h a q u e c o n s tructeur Il n ' y a rien de p l u s d s a g r a b l e q u e de confier la tche d'initialisation un c o n s t r u c t e u r irresponsable. Le rle du

74

Pont entre C et C++

c o n s t r u c t e u r est de p r p a r e r un n o u v e l objet, et ce rle c o m p r e n d son initialisation. F a u t - i l r e d f i n i r le c o n s t r u c t e u r c o p i e ? Il suffit q u ' u n e d o n n e - m e m b r e soit un p o i n t e u r et v o u s aurez c e r t a i n e m e n t b e s o i n d u c o n s t r u c t e u r c o p i e . C e dernier s e c h a r g e d'initialiser un n o u v e l objet partir d ' u n objet de m m e classe dj existant. R a p p e l o n s l e format d ' u n c o n s tructeur c o p i e :
NomClasse::NomClasse(const NomClass &a_copier) {/*...*/}

L e c o n s t n ' e s t p a s obligatoire, m a i s fortement conseill : v o u s viendrait-il l'ide de modifier l'objet c o p i e r ? Faut-il r e d f i n i r l ' o p r a t e u r d ' a f f e c t a t i o n = ? Si l ' u n e d e s d o n n e s - m e m b r e s est un pointeur, redfinissez l'oprateur = p o u r v o u s a s s u r e r q u e les l m e n t s d s i g n s p a r le p o i n t e u r s o n t b i e n r e c o p i s . A t t e n t i o n : l ' o p r a t e u r = n ' e s t p a s hrit p a r les sous-classes ! F a u t - i l r e d f i n i r d e s o p r a t e u r s de c o m p a r a i s o n ? D a n s d e n o m b r e u x c a s , i l v a u t m i e u x q u e v o u s redfinissiez l'oprateur de c o m p a r a i s o n = = . Si v o u s tes a m e n s trier d e s objets de votre classe, redfinissez g a l e m e n t < ou >, car o n n ' e s t j a m a i s m i e u x servi q u e p a r s o i - m m e . V o u s s a u r e z ce s u r quoi la c o m p a r a i s o n s'effectue : a d r e s s e d e s objets, ou b i e n v a l e u r d ' u n identifiant, o u e n c o r e q u i v a l e n c e l o g i q u e , etc.

Questions-Rponses

Si vous n'avez pas trouv les rponses vos questions dans les chapitres prcdents ou dans l'index, tentez votre chance ici. Mme si vous ne vous posez pas de question, il peut tre instructif de lire les rponses... Q u ' e s t - c e q u ' u n c o n s t r u c t e u r par dfaut ? C ' e s t u n c o n s t r u c t e u r qui n e p r e n d a u c u n p a r a m t r e , o u d o n t les p a r a m t r e s p o s s d e n t tous u n e v a l e u r p a r dfaut. L e C + + g n r e u n c o n s t r u c t e u r p a r dfaut s i v o u s n ' a v e z dfini a u c u n constructeur. Un c o n s t r u c t e u r p a r dfaut est appel en silence p o u r les objets qui ne s o n t p a s e x p l i c i t e m e n t initialiss. Il est g a l e m e n t a p p e l q u a n d v o u s allouez un n o u v e l objet a v e c new obj ; ou un tableau a v e c new obj [TAILLE] ;. C o m m e n t appeler un constructeur d'une classe de base dans le constructeur d'une classe drive Utilisez u n e liste d'initialisation (plus de dtails p a g e 4 7 ) .
class Drive : public Base { public : Drive ( ) : B a s e O { /* ... */ }
} ;

76

Pont entre C et C++

J ' a i r e d f i n i l ' o p r a t e u r = m a i s il n ' e s t pas a p p e l d a n s la d c l a r a t i o n Classe obj2 = objl; Q u ' a i - j e fait de m a l ? R i e n . D a n s l ' e x e m p l e cit, o b j 2 est initialis avec un autre objet ( o b j 1 ) . D a n s c e c a s , c e n ' e s t p a s l'oprateur = q u i est a p p e l , m a i s l e c o n s t r u c t e u r copie. L e rle d e c e dernier c o n siste p r c i s m e n t initialiser un objet partir d ' u n autre o b jet existant. Il s'agit ici d ' u n e initialisation de variable, et n o n d ' u n e affectation, qui serait ralise p a r l'oprateur -. P o u r q u o i l ' o p r a t e u r doit-il tre d c l a r f r i e n d ? Il ne doit p a s forcment tre dclar f r i e n d , m a i s cela facilite les c h o s e s . Q u e l q u e s explications : l'oprateur s ' a d r e s s e l'objet c o u t d e l a classe o s t r e a m . C o m m e n o u s n e p o u v o n s p a s ajouter notre o p r a t e u r d i r e c t e m e n t d a n s la classe o s t r e a m , il faut surcharger l'oprateur global. O r , ce dernier n ' a p a s accs a u x d o n n e s c a c h e s d e notre classe. D e u x solutions s'offrent n o u s : dclarer n o t r e o p r a t e u r f r i e n d d a n s notre classe, o u n e p a s l e faire [ha h a ] . D a n s c e dernier c a s , l e c o r p s d e l'oprateur < < d e v r a s e c o n t e n t e r d'utiliser les fonctions p u b l i q u e s de notre classe. Voici comment se passer du f r i e n d :
#include <iostream.h> class NomClasse { private: int n; public : NomClasse(int i) : n(i) {) int get_n() const { return n; } // vous voyez, on se passe du friend : // friend ostream &operator<< // (ostream& out, const NomClasse& obj);
} ;

// nous devons utiliser get_n() au lieu de n : ostream &operator<< (ostreamk out, const NomClassek obj) { return out << '[' << obj .get _n() << ' ] ' << endl; } void main() { NomClasse

obj1(1), obj2(2);

Questions-Rponses

177

cout << objl << obj 2;


}

// affichage: // [1] // [2]

P o u r q u o i u t i l i s e r cout et c i n au l i e u de p r i n t f et s c a n f ? E n utilisant c o u t , c i n , c e r r e t les autres objets dfinis d a n s i o s t r e a m . h , v o u s bnficiez d e plusieurs a v a n t a g e s : 1. V o u s n ' a v e z p a s b e s o i n de prciser le t y p e d e s objets q u e v o u s utilisez (plus de % d o n c m o i n s d'erreurs p o s s i b l e s ) , car c o u t , c i n e t c e r r intgrent les vrifications d e type ! 2. V o u s p o u v e z redfinir et p o u r qu'ils traitent v o s classes. D e cette m a n i r e , elles s'utiliseront c o m m e les typ e s prdfinis, ce qui est un g a g e de c o h r e n c e . 3. printf et s c a n f sont c o n n u s p o u r leur lenteur, d u e l ' a n a l y s e s y n t a x i q u e qu'ils sont obligs d ' o p r e r sur la c h a n e d e format. R a s s u r e z - v o u s , c o u t e t ses frres s o n t plus rapides. 4 . Utiliser c o u t a u lieu d e p r i n t f est plus chic. Q u e l est l'intrt d e d c l a r e r d e s o b j e t s a u b e a u m i l i e u d'une fonction plutt qu'au dbut ? P e r s o n n e l l e m e n t , je prfre dclarer tous les objets au d b u t d ' u n e fonction. C e l a vite de parcourir tout le c o d e p o u r trouver un objet prcis. M a i s les dclarations tardives ont leur c h a r m e : elles p e u v e n t acclrer le p r o g r a m m e . En effet, la cration d ' u n objet est c o t e u s e p u i s q u ' i l faut allouer la m m o i r e e t appeler l e constructeur. I m a g i n e z u n e fonction c o m p o r t a n t un bloc A e x c u t u n e fois sur cent. Si A utilise dix objets, v o u s a u r e z intrt les dclarer d a n s A plutt q u ' a u d b u t de la fonction. C e l a dit, c'est u n e q u e s t i o n de g o t p e r s o n n e l . C o m m e n t faire p o u r a l l o u e r n octects a v e c n e w ?
char *pointeur; pointeur = new char[n];

// alloue n octets

1 78

Pont entre C et C++

Dois-je b a n i r friend de ma religion ? O u i , autant q u e possible. N ' u t i l i s e z f r i e n d q u e s i v o u s n e p o u v e z p a s raisonnablement faire a u t r e m e n t . P a r e x e m p l e , i m a g i n o n s q u ' u n e classe p r c i s e (et u n e seule) a b e s o i n d ' a c c d e r a u x d o n n e s c a c h e s d ' u n e autre classe. D a n s c e cas d e figure, f r i e n d garantit q u e seule l a c l a s s e v o u l u e p e u t a c c d e r a u x d o n n e s c a c h e s . C e l a vite d ' a v o i r dclarer d e s fonctions d'accs p u b l i q u e s qui ne serviraient q u ' u n e seule autre classe, et q u e les autres d e v r a i e n t i g n o rer. Q u e s i g n i f i e c o n s t d a n s u n e d f i n i t i o n du s t y l e void f() const {I* ... */ i ? L e m o t cl c o n s t , insr ici entre les p a r a m t r e s d ' u n e fonction et s o n corps, signifie q u e cette fonction ne modifie p a s les d o n n e s - m e m b r e s de la classe laquelle elle appartient :
class Awesome { protected: int a ; public : int get_a( ) const { return a; }; };

D a n s cet e x e m p l e , la fonction g e t _ a ( ) ne modifie p a s la d o n n e - m e m b r e a . Elle peut d o n c tre dclare c o n s t . Q u e l l e est la capitale du K a z a k h s t a n ? Alma-Ata. P o u r q u o i d i a b l e n ' a r r i v - j e pas a c c d e r a u x d o n n e s de ma c l a s s e de b a s e d e p u i s ma c l a s s e d r i v e ? V o u s avez peut-tre dclar ces d o n n e s p r i v a t e a u lieu d e p r o t e c t e d . R a p p e l o n s q u e les d o n n e s p r i v a t e n e sont p a s accessibles d a n s les classes drives. Il se p e u t g a l e m e n t q u e votre hritage soit p r i v a t e o u p r o t e c t e d a u lieu d e p u b l i c . V o y e z l e chapitre sur l'hritage, s p c i a l e m e n t page 39.

Index

Symboles

surcharger 92 surcharger 57

comment allouer n octets? 177 new et delete 83 amies classes et fonctions 123

B
base classe de... 38 bases du C + + 11 C

surcharger 92 [] surcharger 142 A accs aux membres dans un hritage 39 affectation oprateur= 57 affichage cout, cin et cerr 89 formater 94 surcharger 92 allocation mmoire

catch 138 cerr 89 surcharger 92 cin 89 surcharger 92 classe 13 abstraite de base 179 amie 123 comment les architecturer? 167 comment les trouver? 166 conseils d'implantation 172

182

Pont entre C et C++

conversion de pointeurs 104 de base 38 dclaration 25 dfinir 15 drive 38 diffrences avec les objets 14 template 119 commentaires 63 ruse de sioux 64 compilateur principes de base 149 compilation spare 149 conseils 151 make 155 conception conseils 166 conseils de codage 170 de conception 166 constantes 67 et compilation spare 159 paramtres 70 propres une classe 71 constructeurs 27 copie 31 et hritage 43 ordre d'appel dans un hritage multiple 129 par dfaut 175 vue d'ensemble 27 conversion constructeur 35 de classe vers un type 61 de type vers une classe 35 drive* vers base* 42; 104 copie constructeur 31 par dfaut 58 cout 89 surcharger 92 tableau rcapitulatif 99 D dec 97

dclaration de classe 25 n'importe o 73 dfinir classe 15 fonction-membre 16 delete 83 syntaxe 84 dmarrer en C + + 20 drive classe... 38 destructeurs 27 et hritage 44 virtuels 106 vue d'ensemble 27
E

encapsulation 14 erreurs gestion des... 137 exceptions 137 crer ses propres... 140 en boucle 146 intercepter plusieurs... 139 non intercepte 145 spcifier dans l'entte 143 extern "nom" 160
F

fichiers sparation en plusieurs... 149 fonctions amies 123 dfinir fonction-membre 16 inline 21 redfinition de fonction-membre 41 retournant une rfrence 115 spcifier les exceptions dans un entte 143 templates 118 virtuelles 101 virtuelles pures 107

Index

183

formater les sorties 94 friend classes et fonctions 123 et o p r a t e u r 176 et religion 178
G

langages travailler avec d'autres... 160 listes d'initialisations 47 M make 155 malloc la fin du... 83 modles Voir templates N new 83 syntaxe 84 notation scientifique 97 O objet accder la partie public 17 crer 17 diffrences avec les classes 14 pourquoi comparer deux objets identiques 61 provisoire et rfrences 115 oct 97 oprateurs Voir Symboles, au dbut de l'index de conversion de classe vers un type 61 surcharge (vue d'ensemble) 54

gnricit 117 guide de survie 163


H

hritage accs aux membres 39 conseils 167 conversion de pointeurs 104 et constructeurs 43 et destructeurs 44 exemple complet 45 multiple 127 multiple, duplication de donnes 131 multiple, masquage de la virtualit 133 rsum 42; 78 simple 37 virtuel 135 hex 97

I
implantation conseils 172 initialisation listes 47 inline 21 iomanip.h 94 ios::fixed 98 ios::left 95 ios::right 95 ios::scientific 98 ios ::showbase 96 ios ::showpoint 96 ios ::showpos 96 iostream.h Voir cout et cin

P
paramtres par dfaut 65 passage par rfrence 113 patrons Voir templates pointeurs sur classe de base 104 polymorphisme 101 printf la fin du... 89 protected 40 conseils 173

184

Pont entre C et C++

Q
questions 175 R redclarations comment viter les... 157 redfinition d'une fonction-membre 41 rfrences 113 et objets provisoires 115 retournes par une fonction 115 rsum 1re partie 75 rutilisabilit 171 S set_terminate 145 set_unexpected 144 setfill 97 setiosflags 95 setprecision 95 setw 94 sioux ruse de 64 static 24 porte 158 surcharge 51

d'oprateurs 54 de l'oprateur 92 de l'oprateur = 57 de l'oprateur 93 de l'oprateur [] 142

T
template ambigut 122 templates 117 terminate 145 this 23 pourquoi retourner *this? 60 throw 140 try 138

U
unexpected 144 V virtualit 101 destructeurs 106 hritage virtuel 135 masquage dans un hritage multiple 133 pure 107

Vous aimerez peut-être aussi