You are on page 1of 184

Facult des Sciences de Monastir Dpartement des Sciences de l'Informatique

Notes de cours

Programmation Oriente Objet


(C++)
Par Karim Kalti

Version 3.7

SOMMAIRE

Partie I :
Les bases du langage (rgles d'criture, types, variables, oprateurs, structures de contrle, ) Les entres /sorties en C++. Les tableaux. Les pointeurs et les rfrences. La gestion dynamique de la mmoire. Les fonctions. Les chanes de caractres Les structures et les numrations.

Partie II :
Introduction la programmation oriente objet. Les classes (attributs, mthodes, droits d'accs, instanciation,...) Constructeurs et destructeur. Espaces de noms. Membres statiques et membres constants. Fonctions amies. Hritage et polymorphisme. La surcharge des oprateurs. Les modles. La gestion des exceptions.

Annexe :
- Les fichiers.

Les rgles dcriture Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les rgles d'criture


Premier programme
#include <iosrteam.h> void main() { cout<<"bonjour"; }

Ce programme affiche le mot Bonjour l'cran. Cet affichage est ralis travers l'oprateur d'extraction appliqu l'objet cout. cout est un objet dfini dans la bibliothque iostream.h. Cette bibliothque doit tre incluse dans le programme. En gnral les dclarations des fonctions standards du langage C++ qui sont susceptibles d'apparatre dans le programme se trouvent dans des fichiers d'entte (header) ayant l'extension .h. Ces fichiers doivent tre inclus au dbut de chaque programme avec la directive du prprocesseur include. La fonction main est la fonction principale du programme. C'est elle qui contient le corps du programme. Les accolades jouent le rle de "begin" et "end" du Pascal. Elles indiquent le dbut et la fin d'un bloc de code. Remarque : Ce petit programme donne une premire ide sur la structure d'un programme C++. D'autres lments du langage peuvent encore prendre place. Leurs dfinitions ainsi que leur emplacements seront dcrits dans les parties suivantes du cours.

Structure d'un programme C++


D'une manire gnrale et d'un point de vue structurel, un programme C++ se dcompose en deux grandes parties : Les dclarations : qui comprennent : o Les directives de compilation pour l'inclusion des fichiers d'enttes. o Les dclarations des donnes ou des fonctions partages avec d'autres fichiers. o Les dclarations des types propres au fichier. o Les dclarations des donnes globales du fichier. Les dfinitions : il s'agit des dfinitions des fonctions du fichier et des classes.

Les mots cls


Les mots cls sont rservs par le langage un usage bien dfini et ne peuvent se voir changer de signification, ni tre utiliss comme identificateurs. Les mots cls du C++ sont : Mots cls communs avec le C :
auto double int struct break else long swich case enum register typedef char extern return union const float short unisgned continue for signed void default goto sizeof volatile do if static while

Mots cls spcifiques au C++ :


bool private catch protected class public delete template friend this inline throw new try operator virtual

Version 3.7

Karim Kalti

Les rgles dcriture Programmation oriente objet (C++) _________________________________________________________________________________________________________________

De plus le pr-processeur utilise les mots cls suivants :


#define #ifdef #elif #ifndef #else #include #endif #inline #error #pragma #if #undef

Remarque : Certains compilateurs peuvent ajouter d'autres mots cls qui lui sont propres.

Les identificateurs
Les identificateurs servent dsigner les diffrents "objets" manipuls par le programme (variables, fonctions, etc.). Ils se prsentent sous la forme de chanes de caractres alphanumriques (combinaison de caractres alphabtiques et de chiffres), de taille quelconque, dans les limites acceptes par le compilateur. En C++, les identificateurs doivent respecter les rgles suivantes : Un identificateur doit toujours commencer par une lettre ou par le caractre underscore _. Les caractres accentus (, , , , , ,) ne sont pas accepts par le compilateur. Un identificateur doit tre diffrent des mots cls du langage. Les majuscules et les minuscules sont considres comme des lettres distinctes. En gnrale la taille d'un identificateur ne doit pas dpasser les 32 caractres. Ce nombre varie suivant les compilateurs. Par exemple Borland C++ Version 5.0 utilise 250 caractres comme caractres significatifs. Les caractres situs au del du nombre significatif ne sont pas pris en considration. Les identificateurs servent donner des noms divers lments du langage : les variables, les fonctions, les constantes, les numrations, les structures Exemples d'identificateurs valides :
Nabs n1234_ abc table chaise

Exemple d'identificateurs non valides : 2abc n'est pas un identificateur valide. Exemples d'identificateurs diffrents :
Somme somme sOmme

Les commentaires
Les commentaires sont fondamentaux pour la comprhension des programmes et donc pour leur maintenance et rutilisation. C'est pourquoi, il est trs conseill de les utiliser autant que c'est possible. Ils se prsentent comme des portions de texte qui ne sont pas pris en compte lors de la compilation. Le C++ supporte deux types de commentaires : Le commentaire multi-lignes : Il est introduit par /* et se termine par */ Le commentaire ligne : le commentaire peut s'tendre sur une ligne seulement. Il est introduit par //. Sa fin est indique par un retour la ligne. Exemple :
/* Ceci est un commentaire sur plusieurs lignes */ // Ceci est un commentaire sur une seule ligne.

Le format libre
Le C++ autorise une mise en page libre. Ainsi une instruction peut s'tendre sur un nombre quelconque de lignes pourvu qu'elle se termine par un point virgule. De mme une ligne peut comprendre autant d'instructions que voulues.

Version 3.7

Karim Kalti

Types de base et dclaration des variables Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Types de base et dclaration des variables


Toute variable utilise dans un programme C/C++ doit tre dclare. Une variable possde un nom (identificateur) et un type. Elle contient en gnral une information bien prcise. Lors de l'excution du programme, une zone mmoire ayant la taille du type de la variable lui sera rserve. Cette zone contiendra cette information.

Les types de donnes en C++


Les types de base Les types de base en C++ sont : Type int float double char bool Description Pour les entiers standards. Pour les nombres rels standards. Pour les nombres rels en double prcision. Pour les caractres Pour les variables boolennes

Les types drivs A ces types de base s'ajoutent d'autres variantes obtenues en plaant une spcification supplmentaire devant le type de base. Les mots cls permettant de construire les types drivs sont : Mot-cl
long short unsigned

Signification Pour dfinir des entiers ou des rels de grande taille. long s'applique aux types de base int et double. Lorsque ce mot est utilis tout seul il dsigne alors par dfaut un entier long. Permet de manipuler les entiers de petite taille. Il s'utilise avec int ou tout seul (mme signification). Il se place devant les types entiers ou caractres qui doivent tre considrs comme tant non signs. Il peut s'employer tout seul. Il dsigne alors un entier non sign (unsigned int).

La liste complte des types en C++ est alors : Type Caractres


char unsigned char short int unsigned short long int unsigned long int int unsigned int float double long double bool

Taille
1 1 2 2 4 4 2 ou 4 2 ou 4 4 8 10 1

Plage de valeurs
-128 127 0 255 -32768 32768 0 65535 -2 147 483 648 2 147 483 647 0 4 294 967 295 Comme short ou long Comme unsigned short ou unsigned long 1.175 10-38 3.402 10+38 2.225 10-308 1.797 10+308 3.4 10-4932 1.1 10+4932 true, false

Entiers

Rels Boolen

Remarque 1 : Le type int possde toujours la taille d'un mot machine. Par consquent l'espace qu'il occupe en mmoire dpend de la machine sur laquelle s'excute le programme. Cette taille est par exemple de 2 octets (identique celle du type short) pour les p d'intel 8086 et 80286. Elle est de 4 octets (identique celle du type long) pour les p PENTIUM. Pour cela, et pour assurer la portabilit et le bon fonctionnement des programmes sur n'importe quelle machine, il est prfrable d'utiliser pour les entiers les types short et long.

Version 3.7

Karim Kalti

Types de base et dclaration des variables Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Tous les autres types correspondent des tailles fixes et ne dpendent pas des machines. Remarque 2 : La reprsentation des rels utilise le format suivant : Signe Mantisse 10Exposant. Remarque 3 : Le type char peut servir en C/C++ pour le stockage des entiers qui sont compris entre 128 et 127. Les caractres sont d'ailleurs reprsents sous forme d'entiers. Ces entiers correspondent aux codes ASCII.

Les variables
Dclaration TYPE NomVariable;

O : Type : dsigne le type des donnes que peut stocker la variable. NomVariable : dsigne le nom de la variable. La nomination d'une variable doit respecter les rgles qui rgissent les identificateurs en C++. Exemple :
float tension; int resistance; float Moyenne;

Plusieurs variables peuvent tre dclares simultanment si elles ont le mme type. Elles sont alors spares les unes des autres par des virgules. Exemple :
int a,b,c; float note, moyenne;

Une variable est caractrise par son adresse et par la taille qu'elle occupe en mmoire. L'adresse est automatiquement attribue par le systme alors que la taille dpend du type de la variable. Lieu de dclaration d'une variable Une variable doit tre dclare avant d'tre utilise. En C, les variables qui sont utilises l'intrieur d'un bloc doivent tre dclares au dbut de ce dernier. Cette restriction a t limine en C++. Il est ainsi possible de dclarer une variable n'importe o dans le bloc pourvu que cette dclaration soit faite avant la premire utilisation. Exemple :
// Code correct en C/C++ { double i,j, somme, moyenne; i=5.5; j=6.2; somme = i+j; moyenne = somme/2; } // Code correct en C++ // mais incorrect en C { double i,j; i=5.5; j=6.2; double somme; somme = i+j; double moyenne; moyenne = somme/2; }

Version 3.7

Karim Kalti

Types de base et dclaration des variables Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Initialisation des variables Une valeur initiale peut tre affecte une variable ds sa dclaration.
int i=4; char C ='a'; bool b1 = true; bool b2 = false;

La valeur d'initialisation d'une variable peut tre le rsultat d'un calcul portant sur des constantes.
int i = 4+5; double j=2.5*3.2;

Le contenu d'une variable non initialise est indtermin, sauf pour les variables globales et statiques qui sont automatiquement initialises 0. Affectation d'un contenu une variable L'oprateur qui permet de faire l'affectation d'un contenu une variable est (=). Il est appel oprateur d'affectation ou d'assignation. Dans une affectation (exemple : i=5), la constante ou le rsultat d'une opration arithmtique ou logique se trouvant droite de l'oprateur est copi dans la variable se trouvant sa gauche. Il est possible de faire plusieurs affectations en mme temps et ce en enchanant plusieurs fois l'oprateur (=) de la manire suivante :
int i,j; i = j = 2;

Conversions lors d'assignation Les assignations entre variables de types diffrents sont autorises. Elles engendrent alors des conversions implicites (automatiques) des types de donnes. Ces conversions sont rgies par les rgles prsentes dans le tableau suivant : Commentaire Aucune modification de valeur. - Pour le type char on a expansion du bit de signe. char int - Pour le type unsigned char il n'y a pas d'expansion. int char Perte des octets les plus significatifs de l'entier. short int (4 octets) Pas de modification de valeur int (4 octets) short Rsultat tronqu : perte des octets les plus significatifs. unsigned short Perte des octets les plus significatifs de l'entier. long unsigned Les deux octets les plus significatifs du long prennent la valeur 0. int float Ajout d'une partie dcimale nulle (exemple : 15 15.0f) float int Perte de la partie dcimale (exemple :2.5f2) float double Pas de problme double float Perte de prcision Les conversions prsentes dans le tableau ci-dessus peuvent engendrer des pertes de donnes. C'est pourquoi les assignations entre variables de types diffrents doivent tre effectues avec prcaution. La notion de bloc Un bloc est une suite d'instructions dlimite par une accolade ouvrante et une accolade fermante. Exemple :
{ instruction_1; instruction_2; instruction_n; }

Conversion

Porte des variables : La porte d'une variable est dfinie par les zones du programme o la variable peut tre utilise ou en d'autres mots les zones o la variable est visible. Une variable n'est visible qu' l'intrieur du bloc dans lequel elle est dclare.

Version 3.7

Karim Kalti

Types de base et dclaration des variables Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple 1 :
#include <stdio.h> void main() { int i=5; { int i=1, j=3; printf("i du bloc :%d\n", i); printf("j du bloc :%d\n", j); } printf("i du main : %d\n", i); }

Rsultat :
i du bloc : 1 j du bloc : 3 i du main : 5

Exemple 2 :
#include <stdio.h> void main() { int i=5; { int i=1, j=3; printf("i du bloc :%d\n", i); } printf("j du bloc :%d\n", j); // ERROR car j n'est pas visible ce niveau printf("i du main : %d\n", i); }

Les variables i et j du bloc ne sont vues qu' l'intrieur du bloc et perdent par consquent leur signification la sortie de ce dernier. Elles sont dites locales au bloc. La variable i du bloc cache l'intrieur de ce dernier le i du main. Variable globale Des variables peuvent tre dclares en dehors de tous les blocs et fonctions. Elles sont dites globales et peuvent tre utiliss dans tout le programme. Exemple :
#include <stdio.h> int i; void main() { printf("i vaut : %d\n", i); // i vaut 0 }

La variable i dans ce cas de figure est considre comme une variable globale puisqu'elle ne fait partie d'aucun bloc. Elle est automatiquement initialise 0.

Les constantes
Une constante est une donne qui ne peut pas tre modifie. Cette donne peut tre un entier, un rel, un caractre ou une chane de caractres. C distingue les constantes entires, les constantes virgules flottantes, les constantes de type caractre et les constantes de type chane. C++ introduit en plus les constantes boolennes. Les constantes boolennes Elles peuvent avoir deux valeurs true ou false.

Version 3.7

Karim Kalti

Types de base et dclaration des variables Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les constantes entires Elles peuvent tre dfinies dans les diffrentes bases du codage. Format dcimal : (0,1,2, , 9). Format octal : (1,2,7) : Ce format se distingue par l'adjonction du chiffre 0 devant la valeur ou par le prfixe \. Format hexadcimal : (0,1,2, ,9,A,BC,D,E,F) : ce format est caractris par l'ajout du prfixe 0x ou 0X ou \x devant la valeur. De plus quand il s'agit : D'une constante reprsentant une valeur ngative, il faut faire prcder la valeur de l'oprateur de signe -. D'une valeur affecte un entier long, il faut adjoindre la fin de la valeur la lettre l ou L. Par rapport au C, le C++ introduit les suffixes u et U pour spcifier qu'une constante est un entier non sign. Exemples : Constante 12 12L 12U 12LU 014 0xC 0xCLU Les constantes flottantes Deux notations sont possibles pour les constantes flottantes : Notation littrale avec virgule flottante seule. Cette notation doit comporter obligatoirement un point (qui correspond la virgule). La partie entire ou la partie dcimale peut tre omise (seule une des deux mais pas les deux en mme temps). Exemples :
12.75, -0.58, -.58 , 4. , 0.27, 4.00

Signification Constante entire dcimale Constante entire de type long Constante entire non signe Constante entire non signe de type long Constante entire octale Constante entire hexadcimale (12 dans la base dcimale) Constante entire hexadcimale (12) affecte un entier long non sign

La notation scientifique avec virgule flottante et exposant not e ou E reprsentant l'exposant en base 10. (le point peut tre absent dans cette notation). Exemples : 5.69E4

5.69E+4

56.9e3

48e13

48.e13

48.E13

57.3e-5

Remarque : Par dfaut, toutes les constantes flottantes sont codes par le compilateur comme tant de type double. Il est toutefois possible de leur imposer d'tre de type : float : en les faisant suivre de la lettre f ou F. long double : en les faisant suivre de la lettre l ou L. Exemple :
12.34 12.34f 12.34L

Nombre flottant de double prcision (double) Nombre flottant de simple prcision (float) Nombre flottant de trs grande prcision (long double).

Version 3.7

Karim Kalti

Types de base et dclaration des variables Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les constantes caractres Les constantes de type caractres peuvent tre reprsentes de deux manires : Pour les caractres imprimables : par la valeur du caractre place entre deux apostrophes [simples quottes]. Pour les caractres imprimables ou non (d'une manire gnrale) : une constante caractre peut tre dfinie par son code ASCII octal ou hexadcimal prcd d'un antislash le tout plac entre deux quottes. Il est noter que la reprsentation l'aide du code hexadcimal doit tre prfix en plus d'un x. Exemple : Le caractre (a) peut tre reprsent de plusieurs manires :
Base dcimale : 'a' Base octale : '\101' Base hexadcimale : '\x41'

Les caractres spciaux Par ailleurs, certains caractres non imprimables possdent une reprsentation spciale utilisant l'antislash. Le tableau suivant prsente ces caractres et leur signification. Notation \n \t \v \b \r \f \a \' \" \? \\ Code ASCII 0A 09 0B 08 0D 0C 07 2C 22 3F 34 Signification Gnre une nouvelle ligne (saut de ligne). Tabulation horizontale Tabulation verticale Retour d'un caractre en arrire (backspace) Retour chariot Saut de page Bip sonore ' " ? \

Les constantes chanes de caractres Une chane est une squence de plusieurs caractres. Une constante de ce type peut tre dfinie en dlimitant cette squence par des guillemets.
Exemple : "Ceci est une constante chane de caractres"

Les constantes symboliques


Il est possible d'associer une valeur particulire un nom identificateur qui permet de faire rfrence cette valeur sous forme symbolique en utilisant le mot-cl const. La dclaration d'une constante symbolique se fait comme suit : const Type NomConstante = Valeur; Exemple :
const int moyenne = 10;

Remarque : En C++, une constante symbolique est value au moment de la compilation. Ce n'est pas le cas pour les constantes symboliques en langage C. De ce fait, les constantes symboliques peuvent tre utilises en C++ dans la dclaration des tableaux. Exemple : Le code suivant est correct en C++ alors qu'il ne l'est pas en C :
const int MAX = 1000; char tab[MAX]; /

En langage C il faut plutt utiliser le code suivant :


#define MAX 1000 char tab[MAX]

Version 3.7

Karim Kalti

Les oprateurs Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les oprateurs
Les oprateurs arithmtiques
Oprateur + * / % Signification Addition Soustraction Multiplication Division entire ou relle. Reste de la division entire

Remarques : Les oprateurs "+, - , *, / " peuvent effectuer des oprations entre entiers ou entre rels. L'oprateur "/" effectue en fonction du type des oprandes soit une division entire soit une division relle. L'oprateur " % " ne s'applique pas aux rels (aussi bien float que double). Il n'est dfini que pour des oprandes de type entier. Exemples :
7/3=2 7/-3= -2 7/-3= 2 7%3=1 7%-3=1 -7%-3= -1 car (7=-3*-2+1) car (-7= -3*2-1)

Dpassement de capacit des entiers : Soit l'instruction suivante : short n = 10000*100; La valeur place dans n se situe en dehors de la capacit du type short. Le contenu de n sera alors erron mais il n'y aura pas d'indication d'erreur ni au moment de la compilation ni au moment de l'excution. Dpassement de capacit des rels : Pour les rels ou pour la division par zro, le dpassement est immdiatement signal par le message "floating point error : overflow". Combinaison d'oprandes de types diffrents Une opration arithmtique peut faire intervenir des oprandes de types diffrents. Dans ce cas le compilateur effectue des conversions temporaires et automatiques de types. Ces conversions obissent aux rgles suivantes : Rgles
R0 R1 R2 R3 R4 R5 R6 long double double float unsigned long long unsigned int char int

Si un des deux types est

alors l'autre est converti en


long double double float unsigned long long unsigned int

Ces rgles possdent une priorit descendante. (R0 est prioritaire par rapport R1, R1 est prioritaire par rapport R2 et ainsi de suite).

Forage de type (casting)


Il est possible d'imposer d'une manire explicite une conversion de type (forage ou casting) en prfixant l'lment convertir du type de destination plac entre parenthses. La syntaxe du forage de type se prsente comme suit : (TypeDestination) var

Version 3.7

Karim Kalti

Les oprateurs Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
int i=5,j=2; double x; x=i/j; x=(double)i/j; // x= 2.5

Les oprateurs d'affectation

Oprateur
= += -= *= /= i=5 ; permet d'affecter i+=5 i=i+5 i-=5 i=i-5 i*=5 i=i*5 i/=5 i=i/5

Signification le contenu du membre de droite au membre de gauche

Les oprateurs d'incrmentation et de dcrmentation


Oprateur
++ --

Signification oprateur d'incrmentation i-- i=i-1 oprateur de dcrmentation


i++ i=i+1

Les oprateurs logiques


Ce sont les oprateurs qui effectuent des comparaisons de valeurs ou des oprations logiques usuelles. Ils renvoient une valeur boolenne (true, false). Oprateur
< > <= >= == && || !

Description Le test infrieur entre deux expressions arithmtiques (entires ou flottantes). Cet oprateur renvoie true si la valeur de l'oprande gauche est infrieur celle de l'oprande de droite et false si non. Le test suprieur. Cet oprateur renvoie true si la valeur de l'oprande gauche est suprieur celle de l'oprande de droite et false si non. Le test infrieur ou gal. Cet oprateur renvoie true si la valeur de l'oprande gauche est infrieur ou gale celle de l'oprande de droite et false si non. Le test suprieur ou gal. Cet oprateur renvoie true si la valeur de l'oprande gauche est suprieur ou gale celle de l'oprande de droite et false si non. Le test d'galit. Renvoie true si l'oprande de gauche est gale l'oprande de droite et false sinon. ET logique : renvoie true si les deux oprandes sont valus true. OU logique : renvoie true si au moins un des deux oprandes est valu true. NON logique : renvoie true si l'oprande est valu false et false dans le cas contraire.

Remarque : Les valeurs (entires, flottantes, caractres, ) non nulles sont values true. Les valeurs (entires, flottantes, caractres, ) nulles sont values false. Exemple :
int i=0, j=5; bool b1=i<j; // b1 vaut true bool b2= i==j && i<j // b2 vaut false

Version 3.7

10

Karim Kalti

Les oprateurs Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Priorit des oprateurs


La priorit des oprateurs permet de dfinir l'ordre dans lequel sont valus les oprateurs dans une expression lorsque cette dernire en fait intervenir plusieurs. Le tableau suivant donne la priorit des oprateurs les plus utiliss : Oprateur
:: oprateur de rsolution de porte () [] -> (casting) sizeof & * ! ++ -- new delete * / % + < <= > >= == != && || ? : = += -= *= /= %=

Remarque : Les oprateurs prsents dans le tableau ci-dessus possdent une priorit descendante : les oprateurs de la premire ligne sont prioritaires par rapport ceux de la deuxime ligne et ainsi de suite. Les oprateurs d'une mme ligne possdent la mme priorit. Si une expression fait intervenir en mme temps plusieurs oprateurs qui ont la mme priorit alors l'oprateur situ le plus gauche dans l'expression sera le premier valu. Exemple : Expression
8/4*6 8*6/4 28/(3*4) 3/4*6 3*6/4 (float)2/4 (float)(2/4) -3+4%5/2

Oprations

rsultat

Oprateur conditionnel
Cet oprateur permet de tester une expression et de retourner une valeur suivant le rsultat du test. Sa syntaxe est donne comme suit :
Expression ? Valeur renvoye si Expression vaut vrai : Valeur renvoye sinon

Remarque : Les valeurs renvoyes doivent tre du mme type. Exemple 1 :


int i=5,j=6,k=18,m; m=i<j ? k- 2*i : i+j;

Exemple 2 : Cet exemple montre l'utilisation de l'oprateur conditionnel dans le calcul du maximum de deux entiers :
int FMAX(int a, int b) { return (a>b ? a : b); }

Version 3.7

11

Karim Kalti

Les oprateurs Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Oprateur sizeof( )
L'oprateur sizeof renvoie la taille en octets d'un type ou d'une variable. Le type ou la variable sont passs en argument. Exemple :
unsigned i; float j; i = sizeof(short); // i vaut 2 i = sizeof(j); // i vaut 4 i = sizeof(long[12]); // i vaut 48

Version 3.7

12

Karim Kalti

Les entres/sorties en C++ Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les entres / sorties en C++


Les entres/sorties dsignent les oprations de lecture et d'criture de donnes. Les oprations de lecture se font partir du fichier standard d'entre (stdin en C). Par dfaut ce fichier est associ au clavier mais il peut tre redirig vers d'autres priphriques ou d'autres fichiers sur le disque. Les oprations d'criture se font dans le fichier standard de sortie (stdout en C). Par dfaut ce fichier est associ l'cran mais il peut tre redirig vers d'autres priphriques tels que l'imprimante par exemple. Le C++ offre deux objets appels flux (streams) pour la gestion des oprations d'E/S : o L'objet cout de type ostream associ la sortie standard (cran). o L'objet cin de type istream associ l'entre standard (clavier). Ces deux objets sont dfinis dans la bibliothque iostream.h.

Les oprations de sortie


Les oprations de sortie des donnes sont effectues l'aide de l'objet cout auquel est associ un oprateur de redirection not (<<) qui indique le sens de transfert des donnes. La syntaxe de l'utilisation de l'objet cout avec l'oprateur (<<) est la suivante : cout<<Donne; Le paramtre Donne dsigne la donne afficher. Il peut tre une variable, une constante ou une expression. Le paramtre Donne peut avoir comme type un des types prdfinis du C++ (bool, char, int, short, long, float, double, char*, ). L'oprateur (<<) indique que le sens de transfert des informations se fait de Donne vers le flux de sortie cout. Il est galement appel oprateur d'insertion. L'oprateur (<<) peut tre surcharg afin de permettre l'affichage de donnes ayant des types personnaliss (classes, structures, ). Contrairement aux fonctions de sortie du C, l'objet cout n'utilise aucun caractre de formatage. La reconnaissance du type des informations afficher est automatique. Il est possible d'enchaner plusieurs oprateurs de redirection de la manire suivante : cout<<Donne1<<Donne2<<Donne3; Exemple 1 : Affichage d'un texte
#include <iostream.h> cout<<"Ceci est un message";

Exemple 2 : Affichage d'un nombre


int i=12; float j = 2.5f; cout<<"i="<<i<<'\n'; cout<<"j="<<j<<'\n';

Exemple 3 : Affichage d'un caractre


char c='a'; cout<<"le caractre c contient : "<<c<<'\n';

Exemple 4 : Affichage d'une chane de caractres


char T[10]="Bonjour"; cout<<T;

Exemple 5 : Affichage du rsultat d'une expression


int i =5; int j=7; cout<<"La somme de i et de j est : "<<i+j;

Version 3.7

13

Karim Kalti

Les entres/sorties en C++ Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Manipulateurs d'affichage en C++ Il existe un ensemble de manipulateurs en C++ qui offrent diffrentes possibilits concernant le formatage de l'affichage autres que celles proposes par dfaut.

Manipulateur de saut de ligne : Le manipulateur endl (end line) permet d'insrer un saut de ligne dans le flux de texte envoy l'cran.
cout<<"Bonjour"<<endl<<"Au revoir"<<endl; // Rsultat de l'excution Bonjour Au revoir

Manipulateurs d'affichage des entiers : Il est possible de modifier la base dans laquelle est affich un entier l'aide des manipulateurs suivants : Manipulateur
dec hex oct

Signification Affichage dans la base dcimale pour les entiers (affichage par dfaut). Affichage dans la base hexadcimale pour les entiers. Affichage dans la base octale pour les entiers.

int i=75; cout<<"Affichage par dfaut : "<<i<<endl; cout<<"Affichage hexa : "<<hex<<i<<endl; cout<<"Sans manipulateur : "<<i<<endl; cout<<"Affichage dcimal :"<<dec<<i<<endl; cout<<"Sans manipulateur : "<<i<<endl;

// Rsultat de l'excution Affichage par dfaut : 75 Affichage hexa : 4b Sans manipulateur : 4b Affichage dcimal : 75 Sans manipulateur : 75

L'exemple prcdent montre qu'un manipulateur de conversion de la base d'affichage d'un entier reste actif depuis l'endroit de son application et jusqu' l'application d'un autre manipulateur.

Manipulateur du gabarit d'affichage Manipulateur


setw(nombre)

setfill(caractre)

Signification Dfinit le gabarit de la variable afficher avec une justification droite par dfaut. Si la valeur afficher est plus importante que le gabarit, alors ce dernier ne sera pas respect et la variable sera affiche de faon conventionnelle. Le manipulateur setw doit tre utilis pour chacune des informations afficher. Dfinit le caractre de remplissage lorsqu'on utilise un affichage avec la gestion de gabarit. Par dfaut, le caractre de remplissage est l'espace.

Les manipulateurs setw et setfill sont dfinis dans la bibliothque iomanip.h.


#include <iostream.h> #include <iomanip.h> int i=55; cout<<setw(4)<<i<<endl; cout<<setfill('0')<<setw(4)<<i<<endl;

// Rsultat de l'excution 55 0055

Version 3.7

14

Karim Kalti

Les entres/sorties en C++ Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les oprations d'entre


Les oprations d'entre sont effectues l'aide de l'objet cin auquel est associ l'oprateur de redirection (>>). La syntaxe de l'utilisation de l'objet cin avec l'oprateur (>>) est la suivante : cin>>var; Le paramtre var dsigne la variable qui va stocker l'information saisie. La variable var peut avoir comme type un des types prdfinis du C++ (bool, char, int, short, long, float, double, char*, ). L'oprateur (>>) indique que le sens de transfert des informations se fait du flux d'entre cin vers la variable var. Cet oprateur est galement appel oprateur d'extraction. Tout comme cout, cin n'utilise aucun caractre de formatage. La reconnaissance des types des donnes saisir est automatique. Exemple :
#inlcude <iostream.h> void main( ) { int i; float f; char c; cout<<"donnez un entier"<<endl; cin>>i; cout<<" donnez un rel"<<endl; cin>>f; cout<<" donnez un caractre"<<endl; cin>>c; }

Il est possible d'enchaner plusieurs oprateurs de redirection avec cin afin de faire la saisie de plusieurs variables en mme temps. Dans ce cas les valeurs faire entrer doivent tre spares au moment de la saisie par un blanc (tabulation, espace ou retour chariot). Exemple :
int v1,v2; cout<<"Veuillez saisir deux entiers"; cin>>v1>>v2;

Version 3.7

15

Karim Kalti

Les structures de contrle Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les structures de contrle


Introduction
Il existe trois catgories de structures de contrle : Les instructions de branchement conditionnel (alternative). Les instructions de branchement inconditionnel. Les instructions rptitives.

Les instructions de branchement conditionnel


Trois types de branchement conditionnel peuvent tre distingus : le branchement conditionnel simple, le branchement conditionnel imbriqu et le branchement conditionnel multiple. Le branchement conditionnel simple Ce branchement est ralis par le mot rserv if de la manire suivante :

if(Condition est vraie) { Bloc d'instructions excuter }

Si la condition teste est vraie alors le programme excute le bloc d'instructions. Si cette condition est fausse alors le programme saute ce bloc et continue son excution normalement. Il est possible de spcifier un autre bloc d'instructions excuter dans le cas o la condition est fausse par l'adjonction de l'instruction else au branchement if. Cette structure de contrle devient alors : if(Condition est vraie) { Bloc 1 d'instructions excuter } else { Bloc 2 d'instructions excuter }

Remarque : Si le bloc (bloc 1 ou bloc 2) se rduit une seule instruction alors les accolades deviennent facultatives. Application 1 : crire un programme qui permet partir de la saisie d'un nombre d'afficher un message pour indiquer la possibilit ou non de l'utiliser comme diviseur.
#include<iostream.h> void main() { int i; cout<<"Donner un entier : "; cin>>i; if(i==0) cout<<"Impossible d'utiliser cet entier comme diviseur"<<endl; else cout<<"Il est possible d'utiliser cet entier comme diviseur"<<endl; }

Version 3.7

16

Karim Kalti

Les structures de contrle Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Application 2 : crire un programme qui permet de dterminer si un nombre entier saisi au clavier est pair ou impair.
#include<iostream.h> void main() { int i; cout<<"Donner un entier : "; cin>>i; if(i%2) cout<<"L'entier saisi est impair"<<endl; else cout<<"L'entier saisi est pair"<<endl; }

Les branchements conditionnels imbriqus En pratique, il est souvent utile de pouvoir enchaner un ensemble de tests pour examiner plusieurs valeurs possibles. Ceci peut tre ralis en imbriquant les if et les else de la manire suivante :

if(test_1 est vrai) { Bloc_1 } else if(test_2 est vrai) { Bloc_2 } else if(test_3 est vrai) { Bloc_3 } else if(test_n est vrai) { Bloc_n } else { Bloc_n+1 }

Ce code est quivalent celui-ci:

if(test_1 est vrai) { Bloc_1 } else { if(test_2 est vrai) { Bloc_2 } else { if(test_3 est vrai) { Bloc_3 } else else{ if(test_n est vrai) { Bloc_n } else { Bloc_n+1 } } } }

Remarque : Lors de l'imbrication de plusieurs instructions de branchement conditionnel simple, un else se rapporte toujours au dernier if rencontr auquel aucun else n'a t encore attribu. Exercice crire un programme qui demande l'utilisateur son age et lui indique le niveau du cours qu'il doit suivre en se basant sur les critres suivants : Si l'age est entre 7 et 15 il lui affiche "vous avez besoin du cours du premier niveau". Si l'age est entre 16 et 20 il lui affiche "vous avez besoin du cours du deuxime niveau". Si l'age est entre 20 et 25 il lui affiche "vous avez besoin du cours du troisime niveau". Si l'ge est infrieur 7 il lui affiche " vous tes encore jeune". Si l'ge est suprieur 25 il lui affiche " vous tes trop g".

Version 3.7

17

Karim Kalti

Les structures de contrle Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Branchement conditionnel multiple Les branchements imbriqus utilisant le if et le else donnent un programme un aspect peu lisible, en plus des risques d'erreurs qu'ils peuvent engendrer surtout lors du placement des accolades. C'est pourquoi, lorsqu'il s'agit de traiter plusieurs alternatives on leur prfre la structure switch dfinie de la manire suivante : switch(variable ou expression) { case constante_1: Bloc 1 d'instructions break; case constante_2: Bloc 2 d'instructions break; case constante_n: Bloc n d'instructions break; default: bloc d'instructions par dfaut }

La structure de contrle switch compare gnralement la valeur d'une variable de type entier ou assimil (char, int, unsigned, long, ) un ensemble de constantes. Lorsque cette valeur correspond l'une des constantes alors le bloc d'instructions associ cette dernire est excut. Le bloc dfini par le mot-cl default est un bloc facultatif (non obligatoire) qui dsigne un ensemble d'instructions qui seront excuts par dfaut. Le mot-cl break permet une sortie immdiate de la structure swtich et vite alors au programme de tester toutes les autres alternatives aprs avoir excut un bloc i donn. Cette instruction n'est pas obligatoire. Exercice: En utilisant la structure de contrle switch, donner un programme qui demande l'utilisateur de saisir un nombre. Si ce nombre est gale 0, il lui affiche "vous avez saisi une valeur nulle". S'il est gale un, il lui affiche "vous avez saisi un". Si ce nombre est diffrent de 1 et de 0 il lui affiche un message d'erreur "valeur incorrecte".
#include <stdio.h> void main() { int i; printf("Donner une valeur entire 0 ou 1:"); scanf("%d",&i); switch( i ) { case 0: printf("\n vous avez saisi une valeur nulle \n"); break; case 1: printf("\n vous avez saisi un "); break; default: printf("\n valeur incorrecte "); } }

Version 3.7

18

Karim Kalti

Les structures de contrle Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Rsultat de l'excution : Si le nombre saisi est 6: le message de sortie sera "valeur incorrecte". Si le nombre saisi est 1: le message de sortie sera "vous avez saisi un". Si on limine le break du case : 1. o Si le nombre saisi est 6: le message de sortie sera "valeur incorrecte". o Si le nombre saisi est 1: le message de sortie sera "vous avez saisi un " suivi du message "valeur incorrecte ". Si le nombre saisi est 0 le message de sortie sera "vous avez saisi une valeur nulle". Si on limine le break du case 0 et on prserve celui de case 1: o Si le nombre saisi est 6, le message de sortie sera "valeur incorrecte". o Si le nombre saisi est 1 le message de sortie sera "vous avez saisi un ". o Si le nombre saisi est 0 le message de sortie sera "vous avez saisi une valeur nulle", "vous avez saisi un". Si on limine le break du case 0 et celui de case 1: o Si le nombre saisi est 6, le message de sortie sera " valeur incorrecte ". o Si le nombre saisi est 1 le message de sortie sera " vous avez saisi un ". o Si le nombre saisi est 0 le message de sortie sera "vous avez saisi une valeur nulle", "vous avez saisi un", "valeur incorrecte". o Si on supprime les instructions break, le programme traitera tous les cas qui suivent la premire correspondance entre la valeur et une des constantes de test. Pour l'exemple prcdent, si on supprime les break et on saisit 0, le programme affichera les messages suivants : "vous avez saisi une valeur nulle", "vous avez saisi un", "valeur incorrecte".

Remarque : L'instruction switch possde l'inconvnient de limiter les comparaisons des valeurs constantes et ne peut pas traiter des intervalles.

Les instructions rptitives


Le langage C++ dispose de trois structures pour le contrle rptitive: while, do while et for. Thoriquement, ces structures sont interchangeables, c.--d. il serait possible de programmer toutes sortes de boucles conditionnelles en n'utilisant qu'une seule des trois structures. La boucle while La syntaxe de cette boucle est :

while(Test est vrai) { Bloc d'instructions excuter }

La boucle while excute un bloc d'instructions tant que le test qui lui est associ est vrai. Si ce bloc se rduit une seule instruction alors les accolades deviennent non obligatoires. Exercice : crire un programme qui affiche tous les multiples d'un entier de rfrence qui sont infrieurs une valeur maximale. L'entier de rfrence et la valeur maximale seront donns par l'utilisateur.

Version 3.7

19

Karim Kalti

Les structures de contrle Programmation oriente objet (C++) _________________________________________________________________________________________________________________ #include <stdio.h> void main( ) { unsigned long ValeurMax,NbRef; printf("Donnez l'entier de rfrence : "); scanf("%lu", &NbRef); printf("Donnez la valeur maximale : "); scanf("%lu", &ValeurMax); while(ValeurMax >= NbRef) { if(ValeurMax % NbRef == 0) printf("%d\t", ValeurMax); ValeurMax--; } printf("C'est fini\n"); }

La boucle do while Cette structure est similaire la boucle while mais avec une petite diffrence qui rside dans le fait qu'elle assure l'excution au moins une fois des instructions du bloc. do { Bloc d'instructions excuter }while(Test est vrai);

En pratique, l'utilisation de la structure do - while n'est pas aussi frquente que while; mais dans certains cas, elle fournit une solution plus lgante. Une application typique de do - while est la saisie contrle de donnes. Exemple 1 : Donner un programme qui permet de contrler la saisie d'un entier compris entre 1 et 10.
int N; do { printf("Introduisez un nombre entre 1 et 10 :"); scanf("%d", &N); } while (N<1 || N>10);

Exemple 2 : On veut crire un programme qui demande l'utilisateur s'il veut continuer ou arrter une tche donne. Si l'utilisateur tape le caractre o alors le programme quitte et termine la tche. S'il tape le caractre n alors le programme continue l'excution de la tche. S'il tape tout autre caractre le programme repose la mme question l'utilisateur.
#include <stdio.h> void main( ) { char c; do { printf("voulez vous terminer et quitter ?"); scanf("%c",&c); fflush(stdin); }while (c!='o' && c!='n'); if(c=='o') printf("la tche se termine tout de suite\n"); else printf("OK on continue\n"); }

Version 3.7

20

Karim Kalti

Les structures de contrle Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La boucle for La structure de la boucle for se prsente comme suit : for (<expr1>;<expr2>;<expr3>) { <bloc d'instructions> }

<expr1> est value une seule fois et ce avant la premire itration de la boucle. Elle est utilise pour initialiser les donnes de la boucle. <expr2> est value avant chaque itration de la boucle. Elle reprsente gnralement une condition qui est utilise pour dcider si la boucle fera une itration supplmentaire ou non. <expr3> est value la fin de chaque itration de la boucle. Elle est utilise pour rinitialiser les donnes de la boucle. Remarque : La boucle for constitue une alternative syntaxique la boucle while dans laquelle tous les lments relatifs au contrle de la boucle sont regroups ensemble d'une manire lisible dans l'entte. En effet, les trois expressions de contrle prsentes ci-dessus peuvent tre places dans la boucle while de la manire suivante : <expr1>; while ( <expr2> ) { <bloc d'instructions> <expr3>; } Le plus souvent, la boucle for est utilise comme boucle de comptage : for(initialisation ; condition de continuit ; compteur) { <bloc d'instructions> } Exercice : crire un programme qui affiche les nombres entiers de 0 jusqu' 100.
#include <stdio.h> void main( ) { int i; for(i=0;i<101;i++) printf("%d\t",i); }

Remarques : Chacune des trois instructions de la boucle for est facultative. Ainsi la boucle de l'exercice prcdent peut tre crite de la manire suivante:
i=0; for( ;i<101;i++) printf("%d\t",i);

ou galement:

i=0; for( ;i<101; ) { printf("%d\t",i); i++; }

Lorsque <expr2> est absente, alors la condition de continuation sera considre comme tant toujours vraie et la boucle for sera une boucle infinie.

Version 3.7

21

Karim Kalti

Les structures de contrle Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Le branchement inconditionnel
Le langage C++ dispose de trois instructions pour le branchement inconditionnel : break, continue et goto. L'instruction break En plus de son utilisation en association avec switch, l'instruction break peut tre utilise dans n'importe quelle boucle. Cette instruction provoque alors une interruption et une sortie immdiate de la boucle. L'excution du programme continue alors au niveau de l'instruction situe tout juste aprs la boucle. Exemple:
void main() { int i; for(i=1;i<=10;i++) { printf("Dbut de l'itration %d\n",i); printf("Bonjour\n"); if(i==3) break; printf("Fin de l'itration %d\n",i); } printf("Aprs la boucle"); }

Excution:
Dbut de l'itration 1 Bonjour Fin de l'itration 1 Dbut de l'itration 2 Bonjour Fin de l'itration 2 Dbut de l'itration 3 Bonjour Aprs la boucle

Remarque : En cas de boucles imbriques, l'instruction break fait sortir seulement de la boucle la plus interne. L'instruction continue L'instruction continue intervient galement pour interrompre l'excution des boucles. Mais contrairement break, elle ne provoque pas la sortie complte de la boucle mais plutt l'interruption de l'itration courante et le passage l'itration suivante de cette boucle. Exemple 1:
#include <stdio.h> void main( ) { int i; for(i=1;i<=4;i++) { printf("Dbut itration %d\n",i); if(i < 3) continue; printf("bonjour\n"); } }

Excution:
Dbut itration Dbut itration Dbut itration bonjour Dbut itration bonjour 1 2 3 4

Exemple 2 : Le programme ci-dessous demande l'utilisateur de saisir un entier positif et lui affiche son carr. Si l'entier est ngatif alors le programme redemande l'utilisateur de saisir un autre entier. L'excution s'arrte lorsque'un entier nul est saisi.
#include <stdio.h> void main( ) {int n; do { printf("\n donnez un entier n > 0: "); scanf("%d",&n); if(n<0) { printf("\n donnez un n positif\n"); continue ; } printf(" le carr de n est : %d\n",n*n); }while(n) ; }

Remarque : En cas de boucles imbriques l'instruction continue ne concerne que la boucle la plus interne.

Version 3.7

22

Karim Kalti

Les structures de contrle Programmation oriente objet (C++) _________________________________________________________________________________________________________________

L'instruction goto L'instruction goto provoque un branchement immdiat du programme un endroit prdfini. Les boucles, les tests sont interrompues. L'endroit o reprend le programme est dfini par une tiquette suivie du symbole : La syntaxe globale de cette instruction est : goto NomEtiquette; NomEtiquette: Exemple:
#include <stdio.h> void main( ) { int i; for(i=1;i<=10;i++) { printf("Dbut de l'itration %d\n",i); printf("Bonjour\n"); if(i==3) goto Sortie; printf("Fin de l'itration %d\n",i); } Sortie: printf("Aprs la boucle"); }

Excution:
Dbut de l'itration 1 Bonjour Fin de l'itration 1 Dbut de l'itration 2 Bonjour Fin de l'itration 2 Dbut de l'itration 3 Bonjour Aprs la boucle

Version 3.7

23

Karim Kalti

Les tableaux Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les tableaux
Introduction
Un tableau est un type particulier de variables qui permet d'associer sous le mme nom, un ensemble fini de valeurs de mme nature (type). Tous les types de base et tous les types personaliss construits en C/C++ peuvent servir dfinir des tableaux.

Dclaration d'un tableau


La syntaxe permettant la dclaration d'un tableau est la suivante : Type NomTableau[Taille];

O : Type dsigne le type des lments du tableau, NomTableau dsigne son nom, Taille dsigne le nombre de ses lments. Exemples: Dclaration:
int TableauEntiers[15]; double TableauFlottants[40];

La taille maximale d'un tableau dpend de la configuration de l'environnement de travail (compilateur). La taille d'un tableau doit tre une constante ou une expression constante, cependant elle ne peut pas tre une variable.
#define N 10 int M =10; int Tab1[10]; // Dclaration correcte int Tab2[N]; // Dclaration correcte int Tab3[M]; // Dclaration incorrecte float Tab4[2*N+1]; // Dclaration correcte

La dimension peut galement tre une constante symbolique (Ceci est possible en C++ mais pas en C).
const int N=10; int Tab[N]; // Dclaration correcte en C++ mais incorrecte en C

Accs aux lments d'un tableau


L'accs la valeur d'un lment du tableau se fait travers son numro d'ordre ou indice de la manire suivante: NomTableau[Indice] Un tableau est toujours numrot partie de 0. Ainsi dans un tableau de N lments, le premier a pour indice 0 et le dernier a pour indice N-1. La rfrence d'un lment du tableau qui n'existe pas (par exemple T[N+2] pour un tableau de taille N) n'est signale comme erreur ni la compilation ni l'excution. En effet, le programme fait comme si cet lment existait. La seule manifestation de cette erreur de rfrence rside dans l'obtention de rsultats imprvisibles.

Version 3.7

24

Karim Kalti

Les tableaux Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Tableaux plusieurs dimensions


En plus des tableaux une dimension (vecteur), le C++ autorise la dclaration des tableaux plusieurs dimensions. Cette dclaration se fait pour un tableau de N dimensions de la manire suivante : Type NomTableau[TailleD_1][TailleD_2][TailleD_N]; O TailleD_i dsigne la taille de la ime dimension. A ce titre, une matrice est dclare d'une manire gnrale comme suit : Type NomTableau[NombreLigne][NombreColonne]; Exemples:
int T[2][3];

L'arrangement en mmoire des cases est comme suit :


T[0][0] T[0][1] T[0][2] T[1][0] T[1][1] T[1][2]

float TabF[3][6]; char TabCh[4][5][6];

Initialisation des tableaux


Tableaux une dimension Type NomTableau[N] = {Valeur1, Valeur2,, Valeur_N-1}; Exemple 1:
int Tab[3] = {1,7,4};

Le nombre des valeurs d'initialisation ne peut pas dpasser la taille du tableau mais il peut tre infrieur. Dans ce cas les lments non explicitement initialiss seront automatiquement initialiss 0. Exemple 2:
int Tableau[5] = { , ,5, ,3};

Lors de l'initialisation d'un tableau, la spcification de sa taille est facultative. Dans le cas o elle est absente, la taille est automatiquement dtermine d'aprs le nombre des valeurs d'initialisation. Exemple 3 :
int Tableau[ ] = {1,7,4} // La taille est du tableau est gale 3

Tableaux deux dimensions Exemples :


int Tab[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; int Tab[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12}};

Il est galement possible d'omettre quelques valeurs qui seront automatiquement initialises 0.
int Tab[3][4] = { {1,2, ,4}, , {9, ,11,12}};

Version 3.7

25

Karim Kalti

Les tableaux Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Affectation des valeurs aux lments d'un tableau


Exemple :
int Tab[10]; Tab[0]=500; Tab[5]=100; Tab[9]=50; char T[5][3]; T[0][2] = 'a'; // affecte au premier lment 500 // affecte au sixime lment 100 // affecte au dernier lment 50

Quelques rgles d'criture


Les lments d'un tableau peuvent tre incrments ou dcrments:
Tab[4] = 5; Tab[4]++ // tab[4] vaut 6.

Les indices peuvent tre des expressions arithmtiques: T[2*i-1] ou Tab[i-3][j+k] Il n'est pas possible d'affecter d'une manire globale un tableau un autre :
int T1[5]; int T2[5]; T1 = T2; // instruction incorrecte.

Exercice : Donner un programme qui fait la copie des lments d'un tableau d'entiers initialis, dans un deuxime tableau de mme taille.
#include<stdio.h> void main() { int i; int T1[3] = {5,6,23}; int T2[3]; for(i=0;i<3;i++) T2[i]=T1[i]; }

Saisie et affichage des lments d'un tableau


La saisie et l'affichage d'un lment d'un tableau se fait de la mme manire que pour n'importe quelle variable possdant le mme type que celui du tableau. Exercice : Donnez un programme qui fait la saisie des lments d'un tableau de caractres de taille 5 et qui les affiche ensuite.
#include<stdio.h> void main() { int i; char T[5]; /* Saisie */ for(i=0;i<5;i++) { printf("\n Donner le caractre numro %d: ", i+1); scanf("%c", &T[i]); } /* Affichage */ for(i=0;i<5;i++) printf("%c \t", T[i]); }

Version 3.7

26

Karim Kalti

Les tableaux Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exercice : crire un programme qui fait la saisie des lments d'un tableau d'entiers deux dimensions 3x4 et qui les affiches sur 3 lignes et 4 colonnes.
#include<stdio.h> void main() { int i,j; int T1[3][4], T2[3][4]; for(i=0;i<3;i++) for(j=0;j<4;j++) scanf("%d",&tab[i][j]); /* Affichage */ for(i=0;i<3;i++) { for(j=0;j<4;j++) printf("%d\t", tab[i][j]); printf("\n"); } }

Version 3.7

27

Karim Kalti

Les pointeurs et les rfrences Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les pointeurs et les rfrences


1 - Introduction
Un pointeur est une donne (constante ou variable) qui reprsente l'adresse d'une variable. Pour que l'adresse stocke dans un pointeur soit exploitable, il faut connatre le type de l'information qui se situe au niveau de cette adresse afin de pouvoir l'interprter convenablement. C'est pour cette raison qu'un pointeur doit tre toujours associ un type donn. Les pointeurs, tout comme les tableaux, les rfrences et les structures sont considrs comme des outils de construction de types tendus (types construits sur la base d'autres types). Un pointeur est dit qu'il pointe (ou renvoie) vers la variable dont il stocke l'adresse.

2 - Dclaration
Formellement la syntaxe de dclaration d'un pointeur est la suivante : Type * NomPointeur ; NomPointeur dsigne le nom d'une variable de type pointeur. Exemples :
char *pc; // dfinit un pointeur vers une donne de type caractre. int * pi; // dfinit un pointeur vers une donne de type int double * pdr; // dfinit un pointeur vers une donne de type rel double unsigned long * pli; // dfinit un pointeur vers une donne de type unsigned long

Remarque : Les pointeurs occupent tous la mme taille en mmoire indpendamment de la taille du type de l'objet point. Cela signifie par exemple qu'une variable de type pointeur sur un long double possde la mme taille en mmoire qu'une variable de type pointeur sur un caractre. Dclaration multiple
int *p1, *p2; // p1 et p2 sont deux pointeurs sur des entiers. int *p1, p2; // p1 est un pointeur sur un entier alors que p2 est une variable entire. int p1, *p2 // p1 est une variable entire alors que p2 est un pointeur sur un entier.

3 - Initialisation des pointeurs


Comme pour les autres variables, il est possible d'initialiser les variables de type pointeur. La valeur initiale est dans ce cas, l'adresse d'une donne possdant comme type, le type vers lequel pointe le pointeur en question. L'initialisation d'un pointeur ne peut s'effectuer qu'en lui affectant comme valeur l'adresse d'une variable dj existante. Exemple :
short s; short *ps1 = &s; short *ps2 = ps1; // short *ps2=&s;

Version 3.7

28

Karim Kalti

Les pointeurs et les rfrences Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Si l'on indique comme valeur d'initialisation pour un pointeur l'adresse d'une variable ayant un autre type de donnes que celui vers lequel le pointeur peut renvoyer, le compilateur affiche alors une erreur lors de la compilation. Exemple :
long l, *pl=&l; unsigned long *pul=&l; // erreur: pul n'est un pointeur vers un long

4 - Affectation des pointeurs et conversion


Une variable de type pointeur peut obtenir sa valeur non seulement par une initialisation, mais galement par une opration d'affectation. Exemples :
float f1; float *pf1,*pf2; pf1 =&f1; // pf1 contient l'adresse de f1. pf2=pf1; // pf2 contient l'adresse de f1.

Conversion Il n'existe aucune conversion implicite d'un type pointeur vers un autre type pointeur. Le seul moyen pour faire des conversions entre types pointeurs est le casting.
int i,*pi=&i; unsigned int *pui; pui = pi; // erreur pui=&i; // erreur pui=(unsigned int *)pi; // OK

5 - Accs indirect aux variables


Il est possible d'accder une variable travers un pointeur qui pointe vers cette variable. Il suffit d'utiliser pour cela l'oprateur * de la manire suivante :
Type Variable, *NomPointeur=&Variable;

Variable

* NomPointeur

L'oprateur * est appel dans ce cas oprateur d'indirection car il permet d'accder d'une manire indirecte au contenu de la variable ( travers le pointeur). Exemple 1 :
int i; int *pi; i =1234; pi= &i // pi contient l'adresse de i // *pi dsigne d'une manire indirecte le contenu de i cout<<i; cout<<*pi;

Exemple 2 : Cet exemple montre la saisie et l'affichage de la valeur d'une variable travers un pointeur :
int i,*pi=&i; scanf("%d",pi); printf("%d",*pi);

Ou galement en utilisant les fonctions d'E/S du C++


int i,*pi=&i; cin>>*pi; cout<<*pi;

Version 3.7

29

Karim Kalti

Les pointeurs et les rfrences Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple 3 :
#include<stdio.h> void main() { int i=125; int *pi=&i; printf("la valeur de i est: %d",i); printf("la valeur du *pi est %d",*pi); i++; printf("la valeur de i incrmente est: %d",i); printf("la valeur du *pi est %d",*pi); (*pi)++; printf("la valeur de i est: %d",i); printf("la valeur du *pi est %d",*pi); *pi=2*(*pi); printf("la valeur de i est: %d",i); printf("la valeur du *pi est %d",*pi); }

//125 //125 //126 //126 //127 //127 //254 //254

6 - Incrmentation de pointeurs et addition


L'incrmentation d'un pointeur donne l'adresse situe sizeof(TYPE) octets partir de la valeur courante du pointeur. TYPE tant le type de la variable pointe par le pointeur. L'addition entre un pointeur et un entier N donne l'adresse situe N*sizeof(TYPE) partir de la valeur courante du pointeur. Exemple 1:
int i; int *p=&i; p++; // incrmente p de 4 octets en dcimal.

Exemple 2:
int * i,* j; int k; i=&k; j=i+10; j++; /* Si i contient par exemple 1600 alors j vaut 1600+10*sizeof(int)=1640. j++ donne 1644 */

Remarque : Pour les pointeurs, l'addition n'est dfinie qu'entre un pointeur et un entier. Elle n'est pas dfinie entre deux pointeurs ni entre un pointeur et un nombre en virgule flottante.

7 - Dcrmentation de pointeurs et soustraction


Contrairement l'addition, on peut soustraire d'un pointeur non seulement un nombre entier mais aussi un autre pointeur de mme type. La soustraction d'un nombre entier un pointeur fonctionne d'une manire analogue l'addition. De mme la dcrmentation fonctionne d'une manire analogue l'incrmentation. La soustraction entre deux pointeurs (de mme type) fournit le nombre d'lments, du type en question, situs entre les deux adresses correspondantes (Ce n'est pas le nombre d'octets). Exemple:
int *a, *b; int tab[6]; a= &tab[5]; b=&tab[1]; cout<<a-b; //donne 4 cout<<b-a; //donne -4

Version 3.7

30

Karim Kalti

Les pointeurs et les rfrences Programmation oriente objet (C++) _________________________________________________________________________________________________________________

8 - Comparaison entre pointeurs


La comparaison de pointeurs est possible mais seulement entre pointeurs de mme type. Exemple 1 :
int *pa, *pb; if(pa = = pb) cout<<"les deux pointeurs pointent vers la mme donne"; else cout<<"les deux pointeurs pointent vers des donnes diffrentes";

Exemple 2 :
if(pa < pb) cout<<"le pointeur pb contient une adresse plus grande que le pointeur pa";

9 - Pointeur nul
Il existe en C/C++ un pointeur particulier qui pointe vers l'adresse 0, appel le pointeur nul. En C/C++ cette adresse ne contient aucune donne, par consquent le pointeur nul ne pourra en aucun cas pointer vers une donne. Chaque pointeur vers n'importe quel type peut tre compar au pointeur nul. Exemple :
int *pi; if(pi==0) cout<<"error";

La constante symbolique NULL peut tre utilise la place de la constante numrique 0. Elle est prdfinie dans le fichier stdio.h. NULL peut tre galement dfinie par la directive define comme suit : #define NULL 0 ou galement #define NULL 0L selon que les adresses sur la machine sont reprsentes par des int ou des long.

10 - Pointeurs gnriques
Il existe en C/C++ des pointeurs particuliers appels pointeurs gnriques qui peuvent pointer vers des donnes de nimporte quel type. Les pointeurs gnriques s'avrent utiles si par exemple l'adresse d'une zone mmoire doit tre enregistre, mais qu'il n'est pas encore tabli quel type de donnes cette zone doit accueillir ou si le programmeur veut se rserver la possibilit d'enregistrer (successivement) des types de donnes diffrents ou bien encore s'il souhaite pour d'autres raisons ne pas se fixer dans l'immdiat sur un quelconque type de donnes. Dclaration Les pointeurs gnriques sont dclars l'aide du type : void* La dclaration se fait comme suit : void* PointeurGenerique; Exemple:
int a; double d; void * vp; // pointeur gnrique vp=&a; // vp stocke l'adresse d'un int vp=&d; // vp stocke l'adresse d'un double

Version 3.7

31

Karim Kalti

Les pointeurs et les rfrences Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Restrictions dans l'utilisation des pointeurs gnriques Un pointeur gnrique ne peut pas servir pour faire des accs indirects aux contenus des variables. Exemple :
double d; void* pg=&d; Ainsi *vp=1.234

gnre une erreur. En effet, mme si vp stocke l'adresse d'un double, le type void* ne donne aucune infromation sur la taille mmoire qu'occupe la variable pointe. Pour rsoudre ce problme, il faut explicitement convertir vp comme suit :
*(double * )vp=1.234;

Par ailleurs, vp++ ou toute autre opration arithmtique sur les pointeurs gnriques gnre une erreur car le compilateur ne peut pas savoir de combien d'octets se dplacer. Affectation d'un pointeur gnrique un pointeur d'un autre type (type* void*) En C++, l'affectation du contenu d'un pointeur gnrique (void*) un pointeur (type*) doit obligatoirement passer par le casting. En C, le casting n'est pas obligatoire pour faire ce genre d'oprations mme s'il reste conseill. Exemple :
int i=5; int *pi1=&i, *pi2; void* vp; vp=pi1; // Ok C et C++ pi2=vp; // Ok en C erreur en C++ pi2=(int*)vp // Ok C et C++ cout<<*pi2; // opration possible car le type de pi2 est connu int* pi2++; // opration possible car le type de pi2 est connu int*

Remarque La conversion implicite entre pointeur gnrique et un pointeur type se fait se fait donc dans les cas suivants : T* vers void* // lgale en C et C++ void* vers T* // lgale en C seulement en C++ le casting est obligatoire

11 - Pointeurs et tableaux
En C/C++, le nom d'un tableau est considr comme une constante d'adresse dfinissant l'emplacement de dbut partir duquel vont se succder les lments du tableau. Ainsi, lorsqu'il est employ seul, l'identificateur d'un tableau, est considr comme un pointeur constant. Notation d'accs aux lments d'un tableau Si on considre la dclaration suivante :
int T[10];

Alors on a les quivalences de notations suivantes :


T &T[0], T+1 &T[1], T+i &T[i] *T T[0], *(T+1) T[1], *(T+i) T[i]

Exemple 1 : Cet exemple montre un programme de remplissage des lments d'un tableau d'entiers avec des 1.
int T[10],i; for(i=0;i<10;i++) T[i]=1; int T[10],i; for(i=0;i<10;i++) *(T+i) = 1; int T[10],i, *p; for(p=T,i=0; i<10 ; i++,p++) *p=1;

Version 3.7

32

Karim Kalti

Les pointeurs et les rfrences Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Le nom du tableau ne peut pas tre utilis pour faire le parcours des lments (l'incrmentation T++ est incorrecte) car T est dans ce cas considr comme un pointeur constant. Ceci explique l'utilisation d'une variable de type pointeur dans l'exemple 3 pour faire ce parcours. Exemple 2 : L'accs aux lments peut se faire l'aide d'indices ngatifs si ces lments sont situs une adresse prcdant l'adresse contenue dans le pointeur permettant de faire l'accs.
int T[5]; int *p=&T[4]; P[-1] T[3], P[-2] T[2], , P[-4] T[0].

Cas d'un tableau deux dimensions: Toute dclaration d'un tableau N dimensions est considre comme une dclaration d'un pointeur constant. Cependant, la rfrence des lments l'aide des pointeurs diffre de celle des tableaux une dimension. En effet si on considre le cas particulier o N=2, la dclaration int T[3][4] est interprte comme tant un tableau de 3 lments, chaque lment de ce tableau tant lui-mme un tableau de 4 entiers. Exemple :
typedef int QuatreEntiers[4]; QuatreEntier Tab[3];

Le tableau tab est quivalent au tableau T dclar d'une manire classique comme suit : int T[3][4] Les lments de T tant des int[4]. De ce qui prcde T+1 correspond l'adresse de T+4*sizeof(int) octets (car l'lment du tableau correspond 4 entiers et non pas un seul). De mme les critures suivantes sont quivalentes :
T &T[0][0] T[0] T+1 &T[1][0] T[1]

Pointeurs et constantes
Pointeur en lecture seule Un pointeur en lecture seule peut pointer sur n'importe quelle variable. Il ne permet toutefois que l'accs en lecture la variable pointe. Toute tentative de modification (accs en criture) est signale comme erreur. Dclaration const Type* NomPointeur; Exemple :
int i=5,j=10; const int* p = &i; // pointeur sur un entier cout<<"Valeur pointe :"<<*p<<endl; p = &j; // ok cout<<"Valeur pointe :"<<*p<<endl; *p = 8; // erreur (tentative de modification) cout<<"Valeur pointe :"<<*p<<endl;

Remarque : On ne peut pas affecter l'adresse d'un objet constant un pointeur sur un type non constant, car une telle affectation pourrait permettre de modifier cet objet par l'intermdiaire du pointeur. Exemple :
const char espace=' ' ; const char *p = &espace ; // ok ; char *q = & espace ;; //erreur

Version 3.7

33

Karim Kalti

Les pointeurs et les rfrences Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Pointeur constant Un pointeur constant est un pointeur qui ne peut pointer que sur une seule variable. Le contenu de la variable pointe n'est pas ncessairement constant et peut par consquent tre modifi. Dclaration Type* const NomPointeur; Exemple :
int i=5,j=10; int* const p = &i; cout<<"Valeur pointe :"<<*p<<endl; i=8; cout<<"Valeur pointe :"<<*p<<endl; p = &j; // erreur cout<<"Valeur pointe :"<<*p<<endl;

Les rfrences
Le C++ introduit un nouvel outil de construction de types drivs appel rfrence. Une valeur de type rfrence est une adresse unique (qui ne change pas) et qui dsigne une variable bien dtermine. Une rfrence doit obligatoirement tre initialise par une variable. Elle joue ds lors le rle d'alias de cette variable (cette variable peut tre manipule travers sa rfrence). Les rfrences sont dfinies selon la syntaxe suivante : Type &NomReference = VariableInitialisation; Les rfrences sont la fois similaires aux pointeurs du fait qu'ils stockent des adresses en mmoire et diffrents de ces derniers du fait que le contenu d'un pointeur peut tre variable alors que celui d'une rfrence est constant. Les rfrences peuvent tre considres comme des "pointeurs constants" qui sont manipuls, d'un point de vue syntaxique comme des variables ordinaires. Exemple :
int i; int &r1 = i; int &r2; // r1 est une rfrence de i. Elle le restera pour tout le programme. r1 peut // dsormais tre utilise en tant qu'autre nom de i. // Erreur, une rfrence doit tre initialise

Dclaration multiple de rfrences


int a=1; int &r1=a, &r2=a; // r1 et r2 rfrencent a.

Remarques : Il n'est pas possible de crer une rfrence gnrique. Ainsi void& n'est pas valide. Il n'est pas possible de crer des pointeurs vers des rfrences, ni des rfrences de rfrences.
int a=1; int &r =a; int & *ptr; // Erreur int &&rr; // Erreur

Il n'est pas possible de dclarer des tableaux de rfrences. Il est possible de dclarer des rfrences de pointeurs.
int i,*p=&i; int* &r=p; // r est une rfrence d'un pointeur sur entier i=5; cout<<*r; // Affiche le contenu de i savoir 5

Les rfrences de constantes En plus des rfrences de variables, il est galement possible de crer des rfrences de constantes en ajoutant const la dclaration.

Version 3.7

34

Karim Kalti

Les pointeurs et les rfrences Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
const int c=2; const int &rc=c; //rc est une rfrence de c. Elle peut la remplacer

Oprations sur les rfrences Une fois qu'une rfrence a t dclare et initialise, toutes les oprations effectues par la suite sur cette rfrence se rapporteront exclusivement "l'objet" rfrenc et non la rfrence elle-mme. (En effet, le contenu d'une rfrence ne peut pas tre modifi aprs son initialisation vu qu'il est constant). Exemples :
int x; int *p; int &r=x; r=1; // x reoit la valeur 1 et r reste inchang et contient toujours l'adresse de x. r++; // incrmente x de 1.

L'exemple ci-dessus montre que les rfrences sont bien manipules comme des variables sans utilisation de l'oprateur d'indirection). Dans les expressions d'adressage x peut galement tre remplace par r, ainsi :
p=&r; // affecte l'adresse de x p. *p=r; // copie le contenu de x dans l'emplacement point par p; cout<<&x<<'\t'<<p<<'\t'<<&r; //Affiche 3 fois la mme valeur qui est l'adresse de x

Initialisation des rfrences Une rfrence non constante ne peut tre initialise qu'avec une lvalue de mme type.
unsigned char uc; double d1,d2; int &ri =1024; // error : not lvalue char &rc =uc // error inexact types double &dr=d1+d2; // error not an lvalue

Une rfrence sur un objet constant peut tre initialise aussi bien avec une rvalue qu'avec une lvalue.
const const const const const const unsigned char uc; double d1; double d2; int &ri=1024; // OK : 1024 c'est l'adresse unsigned char &rc = uc; // OK double &rd = d1+d2; // OK

Version 3.7

35

Karim Kalti

Les chanes de caractres Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les chanes de caractres


Une chane de caractres est un type particulier de donnes qui se prsente comme une srie de caractres alphanumriques. En C/C++, les chanes de caractres sont considres comme des cas particuliers des tableaux de caractres et sont dailleurs dclares de la mme manire que ces derniers. Toutefois, les chanes se distinguent par rapport aux simples tableaux de caractres par le fait qu'elles se terminent toujours par un caractre spcial cod sur un octet ayant la valeur \0 appel le zro de fin de chanes. Ce dernier ne doit pas tre confondu avec le caractre 0 dont le code ASCII est 48. Remarque : La bibliothque standard (STL) fournie avec le langage C++ dispose d'une classe string qui offre tous les outils ncessaires pour la manipulation des chanes en termes de copie, de concatnation, etc.

Dclaration d'une chane


La dclaration d'une chane de caractres est faite comme suit : char NomChaine[Taille]; La taille dun tableau de caractres doit tre suffisante pour contenir le zro de fin de chane. Ainsi une chane dclare avec une taille de 10 ne peut contenir au plus que 9 caractres utiles. [Le compilateur ne procde aucune vrification].
char message[10] ; // chane de 9 caractres

Initialisation d'une chane


Une chane de caractres peut tre initialise comme suit : char ch1={'A','B','C','\0'} ; ou galement char ch2[]= "abc"; L'affectation globale d'une constante chane de caractre une variable de type chane n'est permise que lors d'une initialisation. Ainsi l'instruction suivante provoque une erreur.
char ch3[5]; ch3="abc"; /* donne une erreur */

La spcification de la taille d'une chane lors de sa dclaration n'est pas ncessaire si cette dclaration est suivie d'une initialisation.
char msg []= "Bonjour" ; char phrase[]= "ceci est une phrase" ;

La taille de msg nest pas fixe mais dpend de la taille de la chane dinitialisation. Dans le cas prsent, ce tableau occupe 8 octets, le premier contenant B et le dernier est le zro de fin de chane. Initialisation dun tableau de caractres deux dimensions.
char buf[3][10]={" Ali ", " Mohamed ","Salah"};

La premire dimension : 3, est facultative. Chaque ligne occupe 10 caractres.

Version 3.7

36

Karim Kalti

Les chanes de caractres Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Saisie et affichage d'une chane de caractres


La saisie La saisie d'une chane peut se faire laide de la fonction scanf en utilisant le caractre de formatage %s ou galement laide de la fonction gets dont la syntaxe est la suivante : char* gets(char *NomChane); (stdio.h)

La fonction gets retourne la chane saisie en cas de succs et NULL en cas d'chec. La fonction gets arrte la saisie des caractres lorsque l'utilisateur tape le caractre \n indiquant un retour la ligne. Elle remplace alors ce caractre par \0. L'affichage L'affichage d'une chane peut se faire laide de la fonction printf en utilisant le caractre de formatage %s ou galement laide de la fonction puts dont la syntaxe est la suivante : int puts(const char* NomChane); (stdio.h)

Cette fonction retourne une valeur non ngative en cas de succs. En cas d'chec elle retourne EOF. Elle remplace le caractre \0 de la chane par \n. Exemple 1
# include<stdio.h> void main( ) {

char ligne[81] ;
printf(" donnez une chane ") ; scanf(" %s ", ligne) ; printf(" la ligne saisie est : %s\n ",ligne) ; }

Exemple 2
#include <stdio.h> void main( ) { char tab[]= "Bonjour"; printf("Affichage du contenu dun tableau de caractres : \n " ) ; printf(" %s ",tab) ; }

Exemple 3
#include <stdio.h> void main( ) { char tab[]= "Bonjour ; /* puts ralise un retour automatique la ligne */ puts(" Affichage du contenu dun tableau de caractres : " puts(tab) ; }

) ;

Affectation entre chanes de caractres


Laffectation directe entre chanes n'est pas permise en C/C++ car il n'est pas possible de faire une affectation globale entre tableaux. Pour cela, il est ncessaire de passer par la fonction strcpy. Prototype : char *strcpy( char *Destination, const char *Source ); Bibliothque: (string.h) Valeur de retour: Destination. Action: Elle copie la chane Source dans la chane Destination. Aucune valeur de retour n'a t prvue pour indiquer une erreur.

Version 3.7

37

Karim Kalti

Les chanes de caractres Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
# include <string.h> # include <stdio.h> void main( ) { char string1[10], string2[10]=abcdef; strcpy(string1,string2); printf(" %s\n,string1); }

Longueur d'une chane


La fonction strlen permet de dterminer le nombre de caractres prsents dans une chane sans tenir compte du zro de fin de chane. Prototype: size_t strlen( const char *string ); Bibliothque: string.h Valeur de retour: nombre de caractres Exemple :
#include<string.h> #include<stdio.h> void main( ) { int nb; char ch[20]; strcpy(ch,Bonjour); nb=strlen(ch); printf(" le nombre de caractres est %d ",nb) ; }

Comparaison de deux chanes de caractres


Ordre lexicographique des chanes On appelle ordre lexicographique, l'ordre dans lequel les mots sont rangs dans un dictionnaire. Il est dfini mathmatiquement de la faon suivante : Soit A et B deux mots que l'on peut considrer comme des valeurs de variables de type chane.

A B si et seulement si Long(A) Long(B) et quel que soit i Long(A), A[ i ] = B[ i ] . Ou s'il existe: i, 1 i min{ Long(A), Long(B) }, tel que : A[ i ] < B[ i ] et que quel que soit j, 1 j i A[ j ] = B[ j ]

Exemples : "ARBRE" < "ARBRES" "ARBRE" < "ARBRE " "ARBRE" > "ARBORESENCE" "ARBRE" < "ARBUSTE" Remarque : En C/C++, les oprateurs = =, < ,> ,<= et >= ne permettent pas de comparer le contenu de deux chanes. Il faut dans ce cas passer par la fonction strcmp qui effectue une comparaison sur la base de l'ordre lexicographique susmentionn. Prototype : int strcmp( const char *Chane1, const char * Chane2 );

Version 3.7

38

Karim Kalti

Les chanes de caractres Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Bibliothque : string.h Valeur renvoye : une valeur entire qui est : < 0 si Chane1 est infrieure Chane2 = = 0 si Chane1 es gale Chane2 0 si Chane1 est suprieur Chane2. Exemple 1 :
# include <string.h> # include <stdio.h> void main( ) { int resultat; char ch1[]=ARBUSTE ; char ch2[]=ARBRE ; char ch3[]=ARBORESENCE ; resultat = strcmp(ch1,ch2); if(resultat>0) printf("ch1 est suprieure ch2\n ") ; else printf("ch1 est infrieure ch2\n ") ; resultat = strcmp (ch2,ch3) ; if( resultat > 0 ) printf("ch2 est suprieure ch3\n ") ; else printf("ch2 est infrieure ch3\n ") ; }

Exemple 2 :
# include <string.h> # include <stdio.h> void main( ) { char ch1[10], ch2[10]; int resultat; strcpy(ch1,"Bonjour"); printf("contenu de ch : %s\n",ch1); printf(" deuxime chane ? ") ; scanf(" %s ",ch2) ; resultat = strcmp(ch1,ch2); if(resultat ==0) printf("les deux chanes sont identiques ") ; else printf("les deux chanes sont diffrentes ") ; }

Concatnation de deux chanes


Concatner deux chanes consiste les unir pour obtenir une seule en copiant la deuxime la fin de la premire. La concatnation en C/c++ est ralise avec la fonction strcat dont le prototype se prsente comme suit : Prototype: char* strcat(char *Chane1, const char * Chane2); Bibliothque: string.h Valeur de retour: La chane chane1 qui contient le rsultat de la concatnation de chane1 et chane2. Exemple :
# include <string.h> # include <stdio.h> void main( ) { char Destination[30]; char Ch1[10]= "Turbo " , Ch2[2]= " " , Ch3[5]= "C "; strcpy(destination,Ch1) ; strcat(destination, Ch2); strcat(destination, Ch3); puts(destination); }

Version 3.7

39

Karim Kalti

Les chanes de caractres Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Recherche dun caractre dans une chane


La fonction strchr cherche la premire occurrence d'un caractre dans une chane. Prototype: char*strchr(const char *Str, char car ); Bibliothque : string.h Valeur de retour : un pointeur sur la premire occurrence du caractre car dans la chane Str et NULL si le caractre considr ne figure pas dans la chane. Exemple :
#include<string.h> #include<stdio.h> void main() { char String[15], *ptr, c=r; strcpy(String,Voici une chaine); ptr=strchr(String,c) ; if(ptr) printf(Le caractre %c est dans la position: %d\n,c,ptr-String+1) ; else printf( caractre non dtect.\n ) ; }

Version 3.7

40

Karim Kalti

La gestion dynamique de la mmoire Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La gestion dynamique de la mmoire


Il arrive frquemment en programmation que les besoins effectifs en mmoire ne soient pas connus au moment de l'criture du programme. Par exemple, le nombre d'lments utiliser dans un tableau peut tre inconnu au moment de la compilation et sera spcifi au moment de l'excution par l'utilisateur. Une fixation arbitraire de ces besoins en mmoire peut s'avrer parfois insuffisante et mener vers le blocage du systme. Elle peut s'avrer galement excessive et conduire alors vers un gaspillage de la mmoire. Pour remdier ce problme, il est souvent fait usage des outils de la gestion dynamique de la mmoire.

Outils de gestion dynamique de la mmoire issus du C


Le langage C offre la possibilit de la gestion dynamique de la mmoire travers un ensemble de fonctions dont les principales sont : malloc, calloc, realloc et free. L'usage de ces fonctions est tout fait possible en C++.

La fonction malloc
Prototype : void* malloc(size_t

size);

Cette fonction alloue une zone mmoire de la taille de size. Elle retourne un pointeur gnrique donnant l'adresse de dbut du bloc allou ou un pointeur NULL si l'allocation a chou pour une insuffisance d'espace. Bibliothques : malloc est dfinie dans les bibliothques stdlib.h et alloc.h.

La fonction free
Prototype : void free(void* PtrZone) La fonction free permet de librer un espace pralablement allou. Le paramtre PtrZone dsigne un pointeur qui pointe sur la zone librer. Bibliothques : free est dfinie dans les bibliothques stdlib.h et alloc.h. Exemple :
#include <stdio.h> #include <alloc.h> void main() { int * adr, i; adr= (int *) malloc(10*sizeof(int)); // adr= malloc(10*sizeof(int)); for(i=0;i<10;i++) *(adr+i)=1; for(i=0;i<10;i++) printf("%d\t", *(adr+i)); free(adr); } // Utilisation de la valeur de retour // de malloc #include <stdio.h> #include <alloc.h> void main() { int * adr, i; adr= (int *) malloc(10*sizeof(int)); // adr= malloc(10*sizeof(int)); if(!adr) printf("Echec de l'allocation"); else { for(i=0;i<10;i++) *(adr+i)=1; for(i=0;i<10;i++) printf("%d\t", *(adr+i)); free(adr); } }

Version 3.7

41

Karim Kalti

La gestion dynamique de la mmoire Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La fonction calloc
Prototype : (void*) calloc(size_t nb_blocs, size_t taille_bloc); Cette fonction joue le mme rle que celui de malloc. Ainsi, elle alloue l'emplacement ncessaire nb_blocs conscutifs, occupant chacun en mmoire taille_bloc octets. Contrairement ce qui se passe avec malloc, o le contenu de l'espace mmoire allou est alatoire, calloc remet zro chacun des octets de la zone alloue. calloc retourne un pointeur sur l'espace allou si l'allocation s'est bien droule, sinon elle retourne NULL. Bibliothques : stdlib.h et alloc.h Exemple :
#include<stdio.h> #include<alloc.h> void main ( ) { long* buffer; buffer = (long*) calloc(40, sizeof(long)); if(buffer!=NULL) printf("Espace allou pour 40 longs"); else printf("Impossible d'allouer de l'espace"); free (buffer); }

La fonction realloc
Prototype : void* realloc(void* PtrBloc, size_t taille) Bibliothques : stdlib.h et alloc.h Cette fonction permet de modifier la taille d'une zone pralablement alloue (par malloc, calloc ou realloc). Le paramtre PtrBloc dsigne l'adresse de dbut de la zone dont on veut modifier la taille, quant au paramtre taille, il dsigne la nouvelle taille souhaite. En cas de succs, cette fonction retourne un pointeur sur la zone rallou. Elle retourne NULL si la taille rallouer est 0 et PtrBloc n'est pas NULL ou s'il n'y a pas assez d'espace contigu pour tendre la mmoire la taille demande. Dans le premier cas, le bloc d'origine est libr, dans le second cas, le bloc d'origine reste inchang. Dans le cas o le paramtre PtrBloc vaut NULL alors la fonction realloc se comporte comme malloc. Lorsque la nouvelle taille demande est suprieure l'ancienne, le contenu de l'ancienne zone est conserv. Dans le cas o la nouvelle taille est infrieure l'ancienne, le dbut de l'ancienne zone verra son contenu inchang.

La fonction _msize
Prototype : size_t _msize( void * PtrBloc ); Bibliothques : alloc.h Cette fonction retourne la taille en octets d'une zone mmoire alloue par malloc, calloc ou realloc. Le type size_t est assimil au type unsigned int. Il est utilis pour les variables qui sont senses stocker la taille en octets des blocs mmoires.

Version 3.7

42

Karim Kalti

La gestion dynamique de la mmoire Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple : (realloc et _msize)


#include<stdio.h> #include<stdlib.h> #include<malloc.h> void main(void) { long *buffer; size_t size; if((buffer=(long*)malloc(100*sizeof(long)))==NULL) exit(1); // ERROR size= _msize(buffer); printf("la taille du bloc de 100 long aprs malloc est %u \n", size);

// 400

if((buffer= realloc(buffer, size + (200 * sizeof(long))))==NULL) exit(1); // ERROR size= _msize(buffer); printf("la taille du bloc aprs reallocation de 200 autres long est %u \n", size); // 1200 free(buffer); exit(0); // sortie normale }

L'adresse du buffer peut rester la mme comme elle peut changer. Cela dpend de l'espace mmoire contigu disponible au moment de l'excution.

Outils de gestion de la mmoire spcifiques au C++


Le C++ ralise la gestion dynamique de la mmoire grce des oprateurs et non des fonctions comme c'est le cas pour le C. Ces oprateurs, appels new et delete font partie de la liste des mots rservs du C++ et ne ncessitent par consquent aucune bibliothque inclure pour pouvoir tre utiliss. Par ailleurs ces oprateurs offrent une syntaxe de gestion de la mmoire qui est beaucoup plus simple que celle offerte par le C. Il est noter que les fonctions de gestion de la mmoire du C restent toujours valables en C++.

L'oprateur New
L'oprateur new alloue de l'espace mmoire pour des objets de type lmentaire ou tendu. Il fournit l'adresse mmoire de la zone rserve un pointeur qui servira pour faire les oprations d'accs cette zone. Si l'allocation choue, l'oprateur new retourne NULL. Le contenu de la zone alloue n'est pas initialis (Il est alatoire). Allocation dynamique d'objets isols Pointeur = new type_de_donnees; Exemple 1 :
int *p; // dfinit un pointeur vers int p = new int; //alloue de l'espace pour un entier et affecte l'adresse de cet espace // p. int *x = new int; *x=1; // Accs la zone alloue (seulement travers x car cette zone ne possde // pas de nom).

Exemple 2 : Allocation et initialisation


double* d= new double (3.14); // allocation et initialisation

Version 3.7

43

Karim Kalti

La gestion dynamique de la mmoire Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Allocation des tableaux Tableau une dimension Pointeur = new type_de_donnees[Nb d'lements]; Exemple :
int* p = new[10]; ou galement int* p; p = new int[10];

L'accs aux lments du tableau peut se faire par p[i] ou par *(p+i). L'initialisation d'un tableau dynamique lors de l'allocation n'est pas permise.
int* p = new int[5] (1,2,3,4,5); // ERREUR

Tableau plusieurs dimensions Pointeur = new type_de_donnees[n][Cst1][Cst2] Seule la premire dimension peut tre variable, les dimensions restantes doivent tre constantes, donc connues. Exemple :
int (*Z)[4] = new int [3][4] int Z[3][4];

L'oprateur delete
L'oprateur delete libre un espace mmoire dj allou par new. Il possde une syntaxe double qui diffrencie les objets isols des tableaux. Libration d'un objet isol : delete pointeur; Exemple :
#include <iostream.h> void main() { long* L = new long; cout<<"Donnez un nombre entier"; cin>>*L; cout<<"Le nombre saisi est:"<<*L; delete L; }

Libration des tableaux dynamiques : delete[] pointeur; Remarque : Pour les tableaux de type de base les crochets peuvent tre omis. Ce n'est pas le cas par contre pour les tableaux d'objets. Exemple :
#include <iostream.h> void main() { int i, *p; p = new int [10]; for(i=0;i<10;i++) *(p+i) = i; for(i=0;i<10;i++) cout<<*(p+i); delete[] p; }

Version 3.7

44

Karim Kalti

La gestion dynamique de la mmoire Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Allocation dynamique d'un tableau deux dimensions


malloc et new ne permettent de crer que des blocs une dimension (des vecteurs). Alors pour crer une matrice de L lignes et C colonnes l'ide consiste crer L vecteurs comportant chacun C lments. Le schma suivant montre la reprsentation en mmoire de la structure dynamique crer.
T** M C lments de type T

T* T* T* T* L lignes

Allocation de la matrice Dans le code suivant T dsigne un type quelconque, prdfini ou personnalis :
// Version C++ M= new(T*)[L]; for(int i=0; i<L;i++) *(M+i)=new T[C]; /* Version C */ M= (T**)malloc(L*sizeof(T*)); for(int i=0; i<L;i++) *(M+i)= (T*)malloc(L*sizeof(T));

Accs un lment de la matrice L'accs un lment d'indice (i,j) se fait comme suit : *(*(M+i)+j) Libration de la matrice Le code suivant permet de librer la matrice :
// Version C++ for(int i=0; i<L;i++) delete[] *(M+i); delete[] M;

M[i][j]

/* Version C */ for(int i=0; i<L;i++) free(*(M+i)); free(M);

Il faut toujours commencer par librer les lignes puis on libre la table qui stocke l'adresse de ces lignes.

Variable auto vs variable dynamique


Une variable auto est une variable dont la dure de vie est gre d'une manire automatique par le systme. Une telle variable est alloue sur la pile (empile) suite l'excution de l'instruction qui la dclare. Elle sera ensuite automatiquement libre (dpile) lorsque l'excution du programme atteint la fin du bloc d'instructions dans lequel elle est dclare. Une variable dynamique est une variable dont la dure de vie est gre par le programmeur. Une telle variable est alloue sur le tas (et non sur la pile) par un appel explicite new (ou malloc). Elle ne sera libre de la mmoire qu' travers un appel explicite delete (ou free) ou suite au redmarrage du systme. Les variables dynamiques ne possdent gnralement pas de noms. Le seul moyen permettant de les manipuler et leur adresse en mmoire (renvoye par new ou malloc). Cette dernire doit alors tre prserve dans un pointeur appropri.

Version 3.7

45

Karim Kalti

La gestion dynamique de la mmoire Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Utilit de l'allocation dynamique de la mmoire Les espaces mmoire dynamiques sont utiles : o pour crer des variables dont la taille est inconnue au moment de la compilation. C'est le cas par exemple lorsqu'il s'agit de crer un tableau dont le nombre d'lments sera dtermin au moment de l'excution par l'utilisateur. o Pour crer des variables de trs grande taille qui dpasse ce que peut accepter la pile d'excution (c'est le cas des tableaux de grande taille par exemple). L'allocation des espaces dynamiques s'effectue sur le tas. Ceci fait que la seule contrainte concernant leur taille soit la disponibilit de la mmoire vive physique (taille de la RAM disponible). La taille de la pile d'excution quant elle reste limite et dpend gnralement des compilateurs. Elle peut tre paramtre dans les options de ces derniers.

Problme de fuite de la mmoire


Une zone mmoire dynamiquement cre mais non explicitement libre va persister dans la RAM mme aprs la fin de l'excution du programme. Ce phnomne est communment appel : fuite de mmoire. Le programme suivant donne une illustration de ce phnomne.
01- void main() 02- { 03int i; 04int* p; 05p= new int; 06i=5; 07*p=6; 08cout<<"i :"<<i<<" et *p :"<<*p; 10- }

Ce programme se compile avec succs. Il s'excute galement d'une manire normale. Toutefois sa sortie il engendre une fuite de mmoire. Pour dtecter cette fuite nous allons analyser l'tat de la mmoire qu'il utilise diffrents niveaux d'excution. Etat de la mmoire suite l'excution de l'instruction de la ligne 4

p i Pile

i et p sont deux variables de type "auto". Elles sont alloues sur la pile d'excution. Etat de la mmoire suite l'excution de l'instruction de la ligne 5

p i

FF0EA5C Espace mmoire dynamiquement cr

new int va engendrer la cration d'une variable dynamique dont l'adresse est stocke dans p. Etat de la mmoire suite l'excution de l'instruction de la ligne 7

Pile

p i

FF0EA5C 5

Pile

Le 5 est plac dans i et le 6 dans la zone dynamique pointe par p.

Version 3.7

46

Karim Kalti

La gestion dynamique de la mmoire Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Etat de la mmoire suite l'excution de l'instruction de la ligne 10


6

La ligne 10 dsigne la fin du programme. A ce niveau toutes les variables de type auto seront automatiquement libres (i et p dans ce cas car leur porte est limite au bloc du main) mais pas la zone dynamique pointe par p. Pour remdier ce problme il faut librer la zone pointe avant de quitter le programme.
01- void main() 02- { 03int i; 04int* p; 05p= new int; 06i=5; 07*p=6; 08cout<<"i :"<<i<<" et *p :"<<*p; 10delete p; 11- }

Il est noter que l'instruction delete p ne libre pas p (p tant de type auto) mais libre la zone mmoire dont l'adresse est stocke dans p. Remarque : La fuite de mmoire mme si elle ne cause aucune perturbation directe du fonctionnement intrinsque d'un programme peut tre trs nocive pour l'environnement dans lequel tourne ce dernier. En effet, l'espace mmoire non libre va occuper inutilement une partie des ressources (RAM) de l'environnement. Ce problme est encore plus grave si le programme en question est sens tourner sur un serveur en mode client/serveur. Dans ce cas, chaque client qui lance une excution du programme va engendrer aprs sa sortie une occupation inutile d'une portion de la RAM du serveur. Au bout d'un certain nombre de connexions de clients, une grande partie de cette RAM se trouvera occupe sans tre rellement utilise. Ceci va conduire vers une chute des performances du serveur voire mme son plantage.

Version 3.7

47

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les Fonctions
Introduction
Une fonction est une portion de code regroupant un ensemble d'instructions dlimit par deux accolades et laquelle est associ un nom qui permet de l'appeler. Une fonction peut tre dclare dans un programme ou dans une autre fonction, et peut tre excute suite son appel par le programme englobant. Une fonction est dite paramtre si elle demande des donnes en entre pour pouvoir s'excuter. Ces donnes sont appeles arguments ou paramtres. Gnralement, une fonction retourne une valeur qui est le rsultat de l'excution de son code. Cependant, il est possible en C/C++ de dfinir des fonctions qui ne retournent aucune valeur. De telles fonctions sont quivalentes aux procdures dfinies en algorithmique ou dans d'autres langages de programmation tel que le PASCAL.

Dclaration d'une fonction


La dclaration d'une fonction se fait comme suit : Type NomFonction(TypeArg1 NomArg1,, TypeArg_n NomArg_n); O : Type dsigne le type de la valeur retourne par la fonction. TypeArg_i NomArg_i dsignent respectivement le type et le nom du ime argument de la fonction. Exemple:
int f(int i,int j);

Dclaration du prototype d'une fonction La dclaration d'une fonction peut se rduire seulement son prototype. Ce dernier donne une ide sur le modle de la fonction en termes du nombre et des types des paramtres qu'elle prend, ainsi que du type de sa valeur de retour. Il est dclar de la manire suivante: Type NomFonction(TypeArg1, TypeArg2,, TypeArg_n); Exemple:
int f(int,int);

Dfinition d'une fonction


La partie dfinition d'une fonction correspond au corps de la fonction. Il s'agit donc de la spcification de l'ensemble des instructions dont l'excution ralise la tche assure par la fonction. La dfinition d'une fonction se fait comme suit : Type NomFonction(TypeArg1 NomArg1, , TypeArg_n NomArg_n) { Dclarations des variables locales; instruction_1; instruction_n; return valeur; }

Version 3.7

48

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

En C, les premires instructions du corps de la fonction doivent correspondre aux dclarations des variables qui seront utilises l'intrieur de la fonction. Ce n'est pas le cas en C++ o ces dclarations peuvent tre faites n'importe o dans le bloc dfinissant le corps de la fonction. En C/C++, Ces variables sont dites des variables locales la fonction. Elles ne peuvent par suite tre utilises qu' l'intrieur de cette dernire. Elles ne sont pas visibles l'extrieur. Les dernires instructions d'une fonction servent gnralement renvoyer une valeur, appele valeur de retour, l'extrieur de la fonction. Ce renvoi est effectu l'aide du mot-cl return. En C, lorsqu'une fonction ne prend aucun argument, le mot-cl void doit tre plac entre les parenthses la place de la liste des paramtres. Ceci n'est pas ncessaire en C++ o les parenthses peuvent rester vide.

Type d'une fonction et valeur de retour


Le type de la fonction indique le type de sa valeur de retour. Si aucun type n'est spcifi pour une fonction alors cette dernire est considre par dfaut comme tant de type int. (Elle retourne un entier de type int). L'instruction return provoque un arrt immdiat de l'excution des instructions du bloc associ la fonction et renvoie une valeur ou le rsultat d'une expression qu'elle prend comme paramtre de la manire suivante return (valeur); L'utilisation des parenthses avec return est facultative. Il est possible de spcifier que la fonction ne retourne aucune valeur d'aucun type en la dclarant comme tant de type void. Si linstruction return est absente du corps d'une fonction, alors le programme continue son excution des instructions jusqu' l'accolade fermante. La valeur renvoye par return doit tre du mme type que celui de la fonction. Elle peut tre exploite comme n'importe qu'elle autre valeur du mme type. Par exemple, elle peut tre rcupre et stocke dans une variable de la manire suivante :
int var; var = NomFonction(arg1, arg2, arg3);

Suite une excution une fonction ne peut retourner q'une seule valeur et pas plus. Exemple 1 : Le programme suivant permet de calculer le montant de l'achat d'un nombre donn d'articles possdant chacun un prix unitaires PU.
/* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* 1 */ 2 */ 3 */ 4 */ 5 */ 6 */ 7 */ 8 */ 9 */ 10*/ 11*/ 12*/ 13*/ 14*/ 15*/ 16*/ 17*/ #include <stdio.h> double Montant(int NbArticles, double PU); void main( ) { int nb; double val; printf("\n donnez le nombre d'articles puis leur prix unitaire: "); scanf("%f %lf", &nb, & val); resultat = Montant(nb,val); printf("\n Le Montant est %f", resultat); } double Montant(int NbArticles, double PU) { double total; total = NbArticles * PU; return total; }

Version 3.7

49

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Commentaires La ligne 2 reprsente la dclaration de la fonction Montant. On aurait pu se limiter au prototype en omettant les noms des variables. La ligne 9 est un appel la fonction Montant. La dfinition de la fonction Montant s'tend de la ligne 12 jusqu' la ligne 17. La variable total (14) est une variable locale la fonction Montant, elle ne peut tre utilise qu' l'intrieur de cette dernire. Dans cet exemple on peut vrifier que la dclaration de la fonction s'est faite avant sa premire utilisation. La dfinition peut suivre par la suite. Une fonction doit tre dclare avant son utilisation. Exemple 2 : Il est possible dans un programme de se limiter la dfinition d'une fonction sans faire une dclaration du prototype. Dans ce cas, cette dfinition doit prcder le premier appel de la fonction.
#include <stdio.h> double Montant(int NbArticles, double PU) { double total; total = NbArticles * PU; return total; } void main() { int nb; double val; printf("\n donnez le nombre d'articles puis leur prix unitaire: "); scanf("%f %lf", &nb, & val); resultat = Montant(nb,val); printf("\n Le Montant est %f", resultat); }

Exemple 3 : Il s'agit d'une autre version de l'exemple 2 mais avec moins de variables.
#include <stdio.h> double Montant(int NbArticles, double PU) { return NbArticles * PU;; } void main() { int nb; double val; printf("\n donnez le nombre d'articles puis leur prix unitaire: "); scanf("%f %lf", &nb, & val); printf("\n Le Montant est %f", Montant(nb,val)); }

Exemple 4 : (fonction sans valeur de retour: procdure) Il s'agit de la dfinition d'une fonction qui prend comme paramtre un nombre et qui affiche un message indiquant si ce nombre est strictement positif, strictement ngatif ou nul.
#include <stdio.h> void Signe(int Nb); { if(Nb < 0) printf("le nombre est else if (Nb >0) printf("le nombre est else printf("le nombre est // return; (on aurait pu }

ngatif \n"); positif \n"); nul \n"); ajouter cette ligne pour indiquer que la fonction ne retourne rien)

Version 3.7

50

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________ void main( ) { int Nombre; printf("Donnez le nombre tester"); scanf("%d",&Nombre); Signe(Nombre); }

Exemple 5 :
// Fonction 1 int f1(int i, int j) { int res1, res2; res1 =i+j; res2 = i-j; return res1; return res2; //unreachable code } // Fonction 2 int f2(int i, int j) { if(i<j) return i; return j; }

La dernire instruction de la fonction 1 (return res2) ne peut jamais tre atteinte et par suite excute parce que le premier return (return res1) va engendrer une sortie immdiate de la fonction avec comme valeur de retour res1. Ce n'est pas le cas pour la fonction 2 o chacune des instructions return peut tre atteinte selon que le i est infrieur j ou non. Il est rappeler que pour une excution donne de la fonction 2 c'est un seul return parmi les deux qui sera excut.

Fonction locale et fonction globale


Tout comme les variables, une fonction peut tre dclare l'intrieur d'un bloc (ventuellement une fonction) ou l'extrieur. Si la dclaration est faite l'intrieur alors la fonction est considre comme locale ce bloc et ne peut tre appele en dehors de celui-ci. Une fonction est dite globale, si elle est dclare en dehors de tout bloc. Elle peut tre dans ce cas appele de n'importe qu'elle endroit du programme. Exemple 1 :
#include <stdio.h> void main() { double Montant(int NbArticles, double PU) { return NbArticles * PU;; } int nb; double val; printf("\n donnez le nombre d'articles puis leur prix unitaire: "); scanf("%f %lf", &nb, & val); printf("\n Le Montant est %f", Montant(nb,val)); }

Dans cet exemple la fonction Montant a t dfinie l'intrieur du main. Par consquent, elle ne peut tre appele que de l'intrieur de la fonction main. Exemple 2 : On veut crire un programme qui demande la matricule et les notes d'un tudiant et qui l'invite la fte de fin d'anne s'il possde une moyenne suprieure ou gale 10.
# include <stdio.h> void main( ) { int Mat; float n1,n2; float Moyenne(float, float); // dclaration locale void Invitation(int, float, float ); // dclaration locale

Version 3.7

51

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________ printf("Donnez la matricule de l'tudiant"); scanf("%d",&Mat); printf("Donnez ses deux notes "); scanf("%f %f",&n1,&n2); Invitation(Mat, n1, n2); } void Invitation(int Matricule, float note1, float note2) { float MoyenneLoc; MoyenneLoc = Moyenne(note1,note2); if( MoyenneLoc <10 ) printf(" L'tudiant possdant la matricule %d n'est pas invit \n", Matricule); else printf(" L'tudiant possdant la matricule %d est invit \n", Matricule); } float Moyenne ( float note1, float note2) { return (note1+note2)/2; }

Conformment la norme ANSI1, et puisque la fonction Moyenne a t dclare l'intrieur du main son utilisation dans le bloc de la fonction Invitation devient incorrecte. En effet, dans ce cas cette fonction est considre comme locale la fonction main et elle ne pourra pas tre appele par suite en dehors du bloc de cette dernire. Il est possible pour viter ce problme de dclarer la fonction Moyenne l'intrieure de la fonction Invitation. Ainsi, elle sera reconnue par cette dernire. Une autre alternative consiste faire une dclaration globale de la fonction Moyenne en dehors de tout bloc. Remarque : La norme ANSI concernant la dclaration locale des fonctions est tendue par certains compilateurs qui considrent qu'une fonction est reconnue dans tout le reste du fichier source depuis l'endroit o elle a t dclare peu importe que la dclaration soit interne un bloc ou globale. De ce fait, l'criture prcdente devient correcte avec certains compilateurs. Cependant et pour des raisons de portabilit elle est viter.

Les catgories des paramtres des fonctions


Paramtres formels et paramtres effectifs Deux types de paramtres de fonctions peuvent tre distingus : les paramtres formels et les paramtres effectifs. o Les paramtres formels sont ceux qui figurent dans la dfinition de la fonction. Ils sont utiliss dans les instructions faisant partie du bloc de la fonction et l seulement. Ces paramtres sont considrs comme des variables locales la fonction. o Les paramtres effectifs sont ceux qui figurent dans l'instruction d'appel de la fonction. Ils sont substitus aux paramtres formels au moment de cet appel. Les paramtres formels et effectifs doivent s'accorder en nombre, ordre et type. (les types peuvent tre compatibles seulement et pas ncessairement identiques). Diffrents types des paramtres formels Suivant le rle qu'ils assurent dans la fonction les paramtres formels peuvent tre classs en trois catgories: les paramtres donnes, les paramtres rsultats et les paramtres donnes-rsultats. Un paramtre "donne" est une donne ncessaire pour raliser la tche associe la fonction. Il ne subit aucune modification au cours de l'excution de la fonction.

ANSI : Acronyme de American National Standards Institute, institution qui dfinit les normes d'un ensemble de systmes et de langages de programmation.

Version 3.7

52

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple : Les paramtres NbArticles et PU dans la fonction Montant sont des paramtres de type donnes. C'est le galement le cas des paramtres note1 et note2 dans la fonction Moyenne. Un paramtre "rsultat" est une variable qui est destine contenir le rsultat de l'action de la fonction. Sa valeur n'est pas significative avant le dbut. Exemple:
int CalculerTriple( int x, int triple) { triple = 3 *x; return triple; }

Dans cet exemple x est un paramtre "donne" alors que triple est un paramtre "rsultat". Gnralement un paramtre "rsultat" n'a pas besoin d'tre dclar comme argument de la fonction mais plutt comme une variable locale. Un paramtre "donne-rsultat" est la fois une donne et un rsultat. c'est dire qu'il sert passer une donne la fonction et que cette dernire modifie sa valeur. Exemple : Dans une fonction qui prend deux paramtres entiers et qui permute leurs valeurs. Ces entiers sont des paramtres "donnes-resultats" puisqu'ils fournissent les donnes en entre (les valeurs permuter) et stockent normalement le rsultat de la fonction (les valeurs permutes).

Modes de passage des paramtres


Passage par valeur C'est le mode de passage d'arguments le plus courant en C/C++ et le plus simple galement. Dans ce mode, les valeurs des arguments au moment de l'appel (paramtres effectifs) sont recopies dans les lments de donnes locaux correspondants la fonction (paramtres formels). Aprs la sortie de la fonction, les valeurs des paramtres effectifs restent identiques celles qu'ils avaient avant l'excution de la fonction. Ce mode de passage est adapt aux paramtres de type "donnes". Pour les paramtres de type "rsultat" ou "donne-rsultat", ce mode ne permet pas de rcuprer les ventuels modifications effectues en interne par la fonction l'extrieur de cette dernire. La dclaration d'un passage par valeur s'effectue de la manire suivante : TypeFonction NomFonction(, Type ParamValeur,); ParamValeur tant le paramtre pass par valeur et Type tant son type. Exemple 1 : Les deux notes sont passes la fonction Moyenne par valeur. Exemple 2 :
#include <stdio.h> void CalculerTriple( int x, int triple) { triple = 3 *x; } void main(void) { int ent = 5; int res; CalculerTriple(ent,res); printf("le rsultat est %d", res); // donne un rsultat incorrect }

Le mode de passage par valeur est adapt pour le paramtre x (paramtre "donne") mais ne l'est pas pour le paramtre triple (paramtre "rsultat"). En effet la modification effectue sur ce paramtre l'intrieur de la

Version 3.7

53

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

fonction CalculerTriple n'est pas rcupre l'extrieur de cette dernire (dans le main dans ce cas de cet exemple). Exemple 3 :
#include <stdio.h> void Permuter (int val1,int val2) { int temp; temp =val1; val1= val2; val2= temp; } void main( ) { int x= 5, y=3; Permuter(x,y); printf("x contient %d et y contient %d \n", x,y); // }

5 et 3

Les paramtres val1 et val2 sont de type "donne-rsultat". En les faisant passer par valeur comme cest le cas dans cet exemple, le rsultat de la permutation n'est pas rcupr l'extrieur de la fonction et le rsultat affich est par consquent incorrect. Une des solutions permettant de remdier au problme du passage par valeur des paramtres "rsultat" et "donne-rsultat" consiste liminer ces derniers de la liste des arguments de la fonction et les dclarer comme des variables globales. Toutefois cette solution prsente l'inconvnient de rendre la fonction difficilement rutilisable. Une deuxime solution plus intressante consiste utiliser un autre mode de passage de paramtres qui permet de rcuprer ces modifications : c'est le cas du passage par adresse (C/C++) et du passage par rfrence (C++ seulement). Passage par adresse Dans un passage par adresse d'un argument on impose la fonction de travailler non plus sur une copie locale du paramtre effectif mais plutt directement sur ce dernier. (Pas de copie du paramtre effectif dans le paramtre formel, le travail se fait directement sur le paramtre effectif). De cette faon, toute modification effectue l'intrieur de la fonction sera en ralit ralise sur le paramtre effectif et par consquent visible l'extrieur de la fonction. Ce mode de passage de paramtre est effectu en faisant pass la fonction l'adresse du paramtre effectif. Toute rfrence ce dernier de l'intrieur de la fonction doit se faire l'aide de l'oprateur d'indirection (*). Dclaration d'un passage par adresse TypeFonction NomFonction(, Type* ParamAdresse,); ParamAdresse tant le paramtre pass par adresse et Type tant son type.

Rfrence l'intrieur de la fonction d'un paramtre pass par adresse TypeFonction NomFonction(, Type* ParamAdresse,) { *ParamAdresse; } Passage du paramtre par adresse au moment de l'appel de la fonction NomFonction(, &ParamAdresse,) Les paramtres concerns par le mode de passage par adresse sont ceux de type "rsultat" ou "donnersultat". Les paramtres "donne" peuvent toujours tre passs par valeur.

Version 3.7

54

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple 1 :
#include <stdio.h> void CalculerTriple(int x, int* triple) { *triple = 3*x; } void main(void) { int ent = 5; int res; CalculerTriple(ent, &res); printf("le rsultat est %d", res); // donne un rsultat correct 15 }

Exemple 2 :
#include <stdio.h> void Permuter (int* val1,int* val2) { int temp; temp =*val1; *val1= *val2; *val2= temp; } void main( ) { int x= 5, y=3; Permuter(&x,&y); printf("x contient %d et y contient %d \n", x,y); // }

3 et 5

Passage par rfrence (C++ seulement) Par rapport au C, le C++ introduit un nouveau mode de passage de paramtres appel le passage par rfrence. Il est quivalent de point de vue effet au passage par variable du pascal ou au passage par adresse du C. Dans le passage par rfrence, toute modification effectue sur le paramtre formel de la fonction est rpercute directement sur le paramtre effectif. La dclaration d'un passage par rfrence s'effectue en adjoignant au type du paramtre passer le symbole & de la manire suivante : Dclaration d'un passage par rfrence TypeFonction NomFonction(, Type& ParamRfrence,); ParamRfrence tant le paramtre pass par rfrence et Type tant son type.

Rfrence l'intrieur de la fonction d'un paramtre pass par rfrence TypeFonction NomFonction(, Type& ParamRfrence,) { ParamAdresse; } Passage du paramtre par adresse au moment de l'appel de la fonction NomFonction(,ParamRfrence,)

Exemple
#include <iostream.h> void Permuter (int& val1,int& val2) { int temp; temp =val1; val1= val2; val2= temp; }

Version 3.7

55

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

void main( ) { int x= 5, y=3; Permuter(x,y); cout<<" x contient : "<<x<<"\n y contient : "<<y<<endl; }

Transmission des tableaux comme arguments d'une fonction


Si un tableau figure comme argument d'une fonction alors son passage se fait toujours par adresse. Par consquent, toutes les oprations et modifications appliques ses lments seront visibles l'extrieur de la fonction. Exemple1: Le programme suivant remplace les lments d'un tableau par leurs valeurs absolues sans qu'il soit ncessaire de spcifier que le passage se fait par adresse.
#include <stdio.h> void ValAbsTab( float[], int); void main( ) { float Tableau[5]; int i; for(i=0;i<5;i++) { printf("Donnez un nombre: "); scanf("%f", &Tableau[i]); } ValAbsTab(Tableau, 5); for(i=0;i<5;i++) printf("%f", Tableau[i]); } void ValAbsTab( float TabVal[], int Taille) { int i; for(i=0;i<Taille;i++) if(TabVal<0) TabVal[ i ]= - TabVal[ i ]; }

Remarque : Les deux prototypes suivants sont galement valables pour faire passer un tableau comme paramtre de la fonction ValAbsTab.
void ValAbsTab( float* TabVal, int Taille) void ValAbsTab( float TabVal[Taille], int Taille)

Exemple2: (cas d'un tableau deux dimensions)


#include<stdio.h> void SaisieMatrice( int[5][10], int, int); void main( ) { int M[5][10]; SaisieMatrice(M,5,10); } void SaisieMatrice( int Matrice[5][10], int L, int C) { int i,j; for(i=0;i<L;i++) for(j=0;j<C;j++) { printf("Donnez Matrice[%d][%d]",i,j); scanf("%d", &Matrice[i][j]); } }

Version 3.7

56

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Les deux prototypes suivants sont galement valables pour faire passer un tableau comme paramtre de la fonction SaisirMatrice.
void SaisieMatrice( int Matrice[ ][10], int L, int C) void SaisieMatrice( int** Matrice, int L, int C)

Les fonctions en ligne


Une fonction en ligne est une fonction expanse chaque appel. En d'autres mots, le corps de la fonction est insr dans le module appelant l'endroit de l'appel. Les fonctions en ligne permettent d'acclrer l'excution (en vitant les alles-retours entre la fonction et le module appelant). Dclaration : inline Type NomFonction(Type1 arg1, Type2 arg2, Remarques : La dfinition d'une fonction en ligne doit obligatoirement prcde son appel. La dclaration seule ne suffit pas. inline est une recommandation au compilateur. Ce dernier peut l'ignorer si : o La fonction contient des boucles ou si elle est rcursive. o La fonction est trop grande pour que le gain en temps d'excution soit significatif. Les fonctions en ligne sont gnralement de petite taille (1 3 instructions). Exemple :
inline int abs(int x){return x>0?x:-x;} inline int max(int x, int y){return x>y?x:y;}

, TypeN argN);

Surcharge de fonctions (surdfinition)


La surcharge de fonctions consiste proposer plusieurs dfinitions d'une mme fonction au sein d'un mme programme. Ces dfinitions doivent se distinguer par leurs signatures. Il est rappeler que la signature d'une fonction est dfinie par le nombre, le type et l'ordre de ses paramtres. La valeur de retour n'entre pas en considration dans ce cas. On surcharge gnralement les fonctions qui font le mme traitement mais sur des donnes de types diffrents. Exemple 1:
///////////////////////////////////////////// int MaxTab(int* T, int n) { int i, max; for(i=0, max=T[0];i<n;i++) if(T[i]> max) max = T[i]; return max; } ///////////////////////////////////////////// double MaxTab(double* T, int n) { int i; double max; for(i=0, max=T[0];i<n;i++) if(T[i]> max) max = T[i]; return max; }

Version 3.7

57

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________ ///////////////////////////////////////////// char MaxTab(char* T) { char max = T[0]; while(*T++) if(*T> max) max = *T; return max; }

Exemple 2:
// Surcharge correcte int f(int, char); int f(char, int); // Surcharge incorrecte int f(char, int); char f(char, int);

Arguments par dfaut d'une fonction


Des valeurs par dfaut peuvent tre affectes aux arguments d'une fonction. Ces arguments ont gnralement des valeurs habituelles qu'il n'est pas ncessaire de spcifier chaque utilisation. Dclaration Type NomFonction(Type1 arg1, Type2 arg2, Type3 Arg3 = Val); Exemple :
int f(int a, int b=0);

Remarque 1: Seuls les derniers arguments, de la droite vers la gauche peuvent avoir des valeurs par dfaut. Exemple :
void g(int a = 3, int b, int c=5); // Error void g(int a, int b=6, int c =8); // OK

Remarque 2: Si la valeur d'un argument par dfaut a t spcifie dans la dclaration de la fonction, alors elle ne doit pas tre mentionne nouveau dans la dfinition. Si une fonction est directement dfinie, alors la valeur par dfaut doit tre spcifie dans la dfinition. Exemple :
void f(int a=0); // ou void f(int=0); void f(int a=0) // Error { Corps de la fonction } void f(int a = 0) // OK {Corps de la fonction}

Au moment de l'appel de la fonction, la

spcification de l'argument par dfaut est optionnelle. Exemple :


// Dclaration void Print(int Valeur, int Base = 10); // Appel Print(31) 31 Print(31,10) 31

Il est galement possible de donner une nouvelle valeur pour l'argument par dfaut.
Print(31,16) Print(31,2) 1f 11111

Version 3.7

58

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les Pointeurs sur les fonctions


Les fonctions en C/C++ ne sont pas des entits variables. Nanmoins, il est possible de placer leurs noms dans des variables. En effet, le nom d'une fonction utilis tout seul dsigne l'adresse de l'emplacement mmoire o est charg le code de la fonction (les instructions). Cette adresse peut tre par consquent stoke dans une variable de type pointeur. Elle peut galement tre passe comme paramtre une autre fonction. Dclaration d'un pointeur sur une fonction La dclaration d'un pointeur sur une fonction se fait de la manire suivante : Type(*NomPointeur)(Type1 P1, Type2 P2, ,TypeN PN); NomPointeur dsigne le nom du pointeur dclarer. Type dsigne le type de la valeur retourne par la fonction pointe par le pointeur. Type1, TypeN dsignent la liste des paramtres de la fonction pointe par le pointeur. Les parenthses autour de (*pointeur) sont trs importantes car autrement avec Type* pointeur() et en raison de la priorit de l'oprateur ( ) par rapport * on dclarerait non plus un pointeur sur une fonction mais une fonction qui retourne une valeur de Type*.

Exemple :
int(*pf)(double,int);

Cette dclaration spcifie que pf est un pointeur qui peut pointer sur des fonctions prenant comme argument un double et un int et retournant un int. Soit la fonction int f(double, int) Si pf=f alors int k=f(5.4,6); int k=(*pf)(5.4,6); Exemple : On veut crire un programme qui affiche, suivant le choix de l'utilisateur le sinus, le cosinus ou la tangente d'un angle fourni par l'utilisateur.
#include <iostream.h> #include<math.h> void main( ) { double Deg, Rad; int NumF; double(*Tpf[3])(double)={sin,cos,tan}; cout<<"Donnez un angle en degree : "; cin>>Deg; cout<<"Tapez le numro de la fonction que vous voulez utiliser\n"; cout<<" 1- sinus \n 2- cosinus\n 3- Tangente\n"; cin>>NumF; Rad=3.1416*Deg/180; cout<<"Le rsultat est : "<<(*Tpf[NumF-1])(Rad)<<endl; }

Remarque : Diffrence entre le C et le C++ concernant les pointeurs sur des fonctions sans paramtres En langage C, la liste des paramtres spcifie lors de la dclaration d'un pointeur sur une fonction n'est pas obligatoire. Son absence ne signifie pas forcment que la fonction rfrence par le pointeur ne possde pas de paramtres. En effet, si rien n'est indiqu concernant les paramtres, le compilateur n'effectue aucune vrification en ce qui concerne la correspondance entre paramtres effectifs et paramtres formels (cette proprit existe dj pour les fonctions). Ainsi la dfinition d'un pointeur de fonctions ayant une liste de paramtres vide peut prsenter un avantage puisqu'un tel pointeur peut servir mmoriser les adresses des fonctions admettant toutes sortes de paramtres. Exemple : avec f1 et f2 deux fonctions. La seule contrainte ici pour que ces deux affectations soient correctes est que f1 et f2 retournent une valeur de type int. Les deux fonctions suivantes sont dans ce cas valables.
int (*pf)(); pf=f1; ou pf = f2;

Version 3.7

59

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

int f1() { /* intsructions*/ return 0; }

int f2(int a, int b, int c) { /* intsructions*/ return C; }

Contrairement au C, le compilateur C++ effectue toujours une vrification concernant la correspondance entre paramtres effectifs et paramtres formels mme si ces derniers ne sont pas mentionns. Par consquent un pointeur de fonction dclare sans paramtres ne peut pointer que sur une fonction n'ayant pas de paramtres. Le pointeur pf de l'exemple prcdent ne peut pointer alors que sur f1. Exemple : L'exemple suivant est correct en C mais incorrect en C++.
int(*pf)() = printf; printf("Test des pointeurs de fonctions");(*pf)("Test des pointeurs de fonctions");

Passage d'une fonction comme argument d'une autre fonction


Du fait que le nom d'une fonction soit considr comme une adresse, rien n'empche de le passer en tant qu'argument une autre fonction. Cette possibilit peut tre utile dans certains cas mme si elle reste rare d'utilisation. Exemple : On veut crire deux fonctions qui font la saisie et l'affichage des lments d'un tableau. Ces lments peuvent tre de type entier, rel ou caractre.
#include<stdio.h> void SaisirChar(char* Tab, int l) { int i; for(i=0;i<l;i++) { scanf("%c", &Tab[i]); fflush(stdin); } } void SaisirInt(int* Tab, int l) { int i; for(i=0;i<l;i++) scanf("%d", &Tab[i]); } void SaisirFloat(float* Tab, int l) { int i; for(i=0;i<l;i++) scanf("%f", &Tab[i]); } void AfficherChar(char* Tab, int l) { int i; for(i=0;i<l;i++) printf("%c, ",Tab[i]); } void AfficherInt(int* Tab, int l) { int i; for(i=0;i<l;i++) printf("%d, ",Tab[i]); }

Version 3.7

60

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

void AfficherFloat(float* Tab, int l) { int i; for(i=0;i<l;i++) printf("%f, ",Tab[i]); } void AfficherTableau( void* Tab, int l, void(*pf)(void*, int)) { (*pf)(Tab,l); } void SaisirTableau( void* Tab, int l, void(*pf)(void*, int)) { (*pf)(Tab,l); } void main() { char C[5]; int I[5], l =5,n; float F[5]; printf("Tapez le numro 1 du type des lments que vous voulez saisir : \n 1 - caractre\n 2 - entier\n 3 - rel\n"); scanf("%d", &n); fflush(stdin); printf("****** Dbut de la saisie : *******\n"); switch(n) { case 1: SaisirTableau(C,l,SaisirChar); break; case 2: SaisirTableau(I,l,SaisirInt); break; case 3: SaisirTableau(F,l,SaisirFloat); break; } printf("****** Affichage du rsultat de la saisie: *******\n"); switch(n) { case 1: AfficherTableau(C,l,AfficherChar); break; case 2: AfficherTableau(I,l, AfficherInt); break; case 3: AfficherTableau(F,l, AfficherFloat); break; } }

Fonctions avec un nombre variable de paramtres


En programmation, il existe souvent des oprations pour lesquelles il n'est pas possible de prciser l'avance le nombre de paramtres traiter. L'utilisation des fonctions avec un nombre fixe de paramtres ne convient pas dans ce cas. Il faut plutt passer par des fonctions possdant un nombre variable de paramtres. Les fonctions printf et scanf du C/C++ constituent deux exemples de ce genre de fonctions. Le concept de "fonction avec un nombre variable de paramtres" signifie que la fonction est dfinie avec un nombre quelconque de paramtres fixes (au moins un) et qu'elle accepte lorsqu'on l'appelle un nombre variables de paramtres effectifs supplmentaires sans qu'il y ait besoin que ces derniers soient explicitement dfinis l'aide de paramtres formels. Dclaration La dclaration d'une fonction de la sorte se fait de la manire suivante : Type NomFonction(Liste des paramtres fixes, ); Exemple :
int f(char a, int b,);

Version 3.7

61

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Evaluation des paramtres optionnels L'valuation des paramtres effectifs optionnels doit passer par l'utilisation des trois macros suivantes va_start, va_arg et va_end du header stdarg.h ainsi que par un pointeur de type va_list (dfini comme void* ou char*) et qui va permettre de pointer vers ces arguments optionnels (Un argument est un paramtre effectif). La macro va_start void va_start( va_list arg_ptr, prev_param ); (ANSI version) void va_start( va_list arg_ptr ); (UNIX version) va_start prend deux paramtres, le premier est un pointeur de type va_liste vers les paramtres optionnels de la fonction alors que le second est le nom du dernier paramtre fixe. va_start initialise arg_ptr au premier argument optionnel de la liste des arguments passe la fonction. La macro va_arg TypeArg va_arg( va_list arg_ptr, TypeArg ); La macro va_arg rcupre une valeur de type TypeArg partir de l'endroit donn par arg_ptr. En d'autres mots, elle permet de rcuprer la valeur de l'argument courant. Elle incrmente aussi arg_ptr pour pointer sur l'argument suivant de la liste en utilisant la taille du type pour dterminer l'adresse de dbut du prochain argument. La macro va_end void va_end( va_list arg_ptr ); Aprs valuation de la liste des paramtres, le pointeur d'arguments va_end rinitialise arg_ptr NULL. Remarque : Dans la majorit des cas, Il est ncessaire de faire communiquer la fonction acceptant un nombre variable de paramtres le nombre effectif de paramtres optionnels traiter. Ce nombre est gnralement plac dans un des paramtres fixes. Exemple : L'exemple suivant montre la dfinition d'une fonction Somme qui peut calculer la somme d'un nombre variable d'entiers.
#include "iostream.h" #include "stdarg.h" long Somme(int L,...) { int i; va_list arg_ptr; long resultat; va_start(arg_ptr,L); i=1; resultat=0; while (i<=L) { resultat+=(long)va_arg(arg_ptr,int); i++; } va_end(arg_ptr); return resultat; } void main() { cout<<"le rsultat de la somme est :"<<f(3,3,5,10)<<endl; }

Version 3.7

62

Karim Kalti

Les fonctions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Paramtres sur la ligne de commande


Considrons le programme suivant :
#include <stdio.h> void main() { int a,b,c; printf("donnez trois entiers"); scanf("%d %d %d ",&a,&b,&c); printf("%d",a+b+c); }

Supposons que ce programme soit sauvegard dans un fichier nomm Somme.cpp alors la compilation et l'dition des liens de ce fichier va conduire vers la cration d'un fichier excutable nomm Somme.exe. Pour lancer cet excutable il suffit de taper son nom aprs le prompt du systme d'exploitation puis de valider avec <entre>. Le programme va demander alors l'utilisateur de saisir trois nombres et lui affiche comme rsultat leur somme. Il est galement possible de faire fonctionner le programme Somme la manire d'une commande qui prend trois arguments. Pour ce faire, ces arguments doivent tre passs la fonction principale main. Passage d'arguments la fonction main La fonction main peut prendre normalement deux paramtres formels optionnels communment appels argc et argv. main(int argc, char* argv[]) argc est un argument de type int et dsigne le nombre de paramtres spcifis sur la ligne de commande y compris le nom du programme qui est considr aussi comme paramtre. argv est un tableau de chanes de caractres qui stocke les arguments passs en ligne de commande. La taille de ce tableau dpend du nombre d'arguments passs. Par convention arg[0] renvoie sur la commande avec laquelle le programme est invoqu, arg[1] reprsente le premier argument pass rellement au programme, arg[2] reprsente l'argument suivant, etc. Le dernier lment de argv est toujours argv[argc] et contient le pointeur nul. Le premier argument pass est argv[1] et le dernier est argv[argc-1]. La reconnaissance du nombre de paramtres est automatique. Ces derniers doivent cependant tre spars par des espaces. Si un des paramtres lui mme est un espace alors il doit tre plac entre deux guillemets.

Exemple :
#include <stdio.h> #include <stdlib.h> void main(int argc, char* argv[]) { int a,b,c; a=atoi(argv[1]); b=atoi(argv[2]); c=atoi(argv[3]); printf("%d",a+b+c); }

Si on enregistre ce code dans un fichier portant le nom Somme.cpp alors l'excution en ligne de commande devra se faire de la manire suivante :
C:\ Somme 4 5 2

C:\ 11 Remarque : La fonction main peut prendre un troisime paramtre communment appel envp. Ce paramtre pointe sur les rubriques de l'environnement du programme (les chemins implicites, l'allure du prompt,) il est utilis sous dos et unix. void main(int argc, char* argv[],char* envp[])

Version 3.7

63

Karim Kalti

Structures et numrations Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Structures et numrations
Les types de base du langage C (int, float, double, char, ) ne permettent pas de couvrir tous les besoins de programmation en types de donnes. Les programmeurs ont souvent recours la dfinition de types personnaliss qui rpondent leurs besoins spcifiques. Le langage C met la disposition de ces derniers un ensemble d'outils de construction de types personnaliss parmi lesquels figurent les structures et les numrations.

Dfinition des structures


Une structure est un ensemble d'lments (variables), ayant des types pouvant tre diffrents et regroups sous un mme nom global. Chaque lment de cette structure, appel aussi membre possde un nom qui lui est propre permettant de le distinguer des autres. Il possde galement un type qui peut tre un type de base ou un type personnalis. La structure elle mme est considre comme un type personnalis. Une structure est dfinie l'aide du mot-cl struct selon la syntaxe suivante : struct NomStructure { TypeMembre_1 NomMembre_1; TypeMembre_2 NomMembre_2; TypeMembre_n NomMembre_n; }; Exemple
struct FicheSignaletique { int age; float taille; float poids; char nom[20]; };

Dclaration des variables de type structure


Dclaration en langage C En langage C la dclaration d'une variable de type structure se fait comme suit : struct NomStructure Var; Exemple
struct FicheSignaletique Personne;

Il est galement possible (bien que peu recommand) de regrouper la dfinition du modle structure et la dclaration des variables dans une seule instruction. Exemple
struct FicheSignaletique { int age; float taille; float poids; char nom[20]; }P1, P2;

Il est possible dans ce cas d'omettre le nom du type.


struct { int age; float taille; float poids; char nom[20]; } P1;

Version 3.7

64

Karim Kalti

Structures et numrations Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Dclaration en langage C++ En langage C++, il n'est pas ncessaire d'utiliser struct pour la dclaration des variables et le nom de la structure peut tre par consquent utilis seul pour dsigner le type. Cette dclaration se fait alors comme suit : NomStructure Var; Exemple
struct FicheSignaletique { int age; float taille; float poids; char nom[20]; } FicheSignaletique F;

Pour des raisons de compatibilit la syntaxe de dclaration du C est galement accepte par les compilateurs C++ et peut encore tre utilise.

Manipulation des membres d'une structure


Les membres d'une structure peuvent tre manipuls individuellement. Ceci est ralis en spcifiant le nom de la structure suivi de l'oprateur de champ ( . ) suivi du membre considr. NomVariableStructure.NomMembre Exemple:
FicheSignaletique Per; Per.Taille = 1.80;

Affichage du contenu du membre :


printf("%d", Per.taille);

Saisie de la valeur du membre :


scanf("%d",&Per.taille);

Remarque : Les types des membres d'une structure peuvent tre aussi bien des types prdfinis que personnaliss y compris les structures.

Utilisation globale d'une structure


Il est possible d'affecter une structure le contenu d'une autre structure dfinie partir du mme modle. Par exemple si P1 et P2 sont deux variables de type FicheSignaletique
FicheSignaletique P1,P2;

alors, il est possible d'crire : P1 = P2;

Remarques: L'affectation globale n'est pas possible entre tableaux. Elle l'est par contre entre structures. Paradoxalement, il devient de ce fait possible en crant une structure contenant un seul champ de type tableau de raliser une affectation globale entre tableaux. Deux structures ne peuvent pas tre compares d'une manire globale. Ainsi:
FicheSignaletique P1,P2; P1==P2 et P1>P2 sont deux instructions

incorrectes.

Version 3.7

65

Karim Kalti

Structures et numrations Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Initialisation des structures


L'initialisation d'une variable de type structure se fait travers l'initialisation de ses membres. Cette initialisation s'effectue de la mme manire que pour les tableaux. Exemple :
struct FicheEtudiant {float taille, poids; char Nom[20]; }; FicheEtudiant Personne1 = {1.70, 80,"Ammar"}; FicheEtudiant Personne2 = {1.85, ,"Salah"};

S'il y a moins de valeurs d'initialisation que de champs, les variables qui manquent seront automatiquement initialises 0. S'il y a plus de valeurs d'initialisation que de champs, le compilateur signale une erreur. Il est strictement interdit de dfinir une initialisation systmatique d'un membre d'une structure (il faut sparer dclaration et initialisation). Exemple :
struct FicheEtudiant {float taille; float poids =75; //error char Nom[20]; };

Une constante symbolique peut avoir un type qui est dfini l'aide du mot rserv struct. Exemple :
const FicheSignaletique P1={23,1.78,75,"Ali"};

Imbrication de structures
Un membre d'une structure peut lui mme tre de type structure. Dans ce cas on parle de structures imbriques. Exemple:
struct Date { int jour; int mois; int annee; }; struct personne { char Nom[20]; char Prenom[20]; Date DateNaissance; };

ou galement :
struct FicheEtudiant { float taille, poids; Date DateNaissance; char Nom[20]; };

L'accs la valeur du jour de naissance d'un tudiant est donne par :


FicheEtudiant Etu; Etu.DateNaissance.jour;

Initialisation d'une structure comportant une autre structure L'initialisation de la structure interne se fait en spcifiant les valeurs de ses membres sous forme d'un sous ensemble de la liste des valeurs des membres de la structure externe.

Version 3.7

66

Karim Kalti

Structures et numrations Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
FicheEtudiant P1 = {1.70, 70,{1,4,1985},"Ammar"};

Il est possible d'omettre des valeurs pour certains membres. Ces derniers seront automatiquement initialiss 0. Exemple :
FicheEtudiant P2 = {1.85, 90,{18,6, }, };

Tableau de structures
Dclaration Les lments d'un tableau peuvent tre de type structure. La dclaration de ce tableau se fait comme suit : NomStructure NomTableau[Taille]; Accs au tableau L'accs au ime lment du tableau se fait comme suit : NomTableau[i]; L'accs un membre du ime lment du tableau se fait comme suit : NomTableau[i].NomMembre; Exemple :
struct Point { int x; int y; }; Point Courbe[50];

L'accs l'abscisse du ime point de la courbe: Courbe[i].x L'accs l'ordonn du ime point de la courbe: Courbe[i].y

Utilisation de typedef avec les structures


Le mot-cl typedef permet d'associer un type particulier un nouveau nom plus explicite au sein d'un programme. Il ne dfinit en aucun cas un nouveau type. Formellement la syntaxe de dfinition d'un nouveau nom pour un type est : Typedef NomTypeConnu NouveauNomType;

La dclaration de variables de ce nouveau type s'effectue comme les dclarations usuelles. Exemple 1 :
typedef int Chaise; Chaise Numero; // la variable numro a pour type chaise. Son type de base est int. typedef int vecteur[3]; Les deux dclarations suivantes sont quivalentes : int v1[3]; vecteur v2; // v1 et v2 sont deux tableaux de trois entiers

Exemple 2 :
typedef struct FicheEtudiant { float taille, poids; Date DateNaissance; }; struct FicheEtudiant P; typedef struct { float taille, poids; Date DateNaissance; } FicheEtudiant; FicheEtudiant P;

ou galement

Version 3.7

67

Karim Kalti

Structures et numrations Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple 3 :
struct Date{int jour, mois, annee;}; typedef Date MatriceDate[5][5]; MatriceDate M; // M est une matrice 5X5 de type Date. M[1][2].jour=5;

Pointeurs sur des structures


Dclaration Il est possible de dclarer des pointeurs sur des structures. Cette dclaration se fait comme suit : NomStructure* NomPointeur; Exemple :
FicheEtudiant Etu; FicheEtudiant *pEtu; pEtu=&Etu;

Accs aux membres L'accs aux membres de la structure partir d'un pointeur peut se faire de deux manires : (*NomPointeur).NomMembre Exemple :
Struct Point {int x; int y;}; Point P1; Point* PP; P1.x=10; P1.y=5; PP=&P1 printf(" l'abscisse de P1 est %d", (*pp).x); printf("l'ordonn de P1 est %d", pp->y);

ou galement

NomPointeur->NomMembre

Les numrations
Les numrations reprsentent un outil supplmentaire pour la construction de type. Une numration est constitue d'un ensemble de constantes symboliques associes chacune un numro entier. Dfinition d'une numration La dfinition d'une numration se fait l'aide du mot-cl enum de la manire suivante : enum NomEnumration {NomValeur1, NomValeur2,, NomValeurN}; Chaque valeur de l'numration est associe son numro d'ordre dans la liste. La premire ayant la valeur entire 0, la deuxime la valeur 1 et la nime la valeur N-1. Exemple 1 :
enum Couleur {blanc, noir, gris};

Exemple 2 :
enum Bool {false, true}; // construction du type boolen en C.

Version 3.7

68

Karim Kalti

Structures et numrations Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarques : Il est possible de spcifier des valeurs affecter aux diffrents lments de l'numration. Ces valeurs doivent respecter l'ordre spcifi dans la dclaration et donc tre croissantes du premier lment de l'numration au dernier.
enum Couleur{blanc=3, noir= 255, gris=600}; enum jours{lundi,mardi=7,mercredi, jeudi, vendredi = 25, samedi = 30, dimanche}; Lundi vaut 0, mardi vaut 7, mercredi vaut 8, jeudi vaut 9, vendredi vaut 25, samedi vaut 30, et dimanche vaut 31.

Dclaration d'une variable de type numration en langage C La dclaration d'une variable de type numration se fait de la manire suivante : enum NomEnumration NomVariable; Exemple 1 :
enum Couleur {blanc, noir, gris}; enum Couleur cl=noir; //cl est une variable initialise noir

ou galement :
enum Couleur{blanc, noir,gris} cl; cl=noir;

Exemple 2 :
enum Couleur{rouge, orange, vert}; enum Couleur Feu; // Dclaration d'une variable Feu if(Feu==rouge) printf("il faut stopper"); else if (Feu==vert) printf("vous pouvez passer"); else if (Feu==orange) printf("il faut commencer freiner");

Dclaration d'une variable de type numration en langage C++ En C++, il n'est pas ncessaire d'utiliser le mot rserv enum lors de la spcification du type de la variable. La dclaration de cette dernire se fait alors comme suit : NomEnumration NomVariable; Exemple:
enum Couleur{rouge, orange, vert}; Couleur Feu; // Dclaration d'une variable Feu en C++.

Version 3.7

69

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les classes
Introduction
Les entits du monde rel sont gnralement assez complexes et ne peuvent tre convenablement reprsentes dans les programmes l'aide des types scalaires. Il faut leur construire de nouveaux types qui leurs sont adapts. C++ met la disposition des programmeurs un ensemble d'outils de construction de tels types. Comme exemple de ces outils, il est possible de citer : Les tableaux pour la reprsentation des ensembles d'lments de mme type. Les structures pour la reprsentation des entits complexes comportant plusieurs champs. Considrons l'exemple suivant :
#include <iostream.h> struct Date {int jour, mois, annee;}; Date Saisir() { Date D; cout<<"Donnez le jour :"; cin>>D.jour; cout<<"Donnez le mois :"; cin>>D.mois; cout<<"Donnez l'anne :"; cin>>D.annee; return D; } void Afficher(Date D) { cout<<endl; cout<<D.jour<<'/'<<D.mois<<'/'<<D.annee; cout<<endl; } Date DateRecente(Date D1, Date D2) { if( D1.annee<D2.annee) return D2; else if(D1.annee>D2.annee) return D1; else { if( D1.mois<D2.mois) return D2; else if(D1.mois>D2.mois) return D1; else { if(D1.jour<D2.jour) return D2; else return D1; } } }

Version 3.7

70

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

int main() { Date D1,D2; D1 = Saisir(); D2 = Saisir(); cout<<"la date la plus rcente est"; Afficher(DateRecente(D1,D2)); Return 0 ; }

Les classes : un nouvel outil de construction de type en C++


Les deux fonctions utilises dans le programme prcdent ainsi que la structure Date constituent des modules explicitement distincts mme si sur le plan smantique ils sont implicitement lis. En effet les deux fonctions Saisir et Afficher s'appliquent essentiellement une structure de type Date. Il sera donc intressant de dfinir une structure de donnes qui les regroupe en une seule entit. C++ introduit un nouvel outil de construction de type de donnes, appel classe, qui tend les possibilits des structures et qui offre entre autres cette possibilit de regroupement. Une classe est un type runissant : Une collection de donnes membres appeles attributs. Les attributs dfinissent la partie statique de la classe. Une collection de fonctions membres appeles mthodes et servant faire des traitements sur les donnes membres. Les mthodes dfinissent la partie dynamique de la classe. On dit galement qu'elles dfinissent le comportement de la classe.

CLASSE
Attribut_1 Attribut_2 Attribut_n Mthode_1() Mthode_2() Mthode_m()

Dclaration dune classe


La dclaration dune classe se fait comme suit : class NomClasse ; Exemple :
class Date ; class Personne ;

Version 3.7

71

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Dfinition d'une classe


La dfinition d'une classe dsigne la spcification des attributs et des mthodes de la classe. Rgles syntaxiques de la dfinition : La dfinition commence par le mot-cl class. Ensuite, il y a lieu de dclarer les collections d'attributs et de mthodes. Ces dclarations doivent tre places entre deux accolades. La dfinition d'une classe se termine enfin par un point virgule. Exemple :
class Date { int jour, mois, annee; void Saisir(); void Afficher(); };

Instanciation dune classe


Une classe dfinit un nouveau type. De ce fait, il est possible de dclarer des variables ou des constantes ayant un type classe. Ces variables et constantes sont appeles des objets ou des instances de la classe. Le processus de cration dobjets est appel instanciation. Exemple :
Date D; // D est un objet ayant le type classe Date.

Remarque 1 : Il n'est pas possible d'instancier une classe dclare mais non encore dfinie. Il est possible par contre de dclarer un pointeur vers une classe dclare mais non encore dfinie. Remarque 2 : Dclaration des attributs d'une classe Les attributs d'une classe peuvent tre des variables ou des constantes. D'ailleurs leur dclaration se fait suivant la mme syntaxe que les variables et les constantes classiques. Toutefois par rapport ces dernires, les attributs se distinguent par le fait qu'au moment de leur dclaration dans la dfinition de la classe aucun espace mmoire ne leur est rserv. En effet la dfinition d'une classe constitue un processus de dfinition de type et non un processus de dclaration d'objet. De ce fait, il devient impossible d'initialiser un attribut au moment de la dfinition d'une classe. Exemple :
class A { int i = 5; // Erreur };

Version 3.7

72

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque 3 : Type des attributs Les attributs peuvent tre de n'importe quel type (scalaire ou personnalis y compris de type classe). Toutefois, un attribut ne peut pas tre du type de la classe laquelle il appartient ( cause de la rcursivit dans la rservation de l'espace mmoire que cela engendre lors de l'instanciation de la classe). Il est toutefois possible de dclarer un attribut de type pointeur sur la classe laquelle appartient cet attribut. Exemple :
class A { A pa; // Erreur }; class A { A *pa; // OK };

Classe anonyme
Une classe anonyme est une classe qui ne porte pas de nom (sans nom). Elle sert essentiellement faire des dclarations directes d'objets (dans la mme instruction qui dfinit la classe). Exemple :
class {} D; // D est un objet de type classe sans nom

Remarque : Une instance d'une classe anonyme ne peut pas figurer dans la liste d'arguments d'une fonction (Il est impossible en effet de spcifier le type du paramtre formel puisqu'il est sans nom).

Fonctions membres : mthodes


Dclaration d'une mthode La dclaration des mthodes se fait suivant la mme syntaxe que la dclaration des fonctions classiques. Toutefois une mthode doit tre obligatoirement dclare l'intrieur d'une classe. Dfinition d'une mthode Il existe deux faons pour dfinir une mthode de classe : Premire faon : Elle consiste dfinir la mthode au sein de la classe mme. Exemple :
class Date { int jour, mois, annee; void Saisir() {} void Afficher(){} };

Version 3.7

73

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Deuxime faon : Elle consiste : dclarer la fonction lintrieur de la classe, la dfinir ensuite lextrieur de la classe. Il faut pour cela indiquer la classe dorigine de la fonction. Ceci se fait comme suit : Syntaxe : TypeRetour NomClasse::Methode(type1 arg1,, typeN argN); :: est loprateur de rsolution de porte. NomClasse::Methode est appel le nom qualifi de la mthode. Exemple :
class Date { int jour, mois, annee; void Saisir(); void Afficher(); }; void Date::Afficher(){} void Date::Saisir(){} // Dfinition de Afficher // Dfinition de Saisir

Remarque : Une mthode dfinie lintrieur dune classe est considre par dfaut comme tant inline. Une fonction dfinie lextrieur de la classe nest pas considre comme inline. Pour la rendre ainsi, il faut ajouter explicitement la spcification inline devant sa dfinition. Exemple :
inline void Date :: Afficher() { }

Encapsulation
L'encapsulation est le mcanisme qui permet de regrouper les donnes et les mthodes au sein d'une mme structure (classe). Ce mcanisme permet galement l'objet de dfinir le niveau de visibilit de ses membres. Un membre visible est un membre qui est accessible partir de l'objet : o Si le membre est un attribut alors l'accs concerne une opration de lecture ou d'criture de la valeur de cet attribut. o Si le membre est une mthode alors l'opration d'accs consiste en un appel de cette mthode.

Version 3.7

74

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Gnralement un objet ne doit exposer (rendre visible) que les membres qu'il juge utiles pour ses utilisateurs. Il doit cacher tous les dtails de son implmentation (corps des mthodes ainsi que les attributs et mthodes qui sont utiliss en interne). Spcificateurs des droits d'accs aux membres En C++, la spcification des droits d'accs aux membres d'une classe se fait l'aide des trois mots-cls suivants : public, private et protected. public : ce spcificateur rend les membres dune classe X (attributs ou mthodes) accessibles en dehors de la classe, gnralement par les utilisateurs de cette dernire. private : ce spcificateur restreint laccs aux membres de la classe (attributs et mthodes) aux mthodes de la classe seulement, ainsi quaux fonctions dclares amies de la classe. protected : ce spcificateur a un effet similaire private mais qui est toutefois moins svre. En effet protected restreint laccs aux membres d'une classe X aux mthodes de X, aux fonctions amies de X et aux mthodes des classes bases sur X. Remarque : Il est possible dutiliser au sein dune mme classe diffrents spcificateurs daccs. Ainsi, il est possible de rendre une partie de la classe publique et de cacher une autre partie en la qualifiant de prive. Porte dun spcificateur daccs : La porte dun spcificateur daccs s'tend depuis l'endroit de sa dfinition jusqu' la rencontre de la dfinition d'un autre spcificateur.
class X { private : int a1; //priv char a2; //priv public : void f1(); //publique int f2(double k); //publique }; class X { public : int a1; //publique void f1(); //publique private : char a2; //priv public : int f2(double k); //publique };

Remarque : Spcificateur d'accs par dfaut Si aucun spcificateur daccs na t dfini dans une classe alors les membres de cette dernire sont qualifis par dfaut comme tant privs. Exemple :
class X { int a1; char a2; void f1(); int f2(double k); };

Tous les membres de la classe X sont considrs par dfaut comme tant privs.
Version 3.7

75

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Accs aux membres d'une classe


Signification de laccs Pour un attribut, laccs consiste faire une opration de lecture ou dcriture dans cet attribut. Pour une mthode, laccs consiste faire un appel cette mthode. Accs de l'intrieur de la classe Les membres d'une classe sont locaux cette dernire. Leur porte est donc dfinie par la classe dans laquelle ils sont dclars. Tout membre d'une classe est visible par les autres membres de cette classe sans aucune considration des spcificateurs d'accs (public, private, protected). Les attributs dune classe sont considrs comme des variables globales par rapport aux mthodes de cette classe. Tout attribut peut par consquent tre utilis par n'importe quelle mthode de sa classe. Une mthode d'une classe peut tre appele par toute autre mthode de sa classe indpendamment de l'ordre de dclaration de ces dernires dans la classe. Les membres peuvent tre manipuls directement par leurs noms sans avoir besoin d'aucune qualification. Exemple :
class Date { int jour, mois, annee; public: void Saisir(); void Afficher(); }; void Date::Saisir() { cout<<"Donnez dans lordre le jour, le mois et lanne"; // Accs direct aux attributs par la mthode Saisir cin>>jour>>mois>>annee; } void Date::Afficher() { cout<<endl; cout<<jour<<'/'<<mois<<'/'<<annee; cout<<endl; }

Le fait que les attributs jour, mois, annee soient directement accessibles par les mthodes Saisir et Afficher de la classe Date vite ces dernires de prendre un paramtre de type Date comme cest le cas lorsquil sagit de fonctions indpendantes (Cf. exemple avec les structures).

Version 3.7

76

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Accs de l'extrieur de la classe L'utilisation d'un membre d'une classe en dehors de cette dernire (par exemple par une fonction non membre de la classe) se fait gnralement partir d'une instance (objet). Le membre doit tre associ l'objet auquel il appartient. Cette association se fait l'aide de l'oprateur dyadique (prend deux oprandes) point de la manire suivante : o Pour les attributs : Objet.Attribut; o Pour les mthodes : Objet.Methode(Paramtres effectifs); Seuls les membres publiques peuvent tre accds de l'extrieur d'une classe. Les autres membres (private et protected) ne sont pas accessibles de l'extrieur. Exemple :
class Date { int jour, mois; public: int annee ; void Saisir(); void Afficher(); }; void main() { Date D; D.jour = 5; // Erreur D.Mois = 10; // Erreur D.anne = 2004; // OK cout<<D.jour; // Erreur cout<<D.mois; // Erreur cout<<D.annee; // OK } void main() { Date D; D.Saisir(); // OK D.Afficher(); // OK }

Porte des classes et des objets


Porte d'une classe Un type "classe" nest visible que dans le bloc dans lequel il est dclar. Ainsi : Toute classe dclare dans une fonction nest instanciable que dans cette fonction. Toute classe X dclare lintrieur dune classe Y nest instanciable que dans Y. Une classe dclare en dehors de tout bloc est une classe globale. Elle peut tre instancie nimporte o dans le programme. Porte dun objet Les rgles dfinissant la porte dun objet sont les mmes que celles dfinissant la porte des variables en C++. Ainsi un objet ne peut exister qu' l'intrieur du bloc dans lequel il est dclar ou dans les sous blocs de ce bloc.

Version 3.7

77

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Imbrication de classes
Une classe X peut avoir un membre de type une autre classe Y. Les deux classes sont dites dans ce cas des classes imbriques. Exemple :
class Personne { char Nom[20]; char Prenom[20]; Date Dn; Public : void Saisir(); void Afficher(); };

Date et Personne sont dans ce cas deux classes imbriques. Manipulation des attributs et des mthodes des classes imbriques On considre les classes Personne et Date dfinies ci-dessus. Nous allons discuter dans ce qui suit de la possibilit d'accder aux champs de Date partir de la classe Personne et ce en utilisant diffrents spcificateurs d'accs. - Dn est un champ publique et ses champs sont publiques.
Personne P; P.Dn.Jour; // OK

- Dn est un champ priv et ses champs sont publiques.


Personne P; P.Dn.Jour; // Erreur

- Dn est un champ publique et ses champs sont privs.


Personne P; P.Dn.Jour; // Erreur

Les tableaux d'objets


Dclaration : TypeClasse NomTableau[NbElements] ; Accs aux attributs dun lment du tableau: NomTableau[indice].NomAttribut Accs aux mthodes dun lment du tableau: NomTableau[indice].NomMthode(<args>) Exemple :
Date TD[5]; // Saisie de 5 dates for(int i=0;i<5;i++) T[i].Saisir(); // Affichage des dates for(int i=0;i<N;i++) T[i].Afficher();

Version 3.7

78

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Pointeur sur un objet


Il est possible de dclarer un pointeur vers un objet. Ce pointeur doit tre obligatoirement initialis pour tre utilisable. L'accs aux membres d'une classe partir d'un pointeur se fait l'aide de l'oprateur -> Dclaration : TypeClasse* PtrObj; Initialisation : PtrObj = new TypeClasse(); Accs aux attributs : PtrOpj->Atribut ou galement : (*PtrOpj).Atribut Accs aux mthodes :
PtrObj->Methode(liste d'arguments)

ou galement : *(PtrObj).Methode(liste d'arguments)

Le pointeur this
this est un pointeur particulier en C++ qui pointe toujours sur lobjet en cours dutilisation. Ce pointeur possde plusieurs champs d'applications. Utilisation implicite de this Chaque objet d'une classe possde sa propre copie des attributs (sa propre zone en mmoire). Cette copie dfinit l'identit mme de l'objet qui permet de le distinguer des autres. Toutefois, tous les objets se partagent la mme copie des mthodes (tous les objets ont le mme comportement). Cette organisation en mmoire des objets peut engendrer un problme lors de l'appel des mthodes. En effet, le problme qui peut se poser est la suivant : en cas de prsence de plusieurs objets d'une mme classe, comment une mthode peut-elle connatre la copie des attributs sur laquelle elle doit travailler (car rappelons-le on peut manipuler l'intrieur d'une mthode les attributs directement par leurs noms sans aucune qualification). La rsolution de ce problme passe par l'utilisation du pointeur this. En effet, toute mthode d'une classe prend d'une manire implicite un argument cach de type pointeur sur la classe laquelle elle appartient. Ce pointeur est utilis implicitement pour rfrencer les attributs l'intrieur de la mthode. Il suffit alors d'affecter le pointeur this cet argument au moment de l'appel de la mthode. Le rfrencement implicite des attributs devient alors : this->Attribut. Cette syntaxe limine toute ambigut concernant la copie de l'attribut sur laquelle on doit travailler puisqu'elle dsigne bien l'attribut de l'objet en cours d'utilisation. Exemple : La mthode Saisir dfinie comme suit :
void Date::Saisir() { cout<<"Donnez dans l'ordre le jour, le mois et l'anne"; cin>>jour>> mois>>annee; }

est en ralit implicitement dfinie comme suit :


void Date::Saisir(Date const* this) { cout<<"Donnez dans l'ordre le jour, le mois et l'anne"; cin>>this->jour>>this->mois>>this->annee; }

Version 3.7

79

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Le pointeur this est en lecture seule. C'est le systme qui lui affecte sa valeur pour un objet donn. Dans un programme, il n'est donc possible que de lire cette valeur mais pas la modifier. D'ailleurs ceci explique le fait que le this est dclar comme un pointeur constant (Type const* this). Exemple d'utilisation explicite du pointeur this Un des problmes qui peuvent tre rsolus par l'utilisation du pointeur this est celui qui se pose lors de l'utilisation d'une mthode qui possde des paramtres portant les mmes noms que les attributs. Une ambigut de qualification se pose dans ce cas. Pour lever cette ambigut il suffit d'appeler les attributs de la classe travers le pointeur this.

Exemple :
class A { int x, y; public : void f(int x, int y) { this->x=x; this->y=y; } };

Oprations applicables aux objets


Lecture de ladresse et de la taille d'un objet Il est possible de rcuprer l'adresse mmoire d'un objet et ce l'aide de l'oprateur &. Il est possible de rcuprer la taille en mmoire d'un objet et ce l'aide de l'oprateur sizeof. Cette taille est gale la somme des tailles des attributs de l'objet. Exemple :
class CX {public : int a1; int a2; void Affiche() {cout<<" a1: "<<a1<<" et a2: "<<a2;} }; void main() { CX x; cout<<"\n Adresse de x :"<<&x; cout<<"\n Taille de x : "<<sizeof(x); }

Version 3.7

80

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Affectation dobjets Il est possible daffecter dune manire globale un objet O1 un autre objet O2 de la mme classe. La valeur de chaque attribut de O1 est alors copie dans lattribut qui lui correspond dans O2. Exemple :
void main() { CX x1,x2; x1.a1=5; x1.a2=8; x2=x1; x2.Affiche(); x1.a1=18; x1.Affiche(); x2.Affiche(); }

Affectation entre pointeurs sur des objets Il est possible de faire une affectation entre deux pointeurs sur des objets de mme type. Toutefois lutilisation de ces pointeurs doit se faire avec prcaution. Exemple :
void main() { CX *px1,*px2; px1=new CX(); // Objet sans nom px1->a1=5; px1->a2=8; px2=px1; // px1 et px2 pointent sur le mme objet px2->Affiche(); px1->a1=18; px1->Affiche(); px2->Affiche(); delete px1; delete px2; // Erreur car l'objet a t dj libr }

Oprations interdites sur les objets


Il nest pas possible dappliquer aux objets, les oprateurs logiques et arithmtiques tels qu'ils sont dfinis par dfaut en C++. Exemple :
CX x1,x2 ; x1+x2 ; // Erreur x1<x2 ; // Erreur

Version 3.7

81

Karim Kalti

Les classes Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Il nest pas possible dutiliser cin>> et cout<< pour faire la saisie et laffichage d'une manire globale du contenu dun objet. Exemple :
CX x ; cin>> x ; // Erreur cout<< x ; // Erreur

Version 3.7

82

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Constructeurs et destructeur
Initialisation d'objets
Il est possible d'attribuer des valeurs initiales aux attributs d'un objet travers une liste de valeurs spcifies entre deux accolades et spares par des virgules (comme pour les tableaux). Exemple :
class Point2D {public: int x; int y; }; class Point3D {public: int x; int y; int z; }; Point2D Point3D Point3D Point2D Point2D P1={5,6}; P2={5,6,9}; P3={4,,9} // le deuxime attribut est initialis 0. T[3] = {1,5,6,8,9,10}; T[3] = {{1,5},{6,8},{9,10}};

Limites de l'initialisation avec les listes de valeurs Il faut que tous les membres de l'objet initialiser soient publiques. Il faut que l'objet soit d'une classe de base et qu'il n'ait pas de membres virtuels. Certaines oprations d'initialisation sont complexes et ncessitent plusieurs tapes comme le montre l'exemple suivant : Exemple :
class TabEntiers { public : int Nb; int* T; };

Pour pouvoir initialiser le membre T, ce dernier doit auparavant tre allou. Solution 1 : Passage par une mthode d'initialisation Avantages : Possibilit de raliser les oprations d'allocation pour les membres dynamiques. Possibilit d'accder aux membres privs et protgs.

Version 3.7

83

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
class TabEntiers { int Nb; int* T; public : void Init(int Ni) { Nb=Ni; T=new int [Nb]; } void Init(int* Ti,int Ni) { Nb=Ni; T=new int [Nb]; for(int k=0;k<Nb;k++) T[k]=Ti[k]; } } void main() { TabEntiers TE; TE.init(5); }

Inconvnients : Pour pouvoir utiliser l'objet, il est ncessaire d'appeler chaque fois, d'une manire explicite, la mthode Init. Cet appel explicite peut engendrer des erreurs surtout dans les programmes de grande taille o le risque d'oubli devient lev. L'utilisation de Init ne peut pas tre considre comme tant une initialisation au vrai sens technique du terme car une initialisation se fait gnralement dans la mme instruction que la dclaration. Solution 2 : Utilisation des constructeurs du C++ Le C++ offre une solution de cration et d'initialisation d'objets qui permet de remdier tous les problmes susmentionns (initialisation au vrai sens du terme, appel implicite, initialisation des membres dynamiques avec allocation de mmoire,).

Les constructeurs
Dfinition Un constructeur est une mthode particulire d'une classe qui s'excute automatiquement d'une manire implicite, lors de la cration d'un objet de cette classe. Utilisation des constructeurs Rle de base : (implicite, transparent par rapport aux programmeurs) Un constructeur d'une classe assure la rservation de l'espace mmoire ncessaire la cration de tout objet de cette classe. L'espace dont on parle ici dsigne l'espace de base ncessaire la cration de l'objet. Il ne comprend pas les espaces dynamiques associs aux membres dynamiques ventuels de la classe.

Version 3.7

84

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple : Pour la classe TabEntiers dfinie prcdemment, l'espace de base ncessaire la cration d'un objet est gale la somme des tailles de Nb et du pointeur T. Il ne comprend pas la taille de la zone mmoire pointe par T (sizeof(Nb)+sizeof(T)). Exploitation usuelle des constructeurs : (explicite, effectue par les programmeurs) Les constructeurs sont gnralement exploits par les programmeurs pour raliser les oprations suivantes : Initialisation des attributs de la classe (publiques, privs ou protgs). Allocation des espaces mmoires ncessaires aux membres dynamiques de la classe.

Dclaration et dfinition d'un constructeur


Un constructeur est une mthode de la classe. Un constructeur doit porter le mme nom que celui de sa classe. Un constructeur peut admettre 0, un ou plusieurs paramtres. Un constructeur ne retourne aucune valeur. (le fait de ne pas mettre un type de retour devant le constructeur ne signifie pas que ce dernier retourne par dfaut un entier (int) comme c'est le cas pour les mthodes classiques). Une classe peut comporter un ou plusieurs constructeurs. Dans le cas d'existence de plusieurs constructeurs, ces derniers doivent respecter les rgles de surcharge des mthodes. Comme toute mthode un constructeur peut tre dfini l'intrieur de la classe ou l'extrieur.

Syntaxe de dclaration d'un constructeur : NomClasse (<paramtres>); Exemple :


class Time { int Hour; int Minute; int Second; public : Time(int H,int M, int S) { Hour = H; Minute = M; Second = S; } void Afficher() { cout<<"L'heure est : "; cout<<Hour<<':'<<Minute<<':'<<Second<<endl; } };

Version 3.7

85

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Droits d'accs aux constructeurs


Les spcificateurs d'accs public, private et protected s'appliquent aux constructeurs de la mme manire que pour n'importe quelle mthode de classe. Toutefois, pour qu'un utilisateur d'une classe puisse appeler un constructeur et par suite crer un objet, ce constructeur doit tre obligatoirement publique.

Appel d'un constructeur


Toute construction d'un objet d'une classe doit tre accompagne obligatoirement par un appel son constructeur. Cas d'un objet de type auto La cration d'un objet de type auto peut se faire travers un appel explicite ou implicite du constructeur. Appel explicite : Le constructeur est appel par son nom comme une mthode classique. NomClasse NomObjet = NomClasse(<paramtres effectifs>);
Time T = Time(16,30,25);

Appel implicite : NomClasse NomObjet(<paramtres effectifs>);


Time T(16,30,25);

Cas d'un objet dynamique Pour la cration dynamique d'objets, seul l'appel explicite du constructeur est possible. NomClasse* ptrObjet = new NomClasse(<paramtres effectifs>);
Time* T = new Time(16,30,25);

Exemple :
Time T1(16,30,25); Time* T2 = new Time(17,44,59); T1.Affiche(); T2->Affiche();

Version 3.7

86

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La surcharge des constructeurs


Il est possible de dfinir plusieurs constructeurs pour une mme classe. Ces constructeurs sont alors dits surchargs. Les constructeurs d'une mme classe doivent respecter les rgles de surcharge des fonctions en C++. En d'autres mots, ils doivent avoir des signatures diffrentes. Exemple :
class Time { int Hour; int Minute; int Second; public : Time(int H,int M, int S) { Hour = H; Minute = M; Second = S; } Time() { } void Saisir() { cout<<"Donner l'heure : " cin>>Hour>>Minute>>Second; } void Afficher() { cout<<"L'heure est : "; cout<<Hour<<':'<<Minute<<':'<<Second<<endl; } }; void main() { Time T1; T1.Saisir(); Time T2(16,15,30); T1.Afficher(); T2.Afficher(); }

La classe Time dispose de deux constructeurs : un paramtr et un autre qui ne l'ai pas. Ces deux constructeurs respectent la rgle de surcharge de mthodes (ils ont deux signatures diffrentes). Remarque 1 : Si une classe dispose de plusieurs constructeurs alors au moment de la cration d'un objet partir de cette classe il y aura appel un parmi ces constructeurs. Dans l'exemple ci-dessus, la cration de l'objet T1 est faite l'aide du constructeur sans arguments. Cet objet est non initialis. Son contenu sera par la suite saisi avec la mthode Saisir. La cration de l'objet T2 est faite l'aide du constructeur paramtr. Son contenu est initialis 16h15mn30s.
Version 3.7

87

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque 2 : Tout objet ne peut tre cr qu' travers un appel un constructeur. Par consquent, ce sont les constructeurs dfinis dans une classe qui dterminent la manire avec laquelle peuvent tre instancis les objets partir de cette classe. Dans l'exemple ci-dessus la classe Time dispose de deux constructeurs et offre donc deux faons pour instancier les objets (celles utilises respectivement par T1 et T2). A titre d'exemple, Si le constructeur sans arguments Time() n'tait pas dfini dans la classe Time alors l'instanciation Time T1; serait syntaxiquement incorrecte (erreur de compilation). Remarque 3 : Il est toujours recommand de dfinir plusieurs constructeurs dans une classe et ce pour offrir aux utilisateurs de cette dernire plus de scnarios possibles et une plus grande souplesse concernant l'instanciation des objets. Par exemple la classe Time, qui constitue un type personnalis, a t conue de faon offrir les mmes scnarii d'instanciation que ceux offerts par les types standards du langage (int, char, float, ) concernant la cration des variables. Ainsi pour le type int par exemple il est possible de crer une variable initialise ou une variable non initialise et laisser la main l'utilisateur du programme pour lui affecter une valeur. Ces deux scnarios sont galement possibles pour le type Time grce aux constructeurs dfinis dans cette classe comme le montre le tableau suivant : Cration sans initialisation puis saisie de valeur int (Type standard) Time (Type personnalis)
int i1; cin>>i1, Time T1; T1.Saisir();

Cration et initialisation
int i2 =5; Time T2(16,15,30);

Le destructeur
Un destructeur joue le rle symtrique du constructeur. Il est automatiquement appel pour librer lespace mmoire de base ncessaire lexistence mme de lobjet (espace occup par les attributs). Cette libration est implicite. Elle est effectue par tout destructeur. Il est gnralement recommand dexploiter le destructeur pour faire : La libration dventuels espaces mmoires dynamiques associs aux attributs de lobjet. Contrairement la libration de lespace de base, cette libration ne seffectue pas dune manire automatique. Elle doit tre faite dune manire explicite par le programmeur. La fermeture dventuels fichiers utiliss par lobjet. Lenregistrement des donnes, etc. Dclaration et dfinition dun destructeur : Un destructeur porte le mme nom que celui de la classe mais prcd dun tilde ~ (pour le distinguer du constructeur). Un destructeur ne retourne aucune valeur.

Version 3.7

88

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Un destructeur ne prend jamais de paramtres. Un destructeur est une mthode de la classe. Toutes les rgles qui sappliquent aux mthodes (porte, qualification daccs, dclaration, dfinition) sappliquent donc galement au destructeur. Une classe ne peut disposer que dun seul destructeur. Il est toujours non paramtr. Exemple 1 :
class Time { int Hour; int Minute; int Second; public : Time () { .... } Time (int H, int M, int S) { .... } Time (const char* str) { .... } ~Time() {} // Destructeur vide Afficher() { .... } } ;

La classe Time possde des attributs de type auto. Donc la libration d'un objet de cette classe ne demande aucun traitement supplmentaire particulier. Par consquent son destructeur est dfini vide. Appel du destructeur : Un destructeur peut tre appel dune manire implicite ou explicite. Appel explicite : NomObjet.~NomClasse(); ou

PointeurObjet->.~NomClasse();

Cet appel reste toutefois rare dutilisation. Appel implicite : Un destructeur est implicitement appel lorsque la dure de vie de lobjet est coule. Ceci est le cas : Pour un objet local de la classe mmoire auto, lorsque le domaine de validit de lobjet (bloc dinstructions dans lequel il est dclar) est quitt. Pour un objet global ou local de la classe mmoire "static" lorsque le programme se termine. Pour un objet dynamique cr par new lorsque loprateur delete est appliqu au pointeur qui le dsigne.

Version 3.7

89

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple : L'exemple suivant montre les moments d'appel du constructeur et du destructeur pour les objets de type auto et dynamique.
class X { char NomObjet[20]; public : X(char* NomObj) { strcpy(this->NomObjet, NomObj); cout<<"Excution du constructeur de l'objet : "<<this>NomObjet<<endl; } ~X() { cout<<"Excution du destructeur de l'objet : "<<this>NomObjet<<endl;} }; void main() { X x1("x1"); X* x2 = new X("x2"); delete x2; }

Une excution de ce programme va engendrer la sortie suivante :


Excution Excution Excution Excution du du du du constructeur de l'objet : x1 constructeur de l'objet : x2 destructeur de l'objet : x2 destructeur de l'objet : x1

Destructeur par dfaut Si aucun destructeur na t explicitement dfini pour une classe, alors le compilateur en gnre un par dfaut, public. Ce destructeur ralise par dfaut la libration de lespace mmoire de base (des attributs). Il est par consquent toujours dfini vide de la manire suivante : ~NomClasse(){} Remarque : Le destructeur gnr par dfaut convient gnralement aux classes simples (Exemple : la classe Time). Toutefois, les classes complexes qui demandent des traitements supplmentaires lors de leurs librations ncessitent une dfinition explicite et personnalise de leur destructeur comme le montre le paragraphe suivant.

Application : intrt de la personnalisation des constructeurs et du destructeur


Les constructeurs et le destructeur sont des outils offerts par la POO qui, lorsqu'ils sont bien exploits, peuvent automatiser le droulement de certaines oprations complexes d'initialisation et de libration d'objets et engendrer par consquent un code d'utilisation des classes la fois simple et sre. L'exemple suivant qui a pour objectif de dfinir une classe reprsentant un tableau dynamique d'entiers (TabEntiers) illustre ce cas de figure.

Version 3.7

90

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Premire version de la classe TabEntiers On considre la premire dfinition de la classe TabEntiers.


class TabEntiers { int Nb; int* T; public : void Saisir() { for(int k=0;k<Nb;k++) { cou<<"Donner l'lment d'indice "<<k; cin>>T[k]; } } void Afficher() { for(int k=0;k<Nb;k++) { cout<<T[k]<<' '; } } };

La classe TabEntiers ne dispose d'aucun constructeur ou destructeur explicitement dfinis. Ces derniers seront alors automatiquement gnrs par le compilateur. Ils ont respectivement les dfinitions suivantes :
TabEntiers(){} ~TabEntiers(){}

Avec de telles dfinitions, la fonction principale suivante engendrera une erreur au moment de l'excution.
void main() { TabEntiers TE; // Appel implicite du constructeur par dfaut TE.Saisir(); TE.Afficher(); }

L'erreur survient exactement lors de l'excution de la mthode Saisir et plus prcisment au niveau de l'instruction cin>>T[k]. Elle est due l'absence d'allocation dynamique de l'attribut T de l'objet TE. Puisque tout objet de TabEntiers ncessite obligatoirement une allocation dynamique de l'espace mmoire pour l'attribut T alors il est possible d'automatiser cette opration en plaant le code qui la ralise dans le constructeur de la classe. La deuxime version de la classe TabEntiers met en uvre cette recommandation.

Version 3.7

91

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Deuxime version de la classe TabEntiers


class TabEntiers { int Nb; int* T; public : TabEntiers(int Ni) { Nb=Ni; T=new int [Nb]; } TabEntiers(int* Ti,int Ni) { Nb=Ni; T=new int [Nb]; for(int k=0; k<Nb; k++) { T[k]=Ti[k]; } } void Saisir() { for(int k=0;k<Nb;k++) { cou<<"Donner l'lment d'indice "<<k; cin>>T[k]; } } void Afficher() { for(int k=0;k<Nb;k++) { cout<<T[k]<<' '; } } };

Cette version de la classe TabEntiers dispose de deux constructeurs explicitement dfinis : le premier permet de crer un objet convenablement allou en mmoire mais non initialis. Son contenu peut tre saisi avec la mthode Saisir. Le deuxime constructeur permet de crer un objet directement initialis avec des entiers stocks dans un tableau pass comme argument. Les deux fonctions principales suivantes illustrent l'utilisation de ces deux constructeurs.
void main() { TabEntiers TE(2); TE.Saisir(); TE.Afficher(); } void main() { int Ti[3]={21,3,45}; TabEntiers TE(Ti,3); TE.Afficher(); }

Problme de fuite de mmoire : L'excution de chacune des deux fonctions principales en utilisant la classe TabEntiers telle qu'elle est dfinie dans sa deuxime version engendre une fuite de mmoire qui concerne le tableau point par l'attributs T de l'objet TE. En effet cet espace tant dynamiquement cr par new ne sera pas libr travers l'appel (implicite dans ce cas) du destructeur de la classe (destructeur par dfaut en l'absence d'une dfinition explicite de ce dernier).

Version 3.7

92

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

TE
int Nb 3 21 3 45 21 3 45 int* T FFE4D8A

Etat de la mmoire durant le main

Etat de la mmoire suite l'appel du destructeur par dfaut aprs la fin du main

Cet espace ne peut en effet tre libr de la mmoire qu' travers un appel explicite delete de la manire suivante: delete TE.T; Toutefois un tel appel n'est possible que si T est un attribut publique ce qui n'est pas le cas ici. Mme si T tait publique, il serait dconseill de laisser la tche de libration de la mmoire l'utilisateur de la classe cause du risque d'oubli. Pour remdier ce problme, il serait plus astucieux de placer le code de libration dans le destructeur ce qui conduit vers la version finale de la classe TabEntiers. Version finale de la classe TabEntiers La classe TabEntiers utilise en plus de l'espace mmoire ncessaire pour les attributs un espace mmoire dynamique (tableau point par T) qui est allou chaque cration d'un objet. Pour automatiser la libration de cet espace il suffit de dfinir explicitement le destructeur et d'y placer les instructions qui ralisent cette opration comme suit :
~TabEntiers(){delete[] T;} //Destructeur

La version finale de la classe TabEntiers sera alors comme suit :


class TabEntiers { int Nb; int* T; public : TabEntiers(int Ni) { Nb=Ni; T=new int [Nb]; } TabEntiers(int* Ti,int Ni) { Nb=Ni; T=new int [Nb]; for(int k=0; k<Nb; k++) { T[k]=Ti[k]; } } ~TabEntiers() { delete[]T; } void Saisir() { for(int k=0;k<Nb;k++) { cou<<"Donner l'lment d'indice "<<k; cin>>T[k]; } }

Version 3.7

93

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

void Afficher() { for(int k=0;k<Nb;k++) { cout<<T[k]<<' '; } } }; void main() { TabEntiers TE(2); TE.Saisir(); TE.Afficher(); }

L'instanciation de l'objet TE fait appel au premier constructeur de la classe TabEntiers. Ce dernier va allouer de l'espace mmoire pour les deux attributs ainsi que le tableau dynamique. TE tant un objet de type auto, son destructeur (explicitement dfini dans ce cas) sera automatiquement appel la fin du main. Ce dernier va commencer par librer l'espace mmoire dynamique puis les attributs. Intrt d'une bonne exploitation du constructeur et du destructeur Le fait de placer les instructions d'allocation et de libration dynamiques de la mmoire pour l'attribut T respectivement dans le constructeur et le destructeur de la classe TabEntiers permet d'automatiser le droulement de ces oprations pour tout objet de cette classe. Une telle conception de la classe permet donc d'obtenir un code d'utilisation simple (voir le main) dans lequel toutes les oprations complexes (relatives la gestion dynamique de la mmoire dans ce cas) sont dfinies une seule fois au moment de la construction de la classe et se droulent d'une manire transparente (automatique et cache) pour chaque objet instanci partir de cette classe.

Constructeurs particuliers
Il existe en C++ trois types de constructeurs qui sont un peu particuliers. Leur particularit provient du fait qu'ils peuvent tre appels automatiquement d'une manire parfois cache par le compilateur. Ces constructeurs sont : le constructeur par dfaut, le constructeur de recopie, le constructeur de transtypage.

Constructeur par dfaut


Dfinition On appelle constructeur par dfaut tout constructeur qui peut tre appel sans lui passer d'arguments.

Version 3.7

94

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
#include <iostream.h> #include <stdlib.h> #include <iomanip.h> class Time { int Hour; int Minute; int Second; public : Time() // Constructeur qui initialise l'heure par dfaut Midi {Hour = 12; Minute = 0; Second = 0; } Time(int H, int M, int S) {Hour = H; Minute = M; Second = S;} void Affiche() { cout<<"L'heure est : "; cout<<setw(2)<<setfill('0')<<Hour<<':' <<setw(2)<<setfill('0')<<Minute<<':' <<setw(2)<<setfill('0')<<Second<<endl; } }; Time T; // T contient Hour=12, Minute=0, Second=0;

Remarque : La dfinition donne au dbut de ce paragraphe permet de considrer comme tant un constructeur par dfaut : un constructeur qui ne possde aucun paramtre, un constructeur qui possde des paramtres ayant tous des valeurs par dfaut. En effet, un tel constructeur peut tre appel sans qu'il ncessite aucun passage explicite de paramtres (cf. chapitre les fonctions en C++). Ce type de constructeurs doit tre utilis avec prcaution car il peut poser dans certains cas une ambigut au moment de l'instanciation des objets comme le montre l'exemple suivant. Exemple : Considrons la dfinition suivante de la classe Time :
class Time { int Hour; int Minute; int Second; public : // Constructeur qui initialise l'heure par dfaut Midi Time() { Hour = 12; Minute = 0; Second = 0; }

Version 3.7

95

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

// Constructeur ayant des paramtres qui ont tous des valeurs par // dfaut Time(int H=12,int M=0, int S=0) { Hour = H; Minute = M; Second = S; } };

Au moment de la dfinition de la classe aucun problme d'ambigut ne se pose car les deux constructeurs ont des signatures diffrentes. Toutefois l'ambigut peut tre rencontre au moment de l'instanciation. Considrons par exemple l'instanciation suivante : Time T; Suite cette instruction, le compilateur signale une erreur car il ne peut pas dterminer quel constructeur appeler pour la cration et l'initialisation de T. En effet, tout comme le constructeur sans paramtres, le constructeur paramtr ayant des valeurs par dfaut peut tre appel sans aucun argument. Constructeur par dfaut implicite Si aucun constructeur n'a t explicitement dfini pour une classe, alors le compilateur gnre automatiquement un, publique. Ce constructeur est un constructeur par dfaut (sans paramtres). Ce constructeur assure seulement la cration d'objets. Il ne fait aucune initialisation. De ce fait si un objet cr l'aide de ce constructeur est : o Local : alors son contenu sera alatoire. o statique ou global : alors tous ses attributs seront initialiss 0. Exemple :
class Time { int Hour; int Minute; int Second; public : Affiche(){ .... } }; Time T1; // objet global void main() { static Time T2; // objet static Time T3; // objet local de type auto T1.Affiche(); // 00:00:00 T2.Affiche(); // 00:00:00 T3.Affiche(); // -2145455875:-1245652381:-2896574215 }

Remarque importante : Le compilateur ne gnre aucun constructeur par dfaut pour les classes qui possdent au moins un constructeur explicitement dfini, peu importe que ce constructeur soit de type par dfaut ou non.

Version 3.7

96

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
class Time { int Hour; int Minute; int Second; public : Time (int H, int M, int S){ .... } Affiche(){ .... } }; void main() { .... Time T; // Erreur pas de constructeur par dfaut .... }

Constructeur de recopie (par copie)


Dfinition Un constructeur de recopie est un constructeur qui permet de crer un objet et de l'initialiser avec le contenu d'un autre objet de la mme classe. L'utilisation des constructeurs de recopie revte d'une grande importance surtout avec les objets qui possdent des attributs dynamiques. Caractristiques syntaxiques Le premier paramtre formel du constructeur de recopie est une rfrence un objet de la classe du constructeur. Un constructeur de recopie peut avoir d'autres paramtres supplmentaires. Ces derniers doivent obligatoirement avoir des valeurs par dfaut. Un constructeur de recopie est toujours appel avec un seul argument (le premier argument). L'appel des arguments supplmentaires ventuels se fait d'une manire implicite. Syntaxe gnrale :
NomClasse(<const> NomClasse& Param1<,Type2 Param2=DefVal2,..,Type_n Param_n=DefVal_n>);

Exemple :
class Time { ... public : Time(const Time& T){...} // Constructeur de recopie ... };

Remarque 1 : Pourquoi un premier paramtre de type rfrence ? La question laquelle rpond cette remarque est la suivante : pourquoi passe-t-on au constructeur de recopie une rfrence l'objet copier et non une copie de cet objet ? En d'autres mots, pourquoi utilise-t-on un passage par rfrence l o le passage par valeur semble suffisant? 97

Version 3.7

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Justification du passage par rfrence : Considrons la classe Time dfinie prcdemment. On ajoute cette classe un constructeur de recopie qui prend un paramtre Tm de type Time.
class Time { ... public : Time(){...} Time(Time Tm){...} // On suppose que c'est possible ... };

Soient f une fonction qui prend un paramtre Tf de type Time et X une instance de Time :
void f(Time Tf){...} Time X; L'appel de f avec le paramtre effectif X engendre la copie de X dans le paramtre formel Tf. Cette copie est ralise avec le constructeur de recopie de Time. f(X) // Tf=X Tf(X) Appel au constructeur de recopie de Time et par suite : f(X) f(Time(X)) // instanciation du paramtre formel l'aide du // constructeur de recopie.

Le passage de X au constructeur Time se fait par valeur. Ce constructeur va travailler donc sur une copie de X. Cette copie sera cre par un autre appel au constructeur de recopie. Ce dernier travaillant lui-mme sur une copie de X, il va faire lui aussi un appel lui-mme. Cet appel rcursif du constructeur de recopie va se poursuivre l'infini. Time(X) // Tm=X Tm(X) et par suite Time(X) devient Time(Time(X)) et f(X) devient f(Time(Time(Time ))) Pour rsoudre ce problme il suffit d'viter l'appel implicite du constructeur de recopie qui est engendr par le passage par valeur et de remplacer ce dernier par un passage par rfrence. Ainsi avec Time(Time& Tm), f(X)se rduit seulement f(Time(X)) car l'affectation du paramtre effectif au paramtre formel (Time& Tm=Time(X) ) reprsente une copie de rfrences et non une copie d'objets. Remarque 2 : Pourquoi la recommandation const L'utilisation d'une rfrence constante comme premier paramtre dans les constructeurs de recopie est une recommandation. L'intrt de cette recommandation rside dans la protection de l'objet pass comme argument contre toute modification qui peut survenir par erreur dans le constructeur surtout que son passage se fait par rfrence (on ne travaille pas sur une copie de l'objet mais directement sur ce dernier). Constructeur de recopie par dfaut Considrons l'exemple suivant : Exemple :
class Time { int Hour, Minute, Second;

Version 3.7

98

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

public : Time(){...} Time(int H,int M, int S){...} void Afficher(){...} }; void main() { Time T1(10,12,15); Time T2(T1); T2.Afficher(); }

L'instruction Time T2(T1); reprsente un appel du constructeur de recopie de la classe Time. Cette instruction passe avec succs la phase de compilation malgr l'absence d'une dfinition explicite d'un tel constructeur dans la classe. Par ailleurs, lexcution de ce programme donne 10 :12 :15. Ce rsultat montre bien que lobjet T2 a t initialis avec le contenu de T1. Explication : En cas d'absence d'un constructeur de recopie explicitement dfini, le compilateur en gnre automatiquement un par dfaut. Ce constructeur effectue une copie champ par champ du contenu des deux objets. Limites du constructeur de recopie par dfaut Exemple 1 :
class TabEntiers {public : int Nb; int* T; TabEntiers(int Ni){...} TabEntiers(int* Ti,int Ni){...} void Saisir(){...} void Afficher(){...} }; void main() { int Ti[4]={5,2,8,1,3}; TabEntiers TE1(Ti,4); TabEntiers TE2(TE1); TE2.Afficher(); }

Commentaires : La classe TabEntiers ne dispose pas d'un constructeur de recopie explicitement dfini. Par suite c'est le constructeur de recopie par dfaut qui est utilis dans l'instruction :
TabEntiers TE2(TE1);

De par sa dfinition, ce constructeur effectue une copie champ par champ du contenu de TE1 dans TE2. De ce fait, pour le membre dynamique T, c'est une copie d'adresses qui est effectue entre TE1.T et TE2.T. Ces deux membres font alors rfrence la mme zone en mmoire et par consquent toute modification de la ime case du tableau TE1.T est effectue galement sur la ime case du tableau TE2.T ( cause de la copie incomplte d'objets). 99

Version 3.7

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

TE1
int Nb 5 int* T FFE4D8A copie

TE2
5 int Nb FFE4D8A int* T

Pour remdier cela, il faut que le constructeur de recopie soit dfini de faon ce que chaque objet ait sa propre copie du tableau. L'exemple prcdent montre bien que le constructeur de recopie par dfaut nest pas adapt aux objets possdant des membres dynamiques. Pour ce genre d'objets il faut dfinir explicitement un constructeur qui tient compte de cette particularit. Exemple 2 : Soit la fonction f suivante : void f(TabEntiers TabE); Tout appel de la fonction f avec un paramtre effectif TE va engendrer la cration d'un objet local TabE par recopie par dfaut partir de TE. La fin de l'excution de f va engendrer la suppression de l'objet local TabE par appel son destructeur. Cet appel va conduire vers la libration du tableau d'entiers point dans ce cas la fois par TabE.T et par TE.T. Par consquent, toute rfrence un lment du tableau T partir de TE aprs l'appel de f va engendrer un erreur d'excution.
TE
int Nb 5 int* T FFE4D8A copie

TabE
5 int Nb int Nb FFE4D8A int* T

TE
5 int* T FFE4D8A TE.T ne pointe plus sur aucune zone mmoire alloue. Etat de la mmoire aprs la fin de l'appel de f. Le paramtre formel est dtruit. Il libre galement le tableau.

Etat de la mmoire aprs la copie du paramtre effectif dans le paramtre formel

Solution : Il faut que la copie se fasse d'une manire totale : copie d'attributs mais galement copie des ventuels espaces mmoires dynamiques points par les attributs de type pointeur. Pour remdier ce problme de copie dans la classe TabEntiers, il faut dfinir explicitement le constructeur de recopie de la manire suivante :
TabEntiers::TabEntiers(const TabEntiers& Tc) { Nb=Tc.Nb; T=new int [Nb]; for(int k=0;k<Nb;k++) T[k]=Tc.T[k]; }
TE1
int Nb 5 int* T FFE4D8A Copie

TE2
5 int Nb FFF5E33 int* T

Copie des lments

Chaque objet possde sa propre copie du tableau dynamique d'entiers.

Version 3.7

100

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Lieux dappel dun constructeur de recopie dobjet Un constructeur de recopie est appel : Explicitement par un utilisateur de la classe lors de la cration dun objet laide dun autre objet de la mme classe. Implicitement par le compilateur : o lors de la transmission de la valeur dun paramtre effectif (de type objet) un paramtre formel suite lappel dune fonction. o lors du retour dune valeur de type objet par une fonction. En effet la fonction cre un objet temporaire sans nom laide du constructeur de recopie et ce, partir de lobjet pass return et cest cet objet temporaire qui est affect la variable recevant la valeur de retour de la fonction. Exemple :
class NoData { public : NoData(){cout<<"constructeur par dfaut";} NoData(const NoData& ND){cout<<"constructeur de recopie";} }; void f1(NoData D){} void f2(NoData& D){} NoData f3() { NoData D; ... return D; }; void main() { NoData X; // Constructeur par dfaut f1(X); // Constructeur de recopie (en raison du passage par //valeur) f2(X); // Rien n'est affich X=f3(); // Constructeur par dfaut (pour l'objet local D) // Constructeur de recopie (pour l'objet renvoy) }

Question : Le prototype suivant pour f3 : NoData& f3() permet-il daboutir un rsultat correct mme en l'absence d'un constructeur de recopie explicite ? Le constructeur de recopie n'est pas appel mais on obtient une rfrence sur un objet qui n'existe plus en mmoire puisqu'il est local la fonction.

Version 3.7

101

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les constructeurs de transtypage


Un constructeur de transtypage, appel galement constructeur de conversion, est un constructeur qui permet de crer et d'initialiser un objet d'un type T1 partir d'une donne (objet ou non) d'un autre type T2 diffrent de T1. Caractristiques du constructeur de transtypage Un constructeur de transtypage ne peut tre appel qu'avec un seul paramtre : comme c'est le cas du constructeur de recopie, les paramtres qui suivent ventuellement le premier paramtre formel doivent avoir obligatoirement des valeurs par dfaut. Le premier paramtre du constructeur de transtypage contient la donne convertir. Ce paramtre peut tre de n'importe quel type (prfini, struct, class, etc.) sauf le type de la classe du constructeur. Syntaxe gnrale :
NomClasse(<const> Type1 Param1<,Type2 Param2=DefVal2,..,Type_n Param_n=DefVa_n>);

Exemple :
class Time { int Hour; int Minute; int Second; public : Time(){Hour = 12; Minute = 0; Second = 0;} Time(int H,int M, int S){Hour = H; Minute = M;Second = S;} Time(const char* str) {cout<<"Appel du constructeur de transtypage"; Hour = 10*(*str-'0') + (*(str+1)-'0'); Minute = 10*(*(str+3)-'0') + (*(str+4)-'0'); Second = 10*(*(str+6)-'0') + (*(str+7)-'0'); } void Afficher(){...} };

Le constructeur Time(const char* str) peut tre considr comme tant un constructeur de transtypage puisqu'il rpond aux deux caractristiques de ce type de constructeur savoir un appel l'aide d'un seul paramtre en plus le paramtre possde un type diffrent de Time(const char*). Champs d'utilisation des constructeurs de transtypage Utilisation explicite : Un constructeur de transtypage peut tre explicitement appel par le programmeur chaque fois que ce dernier a besoin de travailler avec un objet d'un certain type et qu'il dispose d'une donne d'un autre type. Exemple
Time X = Time("18:30:12"); ou galement Time X("18:30:12");

Version 3.7

102

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Utilisation implicite : Un constructeur de transtypage d'un type T1 (objet ou non) vers un type objet T2 diffrent de T1 est implicitement appel par le compilateur : Lors de la transmission d'un paramtre effectif de type T1 une fonction ayant un paramtre formel de type T2. Dans une opration d'affectation ayant un membre de droite (objet, variable ou expression) de type T1 et un membre de gauche de type objet T2. Remarque : L'appel implicite du constructeur de transtypage par le compilateur engendre la cration d'un objet temporaire sans non ayant le type de la classe du constructeur. C'est cet objet qui est alors transmis s'il s'agit d'un appel de fonction ou affect s'il s'agit d'une opration d'affectation. Exemple 1 : Soit f une fonction ayant le prototype : void f(Time Tf); Considrons la portion de code suivante :
char T[9] ="18:30:00"; f(T);

A premire vue, cet appel peut sembler incorrect car f prend un argument de type Time et T est de type chane de caractres. Cependant et grce au constructeur de transtypage cet appel est tout fait correct. En effet, au moment de la transmission des paramtres le compilateur effectue un appel implicite au constructeur de transtypage qui va crer partir de T un objet temporaire sans non de type Time. Par la suite c'est cet objet qui sera transmis rellement f. Par ailleurs, et puisque le passage de l'argument de f se fait par valeur, alors le compilateur effectue un autre appel implicite mais cette fois au constructeur de recopie. Le but de cet appel est de faire la copie de l'objet temporaire prcdemment cr dans le paramtre formel de f. L'instanciation du paramtre formel Tf partir du paramtre effectif se traduit rellement comme suit :
Tf(Time(Time(T))); // instanciation du paramtre formel de f.

Exemple 2 : Considrons la portion de code suivante :


Time X; char T[9] ="18:30:00"; X=T;

En considrant la dfinition de la classe Time donne au dbut de ce paragraphe l'affectation entre T et X est tout fait possible. En effet, au moment de l'affectation, le compilateur appelle implicitement le constructeur de transtypage pour crer partir de T un objet temporaire de type Time. C'est cet objet qui est ensuite copi dans X.

Version 3.7

103

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Une classe peut comporter plusieurs constructeurs de transtypage pourvu que ces derniers respectent les rgles de surcharge de mthodes.

Les listes d'initialisation des constructeurs


Une liste d'initialisation de constructeur reprsente une alternative syntaxique pour l'initialisation des attributs d'une classe. Ainsi au lieu de faire cette initialisation dans le corps du constructeur, il devient possible grce cette alternative de la faire tout juste devant l'entte du constructeur selon la syntaxe suivante : NomClasse(arguments) : A1(V1), A2(V2), , An(Vn) O : NomClasse(arguments) reprsente un appel au constructeur, Ai : reprsente les attributs de NomClasse initialiser Vi : valeur initiale affecte l'attribut Ai. Exemple 1 :
// Appel du constructeur de la classe Time Time():Hour(12),Minute(30),Second(45) { }

Exemple 2 :
// Appel du constructeur de la classe TabEntiers TabEntiers(int N, int* Tab) : Nb(N),T(new int[N]) { for(int i = 0;i<Nb;i++) T[i] = Tab[i]; }

Remarques : Il n'est pas obligatoire de mentionner les attributs dans une liste d'initialisation dans le mme ordre que celui de leur dclaration dans la classe. L'ordre de l'initialisation effective des attributs suit toujours l'ordre de dclaration de ces derniers dans la dfinition de la classe mme si cet ordre n'est pas respect dans la liste d'initialisation. Une liste d'initialisation peut se contenter d'initialiser seulement une partie des attributs d'une classe. Initialisation des attributs constants et rfrences Pour les attributs constants et rfrences, les listes d'initialisation ne constituent pas une simple alternative d'criture de code mais reprsentent plutt le seul moyen permettant de leurs affecter leurs valeurs initiales. Considrons la classe ConstRef qui comporte les trois attributs suivants : x (entier), r (rfrence un entier) et y (constante entire).

Version 3.7

104

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

class ConstRef { int x; int& r = x ; const int y = 5; };

// ERROR // ERROR

Le fait que r et y soient respectivement une rfrence et une constante rend leurs initialisations obligatoires. Toutefois leurs initialisations telles que ralises dans la classe ConstRef sont interdites. En effet, la dfinition d'une classe constitue une dfinition de type sans aucune instanciation en mmoire. Par consquent au moment de la dfinition de ConstRef aucun attribut n'a une existence physique en mmoire. Par suite, initialiser r avec la rfrence d'une variable x qui n'existe pas ou galement affecter 5 une constante y qui n'existe pas en mmoire reprsentent des oprations interdites. L'utilisation d'un constructeur dfini comme suit est galement interdite :
ConstRef::ConstRef( ){ x =3; // OK r = x; // ERROR y = 5; // ERROR }

En effet, l'instruction r=x; reprsente une affectation de la valeur 3 la variable rfrence par r. Or r ne rfrence aucune variable car elle n'a pas t initialise. Par ailleurs, l'instruction y=5; reprsente une affectation de la valeur 5 la constante y et non une initialisation. Or les oprations d'affectations ne peuvent pas tre appliques aux constantes. Le seul moyen en C++ permettant d'initialiser de tels membres consiste dans l'utilisation des listes d'initialisation. Exemple :
class ConstRef { int x; int& r ; const int y ; ConstRef( ) : r(x),y(5) {x=3;} };

Initialisation d'objets membres Les listes d'initialisation sont galement utilises pour initialiser les membres qui sont de type classe (membres objet) essentiellement lorsque ces derniers possdent des constructeurs paramtrs. La liste d'initialisation constitue le seul moyen permettant de crer et d'initialiser un objet membre si la classe de ce dernier ne dispose pas de constructeur par dfaut (implicite ou explicite).

Version 3.7

105

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
class X { int a; public : X(int i) { a=i; cout<<"Constructeur de X\n"; } }; class Y { int b; X x; public : Y():b(5),x(55) {cout<<"Constructeur de Y\n";} };

L'initialisation du membre x par la liste comme suit : x(55) est possible parce qu'un constructeur ayant le prototype X(int i) est dfini pour la classe X. Remarque 1: En tenant compte de la dfinition de la classe X, la dfinition suivante du constructeur de Y n'est pas correcte :
Y() { b=5; x=X(55); cout<<"Constructeur de Y"; }

En effet, l'instruction x=X(55); ne constitue pas syntaxiquement une initialisation au vrai sens du terme. Elle est plutt considre comme tant une premire affectation x du contenu de l'objet temporaire X(55). Cela suppose que x a t cr auparavant l'aide d'un constructeur par dfaut chose qui n'est pas possible faute de ce type de constructeur dans la dfinition de la classe X.

Constructeur et destructeur des classes anonymes


Les classes anonymes ne peuvent pas avoir des constructeurs et un destructeur explicitement dfinis. Seuls les constructeurs et le destructeur implicitement synthtiss par le compilateur sont utiliss par ce genre de classes.

Les tableaux d'objets


La cration d'un tableau d'objets d'une classe ncessite un appel du constructeur de la classe pour chaque lment du tableau. Si le constructeur de la classe demande des paramtres d'initialisation, alors ces paramtres doivent tre passs, pour tout le tableau, travers une liste comprenant des enregistrements de valeurs, placs entre deux accolades et spars par des virgules. Dans cette liste la spcification des valeurs d'initialisation doit respecter un certain nombre de rgles :

Version 3.7

106

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

o Si la classe possde un constructeur qui demande des paramtres, alors ce constructeur doit tre explicitement appel dans la liste d'initialisation. o Si la classe dispose d'un constructeur qui prend un seul paramtre, alors ce paramtre peut tre directement spcifi dans la liste d'initialisation sans avoir besoin de faire un appel explicite du constructeur. o Si la classe possde plusieurs constructeurs, il n'est pas obligatoire d'appeler le mme constructeur pour initialiser tous les objets : diffrents constructeurs peuvent en effet tre appels pour les diffrents lments du tableau. o Si un constructeur par dfaut est dfini pour la classe, alors la liste d'initialisation peut tre omise. Dans ce cas le constructeur par dfaut sera automatiquement appel pour tous les lments du tableau. Exemple :
class Time {int Hour; int Minute; int Second; public : Time(){Hour = 12; Minute = 0; Second = 0;} Time(int H,int M, int S){Hour = H; Minute = M; Second = S;} Time(const char* str) { Hour = 10*(*str-'0') + (*(str+1)-'0'); Minute = 10*(*(str+3)-'0') + (*(str+4)-'0'); Second = 10*(*(str+6)-'0') + (*(str+7)-'0'); } void Afficher(){...} }; void AfficherTab(Time* Tab, int N) { for(int i=0;i<N;i++) { (*(Tab+i)).Afficher(); cout<<'\n'; } } void main() { Time Tab1[3]; AfficherTab(Tab1,3); Time Tab2[3]={Time(), Time(), Time()}; AfficherTab(Tab2,3); Time Tab3[4]={"10:15:30", Time("15:30:45"), Time(14,20,30), Time()}; AfficherTab(Tab3,4); Time Tab4[4]={"10:15:30", Time(14,20,30)}; AfficherTab(Tab4,4); }

Remarque : L'appel explicite du constructeur peut tre omis seulement pour les derniers lments du tableau. Dans ce cas c'est le constructeur par dfaut qui est implicitement appel (Exemple Tab4). Un lment du tableau, pour lequel l'appel du constructeur se fait d'une manire implicite ne peut pas avoir sa droite un lment pour lequel l'appel du constructeur se fait d'une manire explicite.

Version 3.7

107

Karim Kalti

Constructeurs et destructeur Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
Time Tab[4]={"10:15:30", , Time(14,20,30), Time()}; //ERREUR

Cas des tableaux dynamiques Il nest pas possible d'utiliser une liste d'initialisation pour crer dynamiquement un tableau dobjets. Exemple :
Time* Tab=new Time[2] (Time(10,04,2000),Time(05,04,1999));// ERREUR

Pour crer dune manire dynamique un tableau dobjets, la classe des objets doit avoir obligatoirement un constructeur par dfaut (implicite ou explicite). Seul ce constructeur peut tre utilis dans ce cas. Exemple :
Time* Tab=new Time[2]//OK:Appel implicite du constructeur par dfaut

La forme canonique d'une classe


Les versions par dfaut du constructeur de recopie, du destructeur et de l'oprateur d'affectation conviennent seulement aux classes "simples" : ne comportant pas d'attributs dynamiques. En cas de prsence de ce genre d'attributs alors la classe sera dans l'obligation de dfinir explicitement ces mthodes afin de tenir compte des espaces points par ces derniers. La forme canonique d'une classe est une sorte de modle de dfinition de base que doit respecter toute classe non triviale (comportant des attributs dynamiques) pour garantir un bon usage pour ses utilisateurs. Cette forme canonique comporte : o La dfinition explicite d'un constructeur par dfaut pour permettre entre autres la cration de tableaux dynamiques d'objets de cette classe. o La dfinition explicite d'un constructeur de recopie pour assurer un passage par valeur et un renvoi correctes des objets de cette classe par les fonctions des utilisateurs. o La dfinition explicite d'un destructeur pour la libration adquate des ressources (espaces points). o La surcharge de l'oprateur d'affectation pour garantir une copie correcte et convenable entre objets de cette classe. D'une manire concrte une classe T sous forme canonique doit se prsenter selon le modle minimal suivant :
class T { ... ... ... public : T(); // Constructeur par dfaut T(const T&); // Constructeur de recopie ~T(); // Destructeur T& T::operator = (const T&); // Surcharge de l'oprateur = ... ... ... };

Version 3.7

108

Karim Kalti

Le espaces de noms Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les espaces de noms


Lintrt derrire la POO est le dveloppement de projets sous formes de modules. Chaque module tant reprsent par une classe spcialise dans une tche donne. Un grand projet peut comporter plusieurs modules. Ces derniers peuvent tre dvelopps par lauteur mme de lapplication ou par des tiers. Le grand nombre de modules fait natre le plus souvent un besoin dorganisation des classes. Une organisation qui concerne le regroupement et lattribution des identificateurs ces classes. Le C++ offre un outil pour assurer cette organisation. Cet outil est celui des espaces de noms. Un espace de noms est un paquetage (bibliothque) qui regroupe des classes ou des fonctions dans une mme entit. Le regroupement des classes peut se faire selon plusieurs critres tels que par thme des classes, par auteur, etc. Outre le regroupement, les espaces de noms permettent de rsoudre les problmes de conflit des identificateurs des classes qui peuvent survenir notamment dans les projets faisant intervenir des bibliothques diverses et comportant des classes diffrentes mais portant le mme nom.

Dfinition dun espace de noms


La dfinition dun espace de noms se fait laide de mot-cl namespace de la manire suivante : Syntaxe : namespace Identificateur { Dclaration des classes et des fonctions de lespace de noms } Identificateur dsigne le nom de lespace de noms en cours de dfinition. La dfinition dun espace de noms ne se termine pas par un point-virgule et ce contrairement la dfinition dune classe. Exemple :
namespace MesClasses { X(){... ...}; }

Accs une classe dfinie dans un espace de noms


Un espace de noms cache les classes quil contient. Ces dernires ne seront alors accessibles qu travers cet espace de noms et ce laide de loprateur de rsolution de porte (::). Syntaxe : NomEspaceDeNoms::NomClasse;
Version 3.7

109

Karim Kalti

Le espaces de noms Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
namespace MesClasses { X(){... ...}; } int main() { MesClasses::X x1(); //OK X x2(); //Erreur return 0; }

Rsolution des conflits de noms


Il est possible grce aux espaces de noms dutiliser deux classes qui portent le mme nom sans engendrer un conflit. Exemple :
namespace MesClasses { X(){... ...}; } namespace MesAutresClasses { X(){... ...}; } int main() { MesClasses::X x1(); MesAutresClasses::X x2(); return 0; }

Dans cet exemple x1 reprsente un objet de la classe X de MesClasses et x2 est un objet de la classe X de MesAutresClasses.

La dclaration using
Afin damliorer la lisibilit du code, il est possible dindiquer ds le dpart en une seule fois la provenance des classes qui seront utilises. Cette indication se fait pour chaque classe laide du mot rserv using de la manire suivante : Syntaxe : using EspaceDeNoms::NomClasse; La classe NomClasse peut alors tre dsigne directement par son nom dans lespace de porte dans lequel a t faite la dclaration using. Exemple :
namespace MesClasses { X(){... ...}; Y(){... ...}; }

Version 3.7

110

Karim Kalti

Le espaces de noms Programmation oriente objet (C++) _________________________________________________________________________________________________________________

using MesClasses::X; int main() { X x(); //OK la classe X est connue dans tout le fichier Y y1(); //Erreur MesClasses::Y y2(); //OK return 0; }

La directive using
En labsence de conflits, il est possible de rendre tous les lments dun espace de noms accessibles et viter de les spcifier ainsi un par un. Cette spcification globale se fait laide de la directive using de la manire suivante : Syntaxe : using namespace EspaceDeNoms; Exemple :
namespace MesClasses { X(){... ...}; Y(){... ...}; } using namespace MesClasses; int main() { X x(); // OK Y y(); // OK return 0; }

Espace de noms anonyme


Il est possible de dfinir en C++ un espace de noms anonyme. Un tel espace ne comporte pas de noms et ne peut tre par consquent invoqu de nimporte quel autre endroit. Les espaces anonymes peuvent tre utiles pour la protection du code puisque toute entit dclare lintrieur dun tel espace ne pourra plus tre invoque de lextrieur et sera par consquent prive cet espace.

Espace de nom standard std .


La bibliothque standard de classes fournie avec tout systme C++ qui respecte la norme ISO/ANSI dfinit tous ses symboles dans lespace de noms appel std. Pour viter de mentionner cet espace lors de la manipulation des lments du C++, il suffit dutiliser la directive using. Exemple :
#include <iostream> std::cout<<Ceci est un message;

ou galement
#include <iostream> using namespace std; cout<<Ceci est un message;

Version 3.7

111

Karim Kalti

Membres statiques et membres constants d'une classe Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Membres statiques et membres constants d'une classe


Attributs statiques
L'une des caractristiques les plus importantes des classes est que chaque instance (objet) possde sa propre copie d'attributs (sa propre zone mmoire). Cette copie constitue l'identit propre l'objet. La dure de vie des attributs est gale dans ce cas la dure de vie de l'objet. Toutefois, dans certains cas, il est intressant de crer des attributs qui sont indpendants des objets et qui sont plutt lis la classe ou en d'autres mots au modle que dfinit la classe. De tels attributs peuvent tre partages par toutes les instances de la classe et sont appels des attributs statiques. Dclaration d'un attribut statique La dclaration d'un attribut statique se fait l'intrieur de la dfinition de la classe l'aide du mot-cl static comme suit : class NomClasse { static Type NomAttributStatique; }; Dfinition d'un attribut statique Un attribut statique doit tre obligatoirement dfini. Cette dfinition se fait d'une manire globale en dehors de la classe sans l'utilisation du mot-cl static. Toutefois le nom de l'attribut doit tre compltement qualifi comme suit : Type NomClasse::NomAttributStatique = Valeur; L'affectation d'une valeur initiale l'attribut statique lors de sa dfinition n'est pas obligatoire. Un attribut statique non explicitement initialis sera automatiquement initialis par le systme 0. Exemple 1 : Supposons que l'on souhaite crer par programmation une scne qui reprsente une chute de neige. Cette neige sera reprsente par un ensemble de cercles de mme rayon remplis par la couleur blanche. Le programme devra galement donner la possibilit l'utilisateur de modifier la taille des grains de neige. Cette modification affectera l'attribut Rayon de tous les cercles de manire uniforme. Une premire possibilit pour reprsenter les cercles consiste utiliser la classe suivante.
class Cercle { int x; int y; int Rayon;

Version 3.7

112

Karim Kalti

Membres statiques et membres constants d'une classe Programmation oriente objet (C++) _________________________________________________________________________________________________________________

public : Cercle(int xi, int yi){x=xi; y=yi;} Cercle(){x=0; y=0;} void Deplacer(int dx, int dy){x+=dx; y+=dy;} void SetRayon(int R){Rayon = R;} void Dessiner(){...} };

Avec cette spcification chaque cercle possde sa propre copie de l'attribut Rayon. Or puisque tous les cercles possdent la mme taille, il devient plus intressent de faire partager l'attribut Rayon par toutes les instances de cette classe en le dclarant comme statique.
class Cercle { int x; int y; static int Rayon; public : Cercle(int xi, int yi){x=xi; y=yi;} Cercle(){x=0; y=0;} void Deplacer(int dx, int dy){x+=dx; y+=dy;} void SetRayon(int R){Rayon = R;} void Dessiner(){...} }; // Dfinition de l'attribut statique int Cercle::Rayon = 2;

Un tel partage possde deux avantages : Il permet un gain en espace mmoire. En effet, une seule zone mmoire sera utilise par toutes les instances. Il permet de diminuer la quantit de code ncessaire la modification d'une caractristique commune toutes les instances. Par exemple, pour modifier la taille de la neige, il suffit de modifier la valeur de l'attribut Rayon une seule fois. Cette modification affectera toutes les instances. Caractristiques d'un attribut statique : Un attribut statique est partag par toutes les instances d'une classe. Sa dure de vie ne dpend pas de celles des objets. Elle s'tend depuis l'endroit de dfinition de l'attribut jusqu' la fin du programme. De ce point de vue, un tel attribut se comporte comme une variable globale. Toutefois, et la diffrence de cette dernire, un attribut statique n'est visible qu' l'intrieur de sa classe (sa visibilit est lie la classe) alors q'une variable globale est visible dans tout le programme. Un attribut statique existe en mmoire mme si aucun objet de la classe na encore t cr. Un attribut statique est souvent appel attribut de classe puisque son existence dpend de l'existence de la classe et non de l'existence des objets. Un attribut non statique est appel attribut d'instance puisque son existence est lie l'instanciation de la classe. Un attribut statique peut tre manipul par les mthodes de sa classe comme n'importe quel autre attribut non statique. Un attribut statique peut tre qualifi de priv, de publique ou de protg.

Version 3.7

113

Karim Kalti

Membres statiques et membres constants d'une classe Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Un attribut statique publique peut tre appel de l'extrieur de la classe travers son nom qualifi. Cette qualification peut tre faite l'aide : o Du nom de la classe : NomClasse::NomAttributStatic; o Du nom d'un objet : NomObjet.NomAttributStatic; o Du nom d'un pointeur sur un objet : NomPtrObjet->NomAttributStatic; Exercice d'application : Supposons que l'on souhaite calculer l'espace mmoire occup par toutes les instances d'une classe dans un programme en cours d'excution. Ce calcul passe par la dtermination du nombre d'objets instancis. Une solution possible ce problme consiste utiliser un attribut statique qui s'incrmente chaque instanciation d'un nouvel objet et qui se dcrmente chaque destruction d'un objet. Proposer une implmentation de cette solution avec la classe Point qui reprsente un point dans un repre deux dimensions. Solution
class Point { int x; int y; public : static int Memory; Point():x(0),y(0) { Memory+=sizeof(Point); } Point(int a, int b):x(a),y(b) { Memory+=sizeof(Point); } ~Point() { Memory-=sizeof(Point); } }; int Point::Memory =0; void main() { cout<<" Mmoire occupe :"<<Point::Memory<<endl; Point P1(10,10); cout<<" Mmoire occupe :"<<Point::Memory<<endl; Point *P2= new Point(5,5); cout<<" Mmoire occupe :"<<Point::Memory<<endl; delete P2; cout<<" Mmoire occupe :"<<Point::Memory<<endl; }

Mthodes statiques
Tout comme les attributs, il est possible de dclarer les mthodes comme tant statiques. Une mthode statique est gnralement une mthode qui ralise une tche qui est indpendante des instances et qui est plutt lie au modle dfini par la classe.

Version 3.7

114

Karim Kalti

Membres statiques et membres constants d'une classe Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Une mthode statique est appele une mthode de classe alors qu'une mthode non statique est appele une mthode d'instance. Dclaration d'une mthode statique Une mthode statique est dclare dans la dfinition d'une classe l'aide du mot-cl static comme suit : class NomClasse { ... ... ... static Type NomMethodeStatique(<Liste des paramtres>); ... ... ... }; Dfinition d'une mthode statique Comme les mthodes classiques, une mthode statique peut tre dfinie l'intrieur ou l'extrieur de la classe. Dfinition l'intrieur de la classe class NomClasse { ... ... ... static Type NomMethodeStatique(<Liste des paramtres>) { ... } ... ... ... }; Dfinition l'extrieur de la classe La dfinition d'une mthode statique l'extrieur de la classe n'utilise pas le mot-cl static. Elle doit tre prcde par la dclaration de la mthode l'intrieur de la classe : Type NomClasse::NomMethodeStatique(<Liste des paramtres>) { ... } Appel d'une mthode statique Appel de l'intrieur de la classe Une mthode statique peut tre appele de l'intrieur de sa classe comme une mthode classique et ce directement par son nom (sans considration des droits d'accs et sans qualification). Appel de l'extrieur de la classe Une mthode statique publique peut tre appele de l'extrieur de la classe partir : Du nom de la classe : NomClasse::NomMethodeStatique(); D'un objet de la classe : NomObjet.NomMethodeStatique(); D'un pointeur sur un objet : NomPtrObjet->NomMethodeStatique(); Remarques : Une mthode statique : 115

Version 3.7

Karim Kalti

Membres statiques et membres constants d'une classe Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Peut appeler les mthodes statiques de sa classe. Ne peut pas appeler les mthodes non statiques de sa classe. Peut appeler les mthodes statiques publiques des autres classes. Peut appeler les mthodes d'instances publiques des objets qui sont dclars en local l'intrieur de cette mthode. Une mthode non statique (mthode d'instance) peut appeler : o Les mthodes statiques et non statiques de sa classe. o Les mthodes statiques publiques des autres classes. o Les mthodes d'instances publiques des objets qui lui sont locaux. o o o o Accs aux attributs par une mthode statique Une mthode statique ne peut accder qu'aux attributs statiques de sa classe. Elle ne peut pas accder aux attributs non statiques. En effet, une mthode statique peut tre appele partir du nom de la classe en dehors de toute cration d'objets. Dans ce genre d'appel, aucun attribut non statique ne peut exister en mmoire car de tels attributs sont lis aux instances. Une mthode statique ne possde pas de paramtre cach this pour rfrencer les attributs vu que les seuls attributs qu'elle peut utiliser sont statiques et sont par consquent uniques et non lis aux instances. Une mthode non statique peut accder la fois aux champs statiques et non statiques de sa classe. Exemple :
class X { int a; static int b; static int c; int f(){return a+b;} static int g(){return a-b;} static int h()({return a*b;} };

Etant donns la classe X dfinie ci-dessus et obj un objet convenablement instanci de X. Indiquer si les appels suivants sont possibles ou non.
int res; res=obj.f(); res=obj.g(); res=obj.h(); X.f(); X.g(); X.h();

Remarque : De par sa dfinition un constructeur doit tre capable d'accder tous les attributs de sa classe (pour faire la cration et l'initialisation de l'objet). Or cette capacit n'est pas la porte des mthodes statiques. C'est pourquoi un constructeur ne peut pas tre dclar comme tant statique. De mme un destructeur ne peut pas tre dclar comme tant statique.

Version 3.7

116

Karim Kalti

Membres statiques et membres constants d'une classe Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
class Cercle { int x; int y; static int Rayon; static int Nb; public : Cercle(int xi, int yi){x=xi; y=yi; Nb++;} Cercle(){x=0; y=0; Nb++;} ~Cercle(){Nb--;} void Deplacer(int dx, int dy){x+=dx; y+=dy;} static void SetRayon(int R){Rayon = R;} static int GetNb(){ return Nb;} void Dessiner(){...} }; int Cercle::Rayon =5; int Cercle::Nb=0; void main() { ... Cercle c1(2,7);Cercle c2(2,17);Cercle c3(2,27); int Nombre = Cercle::GetNb(); ... }

Remarque : Une mthode statique peut tre appele la manire des fonctions du langage C (sans ncessiter une cration d'instance). C'est pourquoi, dans les nouveaux langages orients objet on tend regrouper les fonctions d'une mme bibliothque dans une mme classe sous forme de mthodes statiques pour faciliter leur appel. A titre dexemple, la classe Math en C# reprsente la bibliothque mathmatique et ne possde que des mthodes statiques qui peuvent tre appels facilement sans ncessiter la cration d'une instance au pralable. Dclaration : public static double Cos(double d ) Appel direct : double i = Math.Cos(0) ; Exercice d'application On considre une classe possdant un attribut qui stocke une valeur entire. Complter la dfinition de cette classe pour que l'on puisse calculer tout moment la moyenne des valeurs stockes dans toutes les instances de la classe. Solution
class Entier { int Val; static int Nb; static float Somme; public : Entier() : Val(0) {Nb++;} Entier(int V):Val(V) { Nb++; Somme+=Val; }

Version 3.7

117

Karim Kalti

Membres statiques et membres constants d'une classe Programmation oriente objet (C++) _________________________________________________________________________________________________________________

~Entier() { Nb--; Somme-=Val; } static float Moyenne() {return Somme/Nb;} }; int Entier::Nb=0; float Entier::Somme=0.f; void main() { Entier E1(10); Entier E2(30); cout<<"La moyenne est :"<<Entier::Moyenne()<<endl; Entier *E3=new Entier(50); cout<<"La moyenne est :"<<Entier::Moyenne()<<endl; delete E3; cout<<"La moyenne est :"<<Entier::Moyenne()<<endl; }

Les objets constants


Il est possible de dclarer constantes des donnes membres. Mais il est galement possible de dclarer constants les objets eux-mmes. La dclaration d'un objet constant se fait l'aide du mot-cl const comme suit : Syntaxe : const NomClasse NomObjet(liste des arguments); Toutes les donnes membres d'un objet constant sont considres comme des constantes. Elles ne peuvent tre initialises qu' travers le constructeur. Une fois initialises, ces donnes ne peuvent plus changer de valeurs (mme si elles ne sont pas explicitement dclares comme constantes). L'accs en criture aux attributs d'un objet constant est interdit. Les constructeurs et le destructeur reprsentent les seules mthodes qui peuvent tre appeles par un objet constant. L'appel des autres mthodes de la classe par un tel objet tant interdit. Exemple Considrons la dfinition suivante de la classe Time :
class Time { public : int Hour; int Minute; int Second; Time(int H,int M, int S) : Hour(H),Minute(M),Second(S) { } void Affiche() {

Version 3.7

118

Karim Kalti

Membres statiques et membres constants d'une classe Programmation oriente objet (C++) _________________________________________________________________________________________________________________

cout<<"L'heure est : "; cout<<setw(2)<<setfill('0')<<Hour<<':' <<setw(2)<<setfill('0')<<Minute<<':' <<setw(2)<<setfill('0')<<Second<<endl; } };

Soit T1 un objet de type Time et T2 un objet constant de type Time :


Time T1(10,25,55); const Time T2(12,10,30); T1.Hour = 11; // OK car Hour est public et T1 n'est pas constant. T2.Hour = 11; // ERREUR car T2 est un objet constant. T1.Affiche() // OK car T1 n'est pas constant T2.Affiche() // ERREUR car T2 est constant

Remarque : L'appel d'une mthode qui manipule les attributs de la classe est interdit partir d'un objet constant mme si cette mthode ne modifie pas le contenu de ces attributs comme c'est le cas pour la mthode Afficher. Ceci est d au fait que la compilation des appels des fonctions se base essentiellement sur la dclaration de ces dernires (analyse de la signature). Le corps de la fonction appele peut tre dj dfini et pr-compil dans un module externe au programme (c'est le cas des bibliothques de fonctions lies d'une manire statique ".lib" ou dynamique ".dll"). Le compilateur ne peut pas par consquent dterminer si des fonctions de ce genre effectuent en interne un accs en criture aux attributs puisqu'il ne va pas les recompiler mais tout simplement vrifier la justesse de leur appel.

Les mthodes constantes


Pour qu'une mthode puisse tre appele partir d'un objet constant, elle doit tre dclare et dfinie comme tant constante. Seules les mthodes qui ne font pas des accs en criture aux attributs peuvent tre dclares et dfinies comme constantes. Le caractre constant d'une fonction est indiqu par le mot-cl const. const doit figurer aussi bien dans la dclaration que dans la dfinition de la classe comme suit : Dclaration : Type NomMethodes(Liste des arguments) const; Dfinition :
Type NomClasse:: NomMethodes(Liste des arguments) const { }

Exemple :
class Time { public :

Version 3.7

119

Karim Kalti

Membres statiques et membres constants d'une classe Programmation oriente objet (C++) _________________________________________________________________________________________________________________

int Hour; int Minute; int Second; Time(int H,int M, int S) : Hour(H),Minute(M),Second(S) { } void Affiche() const { cout<<"L'heure est : "; cout<<setw(2)<<setfill('0')<<Hour<<':' <<setw(2)<<setfill('0')<<Second<<endl; } }; void main() { const Time T(12,10,30); T.Hour = 11; // ERREUR car T est un objet constant. T.Affiche(); // OK car Affiche est une mthode constante }

Remarque : Le spcificateur const dans ce cas constitue une garantie pour le compilateur que la mthode en question n'effectue aucune modification des attributs de la classe, mme si la classe est dj prcompile (sous forme de dll par exemple). Un utilisateur de cette classe peut dans ce cas appeler une telle mthode pour des instances constantes de cette classe. Toute instruction d'accs en criture un attribut dans le corps d'une mthode constante est signale comme une erreur au moment de la compilation. Tout passage par rfrence ou par adresse d'un attribut d'une instance constante une fonction appele en interne dans une mthode est signal comme erreur (risque de modification de l'attribut par la fonction).

Version 3.7

120

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les fonctions amies


Introduction
La POO pure impose l'encapsulation des donnes. Ce principe d'encapsulation fait qu'une classe interdit ses utilisateurs externes l'accs ses membres privs et ne leur permet d'appeler que ses mthodes publiques. Contraintes imposes par l'encapsulation Parfois il existe des circonstances o une telle protection peut s'avrer contraignante comme c'est le cas pour l'exemple suivant : On suppose que l'on dispose de deux classes Vecteur et Matrice et que l'on cherche dfinir une mthode qui calcule le produit d'un vecteur et d'une matrice. En appliquant le principe d'encapsulation dune manire stricte (protection des attributs de chaque classe), il ne serait possible de dfinir cette fonction ni comme tant membre de la classe Vecteur, ni comme membre de la classe Matrice ni encore moins comme une fonction indpendante. Comme solution ce problme il est possible de : Dclarer les attributs comme publiques chose qui engendre en contre partie la perte du bnfice de leur protection. Utiliser des mthodes publiques d'accs aux donnes prives mais ceci reste toutefois pnalisant en terme de temps d'excution. Le C++ dispose d'un outil qui permet de contourner le principe d'encapsulation dans certaines circonstances et pour certains utilisateurs (certaines fonctions). Cet outil est celui des fonctions amies.

La notion d'amiti
L'amiti peut exister entre une fonction et une classe ou entre deux classes. Dans ce cadre : o Une fonction amie d'une classe est autorise accder aux membres privs de cette dernire comme n'importe quel autre membre de cette classe. o Les mthodes d'une classe A amie d'une deuxime classe B sont autorises accder tous les membres privs de cette classe B. La notion d'amiti est dclare l'aide du mot-cl friend.

Fonction amie d'une classe


Une fonction peut tre dfinie comme tant amie d'une classe. Cette fonction peut tre une fonction indpendante ou une fonction membre d'une autre classe. Fonction indpendante amie d'une classe La dclaration d'une fonction F comme amie d'une classe A se fait l'intrieur de cette dernire de la manire suivante : class A { friend TypeRetour F(Liste des paramtres); };

Version 3.7

121

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

L'emplacement de la dclaration de l'amiti de F au sein de la classe A peut se faire n'importe o dans cette dernire. D'ailleurs cet emplacement peut figurer indiffremment dans la partie publique ou prive de la classe. Exemple : L'exemple suivant montre la dfinition d'une fonction indpendante appele Somme qui calcule la somme de deux complexes. Pour pouvoir accder aux membres privs cette fonction a t dclare comme amie de la classe Complexe.
class Complexe { double Reel; double Imaginaire; public : Complexe(double r, double i) : Reel(r), Imaginaire(i){} Complexe(){Reel = 0; Imaginaire = 0;} // Dclaration de la fonction Somme comme amie friend Complexe Somme(Complexe C1, Complexe C2); void Afficher() { cout<<"Partie relle : "<<Reel; cout<<"Partie imaginaire : "<<Imaginaire; } }; // Dfinition de la fonction Somme Complexe Somme(Complexe C1, Complexe C2) { Complexe res(C1.Reel+C2.Reel, C1.Imaginaire+C2.Imaginaire); return res; } int main() { Complexe C1(3,6), C2(4,2); Somme(C1,C2).Afficher(); return 0; }

Fonction membre amie d'une classe La fonction dfinir comme tant amie d'une classe peut ne pas tre indpendante mais plutt une fonction membre d'une autre classe. Dans ce cas la dclaration de l'amiti se fait de la manire suivante : class B { TypeRetour F(Liste des paramtres); }; class A { friend TypeRetour B::F(Liste des paramtres); };

Version 3.7

122

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Si la classe A figure dans le prototype de F (comme paramtre ou comme type de retour) alors pour que la compilation russisse il faut que cette classe soit dclare avant la classe B mais pas ncessairement dfinie et ce pour qu'elle soit connue au niveau de la compilation de la dfinition de la classe B. Voici un schma complet de la dclaration d'amiti pour ce cas de figure : // Dclaration de A pour les besoins de compilation class A; // Dfinition de B class B { TypeRetour F(A); }; // Dfinition de A class A { friend TypeRetour B::F(Liste des paramtres); }; Fonction amie de plusieurs classes Une fonction peut avoir besoin d'accder aux membres privs de plusieurs classes et ncessite par consquent d'tre dclare amie de toutes ces classes en mme temps. La dclaration d'amiti doit se faire dans ce cas dans toutes ces classes. Exemple : On considre une fonction indpendante F qui doit accder aux membres privs de deux classes A et B. Le prototype de F est le suivant : void F(A, B); F doit tre dclare amie la fois A et B : Syntaxe : class B; // Dfinition de A class A { friend void F(A, B); }; // Dfinition de B class B { friend void F(A, B); };

Version 3.7

123

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

void F(A a, B b) { } La dclaration suivante class B; est ncessaire pour les besoins de compilation vu que la classe A utilise B (dans F) et que B est dfinie aprs A.

Classe amie d'une autre classe


Ce cas de figure est une gnralisation du cas de l'amiti d'une fonction membre une classe. Il s'agit en effet de dclarer toutes les fonctions d'une classe A comme amies aux fonctions membres d'une autre classe B. Dans ce cas, une solution consiste dfinir autant d'amiti qu'il y a de fonctions concernes. Mais, le C++ offre une alternative plus simple qui consiste effectuer une dclaration d'amiti globale entre classes. Ainsi, il suffit de dclarer la classe A comme tant amie de la classe B. Syntaxe : class A { }; class B { friend class A; }; Dans ce cas, toutes les mthodes de A seront ainsi amies de B.

Version 3.7

124

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Hritage et polymorphisme
Dfinition de l'hritage
L'hritage est le fait de dfinir une nouvelle classe en se basant sur la dfinition d'une classe dj existante. La nouvelle classe hrite alors les attributs et les mthodes de la classe existante et ce en plus de ses propres membres (ses attributs et/ou mthodes spcifiques). L'hritage est un concept propre la POO. Terminologie utilise par le concept d'hritage La nouvelle classe dfinie par hritage est appele classe drive ou classe fille. La classe partir de laquelle se fait l'hritage est appele classe de base, super classe ou galement classe mre. L'hritage est galement appel drivation de classes. La relation d'hritage est schmatise graphiquement par une flche qui part de la classe drive vers la classe de base. Cette relation peut s'exprimer par la phrase "est un". Exemple : ANIMAL

HERBIVORE

CARNIVORE

VACHE

CHEVAL

LION

Un herbivore est un animal, un cheval est un herbivore, etc. Remarque : La classe drive constitue une spcialisation de la classe de base. La classe de base constitue une gnralisation de la classe drive. Intrt de l'hritage L'intrt de l'hritage rside dans : La rutilisation des modules dj existants. La factorisation des proprits communes un certain nombre de classes programmes moins complexes et plus facilement maintenables.

Rendre les

Version 3.7

125

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple : Prenons par exemple le cas de la gestion d'une bibliothque de documents. Ces documents peuvent tre de deux catgories : des livres et des priodiques (magazines, journaux, revues scientifiques, etc.). Les spcifications des classes reprsentant ces deux catgories de documents sont donnes comme suit :

LIVRE
Reference Titre Auteur Editeur GetReference() GetTitre() GetAuteur() GetEditeur()

PERIODIQUE
Reference Titre DirecteurRedaction Numero GetReference() GetTitre() GetDirecteurRedaction() GetNumro()

D'aprs ces spcifications il est facile de remarquer que les deux classes possdent un certain nombre de caractristiques en commun qui se rptent. Afin d'viter cette rptition, il est possible de factoriser ces caractristiques communes et ce l'aide d'une classe gnraliste qui portera le nom Document. Les classes Livre et Periodique constitueront alors des spcialisations de cette classe. La nouvelle spcification des classes devient alors comme suit :

DOCUMENT
Reference Titre GetReference() GetTitre()

LIVRE
Auteur Editeur GetAuteur() GetEditeur()

PERIODIQUE
DirecteurRedaction Numero GetDirecteurRedaction() GetNumro()

Version 3.7

126

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Il ne faut pas confondre l'hritage avec l'agrgation des classes (appele galement composition) dj traite dans le chapitre prcdent. Il est rappeler que dans l'agrgation, la nouvelle classe est compose en partie par une classe dj existante. Cette dernire s'intgre gnralement comme un attribut de la nouvelle classe.

Syntaxe de drivation en C++


La syntaxe permettant de faire driver une classe partir d'une autre dj existante est comme suit : class NomClasseDrive : [Mode_Drivation] NomClasseDeBase { Liste des nouveaux membres de la classe drive }; NomClasseDrive : dsigne la nouvelle classe cre par drivation. NomClasseDeBase : dsigne le nom de la classe partir de laquelle se fait la drivation. Mode_Drivation : sert indiquer le mode avec lequel sera faite la drivation. Ce mode dtermine les droits d'accs qui seront attribus aux membres de la classe de base dans la classe drive. Les modes qui sont dfinis en C++ sont : public, protected et private. Exemple :
class Document { char Reference[10]; char Titre[100]; void Afficher(); public : char* GetReference(); char* GetTitre(); }; class Livre : public Document { char Auteur[10]; char Editeur[100]; public : char* GetAuteur(); char* GetEditeur(); };

Accessibilit des membres d'une classe de base dans la classe drive


L'accessibilit des membres d'une classe de base depuis une classe drive dpend : des droits d'accs de ces membres dans la classe de base, du mode de drivation utilis.

Version 3.7

127

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Le tableau suivant dresse tous les cas de figures possibles concernant cette accessibilit :
Mode de drivation public Statut du membre dans la classe de base public protected private public protected private public protected private Statut du membre dans la classe drive public protected private protected protected private private private private Accessibilit du membre dans la classe drive Accessible Accessible Inaccessible Accessible Accessible Inaccessible Accessible Accessible Inaccessible Accessibilit du membre par les utilisateurs de la classe drive Accessible Inaccessible Inaccessible Inaccessible Inaccessible Inaccessible Inaccessible Inaccessible Inaccessible

protected

private

Exemple : L'exemple suivant donne une illustration de quelques situations de contrle d'accs pouvant tre engendres par une drivation prive.
class A { int a1; protected : int a2; public : int a3; }; class B : private A { private : int b1; int b2; protected : void f() { b1 = a1*2; //Erreur car a1 est inaccessible dans B } void g() { b2 = a2*a3; //OK car a2 et a3 sont accessibles dans B } }; void main() { A Obj1; B Obj2; Obj1.a2 = 6; // Erreur car a2 est protg dans A Obj2.a3 = 8; // Erreur car a3 est priv dans B }

Version 3.7

128

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarques : Le mode de drivation par dfaut (si rien n'est mentionn) est le mode "private". Toutefois le mode le plus utilis est le mode "public". Ce mode permet aux membres hrits de prserver le mme statut que dans la classe de base. A travers les mots-cls : public, protected et private, le C++ fournit aux concepteurs des classes des outils de contrle de l'accs aux membres. Ce contrle est effectu par rapport aux utilisateurs des classes. Ces utilisateurs peuvent en effet tre : o des objets instancis directement partir de ces classes, o des classes drives partir de ces classes, o des instances de classes drives partir de ces classes. Manipulation des membres de la classe de base dans la classe drive Les membres d'une classe de base qui sont accessibles dans la classe drive sont manipuls dans cette dernire directement travers leurs noms la manire des membres de la classe drive.

Redfinition des mthodes


La redfinition des mthodes est le fait de proposer une nouvelle dfinition dans la classe drive d'une mthode dj existante dans la classe de base. Gnralement la mthode de base et celle redfinie assurent la mme fonctionnalit. Toutefois la redfinition est souvent faite dans un souci de prise en compte des spcificits de la classe drive. Exemple : Considrons les deux classes A et B dfinies comme suit :
class A {public : int att_A; A(int p){att_A = p;} void Afficher() {cout<<"La valeur de att_A est : "<<att_A<<endl;} }; class B : public A {public : int att_B; B(int p1, int p2) : A(p1) { att_B = p2;} }; void main() { B Obj(5,9); Obj.Afficher(); }

La mthode Afficher appele partir de Obj va engendrer la sortie suivante :


La valeur de att_A est : 5

Version 3.7

129

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Le contenu du champ att_B n'est pas affich. Il est clair que cette mthode telle qu'elle est dfinie dans A ne convient pas aux objets de la classe B (afffichage partielle du contenu de Obj). Pour cette raison et pour que l'affichage puisse toucher tous les attributs de B il faut redfinir cette mthode dans la classe drive pour qu'elle tienne compte de l'attibut supplmentaire. Nouvelle dfinition de B :
class B : public A {public : int att_B; B(int p1, int p2) : A(p1) { att_B = p2;} void Afficher() { cout<<"La valeur de att_A est : "<<att_A<<endl; cout<<"La valeur de att_B est : "<<att_B<<endl; } };

Avec cette dfinition de B le code suivant :


B Obj(5,9); Obj.Afficher();

va engendrer l'affichage suivant :


La valeur de att_A est : La valeur de att_B est : 5 9

Dans ce cas c'est la mthode Afficher de B qui est appele et non celle de A. Remarque: (masquage des mthodes de base) Lorsqu'une mthode d'une classe de base est redfinie dans une classe drive alors la redfinition masque la mthode de la classe de base pour les objets de la classe drive. Pour l'exemple prcdent, la mthode Afficher de A est masque par la mthode Afficher de B pour les instances de cette dernire. Appel des membres d'une classe de base redfinis dans la classe drive Les membres d'une classe de base qui sont accessibles dans la classe drive sont manipuls dans cette dernire directement travers leurs noms la manire des membres de la classe drive. Toutefois dans le cas o un membre (attribut ou mthode) est redfini, ce dernier sera cach dans la classe drive par la redfinition. Pour pouvoir l'appeler quand mme dans la classe drive, le nom de ce membre doit tre associ celui de la classe de base et ce l'aide de l'oprateur de rsolution de porte selon la syntaxe suivante : NomClasseBase::NomMembre; Si le membre en question est publique dans la classe drive alors il peut tre manipul partir des objets de cette dernire selon la syntaxe suivante : NomObjet.NomClasseBase::NomMembre;

Version 3.7

130

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Dans l'exemple prcdent, il est plus intressant d'appeler la mthode Afficher de la classe A dans la dfinition de Afficher de B pour assurer l'affichage de att_A (afin d'viter de rcrire un code dj crit). Toutefois la dfinition suivante de la mthode Afficher de B :
void Afficher() { Afficher(); cout<<"La valeur de att_B est : "<<att_B<<endl; }

sera interprte comme un appel rcursif de Afficher de B. Pour obtenir l'effet souhait la redfinition de Afficher doit se faire comme suit :
class B : public A {public : int att_B; B(int p1, int p2) : A(p1) { att_B = p2;} void Afficher() { A::Afficher(); cout<<"La valeur de att_B est : "<<att_B<<endl; } };

Gnralisation de la redfinition La redfinition peut s'appliquer aux membres d'une classe d'une manire gnrale. Elle peut donc concerner aussi bien les mthodes que les attributs. Toutefois la redfinition des attributs reste trs rare d'utilisation mais tout fait possible. Exemple :
class X { public : int att; }; class Y : public X { public : int att; }; Y obj; obj.att=5; // att dsigne l'attribut de Y obj.X::att = 8; // att dsigne ici l'attribut de X

Diffrence entre redfinition et surdfinition Il ne faut pas confondre surdfinition et redfinition. Dans la redfinition, la mthode de base et celle redfinie doivent avoir exactement la mme signature. Ce n'est pas le cas dans la surdfinition.

Version 3.7

131

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Compatibilit entre classe de base et classe drive


D'une manire gnrale en POO, il y a une certaine compatibilit entre un objet d'une classe de base et un objet d'une classe drive. Cette compatibilit va dans le sens suivant (Objet drive Objet de base). Elle est interprte comme suit : tout objet d'une classe drive peut tre considr comme un objet de la classe de base. Exemple : Tout livre peut tre considr comme un document. Mais tout document n'est pas ncessairement un livre. Remarque : Tout objet d'une classe drive comporte tous les membres d'un objet de la classe de base. Il peut par consquent le remplacer l ou l'objet de la classe de base est demand (le remplacer syntaxiquement mais cela ne garantit pas l'obtention d'un rsultat toujours satisfaisant). L'opration inverse n'est pas vraie. En d'autres mots, un objet d'une classe de base ne peut pas remplacer un objet d'une classe drive. Compatibilit entre objet de base et objet driv en C++ En langage C++, la compatibilit entre objet driv et objet de base est limite seulement au cas d'une drivation publique. Elle peut se manifester dans ce cas concrtement par une conversion implicite : o D'un objet d'une classe drive vers un objet d'une classe de base. o D'un pointeur (ou une rfrence) vers un objet d'une classe drive en un pointeur (ou une rfrence) vers un objet d'une classe de base. Exemple : Etant donnes les deux classes A et B dfinies comme suit :
class A { public : int att_A; void Afficher(){cout<<" att_A :"<<att_A<<endl} }; class B : public A { public : int att_B; void Afficher() { cout<<" att_A :"<<att_A<<endl; cout<<" att_B :"<<att_B<<endl; } }; A B a, *pa; b, *pb;

Version 3.7

132

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

// Compatibilit entre objets


a = b; // OK

Cette instruction est correcte car tout objet de type B est galement de type A. Dans ce cas le contenu des attributs de b provenant de la classe A sera copi dans les attributs correspondants de l'objet a (att_A de b dans att_A de a).
b = a; // Erreur

Cette instruction est incorrecte car l'objet ne comporte pas tous les attributs de b (exemple : att_B ne peut pas avoir de valeur dans ce cas). // Compatibilit entre pointeurs
pa = &b; // OK

Un pointeur sur une classe de base peut pointer sans problme sur un objet d'une classe drive.
pa->att_A; // OK

Cette instruction dsigne l'attribut att_A de l'objet b.


pa->att_B; // Erreur

Cette instruction va engendrer une erreur malgr le fait que l'attribut att_B existe en mmoire. En effet, syntaxiquement pa ne reconnat pas le nom du champ point parce qu'il est de type A* et non B*.
pa->Afficher();

Cette instruction va engendrer l'appel de la fonction Afficher dfinie dans A. En effet, dans ce cas, le compilateur se base sur le type du pointeur et non sur le type de l'objet point pour dcider de la version de la mthode appeler.

Remarque : Conversion explicite d'un pointeur (Base* vers Drive*) La conversion implicite d'un pointeur d'une classe de base vers un pointeur d'une classe drive n'est pas implicitement accepte par le compilateur.
pb = &a; // Erreur

pb est un pointeur sur un objet d'une classe drive. Il ne peut pas recevoir l'adresse d'un objet d'une classe de base. Toutefois le compilateur peut accepter cette conversion si elle est faite d'une manire explicite travers un casting. Cette conversion explicite mme sil elle est possible reste dangereuse si elle est mal utilise car elle peut mener vers : o des tentatives d'accs des attributs qui ne figurent pas dans la structure de l'objet point. o A la violation des protections pour la classe de base. Exemple 1 : tentative d'accs un attribut non existant physiquement
pb = (A*)&a; // OK car conversion explicite pb->att_B; // Erreur

Cette instruction va engendrer une erreur car l'objet point ne comporte pas un champ att_B.

Version 3.7

133

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple 2 : violation des protections de la classe de base On considre les deux classes suivantes :
class A {public : int att_A; void fa(); }; class C : private A {public : int att_C; };

Soient les dclarations suivantes :


A ObjA; C ObjC; A* pa;

Analysons les instructions suivantes :


ObjA.att_A;

Cette instruction est correcte car att_A est public dans A.


ObjC.att_A;

Cette instruction est incorrecte car att_A est priv dans C.


pa = (A*)&ObjC; // Conversion explicite (cast) pa->att_A; // OK car att_A est public dans A.

Ces deux instructions sont syntaxiquement correctes et possibles. La deuxime instruction va engendrer donc un accs l'attribut att_A d'un objet d'une classe C. Ceci constitue une violation des droits d'accs car att_A est normalement priv pour les objets de la classe C et ne doit pas tre par consquent accessible.
A* pa

EF0A78

ObjC att_A att_B

Construction d'objets drivs


L'instanciation d'un objet d'une classe drive engendre ncessairement l'instanciation de la partie de ses attributs provenant de la classe de base. Ce processus d'instanciation s'effectue en C++ en quatre tapes : o Allocation de l'espace mmoire des attributs, o Appel du constructeur de la classe drive. o Appel et excution du constructeur de la classe de base (initialisation des attributs issus de la classe de base). o Excution du constructeur de la classe drive (initialisation des attributs drivs).

Version 3.7

134

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Ordre d'excution des constructeurs D'une manire gnrale l'instanciation d'un objet d'une classe drive engendre une excution hirarchique de constructeurs qui se fait dans l'ordre suivant : o Constructeur de la (des) classe(s) de base. o Constructeurs des ventuels objets membres de la classe drivs. o Constructeur de la classe drive. Syntaxe d'appel du constructeur de la classe de base dans celui de la classe drive L'appel du constructeur de la classe de base peut se faire d'une manire explicite ou implicite selon que la classe de base ou la classe drive disposent de constructeurs explicitement dfinis ou non et que ces constructeurs soient paramtrs ou non. I - Cas o la classe drive dispose d'un constructeur paramtr : I.1 Cas o la classe de base possde un constructeur paramtr : Dans ce cas, le constructeur de la classe de base est appel par le constructeur de la classe drive dans la liste d'initialisation de ce dernier comme suit :
ConstructeurClasseDrive(Arguments):ConstructeurClasseBase(Arguments) { }

La liste d'initialisation du constructeur d'une classe drive peut comporter gnralement : o Un appel au constructeur de la classe de base. o Des valeurs d'initialisation des membres spcifiques de la classe drives. Exemple :
class A {public : int att_A; A(int p) : att_A(p) { cout<<"Constructeur de A"<<endl; } void Afficher() { cout<<" att_A :"<<att_A<<endl; } }; class B : public A {public : int att_B; B(int p1,int p2) : A(p1),att_B(p2) { cout<<"Constructeur de B"<<endl; } void Afficher() { cout<<" att_A :"<<att_A<<" et att_B :"<<att_B<<endl; } }; B b(5,7); // OK

Version 3.7

135

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

I.2- Cas o la classe de base dispose d'un constructeur par dfaut Ce constructeur peut tre implicite ou explicite. Dans ce cas, l'appel explicite du constructeur de la classe de base dans la classe drive n'est pas ncessaire. En effet cet appel peut se faire d'une manire implicite. Exemple :
class A {public : int att_A; }; class B : public A {public : int att_B; B(int p) : att_B(p) { cout<<"Constructeur de B"<<endl; } }; B b(7);

Ce constructeur de la classe B fait un appel implicite au constructeur par dfaut de la classe A. Avec ce constructeur de B, att_B sera cr et initialis mais att_A sera seulement cr. II - Cas o la classe drive ne dispose pas de constructeurs paramtrs II.1 - Si la classe de base ne dispose que de constructeurs paramtrs Dans ce cas l'instanciation de l'objet driv ne sera pas possible. En effet, l'instanciation de cet objet se fera avec le constructeur par dfaut gnr par le compilateur. Or ce dernier ne peut pas faire un appel automatique au constructeur paramtr de la classe de base car il ne saura pas quels arguments faut-il lui passer. Cette situation engendre une erreur de compilation. Exemple :
class A {public : int att_A; A(int p) : att_A(p) { cout<<"Constructeur de A"<<endl; } }; class B : public A {public : int att_B; }; B b; // Erreur

Il est clair qu'avec cette dfinition il n y a pas moyen d'appeler et de passer des arguments au constructeur de A lors de la cration d'un objet de type B.

Version 3.7

136

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

II.1- Si la classe de base dispose d'un constructeur par dfaut Dans ce cas, c'est ce dernier qui sera automatiquement appel par le constructeur par dfaut de la classe drive. Exemple :
class A {public : int att_A; }; class B : public A {public : int att_B; }; B b; // appel du constructeur par dfaut de B qui fait appel son // tour au constructeur par dfaut de A.

Dfinition et appel du constructeur de recopie d'une classe drive


Le constructeur de recopie d'une classe est appel : o lors de la cration d'un objet partir d'un autre objet. o lors de la transmission de la valeur d'un objet en argument ou en retour d'une fonction. Droulement de la recopie d'un objet driv : La recopie d'un objet d'une classe drive ncessite galement la recopie de la partie correspondant l'objet de la classe de base. Ceci passe par un appel hirarchique des constructeurs de recopie. Le droulement de cet appel dpend des cas de figures qui peuvent se prsenter : Cas 1 : La classe drive n'a pas de constructeur de recopie explicite Dans ce cas, c'est le constructeur de recopie par dfaut qui sera appel. Ce dernier engendrera l'appel automatique du constructeur de recopie de la classe de base. Si ce dernier est explicitement dfini alors il sera appel, sinon c'est le constructeur de recopie par dfaut qui sera appel. Cas 2 : La classe drive a un constructeur de recopie explicite En C++, l'appel du constructeur de recopie explicitement dfini n'engendre aucun appel automatique du constructeur de recopie de la classe de base (mme si ce dernier est explicitement dfini). Dans ce cas, le constructeur de recopie explicite doit prendre en charge la recopie de la totalit de l'objet driv. Cette recopie peut se faire par : o Appel explicite de tout autre constructeur paramtr de la classe de base. o Appel explicite du constructeur de recopie (implicite ou explicite) de la classe de base.

Version 3.7

137

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Problme de l'appel explicite du constructeur de recopie de la classe de base par son homologue de la classe drive Lors de l'appel explicite du constructeur de recopie de la classe de base par le constructeur de recopie de la classe drive le problme suivant se pose : comment dduire l'objet de la classe de base passer comme argument au constructeur de recopie de cette dernire partir de l'objet de la classe drive. L'ide permettant de rsoudre ce problme consiste tout simplement faire passer l'objet de la classe drive au constructeur de recopie de la classe de base. La conversion "objet drive" vers "objet de base" sera dans ce cas automatiquement faite par le compilateur. Exemple 1 : L'exemple suivant montre comment appeler le constructeur de recopie d'une classe de base dans le constructeur de recopie d'une classe drive.
class A {public : int att_A; A(int p) : att_A(p) { cout<<"Constructeur de A"<<endl; void Afficher() { cout<<" att_A :"<<att_A<<endl; } };

class B : public A {public : int att_B; B(int p1,int p2) : A(p1),att_B(p2) {cout<<"Constructeur de B"<<endl;} B(const B& x):A(x) { att_B = x.att_B; } void Afficher() { cout<<" att_A :"<<att_A<<" et att_B :"<<att_B<<endl; } };

L'objet x pass au constructeur de recopie de A dans l'appel suivant :


B(const B& x):A(x)

est de type B mais il sera automatiquement converti vers un objet de type A par le compilateur grce la compatibilit entre "objet drive" et "objet de base". Exemple 2 : L'exemple suivant propose une autre dfinition du constructeur de recopie d'une classe drive qui fait appel non pas au constructeur du recopie de la classe de base mais son constructeur paramtr.
B(const B& x):A(x.att_A) { att_B = x.att_B; }

Dans cette version le constructeur de recopie de B fait appel au constructeur paramtr de A dfini comme suit :
A(int p) : att_A(p){ }

Version 3.7

138

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Les membres issus de la classe de base ne peuvent pas tre initialiss directement par le constructeur de la classe drive. Leur initialisation doit ncessairement passer par l'appel du constructeur de la classe de base. Ainsi une dfinition comme la suivante du constructeur paramtr de B sera incorrecte.
B(int p1,int p2) : att_A(p1),att_B(p2) { cout<<"Constructeur de B"<<endl; }

ou galement :
B(int p1,int p2) { cout<<"Constructeur de B"<<endl; att_A=p1; att_B=p2; }

Dans ce cas le compilateur signalera que la classe B ne possde pas un attribut att_A. En effet lorsqu'il s'agit de construction et d'initialisation chaque classe (de base ou drive) doit s'occuper de la partie qui la concerne. Ce n'est pas le cas lorsqu'il s'agit d'utilisation de l'objet drive.

Destruction d'objets drivs


L'appel du destructeur d'un objet d'une classe drive engendre l'appel automatique du destructeur de la classe de base. L'ordre d'excution de ces destructeurs est inverse l'ordre d'excution des constructeurs : Excution du destructeur de la classe drive puis excution du destructeur de la classe de base D'une manire plus dtaille, la libration se fait comme suit : o Appel et excution du destructeur de la classe drive. o Appel et excution du destructeur de la classe de base. o Libration de la mmoire utilis par les attributs (Il ne s'agit pas des espaces mmoires dynamiques points par les ventuels attributs de type pointeurs mais des espaces occups par les attributs eux mmes). Exemple :
class A { public : ~A(){ cout<<"Destructeur de A"<<endl;} }; class B : public A { public : ~B(){ cout<<"Destructeur de B"<<endl;} };

Version 3.7

139

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La destruction d'un objet dclar comme suit :


B obj(5,7);

va engendrer la sortie suivante


Destructeur de B Destructeur de A

Polymorphisme
Le mot polymorphisme dsigne le fait de pouvoir prendre plusieurs formes. En programmation oriente objet le polymorphisme est un concept qui se manifeste par la capacit d'une mthode s'excuter de diffrentes manires selon la nature de l'objet qui l'appelle (objet de base ou objet driv). Le polymorphisme concerne essentiellement des objets lis par une relation d'hritage. Limite de la liaison statique des mthodes Exemple : Rappel de la liaison statique
class A {public : int att_A; A(int p) : att_A(p) { cout<<"Constructeur de A"<<endl; } void Afficher() { cout<<" att_A :"<<att_A<<endl; } }; class B : public A {public : int att_B; B(int p1,int p2) : A(p1),att_B(p2) { cout<<"Constructeur de B"<<endl; } void Afficher() { cout<<" att_A :"<<att_A<<" et att_B :"<<att_B<<endl; } }; void main() { A* pa; B objB(3,5), *pb; pb=&objB; pb->Afficher(); pa=pb; pa->Afficher(); } pb->Afficher(); engendre l'affichage suivant : att_A : 3 et att_B : 5 pa->Afficher(); engendre l'affichage suivant : att_A : 3

Version 3.7

140

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

L'affichage incomplet partir du pointeur pa provient du fait que le compilateur se base sur le type de pa au moment de la compilation pour dcider de la version de la mthode appeler : pa tant de type A* alors c'est la mthode Afficher de la classe A qui sera appele dans ce cas. Le compilateur effectue dans ce cas une liaison statique. Cette dernire ne tient pas compte du vritable type de l'objet point et ne permet pas par consquent d'appeler les versions appropries des mthodes utiliser. Liaison dynamique et polymorphisme Pour remdier au problme de la liaison statique rencontr lors de la conversion d'un objet driv vers un objet de base il faut tre capable d'appeler la mthode qui correspond au type de l'objet point et non au type du pointeur. Pour ce faire il faut effectuer ce que l'on appelle une liaison dynamique entre l'appel et le corps de la mthode. Cette liaison doit tre faite au moment de l'excution et non au moment de la compilation. La liaison dynamique dote les mthodes de la capacit de se comporter de diffrentes manires selon l'objet appelant. Ces mthodes sont alors dites polymorphes. Mise en uvre du polymorphisme Pour rendre une mthode polymorphe, cette dernire doit tre dclare virtuelle dans la classe de base l'aide du mot-cl virtual selon la syntaxe suivante : virtual Type NomMethodePolymorphe(Paramtres) { } Exemple 1:
class A {public : int att_A; A(int p) : att_A(p){}; virtual void Afficher() { cout<<" att_A :"<<att_A<<endl } }; class B : public A {public : int att_B; B(int p1,int p2) : A(p1),att_B(p2){} void Afficher() { cout<<" att_A :"<<att_A<<" et att_B :"<<att_B<<endl; } }; void main() { A* pa; B objB(3,5), *pb; pb=&objB; pb->Afficher(); pa=pb; pa->Afficher(); }

Version 3.7

141

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

pa->Afficher(); va engendrer dans ce cas l'affichage suivant : att_A : 3 et att_B : 5

Remarque : La liaison dynamique d'une mthode n'est pas limite au cas o cette dernire est appele explicitement partir d'un objet mais s'applique galement au cas de l'appel de la mthode l'intrieur de la classe par d'autres mthodes. L'exemple suivant illustre cette situation. Exemple :
class A {public : int att_A; A(int p) : att_A(p){}; virtual void Contenu() { cout<<" att_A :"<<att_A<<endl; } void Afficher() { cout<<"Le contenu de l'objet est : "<<endl; Contenu(); } }; class B : public A {public : int att_B; B(int p1,int p2) : A(p1),att_B(p2){} void Contenu() { cout<<" att_A :"<<att_A<<" et att_B :"<<att_B<<endl; } };

Considrations relatives la dclaration et l'usage des fonctions virtuelles La spcification du mot-cl virtual n'est pas ncessaire pour les redfinitions de la mthode virtuelle. Cette spcification peut se limiter la classe de base. A partir du moment o une fonction a t dclare virtuelle dans une classe de base, alors elle sera soumise la liaison dynamique dans cette classe et dans toutes les classes drives. Il est noter que l'effet de virtual ne se limite pas aux classes obtenues par drivation directe mais s'tend toute la hirarchie des classes obtenues par drivation successives. La redfinition d'une fonction virtuelle n'est pas obligatoire dans toutes les classes drives. Toutefois une classe drive ne peut appeler une mthode virtuelle que si cette dernire possde au moins une dfinition dans sa hirarchie. Dans ce cas c'est la dernire redfinition qui sera utilise. Si une mthode a t dj dfinie dans une classe de base puis redfinie comme virtuelle mais dans une classe drive, alors la nouvelle redfinition sera soumise la liaison dynamique. Toute autre redfinition de cette mthode dans une classe drive sera galement soumise la liaison dynamique.

Version 3.7

142

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La surdfinition (et non la redfinition) dans une classe drive d'une mthode dclare virtuelle dans une classe de base est interprte comme une nouvelle dfinition de la mthode (une dfinition possdant une signature et une syntaxe d'appel diffrentes). Elle ne sera pas par consquent soumise la liaison dynamique mais plutt la liaison statique. Objets auto, pointeurs, rfrences et possibilit de typage dynamique Le typage dynamique n'est possible qu' partir des pointeurs et des rfrences. Il ne l'est pas dans le cas des objets auto. Cas des objets de type auto : La copie d'un objet auto d'une classe drive dans un objet auto de la classe de base engendre la copie membre par membre des attributs correspondant la classe de base depuis l'objet driv vers l'objet de base. L'appel d'une mthode virtuelle depuis l'objet de base ainsi copi ne peut invoquer que la dfinition associe la classe de base. En effet, il n'est pas concevable d'appeler la version redfinie dans la classe drive car l'objet copi ne dispose dans sa copie d'aucune information sur les donnes drives. Exemple : Etant donnes les classes A et B drive de A.
A ObjA; B ObjB(5,7); ObjA = ObjB;
ObjA att_A = 5 ObjB

Copie

att_A = 5 att_B = 7

ObjA.Afficher(); ne peut invoquer que la version de base de Afficher. Il n'est pas

possible d'utiliser la version redfinie car ObjA n'a aucune information sur les attributs drivs (att_B dans ce cas). Cas des pointeurs : Lorsqu'il s'agit de pointeur la liaison dynamique est envisageable. En effet, dans le cas o ce pointeur pointe sur un objet driv, l'appel de la redfinition d'une mthode virtuelle depuis ce pointeur ne risque pas de poser de problme puisque toutes les informations ncessaires au bon fonctionnement de la redfinition sont ncessairement disponibles dans l'objet point. Exemple : Etant donnes les classes A et B drive de A.
B ObjB(5,7); A* pa = (A*)&ObjB; pa->Afficher();
A* pa

EF0A78

ObjB att_A=5 att_B=7

Il est envisageable en faisant une liaison dynamique de faire appel la redfinition de la mthode Afficher (version dfinie dans B). En effet, l'objet point dispose dans ce cas de toutes les informations ncessaires au bon fonctionnement de cette redfinition.

Version 3.7

143

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Cas des rfrences : Il est possible d'envisager une liaison dynamique avec les rfrences pour les mmes raisons que celles voqus pour les pointeurs. Exemple : Etant donnes les classes A et B drive de A.
B ObjB(5,7); A& r = ObjB; r.Afficher(); A& r

EF0A78

ObjB att_A=5 att_B=7

Il est envisageable en faisant une liaison dynamique de faire appel la redfinition de la mthode Afficher (version dfinie dans B). En effet, l'objet rfrenc (ObjB) dispose dans ce cas de toutes les informations ncessaires au bon fonctionnement de cette redfinition. Restriction du polymorphisme Les constructeurs ne peuvent pas tre virtuels. Tout objet ne peut tre construit que par le constructeur qui a t dfini pour sa classe. Polymorphisme des destructeurs Les destructeurs peuvent tre dclars virtuels. Considrons le cas suivant :
class A {public : ~A(){cout<<"Destructeur de A";} }; class B : public A {public : ~B(){cout<<"Destructeur de B";} }; A* pa = new B(5,7); pa->Afficher(); delete pa;

A* p

EF0A78
att_A=5 att_B=7

Si le destructeur de A n'a pas t dclar comme virtuel dans A. delete pa; va engendrer dans ce cas un appel au destructeur de A (liaison statique car pa est un pointeur sur A et ~A n'est pas virtuel. Or ce destructeur n'est pas appropri pour dtruire l'objet point puisque ce dernier est de type B. Pour rsoudre ce problme il faut modifier la classe A comme suit :
class A {public : virtual ~A(){ } }; delete pa; va engendrer dans ce cas un appel du destructeur de B (liaison dynamique).

Cet appel convient donc au type de l'objet point.

Version 3.7

144

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Mthodes virtuelles pures et classes abstraites


Mthodes virtuelles pures Il y a des situations en hritage o une classe de base se trouve devant l'incapacit de proposer des dfinitions certaines mthodes. Ceci est gnralement d un manque d'informations ncessaires la proposition de ces dfinitions. Ces informations seront le plus souvent fournies au niveau des classes drives. Une mthode qui est dclare mais non dfinie dans une classe de base et qui est redfinie dans les classes drives est appele une mthode virtuelle pure. Une mthode virtuelle pure est dclare comme suit : virtual Type NomMethode(Paramtres) = 0; Exemple : On veut crire un programme de dessin de formes. Ces formes peuvent tre de deux catgories : cercles et carrs. Les classes qui seront utilises par ce programme sont en nombre de trois : - Une classe de base appele Forme pour reprsenter les formes d'une manire gnrale. - Deux classes Carre et Cercle drives toutes les deux de Forme.
class Forme { int x; int y; virtual int Surface() = 0; ... ... ... }; class Carre : public Forme { int Cote; int Surface() { return Cote*Cote; } ... ... ... }; class Cercle : public Forme { int Rayon; int Surface() { return 3.14 * Rayon * Rayon; } ... ... ... };

Toute forme possde une surface. C'est pourquoi une mthode de calcul de surface a t intuitivement associe la classe Forme. Toutefois cette dernire est incapable de dfinir la manire avec laquelle sera calcule cette surface. Ce calcul ncessitant en effet la connaissance de la nature exacte de la forme. Classes abstraites Une classe qui possde au moins une mthode virtuelle pure est appele une classe abstraite. Une classe abstraite ne peut pas tre instancie.

Version 3.7

145

Karim Kalti

Hritage et polymorphisme Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Une mthode virtuelle pure peut tre redfinie dans la classe drive ou laisse encore virtuelle pure (Il faut la dclarer explicitement virtuelle pure dans la classe drive pour les versions 2.0 et antrieures du C++. Ceci n'est plus ncessaire depuis la version 3.0). Une classe drive qui ne dfinit pas toutes les mthodes virtuelles pures de ses classes ascendantes est considre comme une classe abstraite. Elle ne peut pas par consquent tre instancie. Une classe drive (directement ou indirectement) d'une classe abstraite n'est instanciable que si elle dfinit toutes les mthodes virtuelles pures qu'elle hrite. Utilit des classes abstraites Les classes abstraites servent gnralement tablir une sorte de cahier des charges qui doit tre rempli par les classes qui en drivent. Elles permettent galement de factoriser les proprits communes ces classes.

Version 3.7

146

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La surcharge des oprateurs


Introduction
Les oprateurs utiliss en C++ sont essentiellement dfinis pour des oprandes ayant des types prdfinis : int, char, float, etc. Ces oprateurs sont par dfaut inapplicables lorsqu'il s'agit d'oprandes ayant des types personnaliss comme ceux construits l'aide des classes par exemple. Exemple : On considre le type dfini par la classe Complexe :
class Complexe { double Reel; double Imaginaire; public : Complexe(){Reel = 0; Imaginaire = 0;} Complexe(double r, double i) : Reel(r), Imaginaire(i) {} };

C1 et C2 sont deux objets de type Complexe. L'expression suivante C1 + C2 ne sera pas accepte par le compilateur car l'oprateur + n'est pas dfini par dfaut pour des oprandes de type Complexe. La surcharge des oprateurs Le C++ offre aux programmeurs un outil permettant de proposer des dfinitions supplmentaires un oprateur qui soient adaptes aux types personnaliss qu'ils construisent. Cet outil est appel la surcharge d'oprateurs ou galement la surdfinition des oprateurs. Remarque : La surcharge des oprateurs permet de manipuler d'une manire plus simple et plus intuitive des objets de types personnaliss. En effet si on considre l'exemple prcdent, il est tout fait possible de dfinir une mthode qui effectue la somme de deux complexes.
Complexe Complexe::Somme(Complexe C);

La somme sera effectue comme suit : C3 = C1.Somme(C2); Cette expression reste toutefois moins pratique que : C3=C1 + C2;

La fonction oprateur
Tout oprateur du C++ est en ralit dfini comme une fonction classique qui porte comme nom l'oprateur en question prcd du mot-cl operator comme suit : TypeRetour operator SymboleOprateur(paramtres);

Version 3.7

147

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
int a, b, c; L'expression a = b + c est en ralit interprte par le compilateur comme un appel de la

fonction oprateur + comme suit :


a = operator+(b,c);

Syntaxe de la surcharge d'un oprateur


La surcharge d'un oprateur n'est autre que la surcharge de sa fonction prsente dans le paragraphe prcdent. Elle obit de ce fait aux rgles rgissant la surcharge des fonctions d'une manire gnrale. Il s'agit donc de proposer une nouvelle dfinition la fonction suivante : TypeRetour operator SymboleOprateur(paramtres) { Nouvelle dfinition } Cette considration permet d'ailleurs de protger les dfinitions par dfaut prdfinies par le langage. En effet, une nouvelle surcharge ncessite l'utilisation d'une nouvelle signature donc des paramtres diffrents de ceux dj utiliss dans les dfinitions par dfaut. Formes de surcharge d'un oprateur Les formes possibles de surcharge d'un oprateur dpendent de la nature des oprandes manipuls. En effet : Si la surcharge est effectue pour des oprandes de type classe alors la nouvelle dfinition de l'oprateur peut tre ralise sous forme d'une fonction membre de la classe ou sous forme d'une fonction globale. Si aucun des oprandes n'est de type classe (oprande de type numration par exemple) alors la seule manire d'effectuer la surcharge est sous forme d'une fonction globale. Surcharge d'un oprateur sous forme d'une fonction globale L'exemple suivant montre la surcharge de l'oprateur + comme une fonction globale. L'oprateur + tant un oprateur dyadique sa surcharge doit prendre deux arguments reprsentant les oprandes additionner.
Complexe operator+(const Complexe& C1, const Complexe& C2) { Complexe Resultat; Resultat.Reel = C1.Reel + C2.Reel; Resultat.Imaginaire = C1.Imaginaire + C2.Imaginaire; return Resultat; }

Pour que cette dfinition soit correcte, il faut que les deux attributs Reel et Imaginaire soient accessibles par la fonction globale operator+. Pour cela ces deux attributs doivent tre publiques. Pour prserver le principe d'encapsulation (la classe doit cacher son implmentation et la laisser prive), il est possible de procder autrement en laissant ces deux attributs privs et en dclarant la fonction operator+ comme une fonction amie de la classe Complexe comme suit :

Version 3.7

148

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

class Complexe { double Reel; double Imaginaire; public : Complexe(){Reel = 0; Imaginaire = 0;} Complexe(double r, double i) : Reel(r), Imaginaire(i){} friend Complexe operator+(const Complexe& C1, const Complexe& C2); };

Remarque : L'utilisation des rfrences pour les paramtres complexes permet d'viter de copier des objets qui peuvent tre de grande taille et d'optimiser par consquent le code. L'utilisation de const permet par ailleurs de prserver ces paramtres contre toute ventuelle modification par la fonction. Surcharge d'un oprateur sous forme d'une fonction membre La deuxime possibilit consiste raliser la surcharge sous forme d'une fonction membre. Cette forme de surcharge n'est possible que si le premier oprande est de type classe ce qui est le cas dans l'exemple du nombre complexe.
class Complexe {

public : Complexe operator+(const Complexe& C) { Complexe Resultat; Resultat.Reel = this->Reel + C.Reel; Resltat.Imaginaire = this->Imaginaire + C.Imaginaire; return Resultat; }

};

La fonction operator+ prend dans ce cas un seul paramtre qui reprsente le deuxime oprande. Le premier oprande tant l'objet qui va invoquer la mthode operator+. En effet : C3 = C1 + C2; est interprte par le compilateur dans ce cas comme suit :
C3=C1.operator+(C2);

Le problme d'accessibilit des attributs Reel et Imaginaire ne se pose pas dans ce cas car la fonction operator+ est membre de la classe Complexe.

Quelques exemples de surcharge d'oprateurs.


Surcharge de l'oprateur d'affectation = Tout comme pour le constructeur de recopie, le compilateur gnre automatiquement une surcharge pour l'oprateur d'affectation d'une classe. Cette surcharge sous forme d'une fonction membre publique et non statique est possible condition que la classe ne dispose pas de membre statique, constant ou de type rfrence.

Version 3.7

149

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La surcharge par dfaut ralise la copie champ par champ de l'oprande de droite dans l'oprande de gauche et reste par consquent inapproprie pour l'affectation d'objets ayant des membres dynamiques allous sur le tas. Exemple : L'exemple suivant montre la limite de la surcharge par dfaut de l'oprateur d'affectation
class TabEntiers { int Nb; int* T; public : TabEntiers(int Ni) { Nb=Ni; T=new int [Nb]; } ~TabEntiers() { delete[]T; } void Saisir() { for(int k=0;k<Nb;k++) { cou<<"Donner l'lment d'indice "<<k; cin>>T[k]; } } void Afficher() { for(int k=0;k<Nb;k++) { cout<<T[k]<<' '; } } }; void main() { TabEntiers TE1(5), TE2(5); TE1.Saisir(); TE2 = TE1; }
int Nb 5

TE1
copie

TE2
5 int Nb FFE4D8A int* T

int* T FFE4D8A

Etat de la mmoire aprs l'affectation de TE2=TE1

La copie ralise par l'oprateur d'affectation reste donc partielle et incomplte. Dans ce cas il faut modifier le comportement par dfaut de cet oprateur pour raliser une copie complte d'objets. Cette modification doit procder selon les tapes suivantes : o Vrifier le cas de l'auto-affectation (Affectation d'un objet lui-mme) o Librer l'ancien contenu de l'oprande de gauche. o Allouer un nouvel espace pour l'oprande de gauche qui correspond aux donnes copier depuis l'oprande droit. o Copier les donnes de l'oprande droit vers l'oprande gauche.

Version 3.7

150

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Le code quivalent se prsente alors comme suit :


TabEntiers& TabEntiers::operator=(const TabEntiers& TE) { // Vrification s'il s'agit d'une auto affectation if(this != TE) { // Libration de l'ancien contenu delete[] T; // Allocation de l'espace pour le nouveau contenu T= new int [TE.Nb]; // Copie du nouveau contenu Nb = TE.Nb; for(int k=0;k<Nb;k++) T[k]=TE.T[k]; } return *this; }

Remarques : Le C++ impose que la surcharge de l'oprateur d'affectation se fasse comme une fonction membre non statique. Une telle contrainte oblige l'oprande de gauche d'tre un objet de classe et garantit par consquent de pouvoir le manipuler en tant qu'une lvalue dans laquelle on peut placer un contenu. Le fait d'utiliser un paramtre de type const permet de traiter convenablement le cas de l'affectation d'un objet constant et ce en plus des objets non constants comme oprande de droite. La valeur de retour de la surcharge aurait pu tre void auquel cas l'affectation se limiterait la simple expression TE2 = TE1. Le fait de renvoyer un objet TabEntiers offre la possibilit de pouvoir effectuer des affectations multiples du genre : TE3 = TE2 = TE1; TE3 = TE2 = TE1; est traduite comme TE3.operator=(TE2.operator=(TE1)); Nota bene : Renvoi d'une rfrence Le renvoi d'une rfrence par une fonction doit tre employ avec prcaution. En effet, il faut viter de renvoyer la rfrence d'un objet locale la fonction parce que cet objet sera automatiquement dtruit aprs l'appel de la fonction. Ce problme ne se pose pas dans le cas de cette surcharge puisque la rfrence renvoye est celle de l'objet courant qui n'est pas local la fonction. Le fait de renvoyer une rfrence un objet TabEntiers permet par ailleurs d'viter l'appel implicite du constructeur de recopie. Surcharge de l'oprateur d'indexation [ ] Pour les classes qui possdent un attribut de type tableau, la surcharge de l'oprateur d'indexation offre une possibilit syntaxique qui permet d'accder aux lments de ce dernier directement partir de l'objet sans passer d'une manire explicite par le tableau. Exemple : Par exemple pour le cas de la classe TabEntiers, la rcupration de la valeur d'un lment du tableau doit passer par l'appel d'une fonction ddie :

Version 3.7

151

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

int TabEntiers::GetElement(int Indice) { return T[Indice]; }

La surcharge de l'oprateur [] sous forme d'une mthode publique comme suit :


int& TabEntiers::operator[](int Indice) { return T[Indice]; }

permet d'avoir le mme effet que GetElement mais en utilisant en plus une syntaxe plus souple. En effet l'accs un lment du tableau partir d'un objet TE se fait comme suit :
TabEntiers TE(5); TE[i]=8; // Accs l'lment d'indice i grce la surcharge de []

Remarques : Le fait d'utiliser une valeur de retour de type rfrence permet de laisser envisager d'utiliser TE[i] comme un oprande gauche dans une affectation. (Exemple : TE[i]=5). Une valeur de retour de type int est donc possible mais restreint l'utilisation de l'indexeur aux oprations de lecture de valeur seulement. Tout comme l'oprateur d'affectation, le C++ impose que la surcharge de l'oprateur d'indexation se fasse comme une fonction membre non statique. Surcharge de l'oprateur ++ L'oprateur ++ est un oprateur unaire. Il a la particularit de pouvoir tre utilis de deux manires : postfix et prfix. Surcharge de la version prfixe L'exemple suivant montre la surcharge sous forme d'une fonction membre de la version prfixe de l'oprateur ++ pour la classe Complexe :
// Surcharge sous forme d'une fonction membre Complexe Complexe::operator++() { Reel++; Imaginaire++; return *this; }

Il est tout fait possible de raliser cette surcharge sous forme d'une fonction globale et amie. Le prototype de cette dernire prendra alors un seul paramtre qui reprsente l'objet incrmenter. Cette surcharge se prsente comme suit :
// Surcharge sous forme d'une fonction amie Complexe operator++(const Complexe& C) { C.Reel++; C.Imaginaire++; return C; }

Version 3.7

152

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Surcharge de la version postfixe Pour pouvoir la distinguer de la version prfixe, le C++ impose que la version postfixe utilise un paramtre supplmentaire et fictif de type int. Ce paramtre permet tout simplement au compilateur de choisir la version approprie utiliser mais aucune valeur ne lui sera rellement transmise lors de l'appel. La surcharge sous forme d'une fonction membre de cette version se fait comme suit :
Complexe operator++(int n) { Complexe C(*this); Reel++; Imaginaire++; return C; }

La surcharge sous forme d'une fonction globale et amie de cette version se fait comme suit :
Complexe operator++(const Complexe& C, int n) { Complexe R(C); C.Reel++; C.Imaginaire++; return R; // Renvoi de l'objet non incrment }

Considrations relatives la surcharge des oprateurs


Il n'est pas possible de crer de nouveaux oprateurs avec de nouveaux symboles. Les oprateurs surchargs conservent leur ordre de priorit et leur associativit. La commutativit n'est pas toujours conserve. Par exemple la surcharge sous forme d'une fonction membre de la multiplication d'un Complexe et d'un rel ne peut pas servir pour raliser la multiplication d'un rel par un complexe (on ne peut pas appeler la mthode partir d'un rel). Un oprateur garde toujours sa pluralit en terme d'oprandes. Ainsi s'il est unaire (monadique) il reste unaire et s'il est binaire (dyadique) alors il reste galement binaire. Il n'est pas possible de dfinir de valeur par dfaut pour les paramtres d'une fonction oprateur (pour assurer la prservation de la pluralit). Le C++ autorise la surcharge de la majorit des oprateurs sauf quelques uns tels que les oprateurs d'accs aux membres d'une classe (:: , . , .*), l'oprateur sizeof ainsi que l'oprateur ternaire ( ? : ). Considration relative la signification de la surcharge des oprateurs Le C++ laisse le libre choix quant la signification attribuer la surcharge d'un oprateur. Toutefois, il est toujours conseill que cette signification reste guide par le bon sens et ne s'loigne pas trop de celle utilise par dfaut. Ainsi, il est dconseill par exemple d'utiliser le symbole + pour raliser la multiplication de deux objets de la classe Complexe mme si ceci reste tout fait possible et permis par le langage.

Version 3.7

153

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Surcharge de loprateur de cast


Grce au constructeurs de transtypage, le C++ met la disposition des programmeurs un outil permettant de faire des conversions implicites dune donne dun type S (de base ou personnalis) vers un type D quils construisent laide des classes (Cf. chapitre Constructeurs et destructeur). Cela dit, les constructeurs de transtypage ne permettent pas la conversion inverse (du type D vers le type S) surtout si D est un type de base (int, char, etc.) ne disposant pas de mcanisme de construction. Mme si D tait un type classe, sa dfinition pourrait tre inaccessible pour pouvoir la modifier ou lui ajouter un constructeur de transtypage symtrique (Conversion de D vers S) surtout si cette classe a t dveloppe par un tiers. Pour palier ce manque, il est possible dajouter la dfinition dune classe un oprateur de transtypage qui assure la conversion des objets de cette classe vers un type de destination souhait. Lajout de loprateur de cast une classe se fait travers la surcharge de loprateur cast. Cet oprateur est dfini comme une fonction membre non statique, sans paramtres et sans indication pralable du type de retour. Elle se prsente selon le modle suivant : Syntaxe : NomClasse::operator TypeDestination() { return (Valeur) ; } Dans ce modle : NomClasse dsigne la classe de lobjet convertir. TypeDestination dsigne le type de destination vers lequel sera converti lobjet. Ce type dsigne galement dune manire implicite le type de retour de la fonction operator qui nest pas explicitement spcifi dans ce cas. Valeur dsigne le rsultat de la conversion. Valeur doit tre de type TypeDestination. Exemple : Lexemple suivant montre la surcharge de loprateur cast pour la classe complexe. Pour cette surcharge on considre que la conversion renvoie la partie relle seulement du complexe.
class Complexe { public : operator double(); }; Complexe::operator double(); { return Reel; }

Version 3.7

154

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Grce cette surcharge de loprateur cast, il devient possible dutiliser lexpression suivante lors de la manipulation des objets de la classe Complexe :
Complexe C(2.5,3.8); double d = C ; // Conversion implicite de C vers le type double // grce lappel de loprateur cast.

Il est galement possible dcrire lexpression suivante :


double d2 = 5.2 + C ; // Conversion implicite de C vers le type // double grce lappel de loprateur cast.

Laddition est par dfaut dfinie entre deux rels mais elle ne lest pas entre un rel et un complexe. Pour raliser cette addition le compilateur convertit implicitement lobjet C vers le type double puis ralise une addition entre deux rels et le rsultat est par consquent un rel.

Conversions implicites dfinies par lutilisateur


Grce au constructeur de transtypage et la surcharge de loprateur cast, il devient possible au compilateur deffectuer des conversions implicites de type. Ces conversions dfinies par lutilisateur et qui sont ralises dune manire automatique par le compilateur peuvent parfois conduire ce dernier vers des situations dambigut ne pouvant tre rsolues que par des appels explicites des outils de conversion. Exemple : Si on considre la dfinition suivante de la classe Complexe comprenant la fois un constructeur de transtypage et une surcharge de loprateur +.
class Complexe { double Reel; double Imaginaire; public : Complexe(double r, double i) : Reel(r), Imaginaire(i) {} Complexe(){Reel = 0; Imaginaire = 0;} // Constructeur de transtypage Complexe(double val){Reel = val;} // Surcharge de loprateur + Complexe operator+(const Complexe& C) { Complexe Resultat; Resultat.Reel = this->Reel + C.Reel; Resultat.Imaginaire = this->Imaginaire + C.Imaginaire; return Resultat; } // Surcharge de loprateur cast operator double() { return Reel; } void Afficher() { cout<<"Partie relle : "<<Reel; cout<<"Partie imaginaire : "<<Imaginaire; } } ;

Version 3.7

155

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Ambigut dinterprtation des expressions On considre lexpression suivante :


Complexe C(4.6, 3.8) ; 5.2 + C ;

Linterprtation de lexpression 5.2 + C; par le compilateur va conduire ce dernier vers une ambigut. En effet deux interprtations sont possibles dans ce cas : Premire interprtation : o Conversion de 5.2 vers complexe grce au constructeur de transtypage. o Faire une addition entre deux complexes vu que cette opration est surdfinie dans ce cas pour les complexes, chose qui va donner un rsultat complexe. Deuxime interprtation : o Conversion du C vers le type double grce loprateur cast. o Faire une addition entre deux rels, chose qui va donner un rsultat rel. Utilisation des conversions explicites Pour viter cette ambigut, il est ncessaire dutiliser des conversions explicites dans cette expression. Cette dernire pourra alors scrire de deux manires : Premire possibilit :
5.2 + (double)C ;

Le compilateur commence par faire un appel loprateur cast pour la conversion de lobjet C vers le type double. Le rsultat est ensuite ajout au premier double (5.2). Il sagit dans ce cas dune addition entre doubles et le rsultat final est par consquent un double. Deuxime possibilit :
Complexe(5.2) + C ;

Lappel explicite du constructeur de transtypage engendre la conversion du double 5.2 vers un objet Complexe. Ce dernier sera additionn par la suite lobjet C en utilisant la surcharge de loprateur + dfinie dans la classe Complexe. Le rsultat est donc dans ce cas un objet de type Complexe. Interdiction des conversions implicites Lappel implicite du constructeur de transtypage peut conduire parfois vers des situations gnantes. En effet le compilateur considre tout constructeur pouvant tre appel avec un seul paramtre (de type diffrent que celui de la classe en cours) comme un constructeur de transtypage alors quun tel constructeur peut figurer dans une classe sans tre destin lorigine par le concepteur de cette dernire faire des conversions. Cest le cas par exemple du constructeur suivant de la classe TabEntiers qui est sens tout simplement allouer lespace mmoire ncessaire lobjet.

Version 3.7

156

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

class TabEntiers { int Nb; int* T; public : TabEntiers(int Ni) { Nb=Ni; T=new int[Nb]; } } ;

Pour pallier ce problme, le C++ offre un outil permettant de contrler lappel implicite des constructeurs particuliers. Ce contrle est ralis avec le mot-cl explicit. Ce dernier utilis comme prfixe de la dclaration dun constructeur interdit ce dernier dtre appel implicitement par le compilateur et seul les appels explicites lui seront alors autoriss. La signature du constructeur de TabEntiers serait alors :
explicit TabEntiers(int Ni);

Remarque : explicit ne peut tre appliqu quaux constructeurs.

Version 3.7

157

Karim Kalti

Les modles Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les modles
Les modles, appels galement patrons, permettent de crer des fonctions ou des classes gnriques qui peuvent tre paramtres de point de vue types des donnes qu'elles manipulent. Ces modles permettent ainsi d'avoir une criture plus concise du code puisqu'un modle particulier peut remplacer plusieurs dfinitions de fonctions ou de classes.

Les modles de fonctions


Un modle de fonction, appel galement patron de fonction ou fonction gnrique est une fonction dont la dfinition utilise un ou plusieurs types paramtrables. Le type effectif qui sera utilis est dtermin au moment de l'appel de la fonction gnrique. Par exemple si une fonction va effectuer les mmes traitements (mme algorithme) mais sur des donnes de types diffrents alors au lieu de surcharger une telle fonction, il est possible de la dfinir en tant que modle puis instancier le type exact au moment de l'appel de cette dernire. Dclaration du paramtre de type La dclaration d'un paramtre reprsentant un type T , appel galement paramtre de type se fait de la manire suivante : template <class T> template indique que l'on est en train de crer un modle. class T est une dclaration du nom du paramtre qui va dsigner le type. Le nom de ce paramtre est dans ce cas T. Dfinition d'une fonction gnrique La dfinition d'une fonction gnrique qui utilise des paramtres de type se fait de la manire suivante : template <class T> TypeRetour NomFonction(Paramtres) { // Corps } Exemple : On considre la fonction Max qui renvoie le maximum entre deux lments. Ces deux lments peuvent tre d'un type quelconque : int, char, float, double, etc. Au lieu de proposer une dfinition de la fonction Max pour chacun de ces types, il est possible de dfinir une seule version qui servira dans ce cas de modle.

Version 3.7

158

Karim Kalti

Les modles Programmation oriente objet (C++) _________________________________________________________________________________________________________________

template <class T> T Max(T a, T b) { if(a>b) return a; return b; }

Appel d'une fonction gnrique La syntaxe de lappel dune fonction gnrique se prsente comme suit : NomFonction<TypeEffectif>(arguments); Exemple : Lappel suivant renvoie le maximum de deux entiers :
int c ; c = Max<int>(5,19) ;

Toutefois, le compilateur est capable en analysant la signature de lappel de connatre implicitement le ou les types effectifs utiliss. De ce fait, l'appel de la fonction gnrique peut se faire comme pour les fonctions classiques en lui passant des paramtres effectifs sans spcifier explicitement largument du type. NomFonction(arguments); Exemple :
template <class T> T Max(T a, T b) { if(a>b) return a; return b; } int main() { int a=5, b=9, c=Max(a,b); double m=3.5, k=Max(m,n); cout<<"Le max cout<<"Le max return 0; }

c; n=2.9, k; des entiers est : "<<c; des rels est : "<<k;

Remarque : Au moment de lappel, le compilateur va gnrer une nouvelle instance de la fonction gnrique dans laquelle il remplace le type gnrique T par le type effectif qui lui correspond dans l'appel. Cette nouvelle instance reprsente la fonction qui sera vritablement excute au moment de l'appel. Dans lexemple prcdent le compilateur gnre deux instances de la fonction Max qui correspondent aux prototypes suivants : int Max(int; int) pour le premier appel. double Max(double, double) pour le second appel. 159

Version 3.7

Karim Kalti

Les modles Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Le compilateur gnre autant d'instances partir d'un modle qu'il y a d'appels avec des types effectifs diffrents. La fonction gnrique va servir essentiellement pour la synthse des fonctions qui seront vritablement appeles. C'est le code binaire de ces dernires qui va figurer dans l'excutable gnr. Dans cet excutable le code gnrique n'a aucune existence. Utilisation de plusieurs paramtres de type Il est possible d'utiliser plus qu'un paramtre de type dans une fonction gnrique. Dans ce cas, ces paramtres doivent tre dclars chacun avec le mot rserv class et spars par des virgules. L'exemple suivant montre le prototype d'une fonction gnrique qui utilise deux paramtres de type distincts : Exemple :
template <class T1, class T2> T2 F(T1 a, T2 b);

La fonction F prend un premier paramtre de type T1 et un deuxime paramtre de type T2. T1 et T2 sont dans ce cas gnriques et seront instancis au moment de l'appel de F. Utilisation des paramtres classiques dans un modle de fonctions Il est tout fait possible de combiner dans le prototype d'un modle de fonctions des paramtres de type avec des paramtres classiques pour lesquels le type est connu. L'exemple suivant donne une illustration de cette situation. Exemple :
template <class T> int CompteZero(T* Tab, int N) { int NbZeros = 0; for(int i=0;i<N; i++) if(!Tab[i]) NbZeros++; return NbZeros; } int N est dans ce cas un paramtre classique.

Surdfinition d'une fonction gnrique Tout comme les fonctions classiques, il est tout fait possible de surdfinir une fonction gnrique. Cette surdfinition peut tre elle-mme une fonction gnrique comme elle peut comporter des paramtres classiques. Exemple : L'exemple suivant montre deux surdfinitions possibles de la fonction gnrique Max une gnrique et l'autre non :
// Surdfinition gnrique de la fonction Max template <class T> T Max(T a, T b, T c) { return Max(Max(a,b),c); }

Version 3.7

160

Karim Kalti

Les modles Programmation oriente objet (C++) _________________________________________________________________________________________________________________

// Surdfinition comportant des paramtres classiques (int N) template <class T> T Max(T* Tab, int N) { T MaxVal = Tab[0]; for(int i=1;i<N;i++) if(Tab[i]>MaxVal) MaxVal = Tab[i]; return MaxVal; }

Spcialisation d'une fonction gnrique Il y a des situations o l'algorithme d'une fonction gnrique savre inadapt pour certains types d'arguments. Dans ce cas, il est possible de spcialiser la fonction gnrique en ajoutant des versions adaptes ces types tout en conservant le modle du prototype (il ne s'agit pas d'une surdfinition). Exemple : La fonction gnrique Max telle qu'elle est dfinie prcdemment n'est pas adapte pour la comparaison des chanes parce qu'une telle comparaison ne se fait pas l'aide de l'oprateur > mais avec la fonction strcmp. Il est dans ce cas possible d'ajouter une spcialisation de la fonction gnrique pour ce cas particulier.
template <class T> T Max(T a, T b) { if(a>b) return a; return b; } // Spcialisation de Max char* Max(char* a, char* b) { if(strcmp(a,b)>0) return a; return b; } char Ch1[20]="Programme"; char Ch2[20]="Algorithme"; cout<<Max(Ch1,Ch2); // Cest la spcialisation qui est utilise

Dans ce cas le compilateur va commencer sa recherche par la fonction qui correspond exactement la signature de l'appel et va par consquent directement utiliser la spcialisation sans avoir besoin d'analyser la fonction gnrique afin de synthtiser une version adapte. Remarque : Il n'y a pas de moyens de limitation des types qui peuvent tre utiliss avec une fonction gnrique. Par consquent, c'est celui qui fait l'appel de vrifier si le modle est adapt aux types des arguments qu'il utilise. Par exemple la fonction gnrique Max peut accepter n'importe quel type y compris les types personnaliss comme les classes. Si des classes sont passes Max alors, il faut vrifier que l'oprateur > est bien surcharg pour de telles classes.

Version 3.7

161

Karim Kalti

Les modles Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Les classes gnriques


Tout comme cest le cas pour les fonctions, il est possible de dfinir des modles de classes. De tels modles servent crer des types personnaliss qui sont paramtrs par dautres types. Cration dun modle de classe Un modle de classe est cr de la manire suivante : template <class T1, , class Tn> class NomClasse { // Dfinition de la classe }; template sert indiquer quil sagit de la cration de modle. < .. .. .. > dsigne la zone de dclaration du ou des paramtres de type qui seront utiliss par le modle de la classe. Dans cette zone il est galement possible de mentionner directement des types concrets qui seront galement utilise par la classe. class Ti est une dclaration dun paramtre de type qui porte dans le prsent cas le nom Ti. un modle peut toujours utiliser plusieurs paramtres de type. Ces derniers sont dclars chacun avec le mot rserv class et spars par des virgules. NomClasse dsigne le nom de la classe crer. Exemple : Cet exemple montre la cration dun modle de classe servant reprsenter des points.
template <class T> class Point { T x; T y; Point(T abs, T ord) ; void Afficher(); };

Dfinition des mthodes dun modle de classe Les mthodes dun modle de classe sont des modles de fonctions avec les mmes paramtres de type que la classe. Ces mthodes peuvent tre dfinies lintrieur ou lextrieur de la classe. Dfinition lintrieur de la classe (en ligne) La dfinition se fait dans ce cas dune manire classique comme pour les mthodes usuelles. Dfinition lextrieur de la classe La dfinition lextrieur de la classe utilise une syntaxe un peu diffrente que dans le cas classique. Cette syntaxe rappelle au compilateur quil sagit dun modle et mentionne les paramtres de types qui sont utiliss. Cette syntaxe se prsente comme suit :

Version 3.7

162

Karim Kalti

Les modles Programmation oriente objet (C++) _________________________________________________________________________________________________________________

template<class T> NomClasse<T>::NomMethode(Paramtres) { // Dfinition de la mthode } Lexemple suivant donne une illustration de ces deux possibilits de dfinition. Exemple :
template <class T> class Point { T x; T y; // Exemple de dfinition lintrieur de la classe Point(T abs, T ord) { x = abs ; y = ord ; } void Afficher(); }; // Exemple de dfinition lextrieur de la classe template <class T> void Point<T>::Afficher() { cout<<"Abs : "<<x; cout<<"Ord : "<<y; }

Utilisation dun modle de classe Lors de lappel dun modle de fonctions, le compilateur se base sur la signature pour dterminer le type effectif des arguments utiliss. Cette identification automatique par le compilateur nest pas possible lorsquil sagit dutiliser un modle de classe pour instancier un objet. Cest pourquoi la spcification de la classe dans ce cas doit tre accompagne explicitement du (des) types effectif(s) utilis(s). La syntaxe dinstanciation se prsente de ce fait comme suit : NomModleClasse <TypeEffectif> NomObjet(args du constructeur); Exemple :
int main() { Point<int> P1(5,3); Point<double> P2(2.4, 3.7); P1.Afficher(); P2.Afficher(); return 0 ; }

Version 3.7

163

Karim Kalti

Les modles Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Thoriquement, il est possible dinstancier la classe Point avec nimporte quel type. Toutefois, sur un plan pratique les types utiliser doivent tre compatibles avec la signification de cette classe (types numriques). Le bon usage du modle reste alors et toujours laffaire de lutilisateur de ce dernier. Classe gnrique avec des paramtres de type et des paramtres classiques En plus des paramtres de type, il est tout fait possible dutiliser des paramtres classiques ayant des types concrets et ce lors de la cration dun modle de classe. Lexemple suivant montre un exemple de ce type de situation. Exemple : La classe Tableau reprsente des tableaux dlments dune manire gnrale indpendamment du type de ces lments. Cette classe prend deux arguments, le premier reprsente le type des lments manipuler et le second reprsente la dimension du tableau. La dfinition de cette classe se prsente comme suit :
template <class T, int N> class Tableau { T Elements[N]; T& operator[](int Index) { return Elements[Index]; } };

Le code suivant montre lutilisation du modle Tableau :


int main() { Tableau<int, 4> T1; cout<<"Saisie des lments du tableau\n"; for(int i=0;i<4;i++) { cout<<"Donner un lment : "; cin>>T1[i]; } cout<<"Affichage des lments du tableau\n"; for(int i=0;i<4;i++) cout<<T1[i]<<' '; system("PAUSE"); return 0; }

Remarques : Cet exemple montre entre autres quil est tout fait possible de crer un tableau non dynamique avec une dimension paramtre. En effet au moment de la cration de la classe concrte partir du modle, le prprocesseur va dans le cas prsent remplacer linstruction : T Element[N]; par int Element[4]; qui est une instruction accepte par le compilateur.

Version 3.7

164

Karim Kalti

Les modles Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La dimension du tableau aurait pu tre passe comme argument du constructeur ce qui viterait ainsi lutilisation du paramtre int dans le modle de la classe. Mais une telle option ne permettrait pas de crer le tableau sur la pile dexcution. Seul serait possible dans ce cas lallocation dynamique. Valeur par dfaut des paramtres dun patron de classe Il est possible de spcifier des valeurs par dfaut aux paramtres dune classe gnrique. La rgle qui rgit cette spcification est semblable celle utilise avec les fonctions usuelles. Exemple :
template <class T=int, int N=10> class Tableau { T Elements[N] ; T& operator[](int Index) { return Elements[Index] ; } } ;

Les instructions suivantes montrent des exemples dutilisation du modle Tableau ainsi dclar.
Tableau<float, 4> ; // Tableau de 4 rels simples Tableau<Complexe> ; // Tableau de 10 objets de type Complexe. Tableau<> ; // Tableau de 10 entiers.

Remarque : La notion de paramtre par dfaut na pas de signification pour les modles de fonctions. Spcialisation dun modle de classe La possibilit de spcialisation existe galement avec les modles des classes comme cest le cas avec les fonctions. Elle peut tre utile afin dadapter le modle certaines situations particulires. La spcialisation peut concerner une mthode du modle de classe comme elle peut concerner la classe en entier. Spcialisation dune mthode La spcialisation dune mthode dun modle de classe se fait selon la syntaxe suivante : TypeRetour NomClasse<TypeEffectif>::NomMethode(Paramtres) { // Dfinition de la spcialisation de la mthode } Lexemple suivant montre une spcialisation de la mthode Afficher du modle Point pour quelle affiche convenablement les coordonnes dans le cas o le type effectif utilis est le char (codage des entiers sur un octet).

Version 3.7

165

Karim Kalti

Les modles Programmation oriente objet (C++) _________________________________________________________________________________________________________________

// Dfinition de la mthode Afficher pour le modle de classe template <class T> class Point { T x; T y; Point(T abs, T ord) {x= abs ; y ord ;} void Afficher(); }; template <class T> void Point<T>::Afficher() { cout<<"Abs : "<<x<<endl; cout<<"Ord : "<<y<<endl; } // Spcialisation de la mthode Afficher pour les caractres void Point<char> ::Afficher() { cout<<"Abs : "<<(int)x<<endl; cout<<"Ord : "<<(int)y<<endl; } // Utilisation de la classe Point int main() { Point<int> P1(5,9); Point<char> P2('a','b'); P1.Afficher(); // Afficher gnre par le compilateur P2.Afficher(); // Afficher spcialise pour char return 0; }

Spcialisation dune classe Il est tout fait possible de spcialiser la classe dans sa totalit. Dans ce cas la nouvelle spcialisation doit suivre la dfinition du modle tout en indiquant le ou les types concrets quelle utilise. La syntaxe adopter est la suivante : class Nomclasse<TypeConcret> { // Nouvelle dfinition } Exemple : La spcialisation de la classe Point pour le type char doit tre dfinie de la manire suivante :
class Point<char> { // Nouvelle spcialisation }

Version 3.7

166

Karim Kalti

Les modles Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Les modles constituent un outil puissant permettant de rduire considrablement le code. Toutefois cet outil doit tre utilis avec prcaution vu lexistence de situations pouvant mener vers des ambiguts dinterprtation au moment de la compilation. Par ailleurs, la gnration du code faite par le compilateur est ralise dune manire automatique et ne donne aucune garantie sur ladaptation des traitements aux donnes. Cest lutilisateur du modle qui doit toujours veiller sur cette adaptation et lassurer ventuellement par des spcialisations.

Version 3.7

167

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La gestion des exceptions


Introduction
Les exceptions sont des anomalies qui peuvent avoir lieu au moment de l'excution des programmes (pas au moment de la compilation) et qui conduisent gnralement vers des erreurs (exemple : tentative de lecture d'un fichier qui n'existe plus ou qui est dplac). Ancienne technique de gestion des erreurs au moment de lexcution Avec les langages qui ne supportent pas la gestion des exceptions (exemple C), le traitement des erreurs se fait gnralement en effectuant des tests sur les valeurs de retour des fonctions. Ces dernires retournent le plus souvent des codes indiquant le type de l'erreur. Ces codes ne comportent pas des informations supplmentaires sur les causes et les paramtres des erreurs. En plus cette technique devient fastidieuse dans le cas d'appels imbriqus de fonctions (la gestion des erreurs complique le code surtout au niveau des fonctions internes). Avantage de la gestion des exceptions Le mcanisme de gestion des exceptions permet d'viter ces problmes : Il devient ainsi possible de grer un seul niveau les exceptions, mme celles engendres par des fonctions internes (Possibilit de centralisation de la gestion en des points prcis). Il est galement possible d'avoir plus d'informations sur les causes des erreurs. Les erreurs ne sont plus dcrites par des codes mais par des objets qui sont spcifiques chacun un type d'erreur bien prcis. Chaque objet comporte des informations qui dcrivent l'erreur. Familles d'exceptions Il existe deux grandes familles d'exceptions : Les exceptions pouvant engendrer des erreurs systmes comme les divisions par zro, l'accs un tableau en dehors de ses bornes, l'accs un fichier inexistant, etc. Les exceptions pouvant engendrer des erreurs lies la couche mtier de lapplication. Ces exceptions sont gnralement de type smantique et dpendent du contexte de l'application.

Le mcanisme de gestion dune exception


Le mcanisme de la gestion des exceptions se droule en deux phases : La premire phase concerne la dtection de lanomalie. La fonction qui dtecte cette anomalie doit alors provoquer une rupture de lexcution de la squence dinstructions qui la compose et lever (ou lancer) une exception pour informer son utilisateur de cet vnement (utilisateur = le programme qui appelle cette fonction). La deuxime phase concerne la gestion de lexception. Cette gestion doit tre normalement faite par le programme appelant de la fonction ayant lev lexception en question. Ce dernier doit alors dfinir le traitement adquat de cette exception. Ces deux phases (dtection et traitement de lanomalie) sont compltement spares. Elles sont labores le plus souvent par deux utilisateurs diffrents (celui qui lve l'exception n'est pas ncessairement celui qui la traite). Cette sparation entre les instructions essentielles d'un programme qui grent son droulement normal des instructions de gestion d'erreurs permet d'amliorer la lisibilit de ce dernier et facilite par consquent sa maintenance. 168

Version 3.7

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

La leve d'une exception


La leve dune exception se fait laide de linstruction throw selon la syntaxe suivante : Syntaxe : throw NomException ; NomException dsigne une expression pouvant tre de nimporte quel type. Elle reprsente lexception qui est lance par le programme. Toutes les instructions situes aprs le throw (dans le module ayant lev lexception) seront abandonnes et par consquent jamais atteintes. Exemple 1 : Cet exemple illustre la leve dune exception de type chane de caractres par une fonction f.
void f(int minute) { if(minute<1 || minute >=60) throw "La valeur saisie ne correspond pas une minute valide"; // Partie jamais atteinte en cas d'excution du throw }

Remarque : Une exception peut tre de nimporte quel type (entier, chane, numration, etc.). Toutefois en POO, les exceptions sont le plus souvent codes sous forme dobjets (instances de classe). Un tel codage permet de faire accompagner lexception dautres informations qui peuvent savrer utile celui qui va la grer.

Interception et gestion d'une exception


La gestion d'une exception se fait l'aide d'une structure compose de deux blocs dfinis par les deux instructions try et catch : Le bloc try contient les instructions qui sont susceptibles d'engendrer une erreur. try permet donc de laisser le choix au programmeur de grer ou non les exceptions dans son code. En effet on ne tente de grer les exceptions que pour le code situ dans le bloc try. Les instructions places en dehors de ce bloc ne bnficient pas de ce mcanisme de gestion des exceptions. Le bloc catch assure l'interception et le traitement de l'exception. Ce bloc comporte : o Un entte indiquant le type de l'exception qu'il peut intercepter. o Une suite d'instructions dfinissant le code de traitement de l'exception. Syntaxe : try { // Bloc d'instructions du programme } catch(TypeException e) { // Bloc de gestion des erreurs }
Version 3.7

169

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Les accolades dans les blocs de try et de catch sont obligatoires mme si ces derniers comportent une seule instruction. Exemple :
#include <iostream.h> void f(int); int main() { int minute; try { cout<<" Donner une minute : "; cin>>minute; f(minute); } catch(const char* e) { cout<<e<<endl; } return 0; }

Remarque : La porte de l'identificateur e est limite au bloc catch. Il est gnralement utilis pour rcuprer les informations sur l'erreur. S'il n'est pas fait usage de e dans le bloc catch alors cet identificateur peut tre omis. L'entte du catch se prsente dans ce cas comme suit : catch(TypeException) { // Bloc de gestion des erreurs } Droulement de l'excution Le programme entre dans le bloc try. Il commence alors l'excution squentielle des instructions de ce bloc. Ds qu'une anomalie est dtecte, une exception correspondant au type de cette anomalie est cre. Le programme abandonne alors le reste des instructions du bloc, quitte ce dernier et rentre tout aussi automatiquement dans le bloc catch. Aprs le traitement de l'exception, le programme reprend son excution au niveau de l'instruction qui suit immdiatement le bloc catch. Si aucune anomalie n'a t dtecte dans le bloc try alors le gestionnaire catch sera ignor et l'excution continue au niveau de l'instruction qui suit immdiatement ce dernier. Exemple :
#include <iostream.h> float Div(float a, float b) { if(b==0) throw "Impossible de faire une division par zro"; return a/b; }

Version 3.7

170

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

int main() { float a,b; try { cout<<" Donner a :"; cin>>a; cout<<" Donner b :"; cin>>b; cout<<"\nLe rsultat de la division est :"<<Div(a,b); } catch(const char* e) { cout<<e<<endl; } cout<<"FIN DU PROGRAMME"<<endl; return 0; }

Exemple d'une division normale Donner a : 6 Donner b : 3 Le rsultat de la division est : 2 FIN DU PROGRAMME

Exemple d'une division par zro Donner a : 6 Donner b : 0 Impossible de faire une division par zro FIN DU PROGRAMME

Interception et gestion de plusieurs exceptions


Les instructions du bloc try peuvent parfois engendrer plusieurs anomalies de types diffrents. Chacune de ces anomalies peut alors tre traite par un bloc catch part. La structure de gestion d'exceptions se compose dans ce cas d'un bloc try et de plusieurs gestionnaires catch successifs. Chaque gestionnaire catch sera ddi au traitement d'une exception particulire. Syntaxe : blocs catch multiples try { // Bloc d'instructions du programme } catch(TypeException_1 e_1) { // Bloc de gestion des erreurs } catch(TypeException_n e_n) { // Bloc de gestion des erreurs }

Version 3.7

171

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Droulement de l'excution En cas d'anomalie, le contrle passe au premier gestionnaire catch. Si cette anomalie correspond l'argument de ce dernier alors elle sera traite par le bloc qui lui est associ. Dans le cas contraire le catch suivant est inspect et ainsi de suite. Ds qu'une anomalie est traite par un catch les catchs suivants ne sont plus envisags. Dans le cas o l'anomalie ne correspond aucun gestionnaire catch du bloc try-catch courant, la recherche se poursuit dans le bloc try-catch de niveau suprieur (celui qui englobe le try-catch imbriqu). Ce processus de remonte de la recherche se poursuit jusqu' ce qu'un gestionnaire catch pour l'exception courante soit trouv. Ds qu'un gestionnaire catch pouvant grer l'exception est rencontr, on entre dans son bloc et l'excution du programme continue dans ce gestionnaire. Si aucun gestionnaire n'est trouv, le programme appelle la fonction terminate() dfinie dans la bibliothque standard du C++. Cette fonction propose un comportement par dfaut, qui appelle notamment la fonction abort() qui elle-mme indique que le programme se termine anormalement Abnormal program termination . Exemple 1:
class ErreurCreation{ }; class ErreurAcces{ }; class Tableau { int* Elements; int NbElements; public : Tableau(int Taille) { if(Taille<0) throw ErreurCreation(); Elements = new int[Taille]; NbElements = Taille; for(int i=0; i<NbElements; i++) Elements[i]=0; } ~Tableau() { delete[] Elements; } int Get(int Indice) { if(Indice<0 || Indice>=NbElements) throw ErreurAcces(); return Elements[Indice]; } void Set(int Indice, int Valeur) { if(Indice<0 || Indice>=NbElements) throw ErreurAcces(); Elements[Indice] = Valeur; } void Afficher() { for(int i=0; i<NbElements; i++) cout<<Elements[i]<<' '; } };

Version 3.7

172

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

int main() { int Taille, Indice; try { cout<<"Donner la taille du tableau : "; cin>>Taille; Tableau Tab(Taille); cout<<"Donner l'indice de la case mettre 1 : "; cin>>Indice; Tab.Set(Indice,1); Tab.Afficher(); } catch(ErreurAcces) { cout<<"Erreur d'accs"; } catch(ErreurCreation) { cout<<"Erreur de cration du tableau"; } cout<<"\nFIN DU PROGRAMME"; system("PAUSE"); return 0; }
Exemple 1 : Leve d'une exception de cration de tableau Donner la taille du tableau : -3 Erreur de cration du tableau FIN DU PROGRAMME Exemple 2 : Leve d'une exception d'accs au tableau Donner la taille du tableau : 3 Donner l'indice de la case mettre 1 : 5 Erreur d'accs FIN DU PROGRAMME

Dclaration des exceptions leves par une fonction


Gnralement, une fonction lve des exceptions et laisse la main ses utilisateurs pour leurs gestions (la gestion dpend du contexte de l'application). Ces utilisateurs (ceux qui appellent la fonction) ont le plus souvent accs seulement la spcification de la fonction (prototype) mais pas sa dfinition (le corps). Une fonction peut dans ce cas indiquer ses utilisateurs les exceptions qu'elle lve en interne et ce au niveau de sa dclaration selon la syntaxe suivante : Syntaxe : TypeRetour NomFonction(Paramtres) throw(TypeException1,, TypeExceptionN)

Version 3.7

173

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
class Tableau { int* Elements; int NbElements; public : Tableau(int Taille) throw (ErreurCreation); ~Tableau(); int Get(int Indice) throw (ErreurAcces); void Set(int Indice, int Valeur) throw (ErreurAcces); void Afficher() throw(); };

Remarque : L'instruction throw utilise sans paramtres dans la dclaration d'une fonction de la manire suivante throw( ) signifie que cette fonction ne lve aucune exception. L'absence du throw dans la dclaration d'une fonction signifie que cette dernire peut lever tout type d'exceptions.

Type d'exceptions interceptes par un gestionnaire catch


Un gestionnaire catch ayant un paramtre de type T1, T1&, const T1 ou const T1& peut intercepter : Les exceptions de type T1. Les exceptions de type T2 dans le cas o T2 est une classe et T1 est une classe de base accessible de T2. Les exceptions de type T2 dans le cas o T2 est un pointeur sur une classe A et T1 est un pointeur sur une classe de base accessible de A. Remarque : Importance de l'ordre des gestionnaires catch Dans une structure comportant des gestionnaires catch multiples, l'ordre de ces gestionnaires est important surtout si leurs arguments sont des objets qui drivent les uns des autres. Par exemple si on considre les trois classes d'exceptions dclares de la manire suivante :
class A; class B : public A; class C : public A; et les trois gestionnaires catch suivants : catch(A) { } catch(B) // Bloc jamais excut { } catch(C) // Bloc jamais excut { }

Alors les deux derniers gestionnaires de cette structure ne pourront jamais tre excuts. En effet, toutes les exceptions leves de type B et C seront automatiquement interceptes par le premier gestionnaire car elles sont la base de type A. Le gestionnaire interceptant l'exception de type A devrait normalement tre plac en dernier lieu.

Version 3.7

174

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Exemple :
#include <iostream.h> #include <sstream.h> class Erreur { public : string GetMessage() { ostringstream MessageErreur; MessageErreur<<"Erreur d'excution du programme\n"; return MessageErreur.str(); } }; class ErreurCreation : public Erreur { int TailleIncorrecte; public: ErreurCreation(int Valeur) { TailleIncorrecte = Valeur; } string GetMessage() { ostringstream MessageErreur; MessageErreur<<"Erreur de cration du tableau : "<<endl; MessageErreur<<"Taille incorrecte:"<<TailleIncorrecte; return MessageErreur.str(); } }; class ErreurAcces : public Erreur { int IndiceIncorrect; int TailleMax; public: ErreurAcces(int Valeur, int Taille) { IndiceIncorrect = Valeur; TailleMax = Taille; } string GetMessage() { ostringstream MessageErreur; MessageErreur<<"Erreur d'accs au tableau : "<<endl; MessageErreur<<"Indice incorrect:"<<IndiceIncorrect; return MessageErreur.str(); } };

Version 3.7

175

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

class Tableau { int* Elements; int NbElements; public : Tableau(int Taille) { if(Taille<0) throw ErreurCreation(Taille); Elements = new int[Taille]; NbElements = Taille; for(int i=0; i<NbElements; i++) Elements[i]=0; } ~Tableau() { delete[] Elements; } int Get(int Indice) { if(Indice<0 || Indice>=NbElements) throw ErreurAcces(Indice, NbElements); return Elements[Indice]; } void Set(int Indice, int Valeur) { if(Indice<0 || Indice>=NbElements) throw ErreurAcces(Indice, NbElements); Elements[Indice] = Valeur; } void Afficher() { for(int i=0; i<NbElements; i++) cout<<Elements[i]<<' '; } }; int main() { int Taille, Indice; try { cout<<"Donner la taille du tableau : "; cin>>Taille; Tableau Tab(Taille); cout<<"Donner l'indice de la case mettre 1 : "; cin>>Indice; Tab.Set(Indice,1); Tab.Afficher(); } catch(ErreurCreation e) { cout<<e.GetMessage(); } catch(ErreurAcces e) { cout<<e.GetMessage(); } catch(Erreur e) { cout<<e.GetMessage(); } cout<<"\nFIN DU PROGRAMME\n"; return 0; }

Version 3.7

176

Karim Kalti

La gestion des exceptions Programmation oriente objet (C++) _________________________________________________________________________________________________________________

Remarque : Exploitation du polymorphisme dans l'interception des exceptions En dfinissant les mthodes GetMessage() comme tant virtuelles, il devient possible de rduire les trois gestionnaires de l'exemple prcdent un seul tout en obtenant le mme effet. La dfinition de ce seul gestionnaire est comme suit :
try { } catch(Erreur& e) { cout<<e.GetMessage(); }

En effet, l'interception de la rfrence de l'exception permettra dans ce cas de bnficier du polymorphisme de la mthode GetMessage(). Interception de toutes les exceptions Il existe une dfinition de l'entte du catch qui permet d'intercepter toute exception quelque soit son type. Cette dfinition se prsente comme suit : catch() S'il figure dans une structure comportant plusieurs catch, ce gestionnaire doit tre alors le dernier de la liste. En effet, tous les gestionnaires catch qui le succdent ne peuvent jamais tre atteints puisqu'il intercepte tout type d'exception.

Version 3.7

177

Karim Kalti

Annexe

Version 3.7

Karim Kalti

Les Fichiers
Techniques d'accs un fichier
x L'accs squentiel : il consiste traiter les informations "squentiellement" c'est dire dans l'ordre dans lequel elles apparaissent dans le fichier. x L'accs direct : il consiste se placer immdiatement sur l'information souhaite sans avoir parcourir celles qui prcdent.

Les fonctions de base pour l'ouverture et la fermeture des fichiers


La structure FILE x FILE : est une structure prdfinie dans la librairie stdio.h. Elle permet un programme de manipuler les fichiers en stockant toutes les informations utiles se rapportant ces derniers et notamment celles qui se rapportent aux tampons associs ces derniers (adresse de dbut, taille en octets,). Ouverture d'un fichier : (fopen) Prototype : FILE* fopen(char* NomFich, char* ModeOuverture) Paramtres : x Le nom du fichier concern fourni sous forme d'une chane de caractres. x Une chane indiquant le mode d'ouverture. Les modes possibles sont : r w a r+ w+ a+ Lecture seulement (le fichier doit exister). Retourne NULL si le fichier n'existe pas criture seulement. Si le fichier n'existe pas, il est cr. S'il existe son ancien contenu est perdu. criture en fin de fichier (appendding). Si le fichier existe dj, il sera tendu, s'il n'existe pas, il sera cr (cas du w). Lecture et criture. Le fichier doit exister. Le contenue n'est pas perdu. Cration pour lecture et criture. Si le fichier existe, son contenu sera dtruit. Lecture ou extension : si le fichier n'existe pas, il sera cr, s'il existe le pointeur sera positionn en fin de fichier. Lcriture se fait en fin de fichier.

Fermeture d'un fichier (fclose) Prototype : int fclose( FILE *F ); Paramtres : F le fichier fermer. Valeur de retour : 0 si le fichier a t convenablement ferm. Autre fonction de fermeture de fichier int _fcloseall( void ) fcloseall ferme tous les fichiers ouverts.

179

Vidage des tampons (fflush) Prototype : int fflush(FILE *stream ); <stdio.h> Paramtres : F: le tampon vider. Valeur de retour : 0 si succs et EOF si erreur. Autre fonction : int flushall(void); <stdio.h> Vide tous les tampons associs aux fichiers ouverts.

Les fonctions de lecture et d'criture binaires dans les fichiers


criture dans un fichier (fwrite) Prototype : size_t fwrite(const void* ptr,size_t sizeU, size_t count, File* F) Paramtres : x x x x ptr : Adresse de dbut du bloc d'informations crire dans le fichier. SizeU : taille en octets du bloc unitaire de donnes crire. Gnralement, c'est la taille du type des donnes. count : nombre de blocs unitaires de donnes crire. F : pointeur sur le fichier dans lequel l'criture sera faite.

Rle : fwrite permet de transfrer un bloc de donnes de taille (count x sizeU) octets, situ en mmoire l'adresse ptr dans le fichier point par F. Valeur de retour : Nombre d'units de donnes (les SizeU) rellement crites dans le fichier. Lecture partir d'un fichier (fread) Prototype : size_t fread(void* ptr, size_t Paramtres : x x x x ptr : Adresse de dbut du bloc de mmoire dans lequel seront stockes les informations lues partir du fichier. SizeU : taille en octets du bloc unitaire de donnes lire. count : nombre de blocs unitaires de donnes lire. F : pointeur sur le fichier partir duquel les informations sont lues.

sizeU, size_t count, File* F);

Rle : fread lit (count x sizeU) octets d'informations partir du fichier F et les place dans le bloc mmoire point par ptr. Valeur de retour : Le nombre d'units de donnes (les SizeU) rellement lues partir du fichier.

180

Dtection de la fin d'un fichier (feof) Prototype : int feof(FILE* F); Paramtre : Fichier dont on veut dtecter la fin. Valeur de retour : Vrai si la fin du fichier a t atteinte et faux sinon. Remarque : Il est noter qu'il n'est pas suffisent d'avoir lu le dernier octet du fichier pour retourner vrai mais il faut lire au del du dernier octet. Exemple 1: Le programme suivant enregistre d'une manire squentielle une suite d'entiers dans un fichier.
#include <stdio.h> void main() { char NomFichier[21]; int n; FILE* Fichier; printf("Donnez le nom du fichier crer : "); scanf("%20s", NomFichier); Fichier=fopen(NomFichier,"w"); do { printf("Donnez un entier : "); scanf("%d",&n); if(n) fwrite (&n,sizeof(int),1,Fichier); } while(n); fclose(Fichier); }

Exemple 2 : Le programme suivant fait un parcours squentiel du fichier prcdemment cr et liste son contenu.
#include <stdio.h> void main() { char NomFichier[21]; FILE* Fichier; int n; printf("Donnez le nom de fichier lire : "); scanf("%20s",NomFichier); Fichier=fopen(NomFichier,"r"); while(!feof(Fichier)) { if(fread(&n, sizeof(int),1,Fichier)) printf("\n%d",n); } fclose(Fichier); }

181

Accs direct aux fichiers


x Chaque fichier possde un pointeur interne qui indique la position courante partir de laquelle se font les oprations de lecture et d'criture. Au moment de l'ouverture du fichier, cette position se trouve tout au dbut de ce dernier sauf pour les modes a et a+ o elle se trouve en fin de fichier. Elle s'incrmente par la suite, aprs toute opration d'accs, du nombre d'octets lus ou crits. x La position courante se situe toujours au niveau de l'octet qui succde immdiatement le dernier bloc d'informations lu ou crit. Positionnement du pointeur interne des fichiers En mode d'accs direct. Il est possible d'agir sur la position courante d'un fichier de faon la placer directement un endroit prcis sans avoir besoin de faire un dplacement squentiel. Cette action se fait l'aide de la fonction fseek. Prototype : int fseek(FILE* F, int offset, int origine); Paramtres : x F : Le fichier concern par le dplacement. x Offset : la valeur avec laquelle est dplac le pointeur du fichier. x Origine : donne l'origine partir de laquelle est fait le dplacement. Trois origines sont distingues. Elles sont dfinies par les constantes symboliques suivantes : x SEEK_SET ou (0) : offset dsigne dans ce cas un dplacement en octets par rapport au dbut du fichier. x SEEK_CUR ou (1) : Offset dsigne un dplacement par rapport la position courante. Offset peut tre dans ce cas une valeur positive ou ngative. x SEEK_END ou (2) : Offset dsigne un dplacement par rapport la fin du fichier. Valeur de retour x 0 si le positionnement s'est bien droul. x Une valeur non nulle sinon. x Une valeur quelconque pour une tentative de positionnement en dehors du fichier. Exemple :
#include <stdio.h> void main() { char NomFichier[21]; int n; long rang; FILE * Fichier; printf("donnez le nom du fichier consulter : "); scanf("%s",NomFichier); printf("Donnez le rang de l'entier consulter : "); scanf("%ld",&rang); Fichier = fopen(NomFichier, "r"); fseek(Fichier, sizeof(int)*(rang-1),SEEK_SET); fread(&n,sizeof(int),1,Fichier); printf("la valeur est : %d",n); fclose(Fichier); }

Dtermination de la position courante d'un pointeur de fichier (ftell) Prototype : long ftell( FILE * F); Valeur de retour : ftell retourne la position courante du pointeur de fichier par rapport au dbut du fichier.

182

You might also like