Vous êtes sur la page 1sur 59

CPR Informatique

24 septembre 2000

(poste 3159 ou 3164)

Programmer en
C++
avec le

Table des matires


1. PRSENTATION DU LANGAGE C++................................................................................................. 3
2. HISTORIQUE DU C / C++.................................................................................................................. 4
3. OUVRAGES DE RFRENCE............................................................................................................ 4
4. LE LANGAGE C : LES BASES DU C++.............................................................................................. 5
4.1. LES SOURCES ET LES COMMENTAIRES ........................................................................................................................................5
4.2. LES TYPES SIMPLES DE DONNES ...................................................................................................................................................7
4.3. LES INSTRUCTIONS : DE L ALGORITHME AU PROGRAMME ..............................................................................................9
4.4. LES CONVERSIONS ET LES TRANSTYPAGES ............................................................................................................................11
4.5. TABLEAUX ET CHANES DE CARACTRES ...............................................................................................................................12
4.6. LES TYPES "COMPLEXES " ET PRDFINIS ..............................................................................................................................13
4.7. LA REDFINITION D'UN TYPE ...........................................................................................................................................................14
4.8. LES FONCTIONS ET LE PASSAGE DES PARAMTRES .........................................................................................................15
4.9. LES POINTEURS ........................................................................................................................................................................................20
4.10. LES FICHIERS .........................................................................................................................................................................................22
4.11. LALLOCATION PROGRAMME EN C..........................................................................................................................................25
5. PROGRAMMER EN C++.................................................................................................................. 27
5.1. LES SOURCES ET LES COMMENTAIRES .....................................................................................................................................27
5.2. LES TABLEAUX ..........................................................................................................................................................................................29
5.3. LES FONCTIONS ........................................................................................................................................................................................30
5.4. L'ALLOCATION PROGRAMME EN C++.......................................................................................................................................33
5.5. L'ENCAPSULATION ET LES FONCTIONS AMIES ......................................................................................................................37
5.6. LES CONSTRUCTEURS ET LE DESTRUCTEUR (D'UNE CLASSE ) ....................................................................................42
5.7. LA SURCHARGE D 'OPRATEUR ET LES FONCTIONS "INLINE" ......................................................................................46
5.8. LE CONSTRUCTEUR DE COPIE .........................................................................................................................................................48
5.9. LA GNRICIT ........................................................................................................................................................................................48
5.10. L'HRITAGE ..............................................................................................................................................................................................49
5.11. LES FLUX ...................................................................................................................................................................................................54
5.12. LES EXCEPTIONS ...................................................................................................................................................................................57
INDEX ................................................................................................................................................. 59

IGN / ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 2/59

1. Prsentation du langage C++


Le C++ est une extrapolation du langage C, lui-mme invent pour crire le systme d'exploitation
UNIX et trs rpandu dans l'industrie. Le C++ est un langage compil relativement structur : le C
tant trs proche du systme d'exploitation, on peut accder directement la mmoire et c'est mme
parfois ncessaire.
Le C++ est considr comme un langage orient objet car il rpond aux trois principes
fondamentaux : encapsulation, polymorphisme et hritage. En plus, des types simples et
complexes du C, on peut dfinir des classes qui sont des structures volues dont certains champs
(appels membres) sont des fonctions (appeles mthodes) selon le principe d'encapsulation. Le
programmeur doit grer l'accessibilit aux champs des classes qu'il dfinit : certains membres peuvent
tre rendus accessibles directement comme un champ d'une structure. En C++, deux fonctions
peuvent avoir le mme nom (principe de polymorphisme) si leurs paramtres ont des types
diffrents. Elles peuvent galement avoir des paramtres facultatifs dont la valeur par dfaut est
mentionne dans le prototype (appel signature). Les classes peuvent tre dfinies partir d'autres
classes selon le principe d'hritage multiple. De mme, il est possible de dfinir des fonctions ou des
classes gnriques dont le comportement dpendra, lors de l'dition de lien dynamique, d'un type
fourni par l'utilisateur : la fonction min(x,y) est gnrique car elle compare deux valeurs x et y de
mme type et renvoie la plus petite des 2 valeurs.
Pour montrer comment programmer en C++, ce support de cours utilisera comme fil conducteur la
manipulation de tableaux (ensembles de cases contigus de mme type). Le premier programme sera
crit de la manire la plus simple possible jusqu' l'criture d'un vritable gestionnaire de tableaux avec
sauvegarde sur fichiers. Chaque programme apportera son lot de prcisions ou de nouveauts

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 3/59

2. Historique du C / C++
Le langage C est un langage de type dclaratif comme le Pascal. Il a t cr dans les annes 70 par
une seule personne Dennis Ritchie (AT & T). Son but tait de rcrire le systme d'exploitation
UNIX en langage volu car Ken Thompson avait crit UNIX en B : langage non volu qui posait
des problmes de portage sur d'autre machine que PDP-11.
Le C n'a pas t cr pour concurrencer Fortran ou Pascal, mais le succs d'UNIX a contribu sa
popularit. La russite incontestable du C comme langage de programmation systme lui a forg une
fausse rputation de langage portable.
Le C diffre du Pascal ou de l'ADA en ce sens qu'il est plus proche de la machine (plus bas niveau) :
- il demande plus au programmeur ;
- il permet de mieux comprendre certains mcanismes systmes.
En 1978, Brian Kernnighan et D. Ricthie publient la "bible du C", le fameux "K&R" qui fait
rfrence ("norme", 2me dition en 1987). Paralllement en 1982, l'American National Standards
Institute lance le comit de normalisation du C, ce qui a donn la Norme Ansi en 1989 et les meilleurs
compilateurs. L'volution naturelle est le C++. Les programmes crits en ANSI-C sont censs
pouvoir tre recompils nimporte o
Le langage C++ a t conu en 1983 par Rick Mascitti pour tre utilis dans le mme environnement
que le C. Une version du C avec "classes" existe depuis 1980. Ce langage est considr comme une
extension du langage C, il s'appelle C++ par rfrence l'oprateur d'incrmentation du C.

3. Ouvrages de rfrence
On peut citer comme ouvrages de rfrence :
- L'incontournable : Le Langage C (2me dition) de "K&R"
- Pour l'apprentissage : Langage C - Norme ANSI de Ph.Drix
- Pour les problmes de portage : Le Manuel de Rfrence de Harbison-Steele.

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 4/59

4. Le langage C : les bases du C++


4.1. Les sources et les commentaires
Un programme crit en C est un ensemble de fichiers textes documents (appels sources) respectant
une grammaire prcise, bien qu'il existe toujours plusieurs faons d'crire la mme chose. Ce
document prsente la solution prconise mais aussi les alternatives les plus fiables sous forme de
commentaires (c'est--dire une suite de caractres, de prfrence non accentus, qui seront ignors
par le compilateur et ne servent qu' documenter le programme). On indique le dbut des
commentaires et la fin, ventuellement sur des lignes diffrentes :
/* ce symbole indique le debut d'un commentaire qui peut etre sur plusieurs lignes
et ne se terminera qu'apres le symbole */
Un source est organis de la manire suivante :
/* Commentaires sur le contenu du fichier

*/

/* Indications des fichiers qui devront etre inclus par le compilateur

*/

/* Definitions des constantes et des types complexes utilises par la suite

*/

/* Prototypes des fonctions qui seront decrites plus loin

*/

/* Corps des fonctions

*/

/* Fonction principale (appelee "main") si ce fichier est celui qui


contient le "programme principal"

*/

L'affichage et la saisie sont grs par la bibliothque stdio.h Les fonctions printf et scanf sont
utilises respectivement pour laffichage lcran et la saisie au clavier.
Dans ce chapitre, nous ne prsenterons que la fonction printf, car lutilisation de la fonction scanf
requiert des notions que nous verrons plus tard (cf. chapitre 4.9 Les pointeurs)
La fonction printf permet de formater laffichage l cran. Il sagit dune fonction arguments
variables (on peut avoir autant de paramtres que lon veut). Dans le format daffichage, chaque "%"
annonce lutilisation dun paramtre fourni par la suite.
Exemples :
printf ("Bonjour") ; affiche "Bonjour" lcran.
printf ("Bonjour\n") ; affiche "Bonjour" lcran et effectue un retour la ligne.
printf ("la somme de 2 et 3 fait %d\n",n) ; affiche lcran : La somme de 2 et 3
fait 5, si la variable n vaut 5.
printf ("la somme de %d et %d fait %d\n",a,b,n) ; affiche lcran : La somme de 2
et 3 fait 5, si les variables a, b et n valent respectivement 2, 3 et 5.

%d reprsente ici le format daffichage dune variable de type entire. Il existe un format daffichage
pour chaque type simple de donnes (cf. chapitre suivant 4.2).

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 5/59

Lexemple qui suit montre lutilisation de la fonction printf dans un programme simple calculant la
somme de deux entiers.
/* Ce fichier presente un exemple daffichage dune somme de
2 entiers a lecran */

#include <stdio.h>

Pour permettre la saisie et l'affichage, il est ncessaire dinclure le


fichier stdio.h. Le symbole # indique une directive (un ordre) de
prcompilation qui est donc effectue en priorit. Le fichier inclure
tant fourni avec le compilateur, son nom est indiqu entre chevrons
car il n'est pas ncessairement prsent sur le rpertoire courant.

int main () {
/* Declaration des variables */
int a,b,c;
a=2 ;
b=3 ;
c=0 ;
printf ("a = %d\n",a);
printf ("b = %d\n",b);

on affecte des valeurs aux variables a et


b
t
i iti li
lcran

a = 2
b = 3

c = a + b ;
lcran

printf ("La somme %d + %d = %d\n",a,b,c);

return 0 ;

La somme 2 + 3 = 5

on renvoie la valeur 0 au systme, ce qui signifie


que le programme sest correctement termin

} /* main */

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 6/59

4.2. Les types simples de donnes


4.2.1 - La dclaration des variables
dclaration : <type> <identificateur> ;
<identificateur> :
- lettres non accentues (attention minuscules majuscules)
- chiffres (sauf le premier caractre)
- '_'
- attention ne pas utiliser les mots-clefs du langage (class, for, while, if, else, ...)
<type> : entiers, dcimaux.
Attention : les intervalles de valeurs et la prcision dpendent de la machine utilise.
4.2.2 - Les entiers
On distingue quatre types dentiers : char, short (ou short int), int et long (ou long int).

Taille (octet)

char
1

Etendue

0 .. 255

Format daffichage

%c

short
2

int
2 ou 4
.. 32767
-32768 .. 32767 32768
-231 .. 231 -1
%hd
%d

long
4
-231 .. 231 -1
%ld

Remarques :
selon la machine les caractres sont signs ou pas ;
on peut prciser entiers signs (signed) ou non signs (unsigned) ;
un entier sur un octet peut tre cod en char ;
les char sont des entiers donc les calculs sont possibles.
4.2.3 - Les dcimaux
On distingue deux type de dcimaux : float (6 chiffres de prcision) et double (8 chiffres de
prcision).

Taille (octet)
Etendue
Format daffichage

float
4 ou 8
31
-2 .. 231-1
%f

double
16
63
-2 .. 263 -1
%lf

Remarques :
long float : simple ou double prcision selon le compilateur ;
Lgalit de deux nombres flottants est une ineptie.

4.2.4 Les oprations arithmtiques


On distingue cinq oprations arithmtiques fondamentales : +, -, /, *, %
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 7/59

Exemples :
int i ; // declaration
...
i = i + 1 ; // incrementation equivalente a : i++ ;
i = i - 1 ; // decrementation equivalente a : i-- ;
les piges des raccourcis : i++ et ++i
int i, n ; // declarations
...
n = i++ ; // n prend la valeur i puis i est incremente
n = ++i ; // i est incremente puis n prend la valeur de i
les raccourcis peu explicites : +=, -=, /=, *=, %=
int i, n ; // declarations
...
n += i ; // incrementation egale a : n = n + i ;
Les oprations logiques sur les bits ( >>, <<, &, | , ) sont trs peu utiliss en C. Ils permettent de
raliser des oprations directement sur les bits des nombres entiers. Ce document ne traite pas de ces
oprations. Il sagit dun sujet pointu trs bien expliqu dans la plupart des manuels de langage C.
(Cf. K&R pages 48 et 49 ou le Drix pages 49 et 50).

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 8/59

4.3. Les instructions : de lalgorithme au programme


L'ADL ("Algorithm Descriptive Language") est un langage de description d'algorithmes trs concis
(pas de dclarations) proche de la stnographie. Pourquoi faire le parallle avec l'ADL ?
ce langage est utilis l'IGN et surtout l'ENSG
ce langage est concis et facile comprendre
cette mthode de structuration des algorithmes est facile transposer en programmation
les algorithmes des exercices seront prsents en ADL (universalit des cours)
Signification

ADL

C/C++

Affectation

X3

x = 3 ;

Test

Condition

? |

Test choix multiple cas


cas1

if (condition)
{
instructions ;
}
else
{
instructions ;
} /* if */

switch (cas)
{
case cas1 : instructions ;
break ;
case cas2 : instructions ;
break ;
default : instructions ;

.......

cas2
.......
......

} /* switch */

Boucle "tant que"

/ condition

while (condition)
{
instructions ;
} /* while */

?
?

Boucle avec
compteur

Bf , Pas
?
?i

i : Bi

Boucle infinie
?
?

Boucle
"jusqu' ce que"

/ condition
?

for (i=Bi;i<=Bf;i=i+Pas)
{
instructions ;
} /* for i */
for (; ;)
{
instructions ;
} /* for */
do
{
instructions ;

?
} while ! ( condition) ;

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 9/59

Sortie de boucle

break ;

Au suivant
Sortie de procdure

continue ;

!
*

return ;

Autres
dbranchements

goto debranchement;
instructions
debranchement:
instructions

vrai

Toute valeur entire non nulle

faux
non
et
ou
gal
diffrent

Toute valeur entire nulle


Condition

! (condition)

&&

||

==

!=

Les principales remarques :


l'affectation "=" ne doit pas tre confondue avec le test d'galit "==" ;
dans les tests simples, le bloc "else" est facultatif et les limites d'un bloc sont
facultatives s'il ne contient qu'une instruction (pige pour la maintenance) ;
dans les tests choix multiples, le test doit porter sur une valeur d'un type
discret (entier ou boolen), le dernier "break" est facultatif (pas recommand) et
l'absence de sparation ("break") entre 2 choix ("case") entrane l'excution des
instructions du 2me choix (pas de dbranchement implicite) ;
la boucle "while" est la boucle de base du langage (la boucle "for" en drive) ;
la sortie de programme est "return" (sortie de la fonction principale), "exit" est un
arrt violent ;
pas de dbranchement de niveau suprieur 1 (utiliser "goto" avec parcimonie) ;
pas de type boolen, les valeurs entires nulles sont fausses, rciproquement les
valeurs entires non nulles sont vraies ;
les tests logiques "&&" et "||" ne doivent pas tre confondus avec les oprations
logiques sur les bits ("&" et "|") ;
dans les tests, en cas d'galit de priorit, le membre le plus gauche est valu
en premier (les parenthses sont fortement conseilles), les autres membres
peuvent ne pas tre valus (compilation ANSI ou non).

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 10/59

4.4. Les conversions et les transtypages


Les compilateurs C effectuent des transtypages (conversions de type) implicites qui sont dangereux,
par dfaut les valeurs sont de type "int" ou "float". Il est recommand d'expliciter les transtypages
("cast"). Sans transtypage explicite, une opration sur un entier et un flottant peut tre effectue en
entier ou en flottant selon le compilateur, le rsultat n'est donc pas garanti, par exemple (int)(2 * 2.5)
peut donner 4 ou 5.
Exemples de transtypages explicites :
{
int i;
long l;
float f;
double d;
i
l
l
f
f
d
d

=
=
=
=
=
=
=

1;
/*
(long)1; /*
1L;
/*
1.0 ;
/*
1. ;
/*
(double)1;/*
1.L ;
/*

les valeurs entieres sont par defaut des "int"


transtypage explicite en entier long
tres utilise pour les constantes mais risque d'oubli
les valeurs decimales sont par defaut des "float"
possible de ne mettre que le '.' mais risque d'oubli
transtypage explicite en double precision
possible, particulirement dangereux et peu fiable

d = (double)2.0e+12;
i = (int)d;
/* partie entiere de d modulo la borne superieure(int)
/* en 32 bits, i prend une valeur inferieure a 2.4e+9
/* si le type "int" etant signe, la valeur est peut
/* etre negative
}

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

*/
*/
*/
*/
*/
*/
*/

*/
*/
*/
*/

page 11/59

4.5. Tableaux et chanes de caractres


Un tableau est une suite continue (en mmoire) de variables dun mme type. En pratique, le C
rserve en mmoire la place ncessaire au tableau mais ne conserve pas la taille de ce tableau. Il y a de
ce fait un risque important de dpassement des bornes du tableau. Par exemple, on dclare un tableau
de 10 entiers de la manire suivante : int TabEntiers [10] ; mais rien nempche dcrire :
TabEntiers[12] = 4 ; Cest au programmeur dtre vigilant !
Attention : en C, les indices des tableaux commencent 0.

Le cas des chanes de caractres


Les chanes de caractres sont des tableaux de caractres ("char"). Tous les caractres prsents dans le
tableau ne sont pas ncessairement significatifs. Pour grer la longueur variable des chanes, celles-ci
sont postfixes : les chanes sont termines par le caractre '\0' (de code ASCII : 0) qui indique la fin
des caractres utiles (autrement dit afficher grce au format "%s" dans les fonctions printf).Une
chane s devant contenir au plus 10 caractres sera donc dclare : "char s[10+1] ;". Attention, les
chanes constantes sont indiques par des guillemets qui contiennent implicitement le caractre de
fin '\0'.
Une bibliothque de fonctions de manipulation de chanes est fournie en standard, il s'agit de la
bibliothque string.h. Exemple de manipulations de chanes de caractres.
#include <stdio.h>
#include <string.h>
on se rserve une chane de 10 caractres +

int main ()
{
char s[10+1];

attention

strcpy(s,"1234567890");
printf("[%s]\n",s);

lcran

strncpy(s+3,"1234567890",4);
printf("[%s]\n",s);
strcpy(s+3,"1234");
printf("[%s]\n",s);
s[1] = '\0';
printf("[%s]\n",s);

lcran

lcran

lcran

au

risque

de

[1234567890]
[1231234890]
[1231234]

[1]

return 0 ;
} /* main */

A noter que la fonction strcpy affecte dans le premier paramtre, la valeur du second paramtre.
La fonction strcnpy affecte dans le premier paramtre, n caractres de la valeur du second paramtre,
n tant le troisime paramtre de la fonction.

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 12/59

4.6. Les types "complexes" et prdfinis


Il existe 3 types complexes : numrs, structures et unions.
Le type numr est une liste de constantes. Exemple :
enum Jour { lundi,mardi,mercredi,jeudi,vendredi,samedi,dimanche };
enum Jour today;
today = mardi;

Les oprations sont impossibles sur les numrs. On peut cependant affecter une valeur aux valeurs
des numrs, pour pouvoir utiliser ces valeurs comme des constantes numriques.
enum Boolean { vFalse=0L,vTrue,vUnknown=-1L };
long entier;
enum Boolean correct;
printf("vFalse-> %d\n",vFalse);

printf("vTrue-> %d\n",vTrue);
printf("vUnknown-> %d\n",vUnknown);
correct = vFalse;
entier = correct;

lcran

vFalse-> 0
vTrue-> 1
vUnknown-> -1

Equivalent : entier =
0L

Le type "structure" est un regroupement de variables de types (qui peuvent tre diffrents) appeles
"membres de la structure". L'accs aux champs se fait grce la notation pointe : toto.age (o age
est un champ de la structure toto).
struct Personne {
char nom[10+1];
long age;
};
struct Personne toto;
L'accs aux champs se fait grce la
notation
pointe :
toto.age
=
(long)20 ;

toto.age = (long)20;
strcpy(toto.nom,"TOTO");
printf("Nom-> [%s]\n",toto.nom);
printf("Age-> %d\n",toto.age);

lcran

Nom-> [TOTO]
Age-> 10

Le type "union" est un regroupement de plusieurs types sur une mme zone mmoire.
union Personne {
char nom[10+1];
long age;
};
union Personne toto;
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 13/59

toto.age = (long)20;
strcpy(toto.nom,"TOTO");

Nom-> [TOTO]
Age-> 1414485071
lcran

En fait, 1414485071 na aucune


signification, il sagit de la forme
numrique de la chaine TOTO.

printf("Nom-> [%s]\n",toto.nom);
printf("Age-> %d\n",toto.age);

4.7. La redfinition d'un type


Pour pouvoir facilement grer des tableaux d'entiers ou des tableaux de nombres flottants, il est
prfrable de dfinir un type d'lments. Cette opration se fait grce au mot-clef "typedef".
L'instruction "typedef long tELT;" fait de tELT un nouveau type synonyme de "long". Un tableau
tab de 10 cases contenant chacune un tELT se dclare videmment par : "tELT tab[10];".
De mme, pour simplifier les dclarations et surtout le passage des paramtres, il est recommand de
prdfinir les types complexes.
#include <stdio.h>
typedef enum Boolean { vFalse=0L,vTrue,vUnknown=-1L } tBoolean;
tBoolean est maintenant
quivalent enum
B l

typedef struct Personne {


char nom[10+1];
long age;

tPersonne est maintenant


quivalent struct
P
B l

} tPersonne;
int main() {
tBoolean correct;
tPersonne mum,dad,toto;
correct = vFalse;
printf("correct-> %d\n",correct);

Dclarations simplifies de correct et de


toto :
- correct est un numr
- toto et ses parents sont des structures
lcran

correct-> 0

strcpy(mum.nom,"MAMAN");
mum.age = (long)45;
strcpy(dada.nom,"PAPA");
mum.age = (long)54;
strcpy(toto.nom,"TOTO");
toto.age = (long)20;
printf("Nom-> [%s]\n",toto.nom);
printf("Age-> %d\n",toto.age);

lcran

return 0;
} /* main */
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

Nom-> [TOTO]
Age-> 20

29/06/01

page 14/59

4.8. Les fonctions et le passage des paramtres


Il est toujours prfrable de dcouper l'ensemble du programme en modules rutilisables c'est--dire
en fonctions. En C, il n'y a pas de procdure sans type, comme en ADL. On ne manipule que des
fonctions. Le seul mcanisme de passage de paramtres est le passage par valeur, aussi il faut faire
appel la notion d'adresse pour simuler les modes de passages sortie ("out") et entre/sortie ("inout")
grce aux oprateurs * sur les paramtres et & sur les variables.
Dessin explicatif du passage par valeur (sans adresse mode "in")
mmoire

void ma_fonction (int a, int b);


int main {
i1

int i1, i2;

(4

i2

(4

i1

i2

i1 = 7 ;
i2 = 9 ;
ma_fonction (i1, i2) ;

A lappel de la fonction, il y a une copie locale des paramtres

printf ("i1 = %d\n",i1) ;


printf ("i2 = %d\n",i2) ;
return 0 ;

i1

i2

mmoire

du

mmoire de ma_fonction

} /* main */

void ma_fonction (int a, int b)


{
a = 3 ;
b = 5 ;

i1

i2

mmoire

3
a

du

5
b

mmoire de ma_fonction

return ;
} /* ma_fonction */

i1

i2

mmoire

Rsultat lcran

la sortie de la
fonction,
la
mmoire locale est
lib

du

: i1 = 7
i2 = 9

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 15/59

Dessin explicatif du passage par valeur (avec adresse mode "out" ou "inout").
mmoire

void ma_fonction (int *a, int *b);


int main {
i1

int i1, i2;

(4

i1 = 7 ;
i2 = 9 ;
ma_fonction (&i1, &i2) ;
printf ("i1 = %d\n",i1) ;
printf ("i2 = %d\n",i2) ;

i2

(4

i1

i2

A lappel de la fonction, il y a une copie locale des adresses


@i1

@i2
7

i1

i2

@i1

a = @i1

@i2

b = @i2

return 0 ;
mmoire

} /* main */

void ma_fonction (int *a, int *b)


{
*a = 3 ;
*b = 5 ;

@i1

@i2

i1

i2

du

mmoire de ma_fonction

Ici, loprateur * signifie la valeur de la variable pointe.


Ainsi,
Linstruction *a = 5 ; modifie la valeur de la variable pointe
par a, donc i1.

} /* ma_fonction */

i1

i2

mmoire

Rsultat lcran

mmoire de ma_fonction

mmoire

return ;

du

la sortie de la
fonction,
la
mmoire locale est
lib

du

: i1 = 3
i2 = 5

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 16/59

Le mcanisme du passage des paramtres expliqu prcdemment est appliqu la gestion dun
tableau.

/* Gestionnaire de tableaux de nombres */


#include <stdio.h>

tab est entre-sortie


tabcard est en sortie
card et val sont en entre

void InitialiseTableau (long *tab,int *tabcard, int card, long val)


{
int i;
*tabcard = card;
for (i=0;i< card;i++) tab[i]=val;
Linstruction return ; est facultative
car la procdure est void (sans type)

return ;
} /* InitialiseTableau */

void AfficheTableau(long tab[], int tabcard)


{
int i;
La notation tab[ ] est plus explicite
printf("Affichage du Tableau\n");
que la notation *tab
for (i=0;i<tabcard;i++) {
printf("tab[%d]= %ld",i,tab[i]);
} /* for i */
} /* AfficheTableau */
Les tableaux sont implicitement en entresortie.
tablocard est une variable simple en sortie
(donc
d
l &)

int main()
{
long tablo[15] ;
int tablocard ;
long valdef = 10L ;

InitialiseTableau(tablo,&tablocard,15,valdef);
AfficheTableau(tablo,tablocard);
on renvoie une valeur du type de la
f ti

return 0;
} /* main */

Le cas particulier du passage des structures


Il existe 3 mthodes de passage d'une structure en paramtre d'une fonction :
chaque membre de la structure sparment ;
la structure entire par valeur : copie ou adresse selon le compilateur ;
par adresse : solution rapide l'excution mais on risque de modifier le contenu.
Dans chaque fonction, on est oblig de passer en paramtre le cardinal (le nombre d'lments
prsents) de chaque tableau. Il est plus commode de dfinir une structure regroupant le tableau et son
cardinal pour n'avoir qu'un paramtre passer et viter des erreurs.
Si on dfinit une structure comme suit :
typedef struct _tTableau {
long tab[gkNBMAX];
int card;
} tTableau;
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 17/59

Le gestionnaire de tableau dcrit prcdemment devient alors :


/* Gestionnaire de tableaux de nombres */
#include <stdio.h>
typedef struct _tTableau {
long tab[15];
int card;
} tTableau;

dfinition d'une structure tTableau


pour rduire le nombre
de
paramtres

void InitialiseTableau(tTableau *t,int card,long val)


{
int i;
*t.card est dangereux car *(t.card) est
incohrent. On lui prfre la notation
t->card = card;
flche
for (i=0;i< t->card;i++) t->tab[i]=val;
return ;
} /* InitialiseTableau */
void AfficheTableau(tTableau *t)
{
int i;
printf("Affichage du Tableau\n");
for (i=0;i<t->card;i++) {
printf("tab[%d]= %ld",i,t->tab[i]);
} /* for i */
return ;
} /* AfficheTableau */
int main()
{
tTableau tablo;

tablo est une variable locale de type


tTableau

InitialiseTableau(&tablo,15,(long)10);
AfficheTableau(tablo);
return 0;
} /* main */

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 18/59

La porte des variables :

les variables locales ne sont connues que dans la fonction (ou le bloc) o elles sont dclares ;
les variables globales ne sont connues que dans les fonctions qui suivent les dclarations (au
sein du mme fichier source) ;
les variables globales exportes sont connues dans tous les fichiers (car la compilation est
spare). Il existe de multiples solutions, la plus propre tant la suivante :

/* FICHIER 1 */
/* definition ANSI (declaration et initialisation) avec allocation */
int x = 0;
int main ()
{
....
} /* main */

/* FICHIER 2 */
/* seulement une dclaration sans allocation */
extern int x;
int ma_fonction(int x)
{
....
}/* ma_fonction */

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 19/59

4.9. Les pointeurs


Un pointeur est une variable contenant l'adresse d'une variable type : par extension les pointeurs
sont typs. Le typage des pointeurs permet de dfinir une "arithmtique" rduite sur les pointeurs et
une simplification pour les types structurs : ("(*p).a p->a").
Exemple :
/*
/*
/*
/*

<type> *p,x; p est un pointeur de type <type>*


p contient l'adresse d'une variable de type <type>
p + i : adresse de l'ventuelle ime variable de type <type>
en aval de x

*/
*/
*/
*/

/*
/*

p - i : adresse de l'ventuelle ime variable de type <type>


en amont de x

*/
*/

p peut tre initialis avec l'adresse


d i

int i, *p;
i = 13;
p = &i;

*p est la variable de type <type> l'adresse p donc *p ==

lcran

printf("%d %d\n",i,*p);
*p = 5;
printf("%d %d\n",i,*p);

(*p)++;

lcran

13 13

5 5

incrmentation de la valeur de
*
lcran

printf("%d %d\n",i,*p);

6 6

Attention, une adresse non rserve a peut-tre t rserve par ailleurs et donc contenir une donne
Un pointeur de pointeur est un pointeur sur une variable de type pointeur !
int i, *p, **q;
i = 13;

p peut tre initialis avec l'adresse


d i

p = &i;
q = &p;

q peut tre initialis avec l'adresse


d
lcran

13 13 13

printf("%d %d %d\n",i,*p,**q);

Quel que soit le type de pointeur, l'affectation avec NULL est autorise (initialisation recommande
pour indiquer que la mmoire n'a pas t rserve).

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 20/59

Le passage par adresse consiste passer par valeur l'adresse de la variable (cf. page 16 - schma du
mcanisme) :

void ajoute(int a, int *b)


{
*b = *b + a ;
} /* ajoute */
int main()
{
int i;
i = 5;
ajoute(2, &i);

contient
i

l'adresse

d'une

variable

on ajoute la valeur de a au contenu de


l' d
b

on passe l'adresse de i
i a t incrmente de 2 donc
contient 7

return i;
} /* main */

La fonction scanf, qui permet de saisir des donnes au clavier, utilise le mcanisme du passage par
adresse. Comme la fonction printf (cf. chapitre 4.1.), il sagit dune fonction arguments variables.
Dans le format de saisie, chaque "%" annonce lutilisation dun paramtre.
Exemples :
char ch[10] ;
scanf ("%s",ch) ; attend que lutilisateur entre une

chane de caractre au clavier. Le rsultat de la


saisie est stock dans la variable ch. Attention, la chane saisie ne doit pas dpasser 9 caractres (scanf
ajoute le caractre de fin de chane \0).
int age ;
scanf ("%d",age) ; attend que lutilisateur entre une chane de caractre au clavier. Le

rsultat de la
saisie est converti en un entier et stock dans la variable age de type int.
Attention : le format de saisie doit tre prcis. Par exemple, pour les variables de type short, le
format "%hd" est obligatoire (contrairement printf qui peut se contenter de %d). A noter galement
que le transtypage est interdit dans la fonction scanf (ne pas utiliser %d pour lire un flottant par
exemple)
Les structures rcursives sont des structures dont un membre pointe sur une structure du mme
type. C'est possible car la dclaration d'un pointeur ne rserve pas la mmoire de la variable pointe
(ce qui tombe bien puisque le compilateur ne connat pas la taille de la structure tant que sa dclaration
n'est pas finie). Ceci est particulirement utile pour grer le chanage des lments d'une liste.
struct Personne {
char nom[10+1];
long age;
struct Personne *mere,*pere;
};

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 21/59

4.10. Les fichiers


Les fichiers sont des collections (ensembles) d'octets, donc tous binaires. La manipulation des
fichiers est gre par la bibliothque stdio.h. Seuls leur contenu et surtout le moyen d'y accder
diffrencient fichiers textes et fichiers (purement) binaires. Aprs l'ouverture lors de laquelle on
indique le mode d'accs, on manipule une adresse de structure "FILE".
On verra au chapitre suivant que l'ouverture est donc assimilable une vritable construction
(allocation de mmoire et initialisation). De mme, la fermeture est une destruction (libration du
fichier aprs une ventuelle sauvegarde et libration de la mmoire occupe par le descripteur).
L'allocation et la dsallocation d'un descripteur sont donc "transparentes" l'ouverture et la fermeture
du fichier.
Les fichiers se manipulent via les fonctions "fopen", "fprintf", "fscanf", "fwrite", "fread", "fseek",
"feof" et "fclose", grce des pointeurs sur descripteur de type FILE.

#include <stdio.h>
#include <string.h>
typedef enum Boolean { vFalse=0L,vTrue } tBoolean;
typedef struct Personne {
char nom[10+1];
long age;
} tPersonne;
fic est un pointeur sur descripteur de
fi hi
int main() {
FILE *fic;
tPersonne personne;
char chaine[255+1];

/* creation d'un fichier personne.txt en acces texte */


fic = fopen("personne.txt","wt");
tentative de cration en mode
if (fic==NULL) {
printf("Impossible de creer le fichier texte\n");
return 1;
Il y a eu un problme louverture du fichier, on
} /* if */
sort
d
l
d d
1
strcpy(personne.nom,"TOTO");
personne.age = (long)20;
Dans fichier
TOTO 20
fprintf(fic,"%10s %d\n",personne.nom,personne.age);
strcpy(personne.nom,"TITI");
personne.age = (long)32;
fprintf(fic,"%10s %d\n",personne.nom,personne.age);
fclose(fic);

Dans fichier

TITI 32

fermeture du fichier qui peut tre signale par fic =


NULL

/* Relecture securisee du fichier "texte" */


tentative d'ouverture en mode
fic = fopen("personne.txt","rt");
if (fic==NULL) {
printf("Impossible d'ouvrir le fichier texte\n");
return 1;
} /* if */
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 22/59

while (NULL != fgets(chaine,255,fic)) {


dcodage

du

strncpy(personne.nom,chaine,10);
personne.nom[10]='\0';
sscanf(chaine+10,"%ld",&(personne.age));

dcodage

de

printf("Nom: [%s] Age : %d\n",personne.nom,personne.age);


lcran

} /* while */

Nom : [TOTO] Age : 20


Nom : [TITI] Age : 32
fclose(fic);

/* creation d'un fichier personne.bin en acces binaire */


fic = fopen("personne.bin","wb");
tentative de cration en mode
if (fic==NULL) {
printf("Impossible de creer le fichier binaire\n");
return 1;
} /* if */
strcpy(personne.nom,"TOTO");
personne.age = (long)20;
fwrite(&personne,sizeof(tPersonne),1,fic);

criture
P

d'une

variable

de

type

strcpy(personne.nom,"TITI");
personne.age = (long)32;
fwrite(&personne,sizeof(tPersonne),1,fic);
fclose(fic);

/* Relecture securisee du fichier "binaire" */


fic = fopen("personne.bin","rb");

tentative

douverture

en

mode

if (fic==NULL) {
printf("Impossible d'ouvrir le fichier binaire\n");
return 1;
} /* if */
tant qu'on n'a pas lu la fin de
while (! feof(fic)) {
if (fread(&personne,sizeof(tPersonne),1,fic)<1) {
break;
} /* if */

sortie
b
l

de

la

impossible
de
lire
un
tPersonne, car on a atteint la
fi d fi hi

printf("Nom : [%s] Age : %d\n",personne.nom,personne.age);


} /* while */

lcran

Nom : [TOTO] Age : 20


Nom : [TITI] Age : 32
fclose(fic);
return 0;
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 23/59

} /* main */

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 24/59

4.11. Lallocation programme en C


En C, l'allocation de mmoire se fait grce la fonction "malloc" qui prend en paramtre la taille en
octets de la zone allouer et renvoie l'adresse de la zone alloue (NULL si l'allocation est impossible).
La mmoire doit tre dsalloue grce la fonction "free" qui prend en paramtre (en entre
uniquement) la variable contenant l'adresse de la zone alloue.
mmoire

int main {

Le compilateur rserve en mmoire la place dune adresse (2


octets )

long *p;

p (2 octets)

p = (long *)malloc(sizeof(long));

if (p==NULL)
{
printf("Pb dAllocation\n") ;
return 1;
} /* if */

*p = 4 ;

A lexcution, malloc rserve la place ncessaire (4


octets) une adresse donne (@) et recopie cette
adresse dans le pointeur p.
@
p

p pointe alors vers une zone de 4 octects.


@
p

*p est le contenu de la variable pointe


@

*p

free (p) ;
return 0 ;
} /* main */

Ci-dessous un exemple de manipulation de tableau dynamique :


#include <stdio.h>
#include <stdlib.h>
int main()
{
int *tab;

on dclare un pointeur sur


i

tab=(int*)malloc(10*sizeof(int));
if (tab==NULL)
{
fprintf(stderr,"Allocation impossible\n");
return 1;
} /* if */
tab[0] = 18;

free(tab);
return 0;

on rserve la place de 10
i

on manipule tab comme un tableau


normal
on libre la place occupe par le tableau
attention : tab contient toujours la mme
d

} /* main */
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 25/59

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 26/59

5. Programmer en C++
5.1. Les sources et les commentaires
l'instar d'un programme C, un programme crit en C++ est un ensemble de fichiers textes
documents (appels sources) respectant une grammaire prcise bien qu'il existe toujours plusieurs
faons d'crire la mme chose. Ce document prsente la solution prconise mais aussi les
alternatives les plus fiables sous forme de commentaires (c'est--dire une suite de caractres, de
prfrence non accentus, qui seront ignors par le compilateur et ne servent qu' documenter le
programme). On peut indiquer le dbut des commentaires et prciser qu'ils se terminent la fin de la
ligne courante ou bien (comme en C) indiquer le dbut et la fin des commentaires ventuellement sur
des lignes diffrentes :
// ce symbole indique le debut d'un commentaire qui se terminera a la fin de la ligne
ou bien
/* ce symbole indique le debut d'un commentaire qui peut tre sur plusieurs lignes et ne
se terminera qu'apres le symbole */
Un source est organis de la manire suivante :
// Commentaires sur le contenu du fichier
// Indications des fichiers qui devront etre inclus par le compilateur
// Definitions des constantes et des types complexes utilises par la suite
// Prototypes des fonctions qui seront decrites plus loin
// Corps des fonctions
/* Fonction principale (appele "main") si ce fichier est celui qui contient le
"programme principal" */

Pour l'affichage et la saisie, les fonctions standards sont toujours connues mais on dispose galement
de fonctions de gestion des flux gres par la bibliothque iostream.h :
cout est le flux de sortie standard (cran)
cin est le flux d'entre standard (clavier)
cerr est le flux de sortie standard d'erreur (cran)
clog est le flux de sortie standard d'erreur tamponn (moins portable que cerr)
L'oprateur "<<" envoie des valeurs dans "cout", rciproquement ">>" rcupre des valeurs de "cin".
Le saut de ligne est spcifi par "endl", par exemple : "cout << endl;"

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 27/59

#include <iostream.h>
int main()
{
int i,j;

saisie au clavier :
4

cin >> i;
cout << i << endl;

cin >> i >> j;


cout << i << j << endl;

lcran

saisie au clavier : 5
6
lcran

56

} /* main */

Les manipulateurs C++ de la bibliothque iomanip.h permettent de formater les entres/sorties :


dec
oct
hex
endl
setw(int n)
setfill(char c)
setprecision(int n)
fflush

Lecture/criture d'un entier en dcimal


Lecture/criture d'un entier en octal
Lecture/criture d'un entier en hexadcimal
Insre un saut de ligne et vide les tampons
Affichage de n caractres
Caractre de remplissage (pour remplacer les blancs)
Affichage de la valeur avec n chiffres
Vide les tampons aprs criture

Exemple :
cout << setw(4) << setfill(*) << 56 << endl ; // affichage de **56

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 28/59

5.2. Les tableaux


Les tableaux se dclarent et se manipulent comme en C. Exemple :
/* Ce fichier presente l'utilisation simple d'un tableau d'entiers
de type "long" (c'est-a-dire geres sur 4 octets en compilation
16 ou 32 bits) */
Pour permettre la saisie et l'affichage, il est ncessaire dinclure le
fichier iostream.h. Le symbole # indique une directive (un ordre)
de prcompilation qui est donc effectue en priorit. Le fichier
inclure tant fourni avec le compilateur, son nom est indiqu entre
#include <iostream.h>
chevrons car il n'est pas ncessairement prsent sur le rpertoire
courant

#define gkNBTAB 15

int main () {

La directive de prcompilation #define est en fait une sorte de


"chercher/remplacer" qui aura lieu avant la compilation. Il est
galement possible de dfinir une constante grace au mot cl
"const" suivi de la declaration d'une variable avec initialisation
"const int NBTAB=15;" mais il s'agit alors d'une "variable
constante" qui (comme cette antinomie l'indique) peut tre
modifie ultrieurement !!!
Pour simplifier l'extrme, on crit toutes les instructions dans le
"main". Respectons son prototype de base qui est int main () ;

// Declaration des variables


long t[gkNBTAB];
int card=0;
int i;

t est un tableau de gkNBTAB cases de type "long"


card est le nombre d'entiers prsents dans le tableau (0 au
dpart)
i est lindice de boucle pour saisir ou afficher le contenu du

cout << "Combien d'entiers voulez-vous entrer ? ";


cin >> card;
while ((card<0)||(card>=gkNBTAB)) {
cout << "Ce doit etre compris entre 0 et " << gkNBTAB << endl;
cout << "Combien d'entiers voulez-vous entrer ? ";
cin >> card;
on rcupre dans card le nombre d'entiers
} // while

// On fait saisir une par une les card valeurs entieres


for (i=0;i<card;i++) {
cout << "Quelle est la valeur de tab[" << i << "] ? " ;
cin >> t[i];
} // for i

// Affichage des valeurs entieres presentes dans le tableau


cout << "\nContenu du tableau : "<<endl;
for (i=0;i<card;i++) {
cout << "tab[" << i << "] = " << t[i] <<endl ;
} // for i
return 0;
} /* main */

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 29/59

5.3. Les fonctions


La syntaxe des fonctions C et le passage des paramtres par valeur uniquement (avec son cortge
d'adresses) est valable en C++. Mais cette volution du langage permet galement d'utiliser le
mcanisme de rfrence qui facilite la gestion des paramtres en entre/sortie.
En C++, les rfrences sont des "synonymes" de variables existantes. Elles doivent obligatoirement
tre initialises, qui plus est, par une variable du mme type. Elles simplifient le passage des
paramtres qui ressemble alors un passage par variable (et non plus par valeur). Pour viter la
modification d'un paramtre transmis par rfrence on peut utiliser le mot cl "const".
void Ajoute(int a, int &b)
{
b = b + a ;
}

b est synonyme d'une


variable entire
On ajoute la valeur de a
au contenu de b

int main()
{
int i;

le paramtre b sera un synonyme de


i.

i = 5;
Ajoute(2,i);

b, donc i, a t incrment de 2 et
contient 7

return i;
} // main

La surcharge des fonctions : un aspect de la notion de polymorphisme (inexistante en C).


En C++, les fonctions peuvent tre surcharges : deux fonctions peuvent avoir le mme nom si leurs
types de paramtres ou leurs nombres de paramtres diffrent.
void affiche(int);
// affichera un entier
void affiche(const char *); // affichera une chaine de caracteres

En C++, grce la notion de surcharge, les prototypes de fonctions peuvent contenir des paramtres
par dfaut. Attention, ces paramtres sont forcment les derniers ...
void Incremente(int &x, int y = 1);
void Incremente(int &x, int y)
{
x = x + y ;
on ajoute la
valeur de y au
} // Incremente
contenu de x
int main()
{
int i;
i = 5;
Incremente(i);
return i;
} // main

Par dfaut y
sera gal 1
x est synonyme
d'une variable
entire

le paramtre x sera un
synonyme de i et y sera gal
1
x donc i a t incrment de 1
et contient 6

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 30/59

L'oprateur de porte : une consquence de la notion de polymorphisme (inexistante en C).


En C++, apparat l'oprateur de porte :: qui permet d'accder des variables dfinies au niveau
externe :
/* definition ANSI (declaration et initialisation)
avec allocation */
int x = 0;
int main ()
{
int x = 0;

la variable externe
prend
la valeur
115

::x = 115;
x = 56;
....

la variable locale
prend
la valeur
56

} /* main */

L'application de ces notions l'exemple du gestionnaire de tableaux


Pour illustrer ce chapitre, nous allons enrichir l'exemple prcdent sur les tableaux en dfinissant une
fonction de saisie et une fonction d'affichage. La fonction d'affichage ne prendra que des paramtres
en entres (le tableau et le nombre d'entiers prsents) et ne renverra rien c'est donc une "procdure
sans type" autrement dit "void". Par contre, la fonction de saisie doit retourner le nombre d'entiers
saisis et mettre jour le tableau. On peut soit crire une fonction qui retourne le nombre d'entiers soit
crire une fonction ayant le nombre d'entiers en sortie. Comme la surcharge le permet, on va crire les
deux.
// Fonction de saisie du tableau : saisie du nombre d'entiers
// puis des valeurs
int SaisieTableau(long t[]) {
card est un paramtre local initialis du mme type que le
int card=0,i;
cout << "Combien d'entiers voulez-vous entrer ? ";
cin >> card;
while ((card<0)||(card>=gkNBTAB)) {
cout << "Ce doit etre compris entre 0 et " << gkNBTAB << endl;
cout << "Combien d'entiers voulez-vous entrer ? ";
cin >> card;
} // while
l'indice du tableau
// On saisit une par une les card valeurs entieres
doit
for (i=0;i<card;i++) {
i
0
d
cout << "Quelle est la valeur de tab["<< i <<"] ?" ;
cin >> t[i];
} // for i
return card;

on renvoie une valeur du type du retour de la


f ti

} // SaisieTableau

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 31/59

// Procedure de saisie du tableau : saisie du nombre d'entiers


// puis des valeurs
void SaisieTableau(long t[],int &card) {
int i;
cout << "Combien d'entiers voulez-vous entrer ? ";
cin >> card;
while ((card<0)||(card>=gkNBTAB)) {
cout << "Ce doit etre compris entre 0 et " << gkNBTAB << endl;
cout << "Combien d'entiers voulez-vous entrer ? ";
cin >> card;
} // while
// saisie des valeurs
for (i=0;i<card;i++) {
cout << "Quelle est la valeur de tab[" << i << "] ? " ;
cin >> t[i];
le paramtre formel s'appelle
} // for i
return ;
} // SaisieTableau
// Fonction principale
int main () {
// Declaration des variables
long tab[gkNBMAX];
int ntab=0;
ntab = SaisieTableau (tab);
AfficheTableau(tab,ntab);

Saisie du tableau par appel la


fonction.
On aurait pu utiliser la procdure de
saisie
Affichage des valeurs entires prsentes dans le
bl

return 0;
} // main

on renvoie 0 au systme car le programme a t jusquau


b

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 32/59

5.4. L'allocation programme en C++


La dfinition d'un type structur simplifie les paramtres mais on ne peut pas modifier le nombre
maximum d'lments dans le tableau sans modifier le source ("#define gkNBMAX 15") et
recompiler. En outre, les tableaux ont tous la mme taille maximum ; ce qui est certes homogne
mais la ncessit d'un grand nombre d'lments dans un tableau limitera le nombre de tableaux car la
mmoire n'est pas infinie. On a donc recourt l'allocation programme, c'est--dire la possibilit de
dfinir pour chaque tableau la taille maximum occupe en mmoire. Son corollaire est la
dsallocation programme qui va nous permettre de librer la place mmoire occupe inutilement.
Attention, allocation et dsallocation sont compltement la charge du programmeur qui risque
d'oublier de dsallouer des zones mmoires voire de dsallouer des zones mmoires occupes par
d'autres donnes que celles dont il peut se passer !!!
L'allocation de mmoire se fait grce au mot-clef "new" et la dsallocation grce au mot-clef "delete".
On indique par une toile que l'on manipule une variable qui contient, non pas une valeur, mais une
adresse en mmoire celle de la zone alloue. Pour indiquer que l'allocation n'a pas encore t faite ou
que la dsallocation a dj eu lieu, on utilise la valeur NULL car la zone mmoire d'adresse NULL ne
peut pas tre alloue. D'ailleurs, la tentative de dsallocation de la zone d'adresse NULL provoque une
erreur du systme
#include <stdio.h>
typedef long tELT;
int main () {
// variables locales
tELT *tab=NULL;

aprs allocation
i

tab contiendra une

adresse

Allocation de 10 cases de type


tELT
tab = new tELT[10];
le systme renvoie NULL si l'allocation

est

if (tab == NULL) {
cout << "L'allocation a echouee !!!" << endl;
return 1;
sortie avec
tab contient maintenant l'adresse d'une zone mmoire de
code
} // if
10 tELT contigus et se manipule comme un tableau de 10
tELT (comme si on avait crit tELT tab[10] ; )
Attention : ne pas dpasser les bornes (ici de 0 9)
tab[2] = 3;

if (tab != NULL) {
delete[] tab ;

Il faut penser dsallouer toutes les zones rserves


!!!
Si on oublie de dsallouer la zone dont tab contient
l'adresse, elle restera marque comme occupe et le
systme ne pourra pas l'utiliser librement
Les [] indiquent que toutes les cases contigues sont

tab = NULL;
} // if
} // main

par prcaution, on mentionne que tab n'est plus


allou.
Attention : si on inverse les deux lignes, la zone ne
pourra plus tre libre

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 33/59

Si on applique ce mcanisme au gestionnaire de tableaux, il faut ajouter un champ nbmax dans la


structure pour stocker le nombre maximal d'lments dans le buffer allou dynamiquement. La
fonction InitialiseTableau doit tre remplace par une fonction AlloueTableau qui fait l'allocation
dynamique et l'initialisation. Enfin, il faut crire une fonction DesalloueTableau pour librer la
mmoire et penser l'appeler avant la fin du programme (cette contrainte sera leve plus tard) !
// Gestionnaire de tableaux de nombres (entiers ou flottants)
#include <stdio.h>
#include <string.h>
#include <iostream.h>
typedef long tELT;
typedef struct _tTableau {
tELT *tab;
int card,nbmax;
} tTableau;
void Affiche(tELT x);

allocation dynamique
l'i iti li ti
nbmax
d'l

est

le

du

tableau

nombre

lors

de

maximum

void Affiche(tELT x) {
cout << (tELT)x << endl;
}
bool CopieTableau(tTableau &t,tTableau &t2)
{
int i;
if ((t.tab==NULL)||(t2.tab==NULL)) {
cout << "Copie impossible car un des tableaux est NULL" << endl;
return false;
}
if (t.nbmax<t2.card) {
cout << "Copie impossible car " << t.nbmax << "<" << t2.card << endl;
return false;
}
t.card = t2.card;
for (i=0;i<t.card;i++) t.tab[i]=t2.tab[i];
return true;
} // CopieTableau
bool AjouteTableau(tTableau &t,tTableau &t2) // mise a jour
{
int i;
if ((t.tab==NULL)||(t2.tab==NULL)) {
cout << "Somme impossible car un des tableaux est NULL" << endl;
return false;
}
if (t.card!=t2.card) {
cout << "Somme impossible car " << t.card << "!=" << t2.card << endl;
return false;
}
for (i=0;i<t.card;i++) t.tab[i]+=t2.tab[i];
return true;
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 34/59

} // AjouteTableau

bool AdditionneTableau(tTableau &t,tTableau &t2,tTableau &tr)


{
int i;
if ((t.tab==NULL)||(t2.tab==NULL)||(tr.tab==NULL)) {
cout << "Addition impossible car un des tableaux est NULL" << endl;
return false;
} // if
if (tr.nbmax<t.card) {
cout << "Addition impossible car " << tr.nbmax << "<" << t.card << endl;
return false;
} // if
if (t.card!=t2.card) {
cout << "Somme Impossible car " << t.card << "!=" << t2.card << endl;
return false;
} // if
tr.card = t.card;
for (i=0;i<t.card;i++) tr.tab[i]=t.tab[i]+t2.tab[i];
return true;
} // AdditionneTableau

bool AlloueTableau(tTableau &t,int card,tELT val)


{
int i;
t.nbmax = 0;
t.card = 0;
t.tab = new tELT[card];
if (t.tab==NULL) {
cout << "Allocation impossible ...";
return false;
} // if
t.nbmax = card;
t.card = card;
for (i=0;i<t.card;i++) t.tab[i]=val;
return true;
} // AlloueTableau
void AfficheTableau(tTableau &t)
{
int i;
if (t.tab==NULL) {
cout << "Affichage impossible car le tableau est NULL" << endl;
return;
} // if
cout << "Affichage du Tableau\n";
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 35/59

for (i=0;i<t.card;i++) {
cout << "tab[" << i << "]= ";
Affiche(t.tab[i]);
} // for i
} // AfficheTableau

bool DesalloueTableau(tTableau &t)


{
if (t.tab==NULL) {
cout << "Desallocation impossible ...";
return false;
} // if
delete[] t.tab;
t.nbmax = 0;
t.card = 0;
return true;
} // DesalloueTableau

// fonction principale
int main()
{
int retour=0;
tTableau t,w;

le
programme
normalement

se

droule

AlloueTableau(t,15,(tELT)10);
cout << "Le tableau t contient :" << endl;
AfficheTableau(t);
AlloueTableau(w,10,(tELT)5);
cout << "Le tableau w contient :" << endl;
AfficheTableau(w);
CopieTableau(t,w);
cout << "Le tableau t=w contient :" << endl;
AfficheTableau(t);
if (AjouteTableau(t,t)==true) {
cout << "Le tableau t=t+t contient :" << endl;
AfficheTableau(t);
} // if
if (AdditionneTableau(t,t,w)==true) {
cout << "Le tableau w=t+t contient :" << endl;
AfficheTableau(w);
} // if
if (DesalloueTableau(t)==false) {
cerr << "Le tableau t n'a pas pu etre desalloue" << endl;
retour=1;
le programme a eu un
} // if
problme
if (DesalloueTableau(w)==false) {
cerr << "Le tableau w n'a pas pu etre desalloue" << endl;
retour=1;
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 36/59

} // if
return retour;
} // main

5.5. L'encapsulation et les fonctions amies


Comme on l'a vu, les structures permettent de regrouper plusieurs champs et ainsi de rduire le
nombre de paramtres des fonctions. Cependant, les fonctions doivent toujours tre crites
sparment, avoir un nom explicite (pour viter les confusion) et au moins un paramtre (une
instanciation de la structure). Le langage C++ est "orient objet", il offre donc la possibilit
d'encapsuler, c'est--dire de dfinir des classes qui contiennent des membres. Les membres sont soit
des champs comme les structures soit des mthodes. Les mthodes ne sont ni plus ni moins que des
fonctions propres la classe. Pour appeler une mthode, la notation pointe est utilise comme pour
les champs d'une structure. L'instance de la classe (appel objet) est alors implicitement passer en
paramtre la mthode. On conomise ainsi un paramtre !
Pour le gestionnaire de tableaux, on passe de la notion de structure la notion de classe selon le
schma suivant :
typedef struct {
tELT *tab;
int card,nbmax;
} tTableau ;
// Prototypes des fonctions
AlloueTableau (tTableau &t,
int card,tELT val);
DesalloueTableau (tTableau &t);

// Corps des fonctions

class tTableau {
public : //autorise l'acces
tELT *tab;
int card,nbmax;
// Prototype des mthodes
Alloue (int card,tELT val);
Desalloue ();
} ;

// Corps des mthodes

bool AlloueTableau(tTableau &t,


int card,tELT val)
{
...
}

bool tTableau::Alloue (int card,


tELT val)
{
...
}

bool DesalloueTableau(tTableau &t)


{
...
delete[] t.tab ;
...
}

bool tTableau::Desalloue ()
{
...
delete[] tab ;
...
}

Le mot rserv "class" remplace les mots "typedef struct". On autorise l'accs aux champs grce
"public :" (Cf. suivant). Les fonctions manipulant une instance de ce type sont dfinies dans la
classe. Elles ont implicitement comme paramtre l'objet qui va servir les appeler. On crit le corps
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 37/59

de la fonction l'extrieur de la classe. Pour viter toute confusion et prciser qu'il y a un paramtre
implicite (l'objet lui-mme), la fonction est prfixe par le nom de la classe suivie de "::", par
exemple : "bool tTableau::Desalloue() ;".
Dans le corps des mthodes, on peut manipuler les membres de l'objet implicitement pass en
paramtre. Pour ce faire, il suffit de nommer le champ ou la mthode sans le (ou la) faire prcder
d'un "." (c'est--dire omettre la notation pointe), par exemple :
delete[] t.tab;

=>

delete[] tab;

S'il y a une ambigut avec un paramtre ou si on veut tre explicite, on peut prfixer chaque membre
par "this->", par exemple :
delete[] t.tab;

=>

delete[] this->tab;

Contrairement aux champs des structures, les membres d'une classe ne sont pas accessibles
directement sauf par les mthodes. On autorise l'accs certains membres en les faisant prcder par
"public :". En fait, il est possible de restreindre l'accs autrement mais nous le verrons lors la
prsentation de la notion d'hritage. Cette possibilit d'interdire l'accs certains membres permet au
programmeur d'viter des manipulations hasardeuses comme la dsallocation d'une zone mmoire
qui devra servir plus tard.
Si on veut prserver la notion de fonction crite l'extrieur de la classe, on peut dfinir des fonctions
dites amies qui auront le droit de manipuler les membres y compris ceux qui sont privs. Pour ce
faire, il suffit de recopier le prototype de la fonction dans la classe en le faisant prcder de la mention
"friend" signalant l'autorisation de manipuler tous les membres.
// Gestionnaire de tableaux de nombres (entiers ou flottants)
#include
#include
#include
#include

<stdio.h>
<string.h>
<except.h>
<iostream.h>

typedef long tELT;


// Definition d'une classe tableau et encapsulation
class tTableau {
les membres de la classe sont inaccessibles
private :
en dehors des mthodes ou fonctions amies.
tELT *tab;
Le mot-cl private est facultatif (valeur par
int card,nbmax;
public :

Les membres de la classe sont accessibles


partout. Le mot-cl public est obligatoire

bool Copie(tTableau &t2);


bool Ajoute(tTableau &t2);

friend bool AdditionneTableau(tTableau &t,tTableau &t2,tTableau &tr) ;


bool Alloue(int card,tELT val);
bool Desalloue();
} ;
void Affiche(tELT x);
void Affiche(tELT x) {
cout << x << endl;
} // Affiche

La fonction AdditionneTableau n'est pas


une mthode mais une fonction amie
("f i d")

Attention : loubli de ce ; entraine des


messages derreurs peu explicites la
il ti

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 38/59

bool tTableau::Copie(tTableau &t2)


{
int i;
if ((tab==NULL)||(t2.tab==NULL)) {
cout << "Copie impossible car un des tableaux est NULL" << endl;
return false;
} // if
if (nbmax<t2.card) {
cout << "Copie impossible car " << nbmax << "<" << t2.card << endl;
return false;
} // if
card = t2.card;
for (i=0;i<card;i++) tab[i]=t2.tab[i];
return true;
} // tTableau::Copie

bool tTableau::Ajoute(tTableau &t2)


{
int i;
if ((tab==NULL)||(t2.tab==NULL)) {
cout << "Somme impossible car un des tableaux est NULL" << endl;
return false;
} // if
if (card!=t2.card) {
cout << "Somme impossible car " << card << "!=" << t2.card << endl;
return false;
} // if
for (i=0;i<card;i++) tab[i]+=t2.tab[i];
return true;
} // tTableau::Ajoute

bool AdditionneTableau(tTableau &t,tTableau &t2,tTableau &tr)


{
int i;
if ((t.tab==NULL)||(t2.tab==NULL)||(tr.tab==NULL)) {
cout << "Addition impossible car un des tableaux est NULL" << endl;
return false;
} // if
if (tr.nbmax<t.card) {
cout << "Addition impossible car " << tr.nbmax << "<" << t.card << endl;
return false;
} // if
if (t.card!=t2.card) {
cout << "Somme impossible car " << t.card << "!=" << t2.card << endl;
return false;
} // if
tr.card = t.card;
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 39/59

for (i=0;i<t.card;i++) tr.tab[i]=t.tab[i]+t2.tab[i];


return true;
} // AdditionneTableau
bool tTableau::Alloue (int card,tELT val)
{
int i;
nbmax = 0;
card = 0;
tab = new tELT[card];
if (tab==NULL) {
cout << "Allocation impossible ...";
return false;
} // if

nbmax = card;
this->card = card;
for (i=0;i<card;i++) tab[i]=val;
return true;
} // tTableau::Alloue

void tTableau::Affiche ()
{
int i;
if (tab==NULL) {
cout << "Affichage impossible car le tableau est NULL" << endl;
return;
} // if
cout << "Affichage du Tableau\n";
for (i=0;i<card;i++) {
cout << "tab[" << i << "]= ";
Affiche(tab[i]);
} // for i
} // tTableau::Affiche

bool tTableau::Desalloue ()
{
if (tab==NULL) {
cout << "Desallocation impossible ...";
return false;
} // if
delete[] tab;
nbmax = 0;
card = 0;

Pas vraiment ncessaire mais


homogne avec Alloue

return true;
} // tTableau::Desalloue
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 40/59

// fonction principale
int main()
{
int retour=0;
tTableau t,w;
t.Alloue(15,(tELT)10);
cout << "Le tableau t contient :" << endl;
t.Affiche() ;
w.Alloue(10,(tELT)5);
cout << "Le tableau w contient :" << endl;
w.Affiche();
t.Copie(w);
cout << "Le tableau t=w contient :" << endl;
t.Affiche();

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 41/59

if (t.Ajoute(t)==true) {
cout << "Le tableau t=t+t contient :" << endl;
t.Affiche();
} // if
// AdditionneTableau est une fonction "friend" ...
if (AdditionneTableau(t,t,w)==true) {
cout << "Le tableau w=t+t contient :" << endl;
Affiche(w);
} // if
if (t.Desalloue()==false) {
cerr << "Le tableau t n'a pas pu etre desalloue" << endl;
retour=1;
} // if
if (w.Desalloue()==false) {
cerr << "Le tableau w n'a pas pu etre desalloue" << endl;
retour=1;
} // if
return retour;
} // main

5.6. Les constructeurs et le destructeur (d'une classe)


L'encapsulation ayant t prsente, on constate qu'il est encore primordiale de faire appel la
mthode Alloue() pour "crer" un tableau et la mthode "Desalloue()" pour librer la mmoire.
Cette obligation peut tre confie au compilateur grce aux notions de constructeur et de
destructeur. Un constructeur (respectivement le destructeur) d'une classe est appel automatiquement
chaque dclaration (respectivement libration) d'une variable. Comme la dclaration d'une variable
est explicite, on peut dfinir plusieurs constructeurs (polymorphisme) dont le nom est celui de la
classe. Par contre, la libration d'une variable est implicite donc le destructeur est unique (pas de
polymorphisme) et son prototype est le nom de la classe prcd de "~" et il n'a videmment pas de
paramtre (sinon comment les dterminer implicitement ?). Attention, les constructeurs et le
destructeur d'une classe n'ont pas de retour car ils sont appels automatiquement !!!
Pour le gestionnaire de tableaux, on peut considrer que la mthode Alloue() est un constructeur et
que la mthode Desalloue() est le destructeur :
bool Alloue (...)
bool Desalloue ()

=>
=>

tTableau(...);
~tTableau();

Les constructeurs et le destructeur d'une classe sont facultatifs. Lors de la dclaration d'une variable
objet, le compilateur recherche des parenthses derrire le nom de la variable pour identifier le
constructeur appeler. S'il n'y a pas de parenthses, le compilateur considre que le constructeur sans
paramtre est appel et s'il ne trouve pas de constructeur sans paramtre, il construit la variable
comme il construit habituellement une instance de structure. Lors de la libration d'une variable objet,
le compilateur recherche le destructeur de la classe. S'il n'y a pas de destructeur, il libre la mmoire
comme si c'tait une instance de structure.

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 42/59

// Gestionnaire de tableaux de nombres (entiers ou flottants)


#include <stdio.h>
#include <string.h>
#include <iostream.h>
typedef long tELT;
// Definition d'un constructeur et du destructeur
class tTableau {
private :
tELT *tab;
int card,nbmax;
public :
bool Copie(tTableau &t2);
bool Ajoute(tTableau &t2);
friend bool AdditionneTableau(tTableau &t,tTableau &t2,tTableau &tr);
tTableau(int card,tELT val);

un constructeur
( );

<=

bool Alloue

~tTableau();
Le destructeur
() ;

};

<=

bool Desalloue

void Affiche(tELT x);


void Affiche(tELT x) {
cout << x << endl;
} // Affiche

bool tTableau::Copie(tTableau &t2)


{
int i;
if ((tab==NULL)||(t2.tab==NULL)) {
cout << "Copie impossible car un des tableaux est NULL" << endl;
return false;
} // if
if (nbmax<t2.card) {
cout << "Copie impossible car " << nbmax << "<" << t2.card << endl;
return false;
} // if
card = t2.card;
for (i=0;i<card;i++) tab[i]=t2.tab[i];
return true;
} // tTableau::Copie

bool tTableau::Ajoute(tTableau &t2) // mise a jour


{
int i;
if ((tab==NULL)||(t2.tab==NULL)) {
cout << "Somme impossible car un des tableaux est NULL" << endl;
return false;
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique
29/06/01

page 43/59

} // if

if (card!=t2.card) {
cout << "Somme impossible car " << card << "!=" << t2.card << endl;
return false;
} // if
for (i=0;i<card;i++) tab[i]+=t2.tab[i];
return true;
} // tTableau::Ajoute

bool AdditionneTableau(tTableau &t,tTableau &t2,tTableau &tr)


{
int i;
if ((t.tab==NULL)||(t2.tab==NULL)||(tr.tab==NULL)) {
cout << "Addition impossible car un des tableaux est NULL" << endl;
return false;
} // if
if (tr.nbmax<t.card) {
cout << "Addition impossible car " << tr.nbmax << "<" << t.card << endl;
return false;
} // if
if (t.card!=t2.card) {
cout << "Somme Impossible car " << t.card << "!=" << t2.card << endl;
return false;
} // if
tr.card = t.card;
for (i=0;i<t.card;i++) tr.tab[i]=t.tab[i]+t2.tab[i];
return true;
} // AdditionneTableau

// UN CONSTRUCTEUR
tTableau::tTableau(int card,tELT val)
{
int i;
nbmax = 0;
card = 0;
tab = new tELT[card];
if (tab==NULL) {
cout << "Allocation impossible ...";
return ;
} // if
nbmax = card;
this->card = card;
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 44/59

for (i=0;i<card;i++) tab[i]=val;

} // tTableau::tTableau

Attention : il ny a pas de retour possible


pour un constructeur, donc pas dinstruction
return ;

void tTableau::Affiche ()
{
int i;
if (tab==NULL) {
cout << "Affichage impossible car le tableau est NULL" << endl;
return;
} // if
cout << "Affichage du Tableau\n";
for (i=0;i<card;i++) {
cout << "tab[" << i << "]= ";
Affiche(tab[i]);
} // for i
} // tTableau::Affiche

// LE DESTRUCTEUR
tTableau::~tTableau()
{
if (tab==NULL) {
cout << "Desallocation impossible ...";
} // if
delete[] tab;
//nbmax = 0;
//card = 0;

Ces deux lignes sont maintenant


inutiles
l
i
t lib

} // tTableau::~tTableau

int main()
{
int retour=0;
tTableau t(15,(tELT)10),w(10,(tELT)5);

Appel au constructeur pour t et

cout << "Le tableau t contient :" << endl;


t.Affiche();
cout << "Le tableau w contient :" << endl;
w.Affiche();
t.Copie(w);
cout << "Le tableau t=w contient :" << endl;
t.Affiche();
if (t.Ajoute(t)==true) {
cout << "Le tableau t=t+t contient :" << endl;
t.Affiche();
} // if
// AdditionneTableau est une fonction "friend" ...
if (AdditionneTableau(t,t,w)==true) {
cout << "Le tableau w=t+t contient :" << endl;
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 45/59

Affiche(w);
} // if
return retour;
} // main

Le destructeur est automatiquement


appel
pour t et pour w la sortie du main

On verra deux chapitres plus loin, un constructeur trs particulier : le constructeur de copie

5.7. La surcharge d'oprateur et les fonctions "inline"


Le polymorphisme qui permet de dfinir des fonctions diffrentes mais ayant le mme nom est
tendu la surcharge des oprateurs. On peut effectivement simplifier l'criture de certaines
fonctions en leur donnant le nom d'un oprateur. Par exemple, avec le gestionnaire de tableau que
nous manipulons, l'accs direct un des lments d'un tableau est difficile (car si t est un tTableau
alors t.tab[i] est le ime lment) et mme impossible en dehors d'une mthode ou d'une fonction
amie (car le membre tab est priv).
Pour permettre et simplifier l'accs direct aux lments, on peut dfinir une mthode Valeur()
comme suit : tELT& tTableau::Valeur(int i) { return tab[i]; } mais l'appel cette mthode
"t.Valeur(2)" est trs diffrent de la notation classique t[2] laquelle on est accoutum. Il est alors
possible d'appeler cette mthode non pas Valeur mais operator[ ], l'appel devient donc "t.operator[ ]
(2)" ce qui est dj plus explicite. En fait, le mot rserv "operator" signifie que l'on surcharge
l'oprateur qui suit et on peut donc crire librement t[2] pour faire appel la mthode "operator[ ] (int
i)". Notez lutilisation de la rfrence (&), indispensable si on dsire modifier le contenu du tableau
(ex : t[2] = 4 ;) . Dans le cas contraire (int a = t[2] ;), la rfrence nest pas obligatoire.
La mthode "operator[ ] (int i)" ayant un corps trs court et trs simple (pas de boucle, pas de test
imbriqu), il est dommage de devoir crire son prototype dans la classe et son corps l'extrieur,
sachant qu'un appel cette mthode quivaut un vrai appel de fonction (empilement des variables
contextuelles, association des paramtres, excution de la fonction, identification des paramtres en
sortie et dsempilement des variables contextuelles). On prfra alors soit faire prcder le corps de la
fonction du mot rserv "inline", indiquant quun appel cette mthode quivaut un
"chercher/remplacer" avant la compilation, soit carrment crire le corps de la mthode dans la classe
pour la rendre "inline" et ne pas avoir l'crire l'extrieur
class tTableau {
private :
tELT *tab;
int card,nbmax;
public :
bool Copie(tTableau &t2);
bool Ajoute(tTableau &t2);
friend bool AdditionneTableau(tTableau &t,tTableau &t2,
tTableau &tr);
tTableau(int card,tELT val);
~tTableau();
Surcharge de l'oprateur [
]
en mthode "inline"
tELT& operator[](int i) { return tab[i];}
};

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 46/59

On peut videmment surcharger tous les oprateurs arithmtiques standards ainsi que "[ ]" (un seul
paramtre autoris) et "( )" (autant de paramtres que l'on veut) Mais il faut alors dfinir un
constructeur de copie dans certains cas

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 47/59

5.8. Le constructeur de copie


On vient de voir qu'on pouvait dfinir des constructeurs pour une classe. L'un d'entre eux est trs
particulier : il s'agit du "constructeur de copie" qui prend en paramtre une rfrence sur un objet de
la classe. Il sert, comme son nom l'indique, instancier un objet de la classe et l'initialiser en copiant
un autre objet (qui sert de modle), par exemple :
tTableau::tTableau(tTableau &t);
...
int main() {
...
tTableau t(15,(tELT)10), t2(t);
...
} // main

C'est le constructeur de copie de tTableau

t2 contiendra la meme chose que

Au del de cette utilisation, le constructeur de copie est en fait parfois ncessaire car il est appel
implicitement ds que l'on passe un objet en paramtre en entre (c'est--dire sans rfrence). En effet,
un paramtre formel (dans l'entte) en entre est une copie du paramtre actuel (lors de l'appel). Par
dfaut, le constructeur de copie est une simple copie bit bit comme pour une structure. Le
constructeur de copie devient ncessaire ds que la construction d'un objet de la classe implique une
allocation dynamique, sinon les deux objets (le paramtre formel et l'actuel) partage la mme zone
mmoire alloue dynamiquement au risque de la modifier (cf. chapitre suivant) !!!

5.9. La gnricit
Plutt que de devoir crire n versions de la mme fonction pour comparer deux variables d'un mme
type (par exemple trouver le minimum de deux entiers ou de deux nombres flottants), on peut faire
appel au principe de gnricit et dfinir un modle de fonction "min(tElt t1,tElt t2)" renvoyant la
plus petite des valeurs t1 et t2 (ou le plus petit des objets t1 et t2 sous rserve d'avoir surcharg
l'oprateur <). Pour ce faire, on dfinit un modle de fonction, c'est--dire une fonction gnrique
dpendant d'une classe tElt qui sera automatiquement identifie lors de la compilation et de l'dition de
lien :
template <class tElt> tElt min(tElt t1, tElt t2)
{
if (t1<t2) return t1;
return t2;
}
int main () {
int a=3,b=5,minimum;
minimum = min(a,b);

a et b sont d'un mme


type
pour lequel < existe !

...
} // main

On peut sur le mme principe dfinir une classe gnrique dpendant d'une classe tELT qui ne sera
connue que lors de l'instanciation d'un objet de la classe sous rserve que tous les oprateurs
ncessaires soient surchargs si le type tELT choisi n'est pas un type simple.

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 48/59

template <class tELT> class tFichier


{
tELT *tab;
...
plus besoin de passer la taille des
tFichier();
lments
car
sizeof
(tELT)
fonctionne !
};
int main() {
...
tFichier<int> ficint;
...
} // main

ficint est un fichier d'entier


"i t"

5.10. L'hritage
On peut vouloir rutiliser les membres du gestionnaire de fichiers binaires bufferiss et gnriques
prcdent pour dfinir un gestionnaire de listes gnriques ou un gestionnaire de tableaux (voire de
matrices) gnriques. Plutt que de recopier inlassablement les mmes membres dans chaque classe,
on utilise le principe d'hritage qui permet de dfinir une classe partir d'un ou plusieurs autres
classes : "class tListe : public tFichier". Dornavant un objet de la classe tListe contient un objet de
la classe tFichier.
Le mot rserv "public :" permet de transmettre les droits d'accs aux membres de la classe tFichier :
en l'absence de ce mot, l'hritage est priv donc les membres de tFichier deviennent tous privs
Rciproquement dans la classe tFichier, on peut tendre l'accs de certains membres privs aux
mthodes et fonctions amies des classes drives en faisant prcder ces membres du mot rserv
"protected :" : encore une fois, les membres qui suivent "protected :" sont privs sauf pour les
mthodes et fonctions amies des classes drives.
Lorsqu'on dcrit un constructeur d'objet de la classe drive (par exemple tListe), il faut qu'il fasse
appel de manire explicite un constructeur de la classe de base (ici tFichier). Cet appel au
constructeur de la classe de base se fait gnralement sous la forme d'une prinitialisation, c'est-dire juste derrire l'entte du constructeur de la classe drive entre ":" et ";" ou "{". On trouvera cidessous un gestionnaire de listes gnriques et un gestionnaire de tableaux ou de matrices gnriques
qui hritent du gestionnaire de fichiers gnriques.
Lorsqu'il y a hritage, les mthodes des classes drives surchargent celles des classes de base : si la
fonction affiche est redfinie dans une classe drive, celle de la classe de base est rendue inaccessible
sauf recours la notation "::" (classe de base::mthode figures 1 et 2)
Lorsqu'une classe drive de plusieurs classes, il y a un risque que la mme mthode (non surcharge)
soit hrite plusieurs fois (une fois par classe de base) . Dans ce cas le compilateur indique quil y a
ambigut. Il existe alors deux solutions :
appeler explicitement la mthode de la classe grce la notation "::" ;
utiliser le mcanisme des classes virtuelles (figure 3).

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 49/59

La figure 1 (schma ci-dessous) montre comment est dtermin lendroit o se trouve une mthode.
La figure 2 explique le mcanisme de la redfinition de mthodes.
Dfinition

de

la

Classe

Classe

Classe

Classe

Classe

Objet

Le message est envoy


lobjet et transmis la
hirarchie
des
classes
jusqu
ce
quune
dfinition soit trouve

Objet

figure 1

Classe
La dfinition initiale est
remplace par celle de la
mthode polymorphe
Dfinition
la

de

Classe

Classe

Classe

Objet

Le message est envoy


lobjet et transmis la
hirarchie
des
classes
jusqu ce que la premire
dfinition soit trouve.

Objet

figure 2

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 50/59

class A {

} ;

Classe A

class B : public A {

} ;

Classe B

Classe C

class C : public A {

} ;
class D : public B, public D {

Classe D

Les classes B et C hritent de la classe A et la


classe D
hritent des classe B et C. Les membres de la
classe A sont donc prsents deux fois dans la
classe D, ce qui nest pas trs judicieux et peut
entraner des ambigut la compilation.
On peut rsoudre le problme en dclarant virtuel
lhritage entre la classe A et la classe B et/ou
entre la classe A et la classe C. Le mot-cl virtual
signifie que les lments de A ne seront inclus

Classe A

Classe B

Classe C

class A {

} ;
class B : virtual public A {

} ;

Classe D

class C : public A {

} ;
class D : public B, public D {

figure 3 Le mcanisme des classes virtuelles

Un mot sur les mthodes virtuelles


Le mot rserv "virtual" est galement utilis pour les mthodes mais signifie tout autre chose. La
dfinition dans une classe de base dune mthode virtuelle possdant une autre version dans une
classe drive indique au compilateur quil ne doit pas associer de typage statique cette
mthode. Le choix de la mthode appeler ( lexcution de faon dynamique) dpend du type de
lobjet pour lequel elle est appele (figure 4 exemple).
Les mthodes virtuelles pures (indiques par un "=0") sont des mthodes dfinies dans une classe
de base et non implmentes. Ces mthodes doivent obligatoirement tre surcharges dans toutes les
classes qui drivent de cette classe de base (mcanisme utile pour dfinir des spcifications).
Exemple : virtual void Affiche () = 0 ;

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 51/59

class CTab {

void Affiche () ;
int Add (int a, int b) ; // virtual int Add (int a, int b) ;

} ;
void CTab::Affiche() {
cout << Add (4,5) << endl ;
}
int CTab::Add(int a, int b) {
return a+b ;
}
class CMonTab {

int Add (int a, int b) ;

} ;
int CMontab::Add(int a, int b) {
return a+b+2 ;
}

int main () {
CTab t ;
CMonTab mt ;
lcran

t.Affiche() ;
mt.Affiche() ;
return 0 ;

11 seulement si Add est dfinie en virtual, sinon la


mthode appele est la mthode Add associe statiquement
la mthode Affiche de la classe CTab. Dans ce cas le
rsultat nest pas celui escompt (9).

lcran

figure 4 Exemple de mthode virtuelle

Lhritage et lappel des constructeurs


Certaines classes drives ont besoin de constructeurs. Si la classe de base a un constructeur, il doit
tre appel, sil a besoin darguments, ils doivent tre donns. Les arguments du constructeur de la
classe de base sont prciss dans la dfinition du constructeur de la classe drive. La classe de base
agit ainsi comme un membre de la classe drive. Exemple :

class Forme {

Forme (int a) ;

} ;
class Carre : public Forme {

Carre (int n) : Forme(n) ;

Appel explicite du constructeur de la classe de


base
l
d l l
d i

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 52/59


} ;

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 53/59

5.11. Les flux


Comme nous lavons dj prcis dans le chapitre 5.1, l'affichage et la saisie sont grs par la
bibliothque iostream.h et son cortge de flux cout, cerr, clog et cin. En C++, les flux peuvent
galement tre associs des fichiers. La classe fstreambase est la classe de base pour les flux
associs aux fichiers. Les classes ifstream et ofstream drivent de fstreambase et grent
respectivement les flux en entre (lecture - in ) et les flux en sortie (criture - out). Il existe galement
la classe fstream grant les flux la fois en entre et en sortie (lecture/criture).
Les mthodes de lecture (get, read, ) et dcriture (put, write, ) de ces classes ne peuvent grer
quune suite doctets contrairement aux fonctions utilises pour la gestion des fichiers en C (cf.
chapitre 4.10). Cette philosophie rend la manipulation des fichiers en C++ plus complexe et beaucoup
de programmeurs prfrent utiliser la gestion des fichiers en C mme dans un programme crit en
C++.
Exemple : copie dun fichier dans un autre (sans craser le fichier destination sil existe dj).

/* Copie un fichier dans un autre */


#include <iostream.h>
#include <fstream.h>
#include <iomanip.h>
int main () {
char nomficin[20], nomficout[20] ;
ifstream f_in ;
ofstream f_out ;

// saisie des noms des fichiers


cout << "Entrez le nom du fichier en lecture : " ;
cin >> nomficin ;
cout << "Entrez le nom du fichier en ecriture : " ;
cin >> nomficout ;

// ouverture des fichiers


f_in.open (nomficin) ;
if (!f_in) {
cerr << "Probleme louverture du
cerr << nomficin << endl;
return 1 ;
}

Ouverture du fichier f_in en mode


lecture (par dfaut puisque cest un
if
)
fichier " ;
Ouverture, seulement sil existe,
du fichier f_out en mode criture
(par dfaut puisque cest un
ofstream)

f_out.open (nomficout,ios::noreplace) ;
if (!f_out) {
cerr << "Probleme louverture du fichier" ;
cerr << nomficout << endl;
return 1 ;
}

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 54/59

// Copie dun fichier dans lautre


unsigned char byte ;
byte = f_in.get() ;
while (!f_in.eof()) {
f_out.put(byte) ;
byte = f_in.get() ;
}
f_in.close() ;
f_out.close() ;

Lecture
des
fi hi
Fermeture
fi hi

des

et

criture
t t

deux

return 0 ;
} // main

Il est cependant possible (et mme souhaitable) de crer ses propres classes de manipulation de
fichiers en utilisant le mcanisme de lhritage. On peut ainsi dfinir une classe drivant de la classe
fstream et crire ses propres mthodes de lecture et dcriture. Un tel procd facilite grandement la
manipulation des fichiers en C++. Reste que la mise en uvre est plus longue et plus difficile quen
utilisant le mcanisme des fichiers en C. De plus, cette mthode est intressante pour la gestion de
flux de chanes de caractres mais elle est trs difficile appliquer aux flux binaires. Il faut en effet
crire toutes les mthodes permettant de lire et dcrire en binaire chaque type simple (int, float,
double ) ainsi que la mthode quivalente la fonction sizeof () du C. Pour la lecture et lcriture
de structures, on risque en plus de se heurter des problmes dalignements mmoire ( padding )
difficile grer.
Ci-dessous un exemple dutilisation de lhritage pour crire un gestionnaire de fichiers manipulant
des chanes de caractres.
#include <iostream.h>
#include <fstream.h>
#include <stdio.h>
#define NOMFIC

"fichier.txt"

class T {
public :
float x,y,z;
bool b;

Ma classe mon_fichier hrite de la classe

T() {x=0.0;y=0.0;z=0.0;b=true;}
} ;
class mon_fichier : public fstream {
public :
mon_fichier (char nomfic[], int mode) { this->open(nomfic,mode); }
~mon_fichier () { this->close(); }
Les " " sont obligatoires pour permettre loprateur
>> (mthode mon_get) de relire correctement les
champs crits par mon_put (oprateur <<).
void mon_put (T t) {
*this << t.x << " " << t.y << " " << t.z << " " << t.b << endl;
}
int mon_get (T &t) {
*this >> t.x >> t.y >> t.z >> t.b ;
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

A travers le pointeur this, cest un


objet fstream complet qui est
29/06/01

page 55/59

return !this->eof();
}
};

int main() {
mon_fichier ficout("fout.txt",ios::out);
Ouverture

T t1, t2;

du fichier

en

mode

if (!ficout)
{
cout << "Probleme a la creation du fichier " << NOMFIC << endl;
return 1;
}
t1.x=3.456;
t1.y=34.56575;
t1.z=0.0;
t1.b=true;
for (int i=0;i<3;i++)
ficout.mon_put(t1);
ficout.close() ;

Il est ncessaire de fermer le


fichier
d l
i
l
Ouverture

mon_fichier ficin("fin.txt",ios::in);

du fichier

en

mode

if (!ficin)
{
cout << "Probleme a louverture du fichier " << NOMFIC << endl;
return 2;
}
while (ficin.mon_get(t2))
cout << t2.x <<" "<< t2.y << "

"<< t2.z <<"

ficin.close() ;
lcran

return 0;
}

"<< t2.b << endl;


3.456 34.5658 0 1
3.456 34.5658 0 1
3 456 34 5658 0 1

Cette
ligne
nest
pas
obligatoire
car le destructeur de lobjet
t

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 56/59

5.12. Les exceptions


Lauteur dune bibliothque peut dtecter les endroits de son code o des erreurs sont susceptibles de
se produire lexcution, mais il na souvent aucune ide de ce quil faut faire ensuite. Lutilisateur
dune bibliothque peut au contraire savoir comment traiter ces erreurs, mais ne peut pas les dtecter
(sinon elles auraient t traites dans le code de lutilisateur et non dans la bibliothque). La notion
dexception est offerte aux programmeurs C++ pour rsoudre une partie de ces problmes. Lide
fondamentale est quune fonction qui rencontre un problme impossible traiter immdiatement lve
(instruction throw) une exception en esprant que le programme appelant pourra la traiter. Une
fonction qui dsire grer ce genre de problme peut indiquer quelle est dispose intercepter
lexception (instruction try, catch).
Reprenons lexemple du chapitre 5.7 (la surcharge des oprateurs) en lui ajoutant la gestion des index
de tableau hors des limites.
#include <assert.h>
#include <except.h>
#include <iostream.h>

Contient __FILE__ et __LINE__

typedef long tELT;


class Error {};
Error error;
class Erreur {
public :
int ligne;

Classes

pour

grer

les

Erreur(int ligne) { this->ligne=ligne; }


void Affiche() {
cout << "Erreur dans le fichier "<<__FILE__<<" en ligne:"<<ligne<<endl;
}
};
class tTableau {
private :
tELT *tab;
int card,nbmax;
public :
tTableau(int nbmax) { card=0;tab=new tELT[nbmax];this->nbmax=nbmax; }
~tTableau() { card=0; delete[] tab; nbmax=0; }
tELT& operator[](int i) ;
};
tELT& tTableau::operator[](int i) {
if (i>=0 && i<nbmax)
return tab[i];
throw (Erreur(__LINE__)) ;
}
int main() {
tTableau t(5);
Tout le code source initial est inclus dans le bloc
try {
t[0]=12;
cout << "On utilise un indice incorrect..."<< endl;
t[7]=23;
Erreur leve car lindex est suprieur
return 0;
5
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique
29/06/01

page 57/59

Les erreurs connues sont traites


i i

catch (Erreur err) {


err.Affiche();
}
catch ( ... ) {
cout << "Erreur inconnue !!!"<<endl;
}

Toute erreur non gre prcdemment est


traite
par le bloc catch ( ... )

} // main
lcran

On utilise un indice incorrect...


Erreur dans le fichier exceptions.cpp en ligne : 33

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 58/59

INDEX
A
adresse d'une variable...........................................................20
allocation....................................................................................24

inline..............................................................................................43
instance........................................................................................35
instanciation...............................................................................35

break..............................................................................................10

malloc...........................................................................................24
manipulateur..............................................................................26
membre...................................................................................3, 35
mthode..................................................................................3, 35
modle..........................................................................................44

C
case...................................................................................................9
chane de caractres..............................................................12
champ......................................................................................3, 35
classe............................................................................................35
classe de base...........................................................................45
classe drive...........................................................................45
commentaire.........................................................................5, 25
constructeur................................................................................39
constructeur de copie............................................................44
continue.......................................................................................10
D
default..............................................................................................9
delete............................................................................................31
dsallocation.............................................................................31
destructeur..................................................................................39
do ......................................................................................................9

N
new.................................................................................................31
notation pointe........................................................................13
NULL.............................................................................................31
O
objet...............................................................................................35
oprateur de porte.................................................................29
opration logique.......................................................................8
operator.........................................................................................43
P

else....................................................................................................9
encapsulation.......................................................................3, 35
numr.......................................................................................13

pointeur.........................................................................................20
pointeur de pointeur................................................................20
polymorphisme............................................................................3
porte des variables................................................................19
prinitialisation.........................................................................45
protected......................................................................................45
public.....................................................................................36, 45

fclose.............................................................................................22
feof.................................................................................................22
fichier............................................................................................22
FILE...............................................................................................22
fonction amie............................................................................36
fopen..............................................................................................22
for.........................................................................................................
......................................................................................................9
fprintf.............................................................................................22
fread...............................................................................................22
free.................................................................................................24
friend.............................................................................................36
fscanf.............................................................................................22
fseek..............................................................................................22
fwrite.............................................................................................22

rfrence......................................................................................28
return..............................................................................................10

G
gnricit....................................................................................44
goto................................................................................................10
H
hritage...................................................................................3, 45
I
if ......................................................................................................9

S
source......................................................................................5, 25
structure................................................................................13, 17
surcharge des oprateurs......................................................43
switch..............................................................................................9
T
template.......................................................................................44
transtypage..................................................................................11
type complexe...........................................................................13
typedef..........................................................................................14
U
union..............................................................................................13
V
virtual............................................................................................47
void.................................................................................................29
W
while................................................................................................9

IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique

29/06/01

page 59/59