Vous êtes sur la page 1sur 85

LE LANGAGE C++

ENAC 1997

A. DANCEL

1 - GENERALITES

"L'homme se dcouvre quand il se mesure avec l'objet."


Antoine de Saint-Exupry, Terre des hommes

1.1

INTRODUCTION

Ce cours est conu pour permettre aux programmeurs matrisant le langage C de passer facilement au langage C++. Pour pouvoir suivre avec profit ce cours sur le langage C++, il est donc indispensable de bien connatre le langage C-ANSI, car je ne prsente que les aspects nouveaux du langage C++. Le langage C++ a t dvelopp dans les laboratoires AT&T Bell au dbut des annes 1980 par le Danois Bjarne Stroustrup (http://www.research.att.com/~bs). Il est une volution du langage C auquel il apporte :
q q

la programmation oriente par les objets.

des amliorations notables (sans perte d'efficacit) : contrles plus stricts, passage des paramtres par rfrences Il permet de compiler des programmes crits en C-ANSI ( quelques incompatibilits prs). Contrairement ADA qui a t normalis sans avoir de compilateur, le C++ a vu de nombreuses versions de compilateur sans normalisation. On assiste en ce moment la phase finale de normalisation du C++ (celle ci devrait normalement intervenir en 1998, le vote du document final ayant eut lieu en dcembre 97). Ne nous faisons pas d'illusions : le langage C++ est et restera un langage complexe et long matriser. L'apprentissage du C++ n'est donc pas la porte de tout le monde et une formation de plusieurs mois est requise pour pouvoir programmer en C++.

1.2

LA CLASSE

La classe est la structure de base de la programmation oriente par les objets (POO). La classe d'objets est un "moule" partir duquel des objets ayant des proprits analogues seront forms. Un objet est une instance d'une classe, c'est dire qu'il est un lment conforme au modle que constitue sa classe. La programmation oriente par les objets est une approche gnrale de la programmation. C'est une forme de programmation modulaire dans laquelle l'unit de modularit est fortement lie aux structures des donnes manipules par le programme. Un langage objets utilise les notions de classe et d'instance (ou d'objet), que l'on peut comparer aux notions de type et de variable d'un langage tel que le C.

A. DANCEL - Le langage C++

Au lieu d 'appliquer des procdures ou des fonctions globales des variables, en programmation oriente par les objets on invoque les mthodes des instances d'une classe. Cette invocation est souvent appele "envoi de message" une instance d'une classe. L'abstraction et l'encapsulation des donnes sont les deux premiers principes fondateurs de la programmation oriente par les objets. Au lieu de rsoudre, comme en C, un problme par des variables et des fonctions sans lie n explicite, l'abstraction des donnes nous propose de runir dans une mme structure les donnes membres (attributs) et les fonctions membres (mthodes) les manipulant. Pour viter toute altration non dsire, l'encapsulation rend les donnes prives. L'utilisateur d'un objet n'aura pas un accs direct aux donnes, il devra passer par les mthodes publiques. L'ensemble des mthodes publiques est appel interface. Cette dernire spcifie ce qu'un utilisateur peut faire avec un objet. Le bon programmeur OO devra donc se concentrer sur le "ce que peut faire un objet" plutt que de se pencher sur le "comment faut-il le faire". On peut alors manipuler les classes comme des "boites noires" nous permettant ainsi de crer de nouveaux types : types abstraits de donnes. La rutilisabilit est un autre atout essentiel. Les langages objets permettent une rutilisation bien plus importante que la plupart des autres langages.

1.3

L'HERITAGE

L'un des avantages principaux de la POO, la rutilisation du code, est permis grce l'hritage. Au lieu de dvelopper une nouvelle application en crivant du code partir de zro, nos classes hritent des donnes et des oprations issues d'une classe de base intressante et ajoute ensuite de nouvelles fonctionnalits en dcrivant en quoi la nouvelle classe (dite classe drive) diffre de cette classe de base. Une classe drive peut se diffrencier de la classe de base par :
q q q

ajout de mthodes ajout de donnes membres redfinition de mthodes hrites de la classe de base

Une classe drive peut son tour servir de classe de base pour d'autres classes drives, nous permettant ainsi de dfinir une organisation hirarchique de classes. Ceci permet d'organiser les classes de manire cohrente et de partager (factoriser) entre elles du code commun. C++ permet aussi une classe :
q q

d'avoir plusieurs classes de base : hritage multiple de ne servir que de classe de base : classe abstraite (pas d'instanciation possible)

REMARQUE : il ne faut pas confondre l'hritage avec l'agrgation. Une agrgation est une forme d'association qui relie une entit avec ses composants. Elle est dfinie comme la relation "est compose de". Exemple : une voiture est compose d'un moteur. Alors que l'hritage est dfini comme la relation "est une sorte de". Exemple : une voiture est une sorte de vhicule.

A. DANCEL - Le langage C++

1.4

LE POLYMORPHISME

Le polymorphisme permet de dcrire le comportement d'une fonction de faon indpendante de la nature des paramtres. Il existe plusieurs formes de polymorphisme : Polymorphisme (d'hritage) : non seulement une classe de base ayant plusieurs classes drives limine le code redondant, mais elle donne en plus le moyen de rendre les programmes clients plus gnraux. Ainsi, grce au polymorphisme d'hritage, des fonctions n'utilisant que des mthodes d'une classe de base peuvent agir sur des instances de toute classe drive de cette classe de base, comme s'il s'agissait vraiment d'instances de la classe de base.
q

Surcharge (surdfinition de nom de fonction) : la surcharge rduit le nombre de fonctions dont un programmeur doit se souvenir. Elle permet de dfinir des fonctions (ou des oprateurs) ayant le mme nom, ds lors que chacun exige des types et/ou un nombre d'arguments diffrents. Ainsi en programmation systme sous UNIX les fonctions execl(), execle(), execlp(), execv(), execve(), excecvp() pourraient toutes s'appeler exec().
q

Gnricit (modle, patrons) : consiste dfinir un modle de fonction en utilisant des types comme paramtres. Elle nous permet, par exemple, d'crire une classe gnrique Stack permettant l'utilisateur de cette classe de spcifier le type des donnes de la pile. Son utilisation conjointe avec le polymorphisme d'hritage nous permet d'obtenir des structures de donnes polymorphes.
q

1.5
q q q

RESUME
Un type plus fort L'abstraction de donnes (la classe)

Le langage C++ est compatible ascendant avec le langage C auquel il ajoute :

Le support (facultatif) pour la programmation oriente par les objets (hritage, gnricit, polymorphisme...)
q

Le traitement des erreurs l'excution (exception)

Et tout ceci sans perte d'efficacit. Cependant, contrairement un langage comme Java, il n'y a pas de support explicite dans le langage pour la concurrence (thread), pour le rseau, la distribution (CORBA), la persistance, les fuites mmoires ("garbage collection"), etc Sa compatibilit avec les librairies crites en C-ANSI lui permet de faire tout cela facilement et de s'interfacer avec X-windows, d'appeler les primitives du systme d'exploitation... Son avenir ? Java ?

A. DANCEL - Le langage C++

2 - UN MEILLEUR C

Le langage C++ apporte un certain nombre d'amliorations et de nouveauts par rapport au langage C, indpendamment de la POO, qui rendent la vie du programmeur plus agrable.

2.1

LES COMMENTAIRES

Le langage C++ offre une nouvelle faon d'ajouter des commentaires. En plus des symboles /* et */ utiliss en C, le langage C++ offre les symboles // qui permettent d'ignorer tout jusqu' la fin de la ligne.
/* commentaire traditionnel sur plusieurs lignes valide en C et C++ */ void main() { // commentaire de fin de ligne valide en C++ #if 0 // Une partie d'un programme en C ou C++ peut toujours // tre ignore par les directives au prprocesseur // #if .... #endif #endif }

Il est prfrable d'utiliser les symboles // pour la plupart des commentaires et de n'utiliser les commentaires C ( /* */ ) que pour isoler des blocs importants d'instructions.
q

Il est trs important que tout fichier C++ (et C) commence par des commentaires dcrivant le fichier. La forme dpend de vous et de votre entreprise, mais devra comporter au min imum :
q

Le nom du programme, Le nom de la classe ou de la fonction, Le nom du fichier, de l'auteur etc La description du fonctionnement, Les dates de mise jour, Les ventuelles remarques.

/////////////////////////////////////////////////////////////// // PROGRAMME : essai // FICHIER : Commentaire2.cpp // AUTEUR : Alain Dancel - ENAC // DESCRIPTION : essai des diffrents styles de commentaires

A. DANCEL - Le langage C++

// MISES A JOUR : 15/03/98 Version 0.99 // STATUS : en cours de validation // REMARQUES : compilation par : // g++ -o essai Commentaire2.cpp /////////////////////////////////////////////////////////////// int main() { return 0; }

Conseils : viter les commentaires triviaux qui surchargent inutilement le code de votre programme et n'oubliez surtout pas de mettre jour les commentaires de dbut de fichier chaque fois que votre programme est modifi.

2.2

ENTREES/SORTIES SIMPLES

Il est possible d'utiliser l es fonctions scanf et printf pour effectuer les entres/sorties de vos programmes, mais cependant les programmeurs C++ prfrent les entres/sorties par flux (flot ou stream). Quatre flots sont prdfinis lorsque vous avez inclus le fichier d'en-tte iostream.h : 1. cout 2. cin 3. cerr 4. clog
q

qui correspond la sortie standard. qui correspond l'entre standard. qui correspond la sortie standard d'erreur non tamponn. qui correspond la sortie standard d'erreur tamponne.

Loprateur (surcharg) << permet d'envoyer des valeurs dans un flot de sortie :
#include <iostream.h> void main() { int i = 123; float f = 456.789; char ch[80] = Bonjour\n ; cout << i; cout << f= << f; cout << ch; } // affiche : 123 // affiche : 456.789 // affiche : Bonjour

L'oprateur >> permet d'extraire des valeurs d'un flot d'entre :


#include <iostream.h> void main() { int i; float f; char ch[80];

A. DANCEL - Le langage C++

cout << "i = ? "; cin >> i; cout << "f = ? "; cin >> f; cout << "ch = ? "; cin >> ch; cout << "ch= " << ch; }

// lecture d'un entier // lecture d'un rel // lecture du premier mot d'une chane // c'est bien le premier mot ...

Tout comme pour la fonction scanf, les espaces sont considrs comme des sparateurs entre les donnes par le flux cin. Notez l'absence de l'oprateur & dans la syntaxe du cin. Ce dernier n'a pas besoin de connatre l'adresse de la variable lire.

Les programmeurs C++ ne prfrent pas utiliser les fonctions scanf et printf de la librairie standard cause de : 1 2 la vitesse d'excution plus rapide des flux C++ : la fonction printf doit analyser l'excution la chane de formatage, tandis qu'avec les flux C++, la traduction est faite la compilation. la vrification de type : pas d'affichage erron comme dans l'exemple ci dessous :
#include <stdio.h> #include <iostream.h> void main() { double d=567.89; printf("d = %d !!!!\n", d); // Affiche : // ^erreur: %lf normalement cout << "d = " << d; // Affiche : } d = -5243 !!!! d = 567.89

3 4

la taille mmoire rduite : seul le code ncessaire est mis par l'diteur de liens, alors que pour la fonction printf tout le code correspondant toutes les possibilits d'affichage est mis. et surtout, on prfre les flux C++, parce que l'on peut utiliser ceux ci avec ses propres classes (surcharge possible de >> et <<) et l'on pourra donc crire :
Avion av; // une instance de la classe Avion

// Initialisation de av cout << "Avion " << av ;

2.3

LES MANIPULATEURS

Les manipulateurs sont des lments qui modif ient la faon dont les lments sont lus ou crits dans le flot.
q

Les principaux manipulateurs sont : dec lecture/criture d'un entier en dcimal

A. DANCEL - Le langage C++

oct hex endl setw(int n)

lecture/criture d'un entier en octal lecture/criture d'un entier en hexadcimal insre un saut de ligne et vide les tampons affichage de n caractres

setprecision(int n) affichage de la valeur avec n chiffres avec ventuellement un arrondi de la valeur setfill(char) flush dfinit le caractre de remplissage

vide les tampons aprs criture

#include <iostream.h> #include <iomanip.h> void main() { int i=1234; float p=12.3456; cout << "|" << setw(8) << setfill('*') << hex << i << "|\n" << "|" << setw(6) << setprecision(4) << p << "|" << endl; } /*-- rsultat de l'excution ------------------|*****4d2| |*12.35| ------------------------------------------------*/

2.4

LES CONVERSIONS EXPLICITES

En C++, comme en langage C, il est possible de faire des conversions explicites de type, bien que le langage soit plus fortement typ. Le C++ offre aussi une notation fonctionnelle pour faire une conversion explic ite de type :
double d = 3.14159; int i ; i = int(d) / 2 ;

Cette notation ne marche que pour les types simples et les types utilisateurs. Pour les types pointeurs ou tableaux le problme peut tre rsolu en dfinissant un nouveau type :
double d; int *i; typedef int *ptr_int; // ptr_int est du type: pointeur sur entier i = ptr_int(&d);

La conversion explicite de type est surtout utile lorsqu'on travaille avec des pointeurs du type void *. Mais en C++, seule la conversion implicite T * vers void * existe (pas linverse contrairement au langage C) :
void *addr ; int *ptr ;

A. DANCEL - Le langage C++

addr = ptr ; ptr = addr ;

// OK en C et C++ // OK en C, illgal en C++

2.5

DEFINITION DE VARIABLES

En C++ vous pouvez dclarer les variables ou fonctions n'importe o dans le code. La porte de telles variables va de l'endroit de la dclaration jusqu' la fin du bloc courant. Ceci permet : De dfinir une variable aussi prs que possible de son utilisation afin d'amliorer la lisibilit. C'est particulirement utile pour des grosses fonctions ayant beaucoup de variables locales.
q q

D'initialiser un objet avec une vale ur obtenue par calcul ou saisie :


int i; cin >> i; int j = 2 * i; j++; // dfinition d'une variable // instruction // dfinition d'une autre variable // instruction

En C++, il est possible dinitialiser une variable globale (ou de classe static ) avec une valeur non valuable par le compilateur (contrairement au langage C) :
static int i = 3; static int j static int k void f1( int static int } = i + 1; = nbJours(2*j); nbre ) { N = 2 * nbre; // illgal en C // illgal en C // illgal en C

2.6

VARIABLE DE BOUCLE

On peut dclarer une variable de boucle directement dans l'instruction for. Ceci permet de n'utiliser cette variable que dans le bloc de la boucle.
#include <iostream.h> void main() { for(int i=0; i<10; i++) cout << i << ' '; // i n'est pas utilisable l'extrieur du bloc for } /*-- Rsultat de l'excution -------0 1 2 3 4 5 6 7 8 9 -------------------------------------*/

On peut faire la mme chose dans un if ou un while.

A. DANCEL - Le langage C++

2.7

VISIBILITE DES VARIABLES

L'oprateur de rsolution de porte :: permet d'accder aux variables globales plutt qu'aux variables locales.
#include <iostream.h> int i = 22; void main() { int i = 44; { int i = 55; cout << ::i << " " << i; } cout << ::i << " " << i ; }

// affiche : 22 // affiche : 22

55 44

L'utilisation abusive de cette technique n'est pas une bonne pratique de programmation (lisibilit). Il est prfrable de donner des noms diffrents plutt que de rutiliser les mmes noms. En fait, on utilise beaucoup cet oprateur pour dfinir hors d'une classe les fonctions membres ou pour accder un identificateur dans un espace de noms (cf. espace de noms).

2.8

LES CONSTANTES

Les habitus du C ont l'habitude d'utiliser la directive du pr processeur #define pour dfinir des constantes. Il est reconnu que l'utilisation du pr processeur est une source d'erreurs difficiles dtecter. En C++, l'utilisation du pr processeur se limite aux cas les plus srs :
q q

Inclusion de fichiers. Compilation conditionnelle.

Le mot rserv const permet de dfinir une constante. L'objet ainsi spcifi ne pourra pas tre modifi durant toute sa dure de vie et pourra tre traite par un dbogueur. Il est indispensable d'initialiser la constante au moment de sa dfinition.
const int N = 10; const int MOIS=12, AN=1995; int tab[2 * N]; // N est un entier constant. // 2 constantes entires // autoris en C++ (interdit en C)

Mais attention, un pointeur peut toujours violer une constante :


const double salaire = 10000.0; *(double *)&salaire = 50000.0; // Cest mieux pay

2.9

CONSTANTES ET POINTEURS

Il faut distinguer ce qui est point du pointeur lui-mme. Le mot const permet de protger le pointeur ou la valeur pointe ou les deux :
q

La donne pointe est constante :

A. DANCEL - Le langage C++

const char *ptr1 = "QWERTY"; ptr1++; // Autoris *ptr1 = 'A'; // ERROR: assignment to const type

Le pointeur est constant :


char * const ptr2 = "QWERTY"; ptr2++; // ERROR: increment of const type *ptr2 = 'A'; // autoris

Le pointeur et la donne sont constants :


const char * const ptr3 = "QWERTY"; ptr3++; // ERROR: increment of const type *ptr3 = 'A'; // ERROR: assignment to const type

2.10

LES TYPES

2.10.1 Le type boolen


C++ dispose dun type boolen nomm bool, dont les valeurs possibles sont true et false. Les conditions du if, while , for et de loprateur conditionnel ? : sont des expressions du type bool. Les oprateurs relationnels ==, !=, <, etc. retournent un rsultat de type bool. Les oprateurs logiques &&, || et ! ont leurs oprandes et leurs rsultats du type bool. Il existe une conversion implicite du type bool vers le type int (false donne 0 et true donne 1). La conversion inverse existe et est viter.
bool b; b = 2 > 3; if ( b ) // b = false // ...

2.10.2 Les types composs


En C++, comme en langage C, le programmeur peut dfinir des nouveaux types en dfinissant des struct , des enum ou des union. Mais contrairement au langage C, l'utilisation de typedef n'est plus obligatoire pour renommer un type. Cela permet dallger lcriture des dfinitions et des transtypages.
struct FICHE { char *nom, *prenom; }; // en C, il faut ajouter la ligne : typedef struct FICHE FICHE; FICHE adherent, *liste, repertoire[100]; enum BOOLEEN { FAUX, VRAI }; // en C, il faut ajouter la ligne : BOOLEEN trouve = FAUX;

typedef enum BOOLEEN BOOLEEN;

Chaque numration enum est un type particulier, diffrent de int et ne peut prendre que les valeurs numres dans sa dfinition :
q

A. DANCEL - Le langage C++

10

enum Jour {DIMANCHE, LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI}; enum Couleur {NOIR, BLEU, VERT, CYAN, ROUGE, MAGENTA, BRUN, GRIS}; Jour j; j = LUNDI; // dfinition d'une variable de type Jour // OK

j = 2; // ERREUR en C++ (lgal en C) int i = MARDI; // OK il existe une conversion implicite vers le type int Couleur c; c = j; // dfinition d'une variable de type Couleur // ERREUR en C++ (lgal en C)

2.10.3 Les rfrences


En plus des variables normales et des pointeurs, le C++ offre les variables rfrences. Une variable rfrence permet de crer une variable qui est un "synonyme" d'une autre. Ds lors, une modification de l'une affectera le contenu de l'autre.
int i; int & ir = i; // ir est une rfrence i i=2; cout << "i= " << i << " ir= " << ir; // affiche : ir=3; cout << "i= " << i << " ir= " << ir; // affiche : int *ptr = &ir; *ptr = 4; cout << "i= " << i << " ir= " << ir; // affiche :

i= 2

ir= 2

i= 3

ir= 3

i= 4

ir= 4

Une variable rfrence doit obligatoirement tre initialise et le type de l'objet initial doit tre le mme que l'objet rfrence. Intrt :
q q

Passage des paramtres par rfrence, Utilisation d'une fonction en lvalue.

2.11

ALLOCATION MEMOIRE

Le C++ met la disposit ion du programmeur deux oprateurs new et delete pour remplacer respectivement les fonctions malloc et free (bien qu'il soit toujours possible de les utiliser).

2.11.1 Loprateur new


L'oprateur new rserve l'espace mmoire qu'on lui demande et l'initialise. Il retourne l'adresse de dbut de la zone mmoire alloue.

A. DANCEL - Le langage C++

11

int *pt1, *pt2, *pt3; pt1 = new int; // allocation dynamique d'un entier

pt2 = new int [10]; // allocation d'un tableau de 10 entiers /////// ne pas confondre avec : pt3 = new int(10); // allocation d'un entier avec initialisation 10 struct date {int jour, mois, an; }; date *pt4, *pt5, *pt6, d = {25, 4, 1952}; pt4 = new date; // allocation dynamique d'une structure pt5 = new date[10]; // allocation d'un tableau de structure pt6 = new date(d); // allocation dynamique avec initialisation

En cas d'erreur d'allocation par new, une exception bad_alloc est lance s'il n'y a pas de fonction d'interception dfinie par l'utilisateur. L'allocation des tableaux plusieurs dimensions est possible :
typedef char T[80]; // T est un synonyme de : tableau de 80 char T *ecran; ecran = new T[25]; // ecran est un tableau de 25 fois 80 char ecran[24][79]='$'; cout << ecran[24][79]; // 2me solution : char (*ecran)[80] = new char[25][80]; ecran[24][79]='$'; cout << ecran[24][79];

2.11.2 Loprateur delete


L'oprateur delete libre l'espace mmoire dun objet allou par new, tandis que l'oprateur delete[] libre l'espace mmoire allou un tableau d'objets :
delete ptr1; // libration d'un entier delete[] ptr2; // libration d'un tableau d'entier

L'application de l'oprateur delete un pointeur nul est lgale et nentrane aucune consquence fcheuse (l'opration est tout simplement ignore). A chaque instruction new doit correspondre une instruction delete. Il est important de librer l'espace mmoire ds que celui ci n'est plus ncessaire. La mmoire alloue en cours de programme sera libre automatiquement la fin du programme. Tout ce qui est allou avec new [], doit tre libr avec delete[], sinon ce nest que le premier objet du tableau qui est libr (fuite mmoire).
typedef char LIGNE[80]; LIGNE *ptr = new LIGNE; // LIGNE est un tableau de 80 caractres

A. DANCEL - Le langage C++

12

// . . . delete[] ptr;

// ptr est un tableau ...

2.11.3 La fonction d'interception set_new_handler :


Si une allocation mmoire par new choue, une fonction d'erreur utilisateur peut tre appele. La fonction set_new_handler, dclare dans new.h, permet d'installer votre propre fonction d'erreur :
#include <iostream.h> #include <stdlib.h> // exit() #include <new.h> // set_new_handler() // fonction d'erreur d'allocation mmoire dynamique void erreur_memoire( void) { cerr << "\nLa mmoire disponible est insuffisante !!!" << endl; exit(1); } void main() { set_new_handler( erreur_memoire ); double *tab = new double [100000000]; }

Si la fonction d'interception erreur_memoire ne comporte pas un exit, une nouvelle demande d'allocation mmoire est faite, et cela jusqu' ce que l'allocation russisse.

A. DANCEL - Le langage C++

13

3 - LES FONCTIONS

3.1

DECLARATION DES FONCTIONS

Le langage C++ impose au programmeur de dclarer le nombre et le type des arguments de la fonction. Ces dclarations sont identiques aux prototypes de fonctions de la norme C-ANSI et sont par ailleurs obligatoires avant utilisation (contrairement la norme C-ANSI). Ceci permet dliminer un certain nombre derreurs C. En C+, une fonction dclare avec une liste d'arguments vide impose quelle ne puisse pas tre appele avec des paramtres :
int f1(); // dclaration quivalente : int f1( void ); f1("Toto", 123.45); // ERREUR en C++, lgal en C

La norme C-ANSI considre que f1 est une fonction qui peut recevoir un nombre quelconque d'arguments, eux mmes de type quelconques, comme si elle tait dclare int f1( ... ); Une fonction dont le type de la valeur de retour n'est pas void, doit obligatoirement retourner une valeur.

3.2

PASSAGE PAR REFERENCE

En plus du passage par valeur, le C++ dfinit le passage par rfrence. Lorsque l'on passe une fonction un paramtre par rfrence, cette fonction reoit un "synonyme" du paramtre rel. Toute modification du paramtre rfrence est rpercute sur le paramtre rel.
void echange(int &n1, int &n2); // dclaration de la fonction

void main() { int i=2, j=3; echange( i, j ); // appel de la fonction echange cout << "i= " << i << " j= " << j; // affiche : i= 3 }

j= 2

void echange(int &n1, int &n2) { // n1 et n2 sont des alias des paramtres rels i et j int temp = n1; n1 = n2; // toute modification de n1 est rpercute sur i n2 = temp; // toute modification de n2 est rpercute sur j }

Comme vous le remarquez, l'appel se fait de manire trs simple. Le passage par rfrence permet de simplifier lcriture des fonctions mais ne permet pas de rendre explicite le passage des paramtres. Quand il est crit dans le programme ci dessus echange(i,j); on ne sait pas si les paramtres sont passs par valeur ou par rfrence. Cette facilit augmente la puissance du langage mais doit tre utilise avec prcaution, car elle ne protge plus la valeur du paramtre rel transmis par rfrence.

A. DANCEL - Le langage C++

14

L'utilisation du mot rserv const permet d'annuler ces risques, lorsque lon passe des objets de grande taille par rfrence, dans un but defficacit, et ne devant pas tre modifis dans la fonction.
struct FICHE { char nom[30], prenom[20], email[256]; }; void affiche(const FICHE &f) { // passage par rfrence (plutt que par valeur) pour des // raisons d'efficacit. Une modification du paramtre f // dans la fonction provoque une erreur de compilation cout << f.nom << " " << f.prenom; cout << " " << f.adresse << endl; } void main() { FICHE user = { "Dancel", "Alain", "dancel@enac.fr" }; affiche(user); }

Rfrences et pointeurs peuvent se combiner :


int ouverture(FILE *&f, const char *nf, const char *mode) { // passage par rfrence d'un pointeur. f = fopen(nf, mode); return (f == null) ? -1 : 0; } int main() { FILE *fic; if (ouverture(fic, "/tmp/toto.fic", "r") == -1) exit(1); int i; fscanf(fic, "%d", &i); }

Utilisez les rfrences quand vous pouvez, et nutiliser les pointeurs que quand vous devez.

3.3

VALEUR PAR DEFAUT DES PARAMETRES

Certains arguments d'une fonction peuvent prendre souvent la mme valeur. Pour ne pas avoir spcifier ces valeurs chaque appel de la fonction, le C++ permet de dclarer des valeurs par dfaut dans le prototype de la fonction.
void print(long valeur, int base = 10); void main() { print(16); // affiche 16 (16 en base 10) print(16, 2); // affiche 10000 (16 en base 2) } void print(long valeur, int base){ // ... affichage de la valeur dans la base }

Les paramtres par dfaut sont obligatoirement les derniers de la liste.

A. DANCEL - Le langage C++

15

Ils ne sont dclars que dans le prototype de la fonction et pas dans sa dfinition. Pourquoi ? les prototypes sont dans un fichier .h modifiable par lutilisateur. La valeur par dfaut est ainsi modifiable selon le projet, sans avoir remettre en cause le module objet correspondant.

3.4

FONCTION INLINE

Le mot cl inline remplace avantageusement l'utilisation de #define du pr processeur pour dfinir des pseudo fonctions. Afin de rendre l'excution plus rapide d'une fonction et condition que celle ci soit de courte taille, on peut dfinir une fonction avec le mot rserv inline. Le compilateur gnrera, chaque appel de la fonction, le code de celle ci. Les fonctions inline se comportent comme des fonctions normales et donc, prsentent l'avantage de vrifier les types de leurs arguments, ce que ne fait par la directive #define.
#include <iostream.h> inline int carre(int n); // dclaration void main() { cout << carre(10) << endl; } // inline facultatif la dfinition, mais prfrable inline int carre(int n) { return n * n; }

ATTENTION : Contrairement une fonction normale, la porte d'une fonction inline est rduite au module dans lequel elle est dclare.

3.5
1 2 3

SURCHARGE DE FONCTIONS
son nom, sa liste type de paramtres formels, le type de la valeur qu'elle retourne.

Une fonction se dfinit par :

Mais seuls les deux premiers critres sont discriminants. On dit qu'ils constituent la signature de la fonction. On peut utiliser cette proprit pour donner un mme nom des fonctions qui ont des paramtres diffrents :
int somme( int n1, int n2) { return n1 + n2; } int somme( int n1, int n2, int n3) { return n1 + n2 + n3; }

A. DANCEL - Le langage C++

16

double somme( double n1, double n2) { return n1 + n2; } void main() { cout << "1 + 2 = " << somme(1, 2) << endl; cout << "1 + 2 + 3 = " << somme(1, 2, 3) << endl; cout << "1.2 + 2.3 = " << somme(1.2, 2.3) << endl; }

Le compilateur slectionnera la fonction appeler en fonction du type et du nombre des arguments qui figurent dans l'appel de la fonction. Ce choix se faisant la compilation, fait que l'appel d'une fonction surcharge procure des performances identiques un appel de fonction "classique". On dit que l'appel de la fonction est rsolu de manire statique. Autres exemples :
enum Jour {DIMANCHE, LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI}; enum Couleur {NOIR, BLEU, VERT, CYAN, ROUGE, MAGENTA, BRUN, GRIS}; void f1(Jour j); void f1(Couleur c); // OK : les types numrations sont tous diffrents

void f2(char *str) { /* ... */ } void f2(char ligne[80]) { /* ... */ } // Erreur: redfinition de la fonction f // char * et char [80] sont considrs de mme type

int somme1(int n1, int n2) {return n1 + n2;} int somme1(const int n1, const int n2) {return n1 + n2;} // Erreur: la liste de paramtres dans les dclarations // des deux fonctions n'est pas assez divergente // pour les diffrencier.

int somme2(int n1, int n2) {return n1 + n2;} int somme2(int & n1, int & n2) {return n1 + n2;} // Erreur: la liste de paramtres dans les dclarations // des deux fonctions n'est pas assez divergente // pour les diffrencier.

int somme3(int n1, int n2) {return n1 + n2;} double somme3(int n1, int n2) {return (double) n1 + n2;} // Erreur: seul le type des paramtres permet de faire la distinction // entre les fonctions et non pas la valeur retourne.

A. DANCEL - Le langage C++

17

int somme4(int n1, int n2) {return n1 + n2;} int somme4(int n1, int n2=8) {return n1 + n2;} // Erreur: la liste de paramtres dans les dclarations // des deux fonctions n'est pas assez divergente // pour les diffrencier.

typedef int entier; // entier est un alias de int int somme5(int n1, int n2) {return n1 + n2;} int somme5(entier e1, entier e2) {return e1 + e2;}; // Erreur : redfinition de somme5 : des alias de type ne // sont pas considrs comme des types distincts

3.6
3.6.1

RETOUR D'UNE REFERENCE


Fonction retournant une rfrence

Une fonction peut retourner une valeur par rfrence et on peut donc agir, l'extrieur de cette fonction, sur cette valeur de retour. La syntaxe qui en dcoule est plutt inhabituelle et droutante au dbut.
#include <iostream.h> int t[20]; // variable globale - beurk !!! int & nIeme(int i) { return t[i]; } void main() { nIeme(0) = 123; nIeme(1) = 456; cout << t[0] << " " << ++nIeme(1); // affiche : }

123 457

Tout se passe comme si nIeme(0) tait remplac par t[0]. Une fonction retournant une rfrence (non constante) peut tre une lvalue. La variable dont la rfrence est retourne doit avoir une dure de vie permanente, ce qui explique pourquoi jutilise une variable globale.

En C, pour raliser la mme chose, nous aurions du crire :


int t[20]; // variable globale - beurk !!!

int * nIeme(int i) { return &t[i]; } void main() { *nIeme(0) = 123;

A. DANCEL - Le langage C++

18

*nIeme(1) = 456; printf("%d %d\n", t[0] ++(*nIeme(1)) ); }

ce qui est moins lisible et pratique ...

3.6.2

Retour d'une rfrence constante

Afin d'viter la cration d'une copie, dans la pile, de la valeur retourne lorsque cette valeur est de taille importante, il est possible de retourner une valeur par rfrence constante. Le prfixe const, devant le type de la valeur retourne, signifie au compilateur que la valeur retourne est constante.
const Big & f1() { static Big b; // notez le static ... // ... return b; } void main() { f1() = 12; } // erreur

3.7

UTILISATION DUNE FONCTION ECRITE EN C

Le compilateur C++ gnre pour chaque fonction un nom dont l'diteur de liens aura besoin. Le nom gnr en C++ se fait partir de la signature de la fonction. Ainsi, une fonction surcharge 2 fois, correspondront 2 fonctions de noms diffrents dans le module objet. En C, la signature de la fonction ne comporte que le nom de la fonction. Pour pouvoir utiliser dans un programme C++ des fonctions compiles par un compilateur C, il faut dclarer ces fonctions de la faon suivante :
extern "C" int f1(int i, char c); extern "C" void f2(char *str);

ou
extern "C" { int f1(int i, char c); void f2(char *str); }

Par contre, les fonctions f1 et f2 ne pourront pas tre surcharges.

3.8

FICHIER D'EN-TETES POUR C ET C++

Le symbole __cplusplus est dfini pour les compilateurs C++ uniquement. Il facilite l'criture de code commun aux langages C et C++, et plus particulirement pour les fichiers d'en-ttes :
#ifdef __cplusplus extern "C" { #endif

A. DANCEL - Le langage C++

19

void fail(char *msg); int calcul(double d1, double d2); #ifdef __cplusplus } #endif

Cet exemple peut tre compil aussi bien en C qu'en C++.

A. DANCEL - Le langage C++

20

4 - LES CLASSES

4.1

DEFINITION D'UNE CLASSE

La classe dcrit le modle structurel d'un objet comprenant l'ensemble des attributs (ou champs ou donnes membres) dcrivant sa structure oprations (ou mthodes ou fonctions membres) qui lui sont applicables.
class Avion { void init(char [], char *, float); // fonction membre void affiche(); // fonction membre char immatriculation[6], *type; // donnes membres float poids; // donne membre }; // Noubliez pas ce ; aprs l'accolade

4.2

DROITS D'ACCES

L'encapsulation consiste masquer l'accs certains attributs et mthodes d'une classe. Elle est ralise l'aide des mots cls : private les membres privs ne sont accessibles que par les fonctions membres de la classe. La partie prive est aussi appele ralisation. Les donnes membres doivent tre prives -> encapsulation. On y accde par des mthodes. protected les membres protgs sont comme les membres privs. Mais ils sont aussi accessibles par les fonctions membres des classes drives (voir l'hritage). public les membres publics sont accessibles par tous. L'ensemble des mthodes de la partie publique est appel interface. C'est la vue de l'utilisateur. Le concepteur accs tous les membres de la classe (privs, protgs et publics). Les sections private, protected et public peuvent figurer plusieurs fois dans la dclaration de la classe et le droit d'accs ne change pas tant qu'un nouveau droit n'est pas spcifi.

4.3

RECOMMANDATIONS DE STYLE

Elles sont propres l'entreprise. Les plus couramment utilises sont : la premire lettre du nom de la classe en majuscule la liste des membres publics en premier les noms des mthodes en minuscules le caractre _ comme premier caractre du nom d'une donne membre. Cette dernire rgle pose parfois des problmes avec certains #define des fichiers standards.

A. DANCEL - Le langage C++

21

4.4

DEFINITION DES FONCTIONS MEMBRES

En gnral, la dclaration d'une classe contient simplement les prototypes des fonctions membres de la classe.
////////////////// Avion.h class Avion { public : void init(char [], char *, float); void affiche(); private : char _immatriculation[6], *_type; float _poids; void erreur(char *msg); };

// mthode prive

Les fonctions membres sont dfinies dans un module spar ou plus loin dans le code source. L'ordre de leurs dfinitions n'a plus d'importance (mais devrait respecter l'ordre de la dclaration pour des raisons videntes de lisibilit).
////////////////// Avion.cpp

///////////////////////////// Dfinition de la mthode init void Avion::init(char im[], char *type, float poids) { if ( strlen(im) != 5 ) { erreur("Immatriculation invalide"); // appel d'une mthode strcpy(_immatriculation, "?????"); } else strcpy(_immatriculation, m); _type = new char [strlen(type)+1]; strcpy(_type, type); _poids = poids; } ///////////////////////////// Dfinition de la mthode affiche void Avion::affiche() { cout << _immatriculation << " " << _type << " " << _poids << endl; } ///////////////////////////// Dfinition de la mthode erreur void Avion::erreur(char *msg) { cerr << "ERREUR : " << msg << endl; exit(1); }

La dfinition de la mthode peut aussi avoir lieu l'intrieur de la dclaration de la classe. Dans ce cas, ces fonctions sont automatiquement traites par le compilateur comme des fonctions inline. Une fonction membre dfinie l'extrieur de la classe peut tre aussi qualifie explicitement de fonction inline.

A. DANCEL - Le langage C++

22

//////////////////// Nombre.h class Nombre { public : void setnbre(int n) { nbre = n; } // fonction inline int getnbre() { return nbre; } // fonction inline void affiche(); private : int nbre; }; inline void Nombre::affiche() { // fonction inline cout << "Nombre = " << nbre << endl; }

Rappel : la visibilit d'une fonction inline est restreinte au module seul dans laquelle elle est dfinie. Dans l'exemple ci dessus, il faudra que la dfinition de la mthode affiche soit dans un fichier inc lus par le module l'utilisant.

4.5

INSTANCIATION D'UNE CLASSE

De faon similaire une struct ou une union, le nom de la classe reprsente un nouveau type de donne. On peut donc dfinir des variables de ce nouveau type; on dit alors que vous crez des objets ou des instances de cette classe.
Avion av1; Avion *av2; Avion compagnie[10]; av2 = new Avion; // une instance simple (statique) // un pointeur (non initialis ...) // un tableau d'instances // cration (dynamique) d'une instance

4.6

UTILISATION DES OBJETS

Aprs avoir cr une instance (de faon statique ou dynamique) on peut accder aux attributs et mthodes de la classe. Cet accs se fait comme pour les structures l'aide de l'oprateur (point) ou > (tiret suprieur).
av1.init("FGBCD", "TB20", 1.47); av2->init("FGDEF", "ATR 42", 80.0); compagnie[0].init("FEFGH","A320", 150.0); av1.affiche(); av2->affiche(); compagnie[0].affiche(); av1.poids = 0; // erreur, poids est un membre priv

4.7

FONCTIONS MEMBRES CONSTANTES

Certaines mthodes d'une classe ne doivent (ou ne peuvent) pas modifier les valeurs des donnes membres de la classe, ni retourner une rfrence non constante ou un pointeur non constant d'une donne membre. On dit que ce sont des fonctions membres constantes. Ce type de dclaration renforce les contrles effectus par le compilateur et permet donc une programmation plus sre sans surcot d'excution. Il est donc trs souhaitable d'en dclarer aussi souvent que possible dans les classes.

A. DANCEL - Le langage C++

23

class Nombre { public : void setnbre(int n) { nbre = n; } // mthodes constantes int getnbre() const { return nbre; } void affiche() const; private : int nbre; }; inline void Nombre::affiche() const { cout << "Nombre = " << nbre << endl; }

Une fonction membre const peut tre appele sur des objets constants ou pas, alors qu'une fonction membre non constante ne peut tre appele que sur des objets non constants.
const Nombre n1; // un objet constant qu'on ne peut pas // initialiser pour l'instant ... // ERREUR: seule les fonctions const peuvent // tre appeles pour un objet constant // OK

n1.setnbre(15); n1.affiche(); Nombre n2; n2.setnbre(15); n2.affiche();

// OK // OK

4.7.1

Surcharge d'une mthode par une mthode constante

Une mthode dclare comme constante permet de surcharger une mthode non constante avec le mme nombre de paramtres du mme type.
class String { public : // etc ... char & nieme(int n); char nieme(int n) const; // etc... private : char *_str; };

// (1) // (2)

S'il n'y a pas l'attribut const dans la deuxime mthode, le compilateur gnre une erreur ("String::nieme() cannot be redeclared in class"). Cette faon de faire permet d'appliquer la deuxime mthode nieme() des objets constants et la premire mthode nieme() des objets variables :
void main() { String ch1; // Initialisation de ch1

A. DANCEL - Le langage C++

24

const String ch2; // Initialisation de ch2 cout << ch1.nieme(1); cout << ch2.nieme(1); } // appel de la mthode (1) // appel de la mthode (2)

L'criture d'une instruction comme :


ch2.nieme(3) = 'u';

ne sera pas compilable, car la mthode (2) ne retourne pas une rfrence. Si pour des raisons d'efficacit, la mthode (2) doit retourner une rfrence, on retournera alors une rfrence constante et l'on crira donc :
const char & nieme(int n) const; // (2 bis)

Et que se passe-t-il si j'cris :


char & nieme(int n) const; // (2 bis) //on retourne une rfrence non constante

au lieu de :
const char & nieme(int n) const; // (2 bis) //on retourne une rfrence constante

Le programme suivant se compile parfaitement et donne un rsultat surprenant :


void main() { const String ch2; // initialisation de ch2 avec la chane "coco" ch2.nieme(3) = 'u'; ch2.affiche(); // affiche "cocu" !! // vous n'avez vraiment pas de chance !!! }

La question maintenant est : comment une mthode constante a-t-elle pu modifier la valeur d'un objet constant ? Quand on dclare une mthode constante, le compilateur vrifie que la mthode ne modifie pas une donne membre. Et c'est le cas de la mthode nieme(). Pour gnrer une erreur il aurait fallu qu'elle change la valeur de _str, chose qu'elle ne fait pas. Elle ne peut changer la valeur que d'un caractre point par _str.

4.8

EXEMPLE : PILE D'ENTIERS (1)


// IntStack.h : dclaration d'une pile d'entiers class IntStack { public: void init(int taille = 10); // cration d'une pile

A. DANCEL - Le langage C++

25

void push(int n); // int pop(); // bool vide() const; // bool pleine() const;// int getSize() const { private: int _taille; int _sommet; int *_addr; };

empile un entier au sommet de la pile retourne l'entier au sommet de la pile vrai, si la pile est vide vrai, si la pile est pleine return _taille; }

// taille de la pile // position de l'entier empiler // adresse de la pile

// IntStack.cpp : dfinition d'une pile d'entiers // Compilation : g++ -Wall c IntStack.cpp #include <iostream.h> #include <assert.h> #include "IntStack.h" void IntStack::init(int taille ) { _addr = new int [ _taille = taille ]; // chasseur de nS assert( _addr != 0 ); _sommet = 0; } void IntStack::push(int n) { if ( ! pleine() ) _addr[ _sommet++ ] = n; }

// chasseur de nS

int IntStack::pop() { return ( ! vide() ) ? _addr[ --_sommet ] : 0; } int IntStack::vide() const { return ( _sommet == 0 ); } int IntStack::pleine() const { return ( _sommet == getSize() ); }

Exemple d'utilisation de cette classe :


// EssaiPile.cpp // compilation : g++ -Wall EssaiPile.cpp IntStack.o #include <iostream.h> #include <stdlib.h> // rand() #include "IntStack.h" void main() { IntStack pile1;

A. DANCEL - Le langage C++

26

pile1.init(15); while ( ! pile1.pleine() ) pile1.push( rand() % 100 while ( ! pile1.vide() ) cout << pile1.pop() << " cout << endl; }

// pile de 15 entiers // remplissage de la pile ); // Affichage (et dpilage) ";

4.9

CONSTRUCTEUR ET DESTRUCTEUR

Les donnes membres d'une classe ne peuvent pas tre initialises; il faut donc prvoir une mthode d'initialisation de celles-ci (voir la mthode init de l'exemple prcdent). Si l'on oublie d'appeler cette fonction d'initialisation, le reste n'a plus de sens et il se produira trs certainement des surprises fcheuses dans la suite de l'excution... De mme, aprs avoir fini d'utiliser l'objet, il est bon de prvoir une mthode permettant de dtruire l'objet (libration de la mmoire dynamique...).

4.9.1

Constructeur

Le constructeur est une fonction membre spcifique de la classe qui est appele implicitement l'instanciation de l'objet, assurant ainsi une initialisation correcte de l'objet. Ce constructeur est une fonction qui porte comme nom, le nom de la classe et qui ne retourne pas de valeur (pas mme un void). On appelle constructeur par dfaut un constructeur n'ayant pas de paramtre ou ayant des valeurs par dfaut pour tous les paramtres. Ce constructeur est sollicit automatiquement dans certaines situations. Si le concepteur de la classe ne spcifie aucun constructeur, le compilateur gnrera un constructeur par dfaut.
class Nombre { public : Nombre(); // constructeur par dfaut // ... private : int _i; }; Nombre::Nombre() { // dfinition du constructeur _i = 0; }

Comme les autres fonctions, les constructeurs peuvent tre surchargs.


class Nombre { public : Nombre() { _i = 0; }

// constructeur par dfaut

Nombre(int i) { _i = i; } // constructeur un paramtre private : int _i; };

A. DANCEL - Le langage C++

27

ou mieux :
class Nombre { public : Nombre(int i=0) { _i = i; } // ... private : int _i; };

Le constructeur est appel l'instanciation de l'objet.


Nombre n1; // correct, appel du constructeur par dfaut Nombre n2(10); // correct, appel du constructeur 1 paramtre Nombre tab1[10]; // chaque objet du tableau est initialis // par un appel au constructeur par dfaut Nombre tab2[3] = { Nombre(10), Nombre(20), Nombre(30) }; // initialisation des 3 objets du tableau // par les nombres 10, 20 et 30

Il n'est pas appel quand on dfinit un pointeur sur un objet ...


Nombre *ptr1, *ptr2; // correct, pas d'appel aux constructeurs

ptr1 = new Nombre; // appel au constructeur par dfaut ptr2 = new Nombre(12); // appel du constructeur 1 paramtre

4.9.2

Destructeur

De la mme faon que pour les constructeurs, le destructeur est une fonction membre spcifique de la classe qui est appele implicitement la destruction de l'objet. Ce destructeur est une fonction : qui porte comme nom, le nom de la classe prcd du caractre (tilde) qui ne retourne pas de valeur (pas mme un void ) qui n'accepte aucun paramtre (le destructeur ne peut donc pas tre surcharg)
class Nombre { public : // ... ~Nombre(); // Dclaration du destructeur private : // ... }; Nombre::~Nombre() { // ... } // Dfinition du destructeur

Comme pour le constructeur, le compilateur gnrera un destructeur par dfaut si le concepteur de la classe n'en spcifie pas un.

A. DANCEL - Le langage C++

28

4.9.3

Remarques

Les constructeurs et destructeurs sont les seules mthodes non constantes qui peuvent tre appeles pour des objets constants. Attention aux fautes de frappe, particulirement dans l'identificateur du constructeur :
class Essai { private: char *_ptr; public: essai() { _ptr = new char[80];} // mthode ordinaire // cause de l'erreur void init(char c) {for(int i=0; i<80; i++) _ptr[i]=c;} ~Essai() { delete[] _ptr; } // destructeur }; Essai e; e.init('\0');

// Ae, Ae, Ae ... explosion assure

A cause de la faute de frappe dans le nom du constructeur, celui ci n'a pas t appel la cration de l'objet e. Certains compilateurs signalent cette erreur : warning: Essai has Essai::~Essai() but no constructor (273) error: no value returned from Essai::essai() (1404)

4.10

EXEMPLE : PILE D'ENTIERS (2)


// Pile d'entiers avec constructeurs et destructeur. // IntStack.h : dclaration d'une pile d'entiers class IntStack { public: IntStack(int taille = 10); // constructeur ~IntStack() { delete[] _addr; } // destructeur void push(int n); int pop(); bool vide() const; // empile un entier au sommet de la pile // retourne l'entier au sommet de la pile // vrai, si la pile est vide

bool pleine() const;// vrai, si la pile est pleine int getSize() const { return _taille; } private: int _taille; // taille de la pile int _sommet; // position de l'entier empiler int *_addr; // adresse de la pile };

// IntStack.cpp : dfinition d'une pile d'entiers // Compilation : g++ -Wall c IntStack.cpp

A. DANCEL - Le langage C++

29

#include <iostream.h> #include <assert.h> #include "IntStack.h" IntStack::IntStack(int taille ) { //// Constructeur _addr = new int [ _taille = taille ]; // chasseur de nS assert( _addr != 0 ); _sommet = 0; } // ... Sans changement pour les autres mthodes

Exemple d'utilisation de cette classe :


// EssaiPile.cpp // compilation : g++ -Wall EssaiPile.cpp IntStack.o #include <iostream.h> #include <stdlib.h> // rand() #include "IntStack.h" void main() { IntStack pile0; IntStack pile1(15); // etc ... }

// Pile de 10 entiers // Pile de 15 entiers

4.11

CONSTRUCTEUR COPIE

4.11.1 Prsentation du problme


Reprenons la classe IntStack avec un constructeur et un destructeur et crivons une fonction AfficheSommet qui affiche la valeur de l'entier au sommet de la pile qui est passe en paramtre. Exemple d'appel de cette fonction :
void main() { IntStack pile1(15); // cration d'une pile de 15 entiers // ... AfficheSommet( pile1 ); // ... }

Une version fausse (pour l'instant) de cette fonction peut ressembler au code qui suit :
void AfficheSommet( IntStack pile ) { cout << "Sommet de la pile : " << pile.pop() << endl; }

A. DANCEL - Le langage C++

30

Ici, la pile est passe par valeur, donc il y a cration de l'objet temporaire nomm pile cr en copiant les valeurs du paramtre rel pile1. L'affichage de la valeur au sommet de la pile marche bien, mais la fin de cette fonction fait appel au destructeur de l'objet local pile qui libre la mmoire alloue par l'objet pile1 parce que les donnes membres de l'objet pile contiennent les mmes valeurs que celles de l'objet pile1. Pour viter ce mcanisme de recopie et l'appel du destructeur la fin de la fonction AfficheSommet, il faut dfinir la fonction AfficheSommet comme :

void AfficheSommet( IntStack & pile ) { // Version 2 cout << "Sommet de la pile : " << pile.pop() << endl; }

Mais une opration comme pile.pop() dpile un entier de la pile pile1 !!! Solution : il faut faire une copie intelligente : cration du constructeur de copie.

4.11.2 Cration du constructeur de copie


Le constructeur de copie est invoqu implicitement chaque fois que l'on doit construire un objet partir d'un objet existant de la mme classe.
Nombre n1(10); Nombre n2(n1); Nombre n3 = n1; // appel du constructeur 1 paramtre // appel du constructeur de copie // appel du constructeur de copie

Le constructeur de copie est appel aussi pour le passage d'arguments par valeur et le retour de valeur :
Nombre traitement(Nombre n) { static Nombre nbre; // ... return nbre; // appel du constructeur de copie } void main() { Nombre n1, n2; n2 = traitement( n1 ); // appel du constructeur de copie }

Le constructeur de copie d'une classe X est un constructeur dont le premier paramtre est de type rfrence (videmment) sur X et dont tous les (ventuels) paramtres suivants ont une valeur par dfaut Le compilateur C++ gnre par dfaut un constructeur de copie "bte" qui effectue une copie "superficielle", c'est dire membres membres. Cela marche bien pour des classes simples, comme la classe Nombre ci dessus. Pour les classes dont la copie superficielle n'est pas acceptable, il faut imprativement le dfinir explicitement, comme pour la classe IntStack ci dessous.

A. DANCEL - Le langage C++

31

4.11.3 Constructeur de copie de la classe IntStack


class IntStack { public: IntStack(int taille = 10); // constructeur par dfaut IntStack(const IntStack & s); // constructeur de copie ~IntStack() { delete[] _addr; } // destructeur // ... private: int _taille; // taille de la pile int _sommet; // position de l'entier empiler int *_addr; // adresse de la pile }; // ... //////////////////////// Dfinition du constructeur de copie IntStack::IntStack(const IntStack & s) { // Cration de la zone mmoire _addr = new int [ _taille = s._taille ]; // Copie de la valeur du sommet _sommet = s._sommet; // Recopie de tous les lments for (int i=0; i< _sommet; i++) _addr[i] = s._addr[i]; }

4.12

CLASSES IMBRIQUEES

Il est possible de crer une classe par une relation d'appartenance : relation "a un" ou "est compose de". Exemple : une voiture a un moteur, a des roues ...
class Moteur { /* ... */ }; class Roue { /* ... */ };

class Voiture { public: // .... private: Moteur _moteur; Roue _roue[4]; // .... };

Des types peuvent aussi tre dfinis dans la partie prive ou publique d'une autre classe. L'accs aux types imbriqus suit les mmes contrles que ceux des membres de la classe. La classe englobante n'a pas de droit particulier sur le type englob (et inversement).

A. DANCEL - Le langage C++

32

Pour pouvoir utiliser les types (publics) de l'extrieur de la classe, il est obligatoire d'utiliser l'oprateur de rsolution de porte ::.
class Pixel { // A titre pdagogique, pour illustrer les // types imbriqus.

public : enum Color { NOIR, BLEU, VERT, ROUGE, CYAN }; class Point { public : Point(int x=0, int y=0) { _x = x; _y = y; } // ... private : short _x, _y; }; Pixel(Point p, Color c); Color getColor() const; Point getPoint() const; // ... private : Point _p; Color _c; }; void main() { Pixel p1( Point(100, 200), Pixel::ROUGE); Pixel::Color c; // Dfinition d'une variable Color Pixel::Point p(10, 20); // Cration d'un point c = Pixel::NOIR; Pixel p2( p, c); } // Constructeur

4.13

AFFECTATION ET INITIALISATION

Le langage C++ fait la diffrence entre l'initialisation et l'affectation. l'affectation consiste modifier la valeur d'une variable (et peut avoir lieu plusieurs fois) l'initialisation est une opration qui n'a lieu qu'une fois immdiatement aprs que l'espace mmoire de la variable ait t allou. Cette opration consiste donner une valeur initiale l'objet ainsi cr. C'est le constructeur qui effectue cette initialisation.
Nombre n1(123); Nombre n2; Nombre n3 = n1; n2 = n3; // initialisation // initialisation // Affectation

4.14

LISTE D'INITIALISATION D'UN CONSTRUCTEUR

Soit la classe :

A. DANCEL - Le langage C++

33

class Y { /* ... */ }; class X { public: X(int a, int b, const Y &y); ~X(); // .... private: const int _x; Y _y; int _z; }; X::X(int a, int b, const Y &y) { _x = a; // ERREUR: l'affectation une constante est interdite _z = b; // OK : affectation // et comment initialiser l'objet membre _y ??? }

Comment peut-on initialiser la donne membre constante _x ? Comment peut-on appeler le constructeur de la classe Y ? Rponse : la liste d'initialisation. La phase d'initialisation de l'objet utilise une liste d'initialisation qui est spcifie dans la dfinition du constructeur. Syntaxe :
nom_classe::nom_constructeur( args ... ) : liste_d_initialisation { // corps du constructeur }

Exemple :
X::X(int a, int b, const Y &y) : _x( a ) , _y( y ) , _z( b ) { // rien d'autre faire pour cet exemple }

L'expression _x( a ) indique au compilateur d'initialiser la donne membre _x avec la valeur du paramtre a. L'expression _y( y ) indique au compilateur d'initialiser la donne membre _y par un appel au constructeur (avec un argument) de la classe Y.

4.15

LE POINTEUR this

Toute mthode d'une classe X a un paramtre cach : le pointeur this. Celui contient l'adresse de l'objet qui l'a appel, permettant ainsi la mthode d'accder aux membres de l'objet. Il est implicitement dclar comme (pour une variable) :
X * const this;

A. DANCEL - Le langage C++

34

et comme (pour un objet constant) :


const X * const this;

et initialis avec l'adresse de l'objet sur lequel la mthode est appele. Il peut tre explicitement utilis :
classe X { public: int f1() const { return this->i; } // idem que : int f1() { return i; } private: int i; // ... };

Une fonction membre qui retourne le pointeur this peut tre chane, tant donn que les oprateurs de slection de membres . (point) et -> (tiret suprieur) sont associatifs de gauche droite :
classe X { public: X f1() const { cout << "X "; return *this; } // ... private: // ... }; void main() { X x; x.f1().f1().f1(); // affiche : X X X }

La fonction membre f1 a tout intrt de retourner une rfrence :


const X &f1() const { cout << "X "; return *this; }

La valeur ne this ne peut ni tre change Le pointeur this ne peut pas tre explicitement dclar.

4.16

LES MEMBRES STATIQUES

Ces membres sont utiles lorsque l'on a besoin de grer des donnes ou des mthodes communes toutes les instances d'une mme classe.

4.16.1 Donnes membres statiques


Si l'on dclare une donne membre comme static, elle aura la mme valeur pour toutes les instances de cette classe.

A. DANCEL - Le langage C++

35

class Ex1 { public: Ex1() { nb++; /* ... */ } ~Ex1() { nb--; /* ... */ } private: static int nb; // initialisation impossible ici };

L'initialisation de cette donne membre statique se fera en dehors de la classe et en global par une dclaration :
int Ex1::nb = 0; // initialisation du membre static

4.16.2 Fonctions membres statiques


De mme que les donnes membres statiques, il existe des fonctions membres statiques.
q q q

ne peuvent accder qu'aux membres statiques, ne peuvent pas tre surchargs, existent mme s'il n'y a pas d'instance de la classe.
class Ex1 { public: Ex1() { nb++; /* ... */ } ~Ex1() { nb--; /* ... */ } static void affiche() { cout << nb << endl; } private: static int nb; }; int Ex1::nb = 0; // initialisation du membre static (en global) void main() { Ex1.affiche(); Ex1 a, b, c; Ex1.affiche(); a.affiche(); } // fonction membre statique

// affiche 0 (pas d'instance de Ex1) // affiche 3 // affiche 3

A. DANCEL - Le langage C++

36

4.17

CLASSES ET FONCTIONS AMIES


"Un ami est quelqu'un qui peut toucher vos parties prives."

Dans la dfinition d'une classe il est possible de dsigner des fonctions (ou des classes) qui on laisse un libre accs ses membres privs ou protgs. C'est une infraction aux rgles d'encapsulation pour des raisons d'efficacit.
class Nombre { // ici je dsigne les fonctions qui pourront accder // aux membres privs de ma classe friend int manipule_nbre(); // fonction amie public : int getnbre(); // ... private : int _nbre; }; int manipule_nbre(Nombre return n._nbre + 1; // // // } n) { je peux le faire en tout lgalit parce que je suis une fonction amie de la classe Nombre.

Classe amie :
class Window; class Screen { friend class Window; public: //... private : //... }; // Classe amie // dclaration de la classe Window

Les fonctions membres de la classe Window peuvent accder aux membres non publics de la classe Screen.

A. DANCEL - Le langage C++

37

A. DANCEL - Le langage C++

38

5 - LES OPERATEURS

Les oprateurs forment une famille particulire de fonctions. Il faut offrir tous les oprateurs possibles une classe permettant ainsi de la considrer comme un type de base.

5.1

INTRODUCTION A LA SURCHARGE D'OPERATEURS

Le concepteur d'une classe doit fournir l'utilisateur de celle ci toute une srie d'oprateurs agissant sur les objets de la classe. Ceci permet une syntaxe intuitive de la classe. Par exemple, il est plus intuitif et plus clair d'additionner deux matrices en surchargeant l'oprateur d'addition et en crivant :
Matrice m0, m1, result; result = m0 + m1;

que d'crire
matrice_add(result, m0, m1);

Surcharger un oprateur revient crire une fonction (ou une mthode suivant le cas). Toutes les rgles relatives aux fonctions (sauf pour la valeur par dfaut des paramtres) s'appliquent donc la surcharge des oprateurs.

5.2

REGLES D'UTILISATION

Il faut veiller respecter l'esprit de l'oprateur. Il faut faire avec les types utilisateurs des oprations identiques celles que font les oprateurs avec les types prdfinis. La plupart des oprateurs sont surchargeables. Les oprateurs suivants ne sont pas surchargeables : :: . .* ?: sizeof Il n'est pas possible de : changer sa priorit changer son associativit changer sa pluralit (unaire, binaire, ternaire) crer de nouveaux oprateurs Par contre la surcharge d'un oprateur ne conserve pas : Sa commutativit : l'oprateur d'addition dfini pour effecteur X + Y (le type de X et de Y tant diffrent) ne permet pas d'effectuer Y + X # ##

A. DANCEL - Le langage C++

39

Les relations avec les autres oprateurs induits : la surcharge de + est totalement indpendante de celle de +=. Il faudra, dans ce cas, penser galement surcharger l'oprateur +=.

5.3

SURCHARGE D'UN OPERATEUR

Quand l'oprateur + (par exemple) est appel, le compilateur gnre un appel la fonction operator+. Cette fonction peut tre : Une mthode de la classe du premier oprande :
Nombre n; n + 3; // quivalent : n.operator+(3);

Une fonction globale :


Nombre n; n + 3; // quivalent : operator+(n , 3);

5.3.1

Surcharge par une fonction membre

Par exemple, la surcharge des oprateurs + et = par des fonctions membres de la classe Matrice s'crit :
class Matrice { // matrice 2 x 2 d'entiers public: // ... const Matrice &operator=(const Matrice &n2); Matrice operator+(const Matrice &n2); // ... private: int _matrice[2][2]; }; // ... void main() { Matrice a, b, c; b + c ; // appel : b.operator+( c ); a = b + c; // appel : a.operator=( b.operator+( c ) ); }

Les oprateurs = () [] -> new delete ne peuvent tre surchargs que comme des fonctions membres. Une fonction membre (non statique) peut toujours utiliser le pointeur cach this. Dans le code ci dessus, this fait rfrence l'objet b pour l'oprateur + et l'objet a pour l'oprateur =. Dfinition de la fonction membre operator+ :
Matrice Matrice::operator+(const Matrice & m) { Matrice temp;

A. DANCEL - Le langage C++

40

for(int i = 0; i < 2; i++) for(int j = 0; j < 2; j++) temp._matrice[i][j] = _matrice[i][j] + m._matrice[i][j]; return temp; }

Quand on a le choix, l'utilisation d'une fonction membre pour surcharger un oprateur est prfrable. Une fonction membre renforce l'encapsulation. Les oprateurs surchargs par des fonctions membres se transmettent aussi par hritage (sauf l'affectation). La fonction membre operator+ peut elle mme tre surcharge, pour dans l'exemple qui suit, additionner une matrice un vecteur :
class Matrice { // matrice public: // ... Matrice operator=(const Matrice operator+(const Matrice operator+(const // ... }; // ... Matrice b, c; Vecteur v; b + c; // appel b.operator+(c); b + v; // appel b.operator+(v); addition entre une matrice // et un vecteur v + b; // appel v.operator+(b); --> ERREUR si la classe // Vecteur n'a pas dfini l'addition entre un vecteur et // une matrice 2 x 2 entiers

Matrice &n2); Matrice &n2); Vecteur &n2);

5.3.2

Surcharge par une fonction globale

Cette faon de procder est plus adapte la surcharge des oprateurs binaires. En effet, elle permet d'appliquer des conversions implicites au premier membre de l'expression. Le plus souvent, pour des raisons d'efficacit, cette fonction sera dclare amie de la classe.
Class Nombre{ friend Nombre operator+(const Nombre &, const Nombre &); public: Nombre(int n = 0) { _nbre = n; } //.... private: int _nbre; }; Nombre operator+(const Nombre &nbr1, const Nombre &nbr2) {

A. DANCEL - Le langage C++

41

Nombre n; n._nbre = nbr1._nbre + nbr2._nbre; return n; } void main() { Nombre n1(10); n1 + 20; 30 + n1; } // OK appel : // OK appel : operator+( n1, Nombre(20) ); operator+( Nombre(30) , n1 );

5.4

OPERATEUR D'AFFECTATION

C'est le mme problme que pour le constructeur de copie. Le compilateur C++ construit par dfaut un oprateur d'affectation "bte". L'oprateur d'affectation est obligatoirement une fonction membre et il doit fonctionner correctement dans les deux cas suivants :
XXX x1, x2, x3; // 3 instances de la classe XXX x1 = x1; // Qui donc crit des trucs comme cela ??? x1 = x2 = x3; // Ca, oui, c'est utile

Exemple : Oprateur d'affectation de la classe Matrice :

class Matrice { // matrice 2 x 2 entiers public: // ... const Matrice &operator=(const Matrice &m); // ... private: int _matrice[2][2]; // ... }; const Matrice &Matrice::operator=(const Matrice &m) { if ( &m != this ) { // traitement du cas : x1 = x1 for(int i = 0; i < 2; i++) // copie de la matrice for(int j = 0; j < 2; j++) this->_matrice[i][j] = m._matrice[i][j]; } return *this; // traitement du cas : x1 = x2 = x3 }

A. DANCEL - Le langage C++

42

5.5
5.5.1
q

CAS PARTICULIERS
Surcharge de ++ et -notation prfixe : fonction membre : X operator++(); fonction globale : X operator++(X &);

notation postfixe : fonction membre : X operator++(int); fonction globale : X operator++(X &, int);

L'argument entier pass ne sert rien, sauf faire la diffrence dans la surcharge de la fonction operator++.
class BigNombre { public: // ... BigNombre operator++(); // notation prfixe BigNombre operator++(int); // notation postfixe // ... }; // ... void main() { BigNombre n1; n1++; // notation postfixe ++n1; // notation prfixe }

5.5.2

Surcharge de l'oprateur []

L'oprateur [] doit obligatoirement tre dfini comme une fonction membre. Son paramtre est l'indice.
class IntStack { // le retour public: // ... int &operator [](int i) { assert(i>=0 && i<_sommet); return _addr[i]; } // ... private: int _taille; // taille de la pile int _sommet; // position de l'entier empiler int *_addr; // adresse de la pile };

A. DANCEL - Le langage C++

43

Il n'est pas possible de dfinir un oprateur [] avec plusieurs indices car les tableaux 2 dimensions n'existent pas, nous ne pouvons avoir que des tableaux de tableaux. Pour rsoudre ce problme on dfinira [] qui renvoie un objet pouvant lui mme tre indic.
class Matrice { public: // matrice 2 x 2 d'entiers

// ... class Tab2; // Dclaration de classe Tab2 &operator [](int i) { return t1[i]; } private: class Tab2 { // classe imbrique public : int &operator [](int j) { return t2[j]; } private : int t2[2]; }; Tab2 t1[2]; }; void main() { Matrice m; m[0][0] = 123; cout << m[0][0] << endl; } // tableau de tableau

5.5.3

Surcharge de >> et <<

Les oprateurs >> et << sont dfinis pour tous les types prdfinis. Ils permettent d'extraire ou d'injecter des informations formates du flux. Pour pouvoir lire et crire dans un flux une donne de type T il suffit de dfinir les fonctions globales suivantes qui surchargent les oprateurs d'extraction et d'injection :
ostream &operator <<(ostream &os, const T &t); istream &operator >>(istream &is, T &t); // injection // extraction

Exemple pour la classe IntStack :


class IntStack { // Le retour IV

friend ostream &operator <<(ostream &os, const IntStack &s); friend istream &operator <<(istream &is, IntStack &s); public : // ... private : int _sommet, _taille; int *_tab; };

A. DANCEL - Le langage C++

44

////////////////////////////////////// Injection dans un flux ostream &operator <<(ostream &os, const IntStack &s) { for( int i=0; i<_sommet; i++) os << _tab[i]; return os; } //////////////////////////////////////// Extraction d'un flux istream &operator <<(istream &is, IntStack &s) { int v; for( int i=_sommet; i<_taille; i++) { is >> v; s.push(v); } return is; }

5.5.4

Surcharge de new et delete

Si les oprateurs new et delete sont surchargs pour une classe, ils seront automatiquement appels chaque cration ou destruction d'un objet dynamique de la classe considre. Ils doivent obligatoirement tre surchargs sous forme de fonction membre statique. Ils n'auront donc pas accs au pointeur this ou au membres non static de la classe.
q

Surcharge de new :

La mthode operator new est appele avant le constructeur de la classe. Son paramtre est initialis par le compilateur avec la taille de la zone mmoire ncessaire pour construire un objet de cette classe.
class XXX { public : // ... static void *operator new(size_t n); // allocation simple static void *operator new[](size_t n); // pour les tableaux // ... }; // titre d'exemple on ne fait rien de spcial void *XXX::operator new(size_t n) { // dfinition de new return ::operator new(n); // appel l'oprateur global new } void *XXX::operator new[](size_t n) { // dfinition de new return ::operator new[](n); // appel l'oprateur global new }

Surcharge de delete :

La mthode operator delete est appele aprs le destructeur de la classe. Son paramtre est initialis par le compilateur avec l'adresse de dbut de la zone librer.

A. DANCEL - Le langage C++

45

class XXX { public : // ... static void operator delete(void *); // libration simple static void operator delete[](void *); // pour les tableaux }; void XXX::operator delete(void *ptr) { // dfinition de delete ::operator delete(ptr); // appel l'oprateur global delete } void XXX::operator delete[](void *ptr) { // dfinition de delete ::operator delete[](ptr); // appel l'oprateur global delete }

5.5.5

Surcharge de ( )

La surcharge de l'oprateur () permet de simplifier l'appel d'une mthode particulire d'une classe, comme si l'objet tait une fonction. Ces objets, dont l'oprateur () est dfini, sont appels objetsfonctions. Ce concept est largement utilis dans la bibliothque standard du C++. Cet oprateur doit tre dfini comme une fonction membre, et peut, elle aussi, tre surcharge.
class String { public : // ... // Retourne une chane de n caractres String operator() (int debPos, int n=1); // ... } void main() { String s1, s2("abcdef"); s1 = s2(3, 2); // interprt comme : // s1.operator=( s2.operator() (3, 2) ); }

5.6

OPERATEURS DE CONVERSION

Dans la dfinition complte d'une classe, il ne faut pas oublier de dfinir des oprateurs de conversions de types. Il existe deux types de conversions de types :

La conversion de type prdfini (ou dfini pralablement) vers le type classe en question. Ceci sera effectu grce au constructeur de conversion. La conversion du type classe vers un type prdfini (ou dfini pralablement). Cette conversion sera effectue par des fonctions de conversion.

A. DANCEL - Le langage C++

46

5.6.1

Constructeur de conversion :

Un constructeur avec un seul argument de type T permet de raliser une conversion d'une variable de type T vers un objet de la classe du constructeur.
class Nombre { public: Nombre(int n) { _nbre = n; } private: int _nbre; }; void f1(Nombre n) { /* ... */ } void main() { Nombre n = 2; // idem que : Nombre n(2); f1(3); // Appel du constructeur Nombre(int) pour raliser // la conversion de l'entier 3 en un Nombre. // Pas d'appel du constructeur de copie }

Il est prfrable d'utiliser une conversion explicite :


f1(Nombre(3));

Dans cet exemple, le constructeur avec un argument permet donc d'effectuer des conversions d'entier en Nombre. C'est pour cette raison qu'on appelle cette sorte de constructeur : constructeur de conversion.

5.6.2

Fonction de conversion

Une fonction de conversion est une mthode qui effectue une conversion vers un type T. Elle est nomm operator T(). Cette mthode n'a pas de type de retour (comme un constructeur), mais doit cependant bien retourner une valeur du type T.
class Article { public : Article(double prix=0.0):_prix(prix) {} operator double() const { return _prix; } private : double _prix; }; void main() { double total; Article biere(17.50); // utilisation implicite de la conversion Article -> double total = 7 * biere; }

A. DANCEL - Le langage C++

47

5.6.3

Conversion explicite

La conversion implicite effectue par le compilateur est parfois gnante et source d'ennuis. Le mot rserv explicit peut tre utilis pour empcher qu'un constructeur de conversion ou qu'un oprateur de conversion puissent tre utiliss pour des conversions implicites :
class XXX { public : explicit XXX(int n); // constructeur // ... explicit operator int(); // ... }; XXX x; int i = x; // ERREUR: il faut crire :

int i = int(x);

void f(XXX x); // dclaration d'une fonction f( 234 ); // ERREUR: il faut crire : f( XXX(234) );

A. DANCEL - Le langage C++

48

A. DANCEL - Le langage C++

49

6 - LES MODELES

Les modles (ou patrons ou gabarits) permettent de dfinir des fonctions ou des classes paramtres par un type (ou une classe). C'est une mise en uvre de la gnricit.

6.1

LES MODELES DE FONCTIONS

(patron de fonction ou fonction gnrique ou fonction template) Lorsque l'algorithme est le mme pour plusieurs types de donnes, il est possible de crer un modle de fonction. C'est un modle partir duquel le compilateur gnrera les fonctions qui lui seront ncessaires.
q

Exemple 1 :
template <class TTT> void affiche(TTT tab[], int nbre) { for(int i = 0; i < nbre; i++) cout << tab[i] << " "; cout << endl; }

La fonction modle affiche marchera pour tous les types de base et les classes ayant redfini l'oprateur [].
int tabi[6] = {25, 4, 52, 18, 6, 55}; affiche(tabi, 6); double tabd[3] = {12.3, 23.4, 34.5}; affiche(tabd, 3); char *tabs[] = {"Bjarne", "Stroustrup"}; affiche(tabs, 2); Avion compagnie[5]; // Remplissage du tableau d'avions affiche(compagnie, 5);

Exemple 2 : exemple de dclaration puis de dfinition de fonction modle.


template <class TTT> TTT min(TTT, TTT); // ... // dclaration du patron

la suite de votre programme

A. DANCEL - Le langage C++

50

template <class TTT> // dfinition du patron TTT min(TTT t1, TTT t2) { return t1 < t2 ? t1 : t2; }

La fonction modle affiche marchera pour tous les types de base et les classes (ayant redfini l'oprateur relationnel <).

Exemple 3 : le modle peut avoir plusieurs paramtres de type :


template <class X, class Y> int valeur(X x, Y y) { // ... }

Les fonctions gnriques peuvent, tout comme les fonctions normales, tre surcharges. Ainsi il est possible de spcialiser une fonction une situation particulire, comme dans l'exemple qui suit :
template <class TTT> TTT min(TTT t1, TTT t2) { return t1 < t2 ? t1 : t2; } char *min(char *s1, char *s2) { // fonction spcialise pour les chanes. Il est vident // que la comparaison des adresses des chanes est absurde ... return strcmp(s1, s2)<0 ? s1 : s2; } void main() { cout << min(10, 20) << " " << min("Alain", "Bjarne") << endl; }

6.2

LES CLASSES PARAMETREES

Les classes paramtres permettent de crer des classes gnrales et de transmettre des types comme paramtres ces classes pour construire une classe spcifique. Par exemple la classe Pile n'a pas se soucier du type rel de ses lments. Elle ne doit s'occuper que d'empiler, dpiler ... ses lments. On a donc intrt paramtrer cette classe par le type des lments qu'elle stocke.
template <class TTT> class Stack { public: stack(int n=10); void push(const TTT &e); TTT &pop(); // ... private: TTT *_tab; };

A. DANCEL - Le langage C++

51

La dfinition des mthodes se fait de la faon suivante :


template <class TTT> Stack<TTT>::Stack(int n) { // ... _tab = new TTT[n]; // ... } template <class TTT> void Stack<TTT>::push(const TTT &e) { /*...*/ } template <class TTT> TTT &Stack<TTT>::pop() { /*...*/ }

Une utilisation de cette classe sera, par exemple :


Stack<int> resultat(20); // Pile de 20 entiers Stack<Employe> r; // Pile de 10 employs resultat.push(234);

Comme avec les fonctions modles, il est possible de spcialiser une classe une situation particulire :
class Stack<char *> { // classe Stack spcialise pour les char * public : // ... };

6.3

ARGUMENTS DUNE FONCTION MODELE

Outre des types de donnes, les arguments d'une fonction modle ou d'une classe modle peuvent galement reprsenter des valeurs :
template<class TTT, int size> class Stack { public : Stack() { // constructeur // ... _tab = new TTT[size]; // ... } // ... }; Stack<Employe, 30> p; // Pile de 30 employs

Les arguments du modle peuvent aussi avoir des valeurs par dfaut :
template<class TTT=int, int size=10> class Stack {

A. DANCEL - Le langage C++

52

// ... }; Stack<char *,20> // Pile de 20 chanes Stack<float> pf; // Pile de 10 rels Stack<> pi; // Pile de 10 entiers

Les arguments du modle peuvent tre eux mmes des instances de modle permettant ainsi d'crire :
Stack< Stack<int, 20> , 30> p; // p est une pile de 30 piles de 20 entiers.

A. DANCEL - Le langage C++

53

7 - L'HERITAGE

"Il est plus facile de modifier que de rinventer"

7.1

L'HERITAGE

L'hritage, galement appel drivation, permet de crer une nouvelle classe partir d'une classe dj existante, la classe de base (ou super classe). La nouvelle classe (ou classe drive ou sous classe) hrite de tous les membres, qui ne sont pas privs, de la classe de base et ainsi rutiliser le code dj crit pour la classe de base. On peut aussi lui ajouter de nouveaux membres ou redfinir des mthodes. Syntaxe :

La classe B hrite de faon publique de la classe A. Tous les membres publics ou protgs de la classe A font partis de l'interface de la classe B.

7.2

MODE DE DERIVATION

Lors de la dfinition de la classe drive il est possible de spcifier le mode de drivation par l'emploi d'un des mots cls suivants : public, protected ou private. Ce mode de drivation dtermine quels membres de la classe de base sont accessibles dans la classe drive. Au cas o aucun mode de drivation n'est spcifi, le compilateur C++ prend par dfaut le mot cl private pour une classe et public pour une structure. Les membres privs de la classe de base ne sont jamais accessibles par les membres des classes drives.

A. DANCEL - Le langage C++

54

7.2.1

Hritage public

Il donne aux membres publics et protgs de la classe de base le mme statut dans la classe drive. C'est la forme la plus courante d'hritage, car il permet de modliser les relations "Y est une sorte de X" ou "Y est une spcialisation de la classe de base X".
class Vehicule { public: void pub1(); protected: void prot1(); private: void priv1(); }; class Voiture : public Vehicule { public: int pub2() { pub1(); // OK prot1(); // OK priv1(); // ERREUR } }; Voiture safrane; safrane.pub1(); // OK safrane.pub2(); // OK

7.2.2

Hritage priv

Il donne aux membres publics et protgs de la classe de base le statut de membres privs dans la classe drive. Il permet de modliser les relations "Y est compos de un ou plusieurs X" . Plutt que d'hriter de faon prive de la classe de base X, on peut faire de la classe de base une donne membre (composition).
class String { public: int length(); // ... }; class Telephone_number : private String { void f1() { // ... i = length(); }; Telephone_number tn;

// OK

A. DANCEL - Le langage C++

55

cout << tn.length();

// ERREUR

7.2.3

Hritage protg

Il donne aux membres publics et protgs de la classe de base le statut de membres protgs dans la classe drive. L'hritage fait partie de l'interface mais n'est pas accessible aux utilisateurs.
class String { protected: int n; }; class Telephone_number : protected String { protected: void f2() { n++; } }; class Local_number : public Telephone_number { protected: void f3() { n++; } // OK }; // OK

7.2.4

Tableau rsum de l'accs aux membres


Statut dans la Statut dans la

classe de base classe drive public public protected private mode de drivation public protected protected private public private protected private public protected inaccessible protected protected inaccessible private private inaccessible

A. DANCEL - Le langage C++

56

7.3

REDEFINITION DE METHODES

On peut redfinir une fonction dans une classe drive si on lui donne le mme nom que dans la classe de base. Il y aura ainsi, comme dans l'exemple ci aprs, deux fonctions f2(), mais il sera possible de les diffrencier avec l'oprateur :: de rsolution de porte.
class X { public: void f1(); void f2(); protected: int xxx; }; class Y : public X { public: void f2(); void f3(); }; void Y::f3() { X::f2(); X::xxx = 12; f1(); f2(); }

// // // //

f2 de accs appel appel

la au de de

classe X membre xxx de la classe X f1 de la classe X f2 de la classe Y

7.4

AJUSTEMENT D'ACCES

Lors d'un hritage protg ou priv, nous pouvons spcifier que certains membres de la classe anctre conservent leur mode d'accs dans la classe drive. Ce mcanisme, appel dclaration d'accs, ne permet en aucun cas d'augmenter ou de diminuer la visibilit d'un membre de la classe de base.
class X { public: void f1(); void f2(); protected: void f3(); void f4(); }; class Y : private X { public: X::f1; // f1() reste public dans Y X::f3; // ERREUR: un membre protg ne peut pas devenir public protected: X::f4; // f3() reste protg dans Y X::f2; // ERREUR: un membre public ne peut pas devenir protg };

A. DANCEL - Le langage C++

57

7.5

HERITAGE DES CONSTRUCTEURS / DESTRUCTEURS

Les constructeurs, constructeur de copie, destructeurs et oprateurs d'affectation ne sont jamais hrits. Les constructeurs par dfaut des classes de bases sont automatiquement appels avant le constructeur de la classe drive. Pour ne pas appeler les constructeurs par dfaut, mais des constructeurs avec des paramtres, vous devez employer une liste d'initialisation. L'appel des destructeurs se fera dans l'ordre inverse des constructeurs.
class Vehicule { public: Vehicule() { cout<< "Vehicule" << endl; } ~Vehicule() { cout<< "~Vehicule" << endl; } }; class Voiture : public Vehicule { public: Voiture() { cout<< "Voiture" << endl; } ~Voiture() { cout<< "~Voiture" << endl; } }; void main() { Voiture *R21 = new Voiture; // ... delete R21; } /********** se programme affiche : Vehicule Voiture ~Voiture ~Vehicule ***********************************************/

Exemple d'appel des constructeurs avec paramtres :


class Vehicule { public: Vehicule(char *nom, int places); //... }; class Voiture : public Vehicule { private: int _cv; // puissance fiscale public: Voiture(char *n, int p, int cv); // ... }; Voiture::Voiture(char *n, int p, int cv): Vehicule(n, p), _cv(cv)

A. DANCEL - Le langage C++

58

{ /* ... */ }

7.6
q

HERITAGE ET AMITIE

L'amiti pour une classe s'hrite, mais uniquement sur les membres de la classe hrits, elle ne se propage pas aux nouveaux membres de la classe drive et ne s'tend pas aux gnrations suivantes.
class A { friend class test1; public: A( int n= 0): _a(n) {} private: int _a; }; class test1 { public: test( int n= 0): a0(n) {} void affiche1() { cout << a0._a << // OK: test1 est amie de A } private: A a0; }; class test2: public test { public: test2( int z0= 0, int z1= 0): test( z0), a1( z1) {} void Ecrit() { cout << a1._a; // ERREUR: test2 n'est pas amie de A } private: A a1; };

L'amiti pour une fonction ne s'hrite pas.

A chaque drivation, vous devez redfinir les relations d'amiti avec les fonctions.

7.7

CONVERSION DE TYPE

Il est possible de convertir implicitement une instance d'une classe drive en une instance de la classe de base si l'hritage est public. L'inverse est interdit car le compilateur ne saurait pas comment initialiser les membres de la classe drive.
class Vehicule { public:

A. DANCEL - Le langage C++

59

void f1(); // ... }; class Voiture : public Vehicule { public: int f1(); // ... }; void traitement1(Vehicule v) { // ... v.f1(); // OK // ... } void main() { Voiture R25; traitement1( R25 ); }

De la mme faon on peut utiliser des pointeurs : Un pointeur (ou une rfrence) sur un objet d'une classe drive peut tre implicitement converti en un pointeur (ou une rfrence) sur un objet de la classe de base. Cette conversion n'est possible que si l'hritage est public, car la classe de base doit possder des membres public accessibles (ce n'est pas le cas d'un hritage protected ou private). C'est le type du pointeur qui dtermine laquelle des mthodes f1() est appele.
void traitement1(Vehicule *v) { // ... v->f1(); // OK // ... } void main() { Voiture R25; traitement1( &R25 ); }

7.8

HERITAGE MULTIPLE

En langage C++, il est possible d'utiliser l'hritage multiple. Il permet de crer des classes drives partir de plusieurs classes de base. Pour chaque classe de base, on peut dfinir le mode d'hritage.
class A { public: void fa() { /* ... */ } protected:

A. DANCEL - Le langage C++

60

int _x; }; class B { public: void fb() { /* ... */ } protected: int _x; }; class C: public B, public A { public: void fc(); }; void C::fc() { int i; fa(); i = A::_x + B::_x; // rsolution de porte pour lever l'ambigut }

7.8.1

Ordre d'appel des constructeurs

Dans l'hritage multiple, les constructeurs sont appels dans l'ordre de dclaration de l'hritage. Dans l'exemple suivant, le constructeur par dfaut de la classe C appelle le constructeur par dfaut de la classe B, puis celui de la classe A et en dernier lieu le constructeur de la classe drive, mme si une liste d'initialisation existe.
class A { public: A(int n=0) { /* ... */ } // ... }; class B { public: B(int n=0) { /* ... */ } // ... }; class C: public B, public A { // ^^^^^^^^^^^^^^^^^^ // ordre d'appel des constructeurs des classes de base // public: C(int i, int j) : A(i) , B(j) { /* ... */ } // ... }; void main() {

A. DANCEL - Le langage C++

61

C objet_c; // appel des constructeurs B(), A() et C() // ... }

Les destructeurs sont appels dans l'ordre inverse de celui des constructeurs.

7.9

HERITAGE VIRTUEL

Un objet de la classe D contiendra deux fois les donnes hrites de la classe de base A, une fois par hritage de la classe B et une autre fois par C. Il y a donc deux fois le membre _base dans la classe D. L'accs au membre _base de la classe A se fait en levant l'ambigut.
void main() { D od; od._base = 0; // ERREUR, ambigut od.B::_base = 1; // OK od.C::_base = 2; // OK }

Il est possible de n'avoir qu'une occurrence des membres de la classe de base, en utilisant l'hritage virtuel. Pour que la classe D n'hrite qu'une seule fois de la classe A, il faut que les classes B et C hritent virtuellement de A. Permet de n'avoir dans la classe D qu'une seule occurrence des donnes hrites de la classe de base A.
void main() { D od; od._base = 0; // OK, pas d'ambigut }

Il ne faut pas confondre ce statut "virtual" de dclaration d'hritage des classes avec celui des membres virtuels que nous allons tudier. Ici, Le mot cl virtual prcise au compilateur les classes ne pas dupliquer.

7.10

POLYMORPHISME

L'hritage nous permet de rutiliser le code crit pour la classe de base dans les autres classes de la hirarchie des classes de votre application. Le polymorphisme rendra possible l'utilisation d'une mme instruction pour appeler dynamiquement des mthodes diffrentes dans la hirarchie des classes. En C++, le polymorphisme est mis en uvre par l'utilisation des fonctions virtuelles.

A. DANCEL - Le langage C++

62

7.10.1 Fonctions virtuelles


class ObjGraph { public: void print() const { cout <<"ObjGraph::print()"; } }; class Bouton: public ObjGraph { public: void print() const { cout << "Bouton::print()"; } }; class Fenetre: public ObjGrap { public: void print() const { cout << "Fenetre::print()"; } }; void traitement(const ObjGraph &og) { // ... og.print(); // ... } void main() { Bouton OK; Fenetre windows97; traitement(OK); traitement(Window97); } // Qu'affiche ce programme ???

// affichage de ..... // affichage de .....

Comme nous l'avons dj vu, l'instruction og.print() de traitement() appelera la mthode print() de la classe ObjGraph. La rponse est donc :
traitement(OK); traitement(Window97); // affichage de ObjGraph::print() // affichage de ObjGraph::print()

Si dans la fonction traitement() nous voulons appeler la mthode print() selon la classe laquelle appartient l'instance, nous devons dfinir, dans la classe de base, la mthode print() comme tant virtuelle :
class ObjGraph { public: // ... virtual void print() const { cout<< "ObjetGraphique::print()" << endl;} };

Pour plus de clart, le mot-cl virtual peut tre rpt devant les mthodes print() des classes Bouton et Fenetre :

A. DANCEL - Le langage C++

63

class Bouton: public ObjGraph { public: virtual void print() const { cout << "Bouton::print()"; } }; class Fenetre: public ObjGrap { public: virtual void print() const { cout << "Fenetre::print()"; } };

On appelle ce comportement, le polymorphisme. Lorsque le compilateur rencontre une mthode virtuelle, il sait qu'il faut attendre l'excution pour dterminer la bonne mthode appeler.

7.10.2 Destructeur virtuel


Il ne faut pas oublier de dfinir le destructeur comme "virtual" lorsque l'on utilise une mthode virtuelle :
class ObjGraph { public: //... virtual ~ObjGraph() { cout << "fin de ObjGraph\n"; } }; class Fenetre : public ObjGraph { public: // ... ~Fenetre() { cout << "fin de Fentre }; void main() { Fenetre *Windows97 = new Fenetre; ObjGraph *og = Windows97; // ... delete og; // affichage de : fin de Fentre fin de ObjGraph // si le destructeur n'avait pas t virtuel, // l'affichage aurait t : fin de ObjGraph

"; }

q q q

Un constructeur, par contre, ne peut pas tre dclar comme virtuel. Une mthode statique ne peut, non plus, tre dclare comme virtuelle.

Lors de l'hritage, le statut de l'accessibilit de la mthode virtuelle (public, protg ou priv) est conserv dans toutes les classes drive, mme si elle est redfinie avec un statut diffrent. Le statut de la classe de base prime.

A. DANCEL - Le langage C++

64

7.11

CLASSES ABSTRAITES

Il arrive souvent que la mthode virtuelle dfinie dans la classe de base serve de cadre gnrique pour les mthodes virtuelles des classes drives. Ceci permet de garantir une bonne homognit de votre architecture de classes. Une classe est dite abstraite si elle contient au moins une mthode virtuelle pure. On ne peut pas crer d'instance d'une classe abstraite et une classe abstraite ne peut pas tre utilise comme argument ou type de retour d'une fonction. Par contre, les pointeurs et les rfrences sur une classe abstraite sont parfaitement lgitimes et justifis.

7.11.1 Mthode virtuelle pure


Une telle mthode se dclare en ajoutant un = 0 la fin de sa dclaration.
class ObjGraph { public: virtual void print() const = 0; }; void main() { ObjGraph og; // ... }

// ERREUR

q q

On ne peut utiliser une classe abstraite qu' partir d'un pointeur ou d'une rfrence.

Contrairement une mthode virtuelle "normale", une mthode virtuelle pure n'est pas oblig de fournir une dfinition pour ObjGraph::print().
q

Une classe drive qui ne redfinit pas une mthode virtuelle pure est elle aussi abstraite.

A. DANCEL - Le langage C++

65

8 - LES FLUX

8.1
q q

GENERALITES
une source produisant de l'information une cible consommant cette information.

Un flux (ou stream) est une abstraction logicielle reprsentant un flot de donnes entre :

Il peut tre reprsent comme un buffer et des mcanismes associs celui-ci et il prend en charge, quand le flux est cr, l'acheminement de ces donnes. Comme pour le langage C, les instructions entres/sorties ne font pas partie des instructions du langage. Elles sont dans une librairie standardise qui implmente les flots partir de classes. Par dfaut, chaque programme C++ peut utiliser 3 flots : cout qui correspond la sortie standard cin qui correspond l'entre standard

cerr qui correspond la sortie standard d'erreur. Pour utiliser d'autres flots, vous devez donc crer et attacher ces flots des fichiers (fichiers normaux ou fichiers spciaux) ou des tableaux de caractres.

8.2
8.2.1

FLOTS ET CLASSES
Iostream.h

Classes dclares dans iostream.h et permettant la manipulation des priphriques standards : ios classe de base des entres/sorties par flot. Elle contient un objet de la classe streambuf pour la gestion des tampons d'entres/sorties.
q q q q q

istream classe drive de ios pour les flots en entre. ostream classe drive de ios pour les flots en sortie. iostream classe drive de istream et de ostream pour les flots bidirectionnels.

istream_withassign, ostream_withassign et iostream_withassign : classes drives respectivement de istream, ostream et iostream et qui ajoute l'oprateur d'affectation. Les flots standard cin, cout et cerr sont des instances de ces classes.

A. DANCEL - Le langage C++

66

8.2.2

fstream.h

Classes dclares dans fstream.h permettant la manipulation des fichiers disques :

fstreambase classe de base pour les classes drives ifstream, ofstream et fstream. Elle mme est drive de ios et contient un objet de la classe filebuf (drive de streambuf). ifstream ofstream fstream classe permettant d'effectuer des entres partir des fichiers. classe permettant d'effectuer des sorties sur des fichiers. classe permettant d'effectuer des entres/sorties partir des fichiers.

8.2.3

Strstream.h

Classes dclares dans strstream.h permettant de simuler des oprations d'entres/sorties avec des tampons en mmoire centrale. Elles oprent de la mme faon que les fonctions du langage C sprintf() et sscanf() :

strtreambase classe de base pour les classes suivantes. Elle contient un objet de la classe strstreambuf (drive de streambuf). istrstream classe drive de strtreambase et de istream permettant la lecture dans un tampon mmoire ( la manire de la fonction sscanf).

A. DANCEL - Le langage C++

67

ostrstream classe drive de strtreambase et de ostream permettant l'criture dans un tampon mmoire ( la manire de la fonction sprintf). strstream classe drive de istrstream et de iostream permettant la lecture et l'criture dans un tampon mmoire.

8.3
q q q

LE FLOT DE SORTIE : ostream


Il redfinit l'oprateur << Il gre les types prdfinis du langage C++ On peut (doit) le surdfinir pour ses propres types

Il fournit des sorties formates et non formates (dans un streambuf) .

8.3.1

Surdfinition de l'oprateur d'injection dans un flot :


class Exemple { friend ostream &operator<<(ostream &os, const Exemple &ex); public : // ... private : char _nom[20]; int _valeur; }; ostream &operator<<(ostream &os, const Exemple &ex) { return os << ex._nom << " " << ex._valeur; } void main() { Exemple e1; // ... cout << "Exemple = " << e1 << endl; // ... }

La fonction operator<< doit retourner une rfrence sur l'ostream pour lequel il a t appel afin de pouvoir le cascader dans une mme instruction, comme dans la fonction main() ci-dessus.

8.3.2

Principales mthodes de ostream

En plus de l'oprateur d'injection (<<), la classe ostream contient les principales mthodes suivantes :
q

ostream & put(char c); : insre un caractre dans le flot :


cout.put('\n');

ostream & write(const char *, int n); : insre n caractres dans le flot :

A. DANCEL - Le langage C++

68

cout.write("Bonjour", 7);
q q

streampos tellp(); : retourne la position courante dans le flot

ostream & seekp(streampos n); : se positionne n octet(s) par rapport au dbut du flux. Les positions dans un flot commencent 0 et le type streampos correspond la position d'un caractre dans le fichier.
q

ostream & seekp(streamoff dep, seek_dir dir); : se positionne dep octet(s) par rapport :

au dbut du flot : dir = beg la posit ion courante : dir = cur la fin du flot : dir = end (et dep est ngatif!) :

streampos old_pos = fout.tellp(); // mmorise la position fout.seekp(0, end); // se positionne la fin du flux cout << "Taille du fichier : " << fout.tellp() << " octet(s)\n"; fout.seekp(old_pos, beg); // se repositionne comme au dpart
q

ostream & flush(); : vide les tampons du flux

8.4
q q q q

LE FLOT D'ENTREE : istream


Il redfinit l'oprateur >> Il gre les types prdfinis du langage C++ On peut (doit) le surdfinir pour ses propres types Par dfaut >> ignore tous les espaces (voir ios::skipws)

Il fournit des entres formates et non formates (dans un streambuf)

8.4.1

Surdfinition de l'oprateur d'extraction >> :


class Exemple { friend istream &operator>>(istream &is, Exemple &ex); public : // ... private : char _nom[20]; int }; istream &operator>>(istream &is, Exemple &ex) { return is >> ex._nom >> ex._valeur; } void main() { Exemple e1; // ... cout << "Entrez un nom et une valeur : "; _valeur;

A. DANCEL - Le langage C++

69

cin >> e1; // ... }

La fonction operator>> doit retourner une rfrence sur l' istream pour lequel il a t appel afin de pouvoir le cascader dans une mme instruction.

8.4.2

Principales mthodes de istream

En plus de l'oprateur d'extraction (>>), la classe istream contient les principales mthodes suivantes :
q

Lecture d'un caractre :


int get(); : retourne la valeur du caractre lu (ou EOF si la fin du fichier est atteinte) istream & get(char &c); : extrait le premier caractre du flux (mme si c'est un espace) et le place dans c. int peek(); : lecture non destructrice du caractre suivant dans le flux. Retourne le code du caractre ou EOF si la fin du fichier est atteinte.

Lecture d'une chane de caractres :

istream & get(char *ch, int n, char delim='\n'); : extrait n -1 caractres du flux et les place l'adresse ch. La lecture s'arrte au dlimiteur qui est par dfaut le '\n' ou la fin de fichier. Attention : Le dlimiteur ('\n' par dfaut) n'est pas extrait du flux. istream & getline(char *ch, int n, char delim='\n'); : comme la mthode prcdente sauf que le dlimiteur est extrait du flux mais n'est pas recopi dans le tampon.

istream & read(char *ch, int n); : extrait un bloc d' au plus n octets du flux et les place l'adresse ch. Le nombre d'octets effectivement lus peut tre obtenu par la mthode gcount().
q q q

int gcount(); : retourne le nombre de caractres non formats extraits lors de la dernire lecture streampos tellg(); : retourne la position courante dans le flot.

istream & seekg(streampos n); : se positionne n octet(s) par rapport au dbut du flux. Les positions dans un flot commencent 0 et le type streampos correspond la position d'un caractre dans le fichier.
q

istream & seekg(streamoff dep, seek_dir dir); : se positionne dep octet(s) par rapport :

au dbut du flot : dir = beg la position courante : dir = cur la fin du flot : dir = end (et dep est ngatif!)

istream & flush(); : vide les tampons du flux.

8.5

CONTROLE DE L'ETAT D'UN FLUX

La classe ios dcrit les aspects communs des flots d'entre et de sortie. C'est une classe de base virtuelle pour tous les objets flots. Vous n'aurez jamais crer des objets de la classe ios. Vous utiliserez ses mthodes pour tester l'tat d'un flot ou pour contrler le formatage des informations.

A. DANCEL - Le langage C++

70

8.5.1
q

Mthodes de la classe de base ios :

int good(); : retourne une valeur diffrente de zro si la dernire opration d'entre/sortie s'est effectue avec succs et une valeur nulle en cas d'chec.
q q

int fail(); : fait l'inverse de la mthode prcdente

int eof(); : retourne une valeur diffrente de zro si la fin de fichier est atteinte et une valeur nulle dans le cas contraire. int bad(); : retourne une valeur diffrente de zro si vous avez tent une opration interdite et une valeur nulle dans le cas contraire.
q

int rdstate(); : retourne la valeur de la variable d'tat du flux. Retourne une valeur nulle si tout va bien.
q

void clear(); : remet zro l'indicateur d'erreur du flux. C'est une opration obligatoirement faire aprs qu'une erreur se soit produite sur un flux.
q q

surdfinition de () et !

Exemples :
if ( f1 ) ... // quivalent : if ( f1.good() ) if ( !f1 ) ... // quivalent : if ( !f1.good() ) if ( ! (cin >> x) ) ...

8.6

ASSOCIER UN FLOT D'E/S A UN FICHIER

Il est possible de crer un objet flot associ un fichier autre que les fichiers d'entres/sorties standards. 3 classes permettant de manipuler des fichiers sur disque sont dfinies dans fstream.h : ifstream : (drive de istream et de fstreambase) permet l'accs du flux en lecture seulement ofstream : (drive de ostream et de fstreambase) permet l'accs du flux en criture seulement fstream : (drive de iostream et de fstreambase) permet l'accs du flux en lecture/criture. Chacune de ces classes utilise un buffer de la classe filebuf pour synchroniser les oprations entre le buffer et le flot. La classe fstreambase offre un lot de mthodes communes ces classes (open, close, attach ...).

8.6.1

Ouverture du fichier et association avec le flux :

C'est la mthode open() qui permet d'ouvrir le fichier et d'associer un flux avec ce dernier.
void open(const char *name, int mode,int prot=filebuf::openprot);

name : nom du fichier ouvrir mode : mode d'ouverture du fichier (enum open_mode de la classe ios) : enum open_mode { app, ate, // ajout des donnes en fin de fichier. // positionnement la fin du fichier.

A. DANCEL - Le langage C++

71

in, out,

// ouverture en lecture (par dfaut pour ifstream). // ouverture en criture (par dfaut pour ofstream).

binary, // ouverture en mode binaire (par dfaut en mode texte). trunc, // dtruit le fichier s'il existe et le recre (par dfaut // si out est spcifi sans que ate ou app ne soit activ). nocreate, // si le fichier n'existe pas, l'ouverture choue. noreplace // si le fichier existe, l'ouverture choue, // sauf si ate ou app sont activs. }; Pour les classes ifstream et ofstream le mode par dfaut est respectivement ios::in et ios::out. prot : il dfinit les droits d'accs au fichier (par dfaut les permissions de lecture/criture sont positionns) dans le cas d'une ouverture avec cration (sous UNIX). Exemple :
#include <fstream.h> ifstream f1; f1.open("essai1.tmp"); // ouverture en lecture du fichier ofstream f2; f2.open("essai2.tmp"); // ouverture en criture du fichier fstream f3; f3.open("essai3.tmp", ios::in | ios::out); // ouverture en // lecture/criture

On peut aussi appeler les constructeurs des diffrentes classes et combiner les 2 oprations de dfinition du flot et d'ouverture.

#include <fstream.h> ifstream f1("essai1.tmp"); // ouverture en lecture du fichier ofstream f2("essai2.tmp"); // ouverture en criture du fichier fstream f3("essai3.tmp", ios::in| ios::out); // ouverture en // lecture/criture

8.7

FORMATAGE DE L'INFORMATION

Chaque flot conserve en permanence un ensemble d'indicateurs spcifiant l'tat de formatage. Ceci permet de donner un comportement par dfaut au flot, contrairement aux fonctions printf() et scanf() du langage C, dans lesquelles on fournissait pour chaque opration d'entre/sortie l'indicateur de format. L'indicateur de format du flot, est un entier long dfini (en protected) dans la classe ios, dont les classes stream hritent.

A. DANCEL - Le langage C++

72

Extrait de ios :
class ios { public : // ... enum { skipws, left, right, internal,

// // // //

ignore les espaces en entre justifie les sorties gauche justifie les sorties droite remplissage aprs le signe ou la base conversion en dcimal conversion en octal conversion en hexadcimal affiche l'indicateur de la base affiche le point dcimal avec les rels affiche en majuscules les nombres hexadcimaux affiche le signe + devant les nombres positifs notation 1.234000E2 avec les rels notation 123.4 avec les rels

dec, // oct, // hex, // showbase, // showpoint, // uppercase, // showpos, // scientific,// fixed, // unitbuf, stdio

// vide les flots aprs une insertion // permet d'utiliser stdout et cout

}; //... protected : long x_flags; // indicateur de format //... };

Ces valeurs peuvent se combiner avec l'oprateur | comme dans l'exemple :


ios::showbase | ios::showpoint | ios::showpos

Des constantes sont aussi dfinies dans la classe ios pour accder un groupe d'indicateurs :
q q q

static const long basefield; : permet de choisir la base (dec, oct ou hex) static const long adjustfield; permet de choisir son alignement (left, right ou internal) static const long floatfield; permet de choisir sa notation pour les rels (scientific ou fixed)

Les mthodes suivantes (dfinies dans la classe ios) permettent de lire ou de modifier la valeur des indicateurs de format :
q q

long flags(); : retourne la valeur de l'indicateur de format

long flags(long f); : modifie l'ensemble des indic ateurs en concordance avec la valeur de f. Elle retourne l'ancienne valeur de l'indicateur. long setf(long setbits, long field); : remet zro les bits correspondants field (basefield , adjustfield ou floatfield) et positionne ceux dsigns par setbits. Elle retourne l'ancienne valeur de l'indicateur de format.
q

cout << setf(ios::dec, ios::basefield) << i; cout << setf(ios::left, ios::adjustfield)<< hex << 0xFF;

A. DANCEL - Le langage C++

73

// affiche : 000xFF cout << setf(ios::internal, ios::adjustfield) << hex << 0xFF; // affiche : 0x00FF cout << setf(ios::scientific, ios::floatfield) << f; long old = cout.setf(ios::left, ios::adjustfield); cout << data; cout.setf(old, ios::adjustfield); cout.setf(0L, ios::basefield); // tat par dfaut de basefield

long setf(long f); : positionne l'indicateur de format. Elle retourne l'ancienne valeur de l'indicateur de format.
q

cout.setf( ios::skipws ); cout.setf( ios::dec | ios::right );

long unsetf(long); : efface les indicateurs prciss.

Mthodes de la classe ios manipulant le format des informations :


q q q q q

int width(int); : positionne la largeur du champ de sortie. int width(); : retourne la largeur du champ de sortie. char fill(char); : positionne le caractre de remplissage char fill(); : retourne la valeur du caractre de remplissage

int precision(int); : positionne le nombre de caractres (non compris le point dcimal) qu'occupe un rel.
q

int precision(); : retourne la prcision (voir ci-dessus).

cout << "Largeur par dfaut du champ de sortie : "; cout << cout.width() << endl; // affiche: 0 cout.width(10); cout.fill('*'); cout << '|' << 1234 << "|\n"; // affiche: |******1234| cout.setprecision(6); cout << 12.345678 << endl; // affiche: 12.3457 // (noter l'arrondi l'affichage)

8.8

LES MANIPULATEURS

L'usage des mthodes de formatage de l'information rend les instructions lourdes crire et un peu trop longues. Les manipulateurs permettent d'crire un code plus compact et plus lisible. Ainsi, au lieu d'crire :
cout.width(10); cout.fill('*'); cout.setf(ios::hex, ios::basefield); cout << 123 ;

A. DANCEL - Le langage C++

74

cout.flush();

on prfrera crire l'instruction quivalente :


cout << setw(10) << setfill('*') << hex << 123 << flush;

dans laquelle, setw et setfill sont des manipulateurs avec un argument alors que hex et flush sont des manipulateurs sans argument. De plus nous pourrons crire nos propres manipulateurs.

8.8.1

Comment cela marche-t il ?

Les manipulateurs les plus clbres sont endl et flush. Vous les connaissez certainement. Voici comment ils sont dfinis :
ostream &flush(ostream &ps) { return os.flush(); } ostream &endl(ostream &os) { return os << '\n' << flush; }

Pour pouvoir les utiliser dans une instruction comme :


cout << end;

La fonction operator<< est surcharge dans la classe ostream comme :


ostream &ostream::operator<<(ostream& (* f)(ostream &)) { (*f)(*this); return *this; }

Il est donc trs simple de dfinir son propre manipulateur (sans paramtre) : Exemple : cration du manipulateur tab :
ostream &tab(ostream &os){ return os << '\t'; }

que l'on peut utiliser ainsi :


cout << 12 << tab << 34;

8.8.2

Manipulateurs prdfinis

Les classes ios, istream et ostream implmentent les manipulateurs prdfinis. Le fic hier d'entte iomanip.h dfini un certain nombre de ces manipulateurs et offre la possibilit de crer ses propres manipulateurs.
q q q q q q q

dec : la prochane E/S utilise la base dcimale hex : la prochane E/S utilise la base hexadcimale oct : la prochane E/S utilise la base octale endl : crit un '\n' puis vide le flot ends : crit un '\0' puis vide le flot flush : vide le flot ws : saute les espaces (sur un flot en entre uniquement)

A. DANCEL - Le langage C++

75

setbase(int b) : positionne la base b pour la prochaine sortie. n vaut 0 pour le dcimal, 8 pour l'octal et 16 pour l'hexadcimal.
q q q q q

setfill(int c) : positionne le caractre de remplissage c pour la prochane E/S setprecision(int p) : positionne la prcision p chiffres pour la prochane E/S setw(int l) : positionne la largeur n caractres.

setiosflags(long n) : active les bits de l'indicateur de format spcifis par l'entier n. On l'utilise comme la mthode flags().
q

resetiosflags(long b) : dsactive les bits de l'indicateur de format.

8.8.3

Cration d'un nouveau manipulateur (avec un paramtre)

L'implmentation d'un nouveau manipulateur est implant en 2 parties :


q

le manipulateur : sa forme gnrale est (pour un ostream) :


ostream &nom_du_manipulateur(ostream &, type );

type est le type du paramtre du manipulateur. Cette fonction ne peut pas tre appele directement par une instruction d'entre/sortie. Elle sera appele seulement par l'applicateur.
q

l'applicateur : il appelle le manipulateur. C'est une fonction globale. Sa forme gnrale est :
xxxMANIP( type ) nom_du_manipulateur(type arg) { return xxxMANIP(type) (nom_du_manipulateur, arg); }

avec xxx valant O pour les flots manipulant un ostream (ou ses drives), I, S et IO pour respectivement les flots manipulant un istream, ios et un iostream. Exemple : un manipula teur pour afficher un entier en binaire :
#include <iomanip.h> #include <limits.h> // ULONG_MAX

ostream &bin(ostream &os, long val) { unsigned long masque = ~(ULONG_MAX >> 1); while ( masque ) { os << ((val & masque) ? '1' : '0'); masque >>= 1; } return os; } OMANIP(long) bin(long val) { return OMANIP(long) (bin, val); } void main() { cout << "1997 en binaire = " << bin(1997) << endl; }

A. DANCEL - Le langage C++

76

9 - LES EXCEPTIONS

9.1
q q

GENERALITES
erreurs matrielles : saturation mmoire, disque plein ... erreurs logicielles : division par zro ...

Le langage C++ nous propose une gestion efficace des erreurs pouvant survenir pendant l'excution :

La solution habituellement pratique est l'affichage d'un message d'erreur et la sortie du programme avec le renvoi d'un code d'erreur au programme appelant.
void lire_fichier(const char *nom) { ifstream f_in(nom); // ouverture en lecture if ( ! f_in.good()) { cerr << "Problme l'ouverture du fichier " << nom << endl; exit(1); } // lecture du fichier ... }

En C++, cest une nouvelle structure de contrle qui permet la gestion des erreurs d'excution : la structure try ... catch

9.2
q q q q q

SCHEMA DU MECANISME D'EXCEPTION


constuction d'un objet (d'un type quelconque) qui reprsente l'erreur lancement de l'exception (throw) l'exception est alors propage dans la structure de contrle try ... catch englobante cette structure essaye attraper (catch ) l'objet si elle n'y parvient pas, la fonction terminate() est appele.

try { // ... throw objet }

// lancement de l'exception

catch ( type ) { // traitement de l'erreur }

A. DANCEL - Le langage C++

77

9.3
q

LA STRUCTURE try ... catch

Quand une exception est dtecte dans un bloc try, le contrle de l'excution est donn au bloc catch correspondant au type de l'exception (s'il existe).
q q q

Un bloc try doit tre suivi d'au moins un bloc catch Si plusieurs blocs catch existent, ils doivent intercepter un type d'exception diffrent.

Quand une exception est dtecte, les destructeurs des objets inclus dans le bloc try sont appels avant d'appeler un bloc catch. A la fin du bloc catch, le programme continue son excution sur l'instruction qui suit le dernier bloc catch.
q

Exemple : interception d'une exception de type bad_alloc : (Cette exception est lance en cas d'echec d'allocation mmoire)
#include <exception.h> // ... try { char *ptr = new char[1000000000]; / // ... suite en cas de succs de new (improbable ...) } catch ( bad_alloc ) { // en cas d'chec d'allocation mmoire par new // une exception bad_alloc est lance par new // traitement de l'erreur d'allocation }

9.3.1
q q

Syntaxe de catch
catch ( TYPE ) : intercepte les exceptions du type TYPE, ainsi que celles de ses classes drives.

catch ( TYPE o) : intercepte les exceptions du type TYPE, ainsi que celles de ses classes drives. Dans le catch un objet o est utilisable pour extraire d'autres informations sur l'exception.
q

catch ( ... ) : intercepte les exceptions de tous types, non traites par les bocs catch prcdents.

class Erreur { // ... int get_erreur() { /* ... */ } }; try { // instruction(s) douteuse(s) qui peu(ven)t lancer une exception } catch ( xalloc ) { // ... } catch ( Erreur err ) {

A. DANCEL - Le langage C++

78

cerr << err.get_erreur() << endl; } catch (...) { // traitement des autres exceptions }

9.3.2
q

Syntaxe de throw
throw obj : elle permet de lever une exception donne
#include <except.h> #include <iostream.h> class Essai { public : class Erreur { // ... void f1() {

};

throw Erreur(); // construction d'une instance de Erreur // et lancement de celle-ci } }; void main() { try { Essai e1; e1.f1(); } catch ( Essai::Erreur ) { cout << "interception de Erreur" << endl; } }

throw : l'instruction throw sans paramtre s'utilise si l'on ne parvient pas rsoudre une exception dans un bloc catch. Elle permet de relancer la dernire exception.
q

9.4

EXCEPTIONS LEVEES PAR UNE FONCTION

Il faut dclarer les exceptions leves par une fonction. Cette dclaration permet de spcifier le type des exceptions pouvant tre ventuellement leves par votre fonction (ou mthode). Exemples de dclaration :
void f1() throw (Erreur); void f2() throw (Erreur, Division_zero); void f3() throw (); void f4();

Remarques :

A. DANCEL - Le langage C++

79

Par dfaut de dclaration (comme dans le cas de la fonction f4()), toute exception peut tre lance par une fonction.
q q q q

La fonction f3(), dclare ci dessus, ne peut lancer aucune exception. La fonction f1() ne peut lancer que des exceptions de la classe Erreur (ou de classes drives).

Si une fonction lance une exception non dclare dans son entte, la fonction unexpected() est appele.
q

A la dfinition de la fonction, throw et ses paramtres doivent tre de nouveau spcifi.

9.5

LA FONCTION terminate()

Si aucun bloc catch ne peut attraper l'exception lance, la fonction terminate() est alors appele. Par dfaut elle met fin au programme par un appel la fonction abort(). On peut dfinir sa propre fonction terminate() par un appel la fonction set_terminate() dfinie dans except.h comme :
typedef void (*terminate_function)(); terminate_function set_terminate(terminate_function t_func);

Exemple :
#include <except.h> #include <iostream.h> void my_terminate() { cout << "my_terminate" << endl; exit(1); // elle ne doit pas retourner son appelant } void main() { try { set_terminate((terminate_function) my_terminate); // ... } catch ( Erreur ) { // ... } }

9.6

LA FONCTION unexpected()

Si une fonction (ou une mthode) lance une exception qui n'est pas dclare par un throw dans son entte, la fonction unexpected() est alors appele. Par dfaut elle met fin au programme par un appel la fonction abort(). On peut dfinir sa propre fonction unexpected() par un appel la fonction set_unexpected() dfinie dans except.h comme :
typedef void (*unexpected_function)(); unexpected_function set_unexpected(unexpected_function t_func);

A. DANCEL - Le langage C++

80

Exemple :
#include <except.h> #include <iostream.h> void my_unexpected() { cout << "my_unexpected" << endl; exit(1); // elle ne doit pas retourner son appelant } class Erreur {}; class Toto {}; class Essai { public: void f1() throw (Erreur); }; void Essai::f1() throw (Erreur) { throw Toto(); } void main() { try { set_unexpected( (unexpected_function) my_unexpected); Essai e1; e1.f1(); } catch ( Erreur ) { // ... } }

9.7

EXEMPLE COMPLET
#include <except.h> #include <iostream.h> class Essai { public : class Erreur { public: Erreur(int n=0): _val(n) { } int get_val() { return _val; } private: int _val; }; Essai() { cout << "Constructeur d'Essai" << endl; }

A. DANCEL - Le langage C++

81

~Essai() { cout << "Destructeur d'Essai" << endl; } // ... void f1() { throw Erreur(10);//construction d'une instance de Erreur //initialise 10 et lancement de celle-ci } }; void main() { try { Essai e1; e1.f1(); cout << "bla bla bla" << endl; // } catch ( Essai::Erreur e ) { cout << "Erreur numro : " << e.get_val() << endl; } } /* rsultat de l'excution *********** Constructeur d'Essai Destructeur d'Essai Erreur numro : 10 *********************************/

A. DANCEL - Le langage C++

82

TABLE DES MATIERES


11.1 1.2 1.3 1.4 1.5 22.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11 33.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 44.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 4.11 4.12 4.13 4.14 4.15 4.16 4.17 55.1 5.2 GENERALITES ....................................................................................................................................................................0 INTRODUCTION.............................................................................................................................................................. 1 LA CLASSE........................................................................................................................................................................ 1 L'HERITAGE...................................................................................................................................................................... 2 LE POLYMORPHISME................................................................................................................................................... 3 RESUME ............................................................................................................................................................................. 3 UN MEILLEUR C..............................................................................................................................................................4 LES COMMENTAIRES ................................................................................................................................................... 4 ENTREES/SORTIES SIMPLES ..................................................................................................................................... 5 LES MANIPULATEURS ................................................................................................................................................. 6 LES CONVERSIONS EXPLICITES .............................................................................................................................. 7 DEFINITION DE VARIABLES ..................................................................................................................................... 8 VARIABLE DE BOUCLE ............................................................................................................................................... 8 VISIBILITE DES VARIABLES ..................................................................................................................................... 9 LES CONSTANTES .......................................................................................................................................................... 9 CONSTANTES ET POINTEURS................................................................................................................................... 9 LES TYPES ....................................................................................................................................................................... 10 ALLOCATION MEMOIRE........................................................................................................................................... 11 LES FONCTIONS ..............................................................................................................................................................14 DECLARATION DES FONCTIONS........................................................................................................................... 14 PASSAGE PAR REFERENCE...................................................................................................................................... 14 VALEUR PAR DEFAUT DES PARAMETRES........................................................................................................ 15 FONCTION INLINE....................................................................................................................................................... 16 SURCHARGE DE FONCTIONS.................................................................................................................................. 16 RETOUR D'UNE REFERENCE ................................................................................................................................... 18 UTILISATION DUNE FONCTION ECRITE EN C ................................................................................................ 19 FICHIER D'EN -TETES POUR C ET C++................................................................................................................... 19 LES CLASSES .....................................................................................................................................................................21 DEFINITION D'UNE CLASSE..................................................................................................................................... 21 DROITS D'ACCES .......................................................................................................................................................... 21 RECOMMANDATIONS DE STYLE .......................................................................................................................... 21 DEFINITION DES FONCTIONS MEMBRES .......................................................................................................... 22 INSTANCIATION D'UNE CLASSE ........................................................................................................................... 23 UTILISATION DES OBJETS ....................................................................................................................................... 23 FONCTIONS MEMBRES CONSTANTES ................................................................................................................ 23 EXEMPLE : PILE D'ENTIERS (1) ............................................................................................................................... 25 CONSTRUCTEUR ET DESTRUCTEUR................................................................................................................... 27 EXEMPLE : PILE D'ENTIERS (2) ............................................................................................................................... 29 CONSTRUCTEUR COPIE ............................................................................................................................................ 30 CLASSES IMBRIQUEES .............................................................................................................................................. 32 AFFECTATION ET INITIALISATION...................................................................................................................... 33 LISTE D'INITIALISATION D'UN CONSTRUCTEUR........................................................................................... 33 LE POINTEUR THIS....................................................................................................................................................... 34 LES MEMBRES STATIQUES...................................................................................................................................... 35 CLASSES ET FONCTIONS AMIES ........................................................................................................................... 37 LES OPERATEURS ..........................................................................................................................................................39 INTRODUCTION A LA SURCHARGE D'OPERATEURS ................................................................................... 39 REGLES D'UTILISATION............................................................................................................................................ 39

A. DANCEL - Le langage C++

83

5.3 5.4 5.5 5.6 66.1 6.2 6.3 77.1 7.2 7.3 7.4 7.5 7.6 7.7 7.8 7.9 7.10 7.11 88.1 8.2 8.3 8.4 8.5 8.6 8.7 8.8 99.1 9.2 9.3 9.4 9.5 9.6 9.7

SURCHARGE D'UN OPERATEUR............................................................................................................................ 40 OPERATEUR D'AFFECTATION ................................................................................................................................ 42 CAS PARTICULIERS .................................................................................................................................................... 43 OPERATEURS DE CONVERSION ............................................................................................................................ 46 LES MODELES ...................................................................................................................................................................50 LES MODELES DE FONCTIONS ............................................................................................................................... 50 LES CLASSES PARAMETREES ................................................................................................................................. 51 ARGUMENTS DUNE FONCTION MODELE ........................................................................................................ 52 L'HERITAGE......................................................................................................................................................................54 L'HERITAGE.................................................................................................................................................................... 54 MODE DE DERIVATION............................................................................................................................................. 54 REDEFINITION DE METHODES ............................................................................................................................... 57 AJUSTEMENT D'ACCES ............................................................................................................................................. 57 HERITAGE DES CONSTRUCTEURS / DESTRUCTEURS.................................................................................. 58 HERITAGE ET AMITIE ................................................................................................................................................ 59 CONVERSION DE TYPE.............................................................................................................................................. 59 HERITAGE MULTIPLE ................................................................................................................................................ 60 HERITAGE VIRTUEL ................................................................................................................................................... 62 POLYMORPHISME....................................................................................................................................................... 62 CLASSES ABSTRAITES .............................................................................................................................................. 65 LES FLUX.............................................................................................................................................................................66 GENERALITES ............................................................................................................................................................... 66 FLOTS ET CLASSES ...................................................................................................................................................... 66 LE FLOT DE SORTIE : OSTREAM................................................................................................................................. 68 LE FLOT D'ENTREE : ISTREAM ................................................................................................................................... 69 CONTROLE DE L'ETAT D'UN FLUX ....................................................................................................................... 70 ASSOCIER UN FLOT D'E/S A UN FICHIER............................................................................................................ 71 FORMATAGE DE L'INFORMATION ....................................................................................................................... 72 LES MANIPULATEURS ............................................................................................................................................... 74 LES EXCEPTIONS ............................................................................................................................................................77 GENERALITES ............................................................................................................................................................... 77 SCHEMA DU MECANISME D'EXCEPTION .......................................................................................................... 77 LA STRUCTURE TRY ... CATCH ................................................................................................................................... 78 EXCEPTIONS LEVEES PAR UNE FONCTION...................................................................................................... 79 LA FONCTION TERMINATE()........................................................................................................................................ 80 LA FONCTION UNEXPECTED()..................................................................................................................................... 80 EXEMPLE COMPLET ................................................................................................................................................... 81

TABLE DES MATIERES ...............................................................................................................................................................83

A. DANCEL - Le langage C++

84