Académique Documents
Professionnel Documents
Culture Documents
24 septembre 2000
Programmer en
C++
avec le
29/06/01
page 2/59
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.
29/06/01
page 4/59
*/
*/
*/
*/
*/
*/
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).
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>
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);
a = 2
b = 3
c = a + b ;
lcran
return 0 ;
La somme 2 + 3 = 5
} /* main */
29/06/01
page 6/59
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.
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).
29/06/01
page 8/59
ADL
C/C++
Affectation
X3
x = 3 ;
Test
Condition
? |
if (condition)
{
instructions ;
}
else
{
instructions ;
} /* if */
switch (cas)
{
case cas1 : instructions ;
break ;
case cas2 : instructions ;
break ;
default : instructions ;
.......
cas2
.......
......
} /* switch */
/ 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) ;
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
faux
non
et
ou
gal
diffrent
! (condition)
&&
||
==
!=
29/06/01
page 10/59
=
=
=
=
=
=
=
1;
/*
(long)1; /*
1L;
/*
1.0 ;
/*
1. ;
/*
(double)1;/*
1.L ;
/*
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
}
29/06/01
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
page 11/59
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.
29/06/01
page 12/59
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
printf("Nom-> [%s]\n",toto.nom);
printf("Age-> %d\n",toto.age);
} tPersonne;
int main() {
tBoolean correct;
tPersonne mum,dad,toto;
correct = vFalse;
printf("correct-> %d\n",correct);
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
i2
(4
i1
i2
i1 = 7 ;
i2 = 9 ;
ma_fonction (i1, i2) ;
i1
i2
mmoire
du
mmoire de ma_fonction
} /* main */
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
29/06/01
page 15/59
Dessin explicatif du passage par valeur (avec adresse mode "out" ou "inout").
mmoire
(4
i1 = 7 ;
i2 = 9 ;
ma_fonction (&i1, &i2) ;
printf ("i1 = %d\n",i1) ;
printf ("i2 = %d\n",i2) ;
i2
(4
i1
i2
@i2
7
i1
i2
@i1
a = @i1
@i2
b = @i2
return 0 ;
mmoire
} /* main */
@i1
@i2
i1
i2
du
mmoire de ma_fonction
} /* 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
29/06/01
page 16/59
Le mcanisme du passage des paramtres expliqu prcdemment est appliqu la gestion dun
tableau.
return ;
} /* InitialiseTableau */
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 */
29/06/01
page 17/59
InitialiseTableau(&tablo,15,(long)10);
AfficheTableau(tablo);
return 0;
} /* main */
29/06/01
page 18/59
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 */
29/06/01
page 19/59
*/
*/
*/
*/
/*
/*
*/
*/
int i, *p;
i = 13;
p = &i;
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 = &i;
q = &p;
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).
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) :
contient
i
l'adresse
d'une
variable
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
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;
};
29/06/01
page 21/59
#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];
Dans fichier
TITI 32
29/06/01
page 22/59
du
strncpy(personne.nom,chaine,10);
personne.nom[10]='\0';
sscanf(chaine+10,"%ld",&(personne.age));
dcodage
de
} /* while */
criture
P
d'une
variable
de
type
strcpy(personne.nom,"TITI");
personne.age = (long)32;
fwrite(&personne,sizeof(tPersonne),1,fic);
fclose(fic);
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
lcran
29/06/01
page 23/59
} /* main */
29/06/01
page 24/59
int main {
long *p;
p (2 octets)
p = (long *)malloc(sizeof(long));
if (p==NULL)
{
printf("Pb dAllocation\n") ;
return 1;
} /* if */
*p = 4 ;
*p
free (p) ;
return 0 ;
} /* main */
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
} /* main */
IGN/ ENSG / Cellule Pdagogique et de Recherche en Informatique
29/06/01
page 25/59
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;"
29/06/01
page 27/59
#include <iostream.h>
int main()
{
int i,j;
saisie au clavier :
4
cin >> i;
cout << i << endl;
lcran
saisie au clavier : 5
6
lcran
56
} /* main */
Exemple :
cout << setw(4) << setfill(*) << 56 << endl ; // affichage de **56
29/06/01
page 28/59
#define gkNBTAB 15
int main () {
29/06/01
page 29/59
int main()
{
int i;
i = 5;
Ajoute(2,i);
b, donc i, a t incrment de 2 et
contient 7
return i;
} // main
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
29/06/01
page 30/59
la variable externe
prend
la valeur
115
::x = 115;
x = 56;
....
la variable locale
prend
la valeur
56
} /* main */
} // SaisieTableau
29/06/01
page 31/59
return 0;
} // main
29/06/01
page 32/59
aprs allocation
i
adresse
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 ;
tab = NULL;
} // if
} // main
29/06/01
page 33/59
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
29/06/01
page 35/59
for (i=0;i<t.card;i++) {
cout << "tab[" << i << "]= ";
Affiche(t.tab[i]);
} // for i
} // AfficheTableau
// 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
class tTableau {
public : //autorise l'acces
tELT *tab;
int card,nbmax;
// Prototype des mthodes
Alloue (int card,tELT val);
Desalloue ();
} ;
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>
29/06/01
page 38/59
29/06/01
page 39/59
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;
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();
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
=>
=>
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.
29/06/01
page 42/59
un constructeur
( );
<=
bool Alloue
~tTableau();
Le destructeur
() ;
};
<=
bool Desalloue
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
// 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
} // tTableau::tTableau
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;
} // tTableau::~tTableau
int main()
{
int retour=0;
tTableau t(15,(tELT)10),w(10,(tELT)5);
29/06/01
page 45/59
Affiche(w);
} // if
return retour;
} // main
On verra deux chapitres plus loin, un constructeur trs particulier : le constructeur de copie
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
29/06/01
page 47/59
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);
...
} // 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.
29/06/01
page 48/59
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).
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
Objet
figure 1
Classe
La dfinition initiale est
remplace par celle de la
mthode polymorphe
Dfinition
la
de
Classe
Classe
Classe
Objet
Objet
figure 2
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
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 {
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 CMontab::Add(int a, int b) {
return a+b+2 ;
}
int main () {
CTab t ;
CMonTab mt ;
lcran
t.Affiche() ;
mt.Affiche() ;
return 0 ;
lcran
class Forme {
Forme (int a) ;
} ;
class Carre : public Forme {
29/06/01
page 52/59
} ;
29/06/01
page 53/59
f_out.open (nomficout,ios::noreplace) ;
if (!f_out) {
cerr << "Probleme louverture du fichier" ;
cerr << nomficout << endl;
return 1 ;
}
29/06/01
page 54/59
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;
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
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() ;
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 << "
ficin.close() ;
lcran
return 0;
}
Cette
ligne
nest
pas
obligatoire
car le destructeur de lobjet
t
29/06/01
page 56/59
Classes
pour
grer
les
page 57/59
} // main
lcran
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
29/06/01
page 59/59