Vous êtes sur la page 1sur 144

Cours de programmation C++

E. Remy IUT de Provence site dArles


27/03/08 E. Remy - IUT de Provence, site d'Arles 1

Historique

Langage C Programmation structure B. Kernighan & D. Ritchie 1973 ANSI/ISO 1989 (et 1999)

Langage C++ Programmation oriente objet B. Stroustrup ~1980 ANSI/ISO

1998

Le C++ est un des principaux langages de dveloppement actuellement en entreprise. Ce sera le langage de la majorit de vos TP l'IUT.
27/03/08 E. Remy - IUT de Provence, site d'Arles 2

Apprendre les bases de C/C++

Je vous suggre fortement le livre (cours et exercices corrigs) Programmer en C++ de John R. Hubbard, srie Schaum's, EdiScience. Ce livre est disponible au prt en deux exemplaires la mdiathque de l'IUT.

27/03/08

E. Remy - IUT de Provence, site d'Arles

Spcificits du langage C++


Compatibilit avec le langage C (aussi bien pour les fichiers sources que pour les fichiers objets) Ajout de techniques de programmation avances Compilateur plus pointilleux que C Programmation oriente objet : langage classes Meilleure gestion des erreurs : les exceptions Programmation gnrique : patrons (templates) Standard Template Library (STL) La syntaxe gnrale de JAVA est directement inspire de celle de C++ (qui elle-mme reprend en totalit celle du C).

27/03/08

E. Remy - IUT de Provence, site d'Arles

Premier programme
Fichier Ex1.cpp

#include <iostream> using namespace std; int main() { cout << "Hello world !" << endl; return 0; }
g++ -Wall -c Ex1.cpp g++ -Wall -o Ex1.exe Ex1.o
Warnings : all
27/03/08

Compilation <- Edition de liens


<-

output : Ex1.exe

Liste des fichiers objets...


5

E. Remy - IUT de Provence, site d'Arles

La nouvelle syntaxe du C++

Ceux parmi vous qui connatraient dj le C++ sont pris de remarquer que nous utiliserons dans ce cours la nouvelle syntaxe issue du standard de 1998. Apprenez connatre les diffrences de syntaxe entre le C+ + normalis et non normalis car vous serez forcement amens utiliser les deux. Ne mlangez jamais les deux styles dans un mme projet !

27/03/08

E. Remy - IUT de Provence, site d'Arles

Les tapes de la compilation

Le prprocesseur L'inclusion de fichiers (.h et parfois autres) Substitutions lexicales : les macros La compilation Vrification de la syntaxe Traduction dans le langage d'assemblage de la machine cible L'assemblage Traduction finale en code machine Production d'un fichier objet (.o ou .obj) L'dition de liens Unification des symboles internes tude et vrification des symboles externes (.so ou .DLL) Production de l'excutable

27/03/08

E. Remy - IUT de Provence, site d'Arles

Les types de base


bool (ex : true, false) char, unsigned char, wchar_t (ex : 'a', '%', '\n') short, unsigned short (ex : -32000) int, unsigned int (ex : 33000) long, unsigned long float, double, long double (ex : 1.2345e+15)

27/03/08

E. Remy - IUT de Provence, site d'Arles

Oprateurs et priorits
Priorit

:: (oprateur de rsolution de porte) . -> [] ()(appel de fonction) ()(parenthses) sizeof() ++ -- ~ ! (unaire) & (prise d'adresse) * (indirection) new delete delete[] ()(conversion de types, cast) * / % (multiplication, division, modulo) + - (addition et soustraction) << >> (dcalages et envoi sur flots) < <= > >= (comparaisons) == != (comparaisons) & (ET bit bit) ^ (OU-Exclusif bit bit) | (OU-Inclusif bit bit) && (ET logique) || (OU logique) (? : ) (expression conditionnelle ou oprateur ternaire) = *= /= %= += -= <<= >>= &= |= ~= , (mise en squence d'expressions)

27/03/08

E. Remy - IUT de Provence, site d'Arles

Structures syntaxiques

Un bloc { } permet de rassembler plusieurs instructions afin que le bloc compte pour une seule instruction. Remarque : Donc ne pas mettre des accolades sans raison autour dune unique instruction Commentaires identiques JAVA : /* */ ou //.. Les structures de contrle sont identiques celles du C (et de JAVA) : for, while, if, switch, break, continue, etc

27/03/08

E. Remy - IUT de Provence, site d'Arles

10

Condition : if

De la forme :

... if( expression_condition ) { ...// effectu si expression_condition est vraie } else // videment le groupe else est facultatif { ... // effectu sinon } ...

27/03/08

E. Remy - IUT de Provence, site d'Arles

11

Choix multiple : switch

De la forme :
case expr_const1: // si expression==expr_const1 ... break; // pour sortir du switch ! case expr_const2: // si expression==expr_const2 ... break; // pour sortir du switch ! // eventuellement encore d'autres cases... default: // si expression est diffrente de tous ... // les cas prcdant

switch( expression ) {

Le break est facultatif !


E. Remy - IUT de Provence, site d'Arles 12

27/03/08

Tant que... et Faire ... tant que

De la forme :

while( expression_condition ) { ...// effectu tant qu'expression_condition est vraie }

De la forme :

do { ...// effectu au moins une fois, puis encore tant // qu'expression_condition reste vraie } while( expression_condition );
27/03/08 E. Remy - IUT de Provence, site d'Arles 13

De la forme :

Boucle for

for( initialisation(s) ; test ; incrmentation(s) ) { ...// effectu tant que test est vrai }

Possible de mettre plusieurs expressions. Par exemple :

for( i=0,c='a' ; i<26 ; i=i+1,c=c+1 ) { // i et c voluent en parallle respectivement de // 0 25 et de 'a' 'z' ! }

Attention : pas de ; aprs le for sinon on rpte l'instruction vide (qui ne fait rien) !
E. Remy - IUT de Provence, site d'Arles 14

27/03/08

Entre/Sortie

Trois flots standards existent : cin (entre standard) cout (sortie standard) cerr (sortie derreur standard)

Exemple :

int n; cout << "Donnez un entier, svp" << endl; cin >> n; cout << "Vous avez tape n=" << n << endl; cerr << "Arg! Panique!!!" <<endl;
27/03/08 E. Remy - IUT de Provence, site d'Arles 15

Formatage des sorties


Dfinis dans #include <iomanip> Formatages de sorties endl : insre une fin de ligne.

setw(x) : la prochaine sortie sera sur une largeur de x caractres quitte rajouter des espaces pour complter. setfill(c) : utilisera c comme caractre de remplissage. left : la prochaine sortie sera cadre gauche (dfaut). right : la prochaine sortie sera cadre droite. uppercase : prochaine sortie en majuscule. lowercase : prochaine sortie en minuscule. fixed : prochaine sortie en virgule fixe, ex. : 0.00012 scientific : prochaine sortie en notation ingnieur, ex. : 1.2e-4. flush : vide le tampon de sortie, en assurant que toutes les oprations prcdentes sont effectues.
E. Remy - IUT de Provence, site d'Arles 16

27/03/08

Les constantes

Faon C : Dfinitions pour le prprocesseur. Cest une substitution syntaxique pure (une sorte de copier/coller). En consquence, il ny a aucun typage de la constante. Ex : #define PI (3.1415)

Faon C++ : Ex : const double pi = 3.1415; La valeur est type, ce qui permet des contrles lors de la compilation. Mais ce nest pas pour autant une pseudovariable : par exemple, elle na pas forcment demplacement en mmoire.

27/03/08

E. Remy - IUT de Provence, site d'Arles

17

L-values

Une Left-value (valeur gauche) est un lment de syntaxe C++ pouvant tre crit gauche dun = daffectation. Exemple : une variable, une case de tableau mais bien dautres choses encore grce lnorme pouvoir dexpression du C++. Une left-value se caractrise par :

Un type prcis (et donc une taille en mmoire) Une valeur mais... Surtout un emplacement de stockage en mmoire.

27/03/08

E. Remy - IUT de Provence, site d'Arles

18

R-values

Une Right-value (valeur droite) est un lment de syntaxe C++ pouvant tre crit droite dun = daffectation. Exemple : une valeur, une constante, une variable, une expression, etc Une right-value se caractrise par : Un type prcis (que vous devez savoir identifier) ; Une valeur. Une right-value na pas forcement de zone de stockage en mmoire. Une affectation est encore une right-value ce qui donne le droit dcrire : i=j=10; ! Cest quivalent i=(j=10); c'est--dire j=10; i=j;. Il existe des dizaines de faons de produire des right- et leftvalues Vous les dcouvrirez progressivement.

27/03/08

E. Remy - IUT de Provence, site d'Arles

19

Oprateurs de post- et princrmentation


Vous croyez certainement connatre linstruction i++ mais savez vous qu' i++ est une right-value ? On peut donc lgalement crire en C++ :
// quivalent int j=i; i=i+1;

int i=10; int j=i++;

Cest une post-incrmentation : on augmente i aprs en avoir pris la valeur. Il existe aussi la pr-incrmentation :
// quivalent i=i+1; int j=i;

int i=10; int j=++i;

On augmente i avant den prendre la valeur. Il existe aussi sur le mme principe la pr- et la post-dcrmentation : --i et i--.

27/03/08

E. Remy - IUT de Provence, site d'Arles

20

Oprateur ternaire

Ressemble au if() {} else {} mais joue un rle de right-value et pas seulement dinstruction. Syntaxe : (A?B:C) prend la valeur de lexpression B si lexpression A est vraie, et la valeur de lexpression C sinon. Exemples :
bool b=; cout <<"b="<<(b ? "true" : "false" )<<endl; // remplace lexpression soit // par "true" soit par "false" string parite = (i%2==0 ? "pair" : "impair" ); // remplace lexpression soit // par "pair" soit par "impair" int j = (i++==10 ? 2*i : 2*i-10 ); // si i++==10 alors j=2*i sinon j=2*i-10

27/03/08

E. Remy - IUT de Provence, site d'Arles

21

La squence de valeurs

Elle constitue une srie de right-values comptant pour une seule right-value : la dernire de la liste. Exemple :
int int int i= 10,15,20; // i vaut 20. j= i++,i; // i augmente et vaut 21, et j vaut 21 ! k=++j,2; // j augmente et vaut 22, et k vaut 2.

La squence est rarement utilise dans les cas ci-dessus ! Elle est utilise pour effectuer plusieurs oprations en une seule fois ; exemple classique (et lui trs courant) :
int i; double x; for(i=0, x=0.0 ; i<=10 ; i++, x+=1./10) {} // boucle sur i et x en parallle !

27/03/08

E. Remy - IUT de Provence, site d'Arles

22

Fonctions (1/2)

Surcharge : Possibilit davoir plusieurs fonctions ayant le mme nom mais des signatures diffrentes : int plus1(int i) { return i+1; } double plus1(double i) { return i+1; } La signature dune fonction est le nombre et le type de chacun de ses arguments. Le prototype dune fonction est le nombre et le type de ses arguments (signature) et aussi de sa valeur de retour. Possibilit davoir des valeurs par dfaut pour les paramtres, qui peuvent alors tre sous-entendus au moment de lappel. int mult(int a=2,int b=3) { return a*b; } mult(3,4) 12 mult(3) mult(3,3) 9 mult() mult(2,3) 6

27/03/08

E. Remy - IUT de Provence, site d'Arles

23

On distingue la dclaration et la dfinition dune fonction (souvent dans deux fichiers diffrents). La dclaration affirme lexistence de la fonction (son nom et son prototype). La dclaration int main() de f() permet dappeler f(). La dfinition de f() prcise ce que f() fait, et permet au compilateur de la fabriquer rellement.

Fonctions (2/2)

mult.h : mult.cpp :

int

mult(int a=2,int b=3);

#include <iostream> using namespace std; #include "mult.h" int mult(int a,int b) { return a*b; } #include <iostream> using namespace std; #include "mult.h" // suite droite... int main() { cout << mult(1) << endl; return 0; }

main.cpp :

L'ordre d'inclusion est important : les <x> puis le using puis vos "x" !
E. Remy - IUT de Provence, site d'Arles 24

27/03/08

Les rfrences

Dclarer une rfrence j sur une variable i permet de crer un nouveau nom j qui devient synonyme de i. La dclaration dune rfrence se fait en prcisant le type de lobjet rfrenc, puis le symbole &, et le nom de la variable rfrence quon cre. Exemple :

int i=10; // i est un entier valant 10 int & j = i; // j est une rfrence sur un entier, // cet entier est i. // A partir dici j est synonyme de i, ainsi j=j+1; // est quivalent i=i+1 !

Attention le = dans la dclaration de la rfrence nest pas rellement une affectation puisquon ne copie pas la valeur de i. En fait, on affirme plutt le lien entre i et j. En consquence,
int & k = 44; // est donc parfaitement illgal
27/03/08 E. Remy - IUT de Provence, site d'Arles 25

Les pointeurs (1/3) : dclaration


Un pointeur est une amlioration de la notion dadresse en mmoire (vue en cours darchitecture des ordinateurs). Un pointeur cest ladresse en mmoire dun objet ainsi que le type exact de lobjet qui se trouve cette adresse. Lide est que si pi est un pointeur sur un entier , alors connatre pi permet de trouver cet entier dans la mmoire (opration dindirection) et de le manipuler (addition, etc) puisquon sait que cest un entier (et pas un rel, une chane de caractre ou tout autre objet).

int *pi;

// pi est un pointeur sur un entier qui se // trouve ailleurs dans la mmoire.

27/03/08

E. Remy - IUT de Provence, site d'Arles

26

Les pointeurs (2/3) : prise dadresse


Supposons quune variable entire i existe dans le programme, comment obtenir ladresse en mmoire de i ? On utilise loprateur & (oprateur de prise dadresse, ne pas confondre avec le & de la dclaration de rfrence !), ainsi &i signifie adresse de i qui est entier . Puisque i est un entier, alors &i est une R-value du type pointeur sur un entier . Cette opration permet dinitialiser une variable pointeur :
// // // // i est une variable entire contenant la valeur 10 pi est un pointeur sur un entier, et dont la valeur est ladresse de i.

int i=10; int *pi=&i;

Attention, il est illgal de prendre l'adresse d'autre chose qu'une L-Value (sinon pas de zone de stockage) !

int *pi2=&44; // est illgal ! int **ppi= &(&i); // est illgal !


27/03/08 E. Remy - IUT de Provence, site d'Arles 27

Les pointeurs (3/3) : indirection


A partir dun pointeur sur un entier contenant bien ladresse dun entier, comment manipuler cet entier ? Il faut utiliser loprateur * (oprateur dindirection, ne pas confondre avec la multiplication) :

int i=10; int *pi=&i; (*pi) = (*pi) + 1; // est quivalent i = i+1 et donc // ici i vaut 11 !

Attention : mme si les parenthses ne sont pas obligatoires, elles sont souvent importantes car elles vous assureront de l'ordre de rsolution des oprations ( moins bien sur que vous ne connaissiez sur le bout des doigts votre tableau de priorit des oprateurs, et que vous soyez certain de votre fait...).

27/03/08

E. Remy - IUT de Provence, site d'Arles

28

Exercices

noncer le type prcis et quand cest possible les valeurs des variables suivantes :

double a = 10.3; double *b = &a; double& c = a; double& d = *b;

Les oprations suivantes sont elles lgales ou non ? Pourquoi ? Et que signifient-elles ?

double **e=&b; (*(*e))++; d=3.14; b=0.69; *(b+1)=3.14;

Contrle de connaissance la prochaine sance.


E. Remy - IUT de Provence, site d'Arles 29

27/03/08

Passage de paramtre par valeur

Cest le passage simple : on cre une copie du paramtre qui existe tant quon est dans la fonction :
void incr(int i) { i++; cout<<"i="<<i<<endl; } int main() { int a=10; incr(a); cout<<"a="<<a<<endl; return 0; }

Produit lcran :

i=11 a=10

Au moment de lappel de incr, la valeur de a est recopie dans une nouvelle variable, nomme i, du mme type int. Comme i disparat la fin de incr, comme nimporte quelle autre variable locale, toute action faite sur i naffecte aucunement a.

27/03/08

E. Remy - IUT de Provence, site d'Arles

30

Passage de paramtre par adresse

On copie ladresse de lobjet utiliser. Cette adresse peut tre utilise pour modifier lobjet grce l'indirection * .

void incr(int *pi) { (*pi)++; cout<<"(*pi)="<<(*pi)<<endl; } int main() { int a=10; incr(&a); cout<<"a="<<a<<endl; return 0; }

Produit lcran :

(*pi)=11 a=11

On donne ladresse de a la fonction incr qui peut ainsi retrouver la variable a dans la mmoire, et ensuite la modifier.

27/03/08

E. Remy - IUT de Provence, site d'Arles

31

Passage de paramtre par rfrence

On donne un nouveau nom un objet dj existant. Ce nouveau nom disparat lorsque la fonction se termine.

void incr(int& ra) { ra++; cout<<"ra="<<ra<<endl; } int main() { int a=10; incr(a); cout<<"a="<<a<<endl; return 0; }

Produit lcran :

ra=11 a=11

Au moment de lappel incr, le nom ra se rajoute et dsigne dans incr la mme variable que celle qui sappelle a dans main.

27/03/08

E. Remy - IUT de Provence, site d'Arles

32

Astuce : Passage de paramtre par rfrence un objet constant

Si le paramtre ne doit pas tre modifi et que le cot dune recopie pour un passage par valeur est trop important, on choisit un passage par rfrence sur un objet constant :

void truc(const gros_objet& rgo) { = rgo ; // cela sera lgal rgo = ; // mais cela sera interdit ! }

Il devient alors impossible de changer rgo mais on peut le consulter librement dans la fonction.
E. Remy - IUT de Provence, site d'Arles 33

27/03/08

Porte (1/2)

La porte dune variable : cest la zone du programme o elle est connue, et peut tre utilise.
int i; void f(int a) { int j; }

27/03/08

E. Remy - IUT de Provence, site d'Arles

34

Porte (2/2)

Si des variables ont le mme nom, les plus profondes sont caches par celles les plus proches. Ainsi :
int i=1; void f() { cout <<i<<endl; int i=2; cout <<i<<endl; if(true) { int i=3; } int main() { cout <<i<<endl; f(); cout <<i<<endl; }
35

cout <<i<<endl; }

27/03/08

E. Remy - IUT de Provence, site d'Arles

Tableaux : dclaration

Comme en C et JAVA :

int tab[10]; // 10 lments entiers float tab2[]={1.2,3.14,2.0}; // 3 lments initialiss char tab3[5]={a,b,c}; // 5 lments, 3 initialiss int tab4[2][4][6]; // tableau 3 dimensions de 48(=2x4x6)

Contrairement JAVA pas de vrification des indices de tableaux lexcution : ATTENTION ! Important : Le nom du tableau est synonyme de ladresse du premier de ses lments, cest--dire tab est synonyme de &tab[0]. Cest donc un pointeur sur le type de chacune des cases du tableau. Les tableaux multidimensionnels sont des tableaux de tableaux , et leur usage dlicat (au niveau DUT) est mon avis viter
E. Remy - IUT de Provence, site d'Arles 36

27/03/08

Tableaux : en paramtre

Passer un tableau une fonction


int main() {

int somme(int a[], int n) { int som=0; for(int i=0;i<n;i++) som += a[i]; return som; }

int t[]={11,33,55,77}; cout <<somme(t,4)<<end; return 0; }

Pour les tableaux multidimensionnels, il faut prciser toutes les dimensions sauf la premire : int chose(int a[][4][6], int n)
E. Remy - IUT de Provence, site d'Arles 37

27/03/08

Allocation statique vs. dynamique

Lallocation standard des variables, dite allocation statique, donne des variables cres sur la pile (stack), et ayant une dure de vie fixe par la porte. Lallocation manuelle de mmoire pour de nouvelles variables, dite allocation dynamique, permet de crer (avec loprateur new) et de dtruire (oprateur delete) les variables volont : lobjet existe partir du new et jusqu une destruction explicite par delete. De plus, la zone mmoire dallocation de telles variables, appele le tas (heap), est diffrente, et permet gnralement de stocker des donnes volumineuses contrairement la pile. Attention cest une erreur doublier de faire avant la fin du programme un delete pour chaque objet allou dynamiquement avec new : vous devez dtruire les objets vous-mme partir du moment o ils ne sont plus utiles.

27/03/08

E. Remy - IUT de Provence, site d'Arles

38

Allocation dobjets simples


Alloc. statique
{ { int *pi; // cration de pi int i; // cration de i i=10; // utilisation pi = new int; //cration de i (*pi)=10; // utilisation de i delete pi; // destruction de i } // destruction de i } // destruction de pi

Alloc. dynamique

Allocation : oprateur new type Libration : oprateur delete var


E. Remy - IUT de Provence, site d'Arles 39

27/03/08

Allocation de tableaux
Alloc. statique
{ { int *t; // cration de t int t[10]; // cration t[1]=10; // utilisation t = new int[10]; // cration t[1]=10; // utilisation delete [] t; // destruction } // destruction } // destruction

Alloc. dynamique

Allocation : oprateur new type[taille] Libration : oprateur delete [] var

27/03/08

E. Remy - IUT de Provence, site d'Arles

40

Les Classes : concept


Les classes sont les lments de base de la programmation objet en C++. Concept similaire celui de JAVA : Dans une classe on runit : Des donnes les donnes membres Des fonctions les fonctions membres, ou les mthodes de la classe. Une class A apporte un nouveau type A ajout aux types (pr)dfinis de base par C++.

27/03/08

E. Remy - IUT de Provence, site d'Arles

41

Les classes : exemple de dclaration


ratio.h :
class Ratio { private: int num, den; // numrateur et dnominateur public: void affecter(int ,int); double valeur_reelle(); void ecrire(); void inverser(); };

Rappel : la dclaration permet de connatre les types des choses constituant la classe, et ainsi de lutiliser.

27/03/08

E. Remy - IUT de Provence, site d'Arles

42

Droits daccs aux membres


Ils concernent aussi bien les fonctions membres que les donnes membres. Considrons une classe A : Accs public : on peut utiliser le membre de nimporte o dans le programme. Accs private : seule une fonction membre de la mme classe A peut utiliser ce membre ; il est invisible de lextrieur de A. Accs protected : ce membre peut tre utilis par une fonction de cette mme classe A, et pas ailleurs dans le programme (ressemble donc private), mais il peut en plus tre utilis par une classe B qui hrite de A.

27/03/08 E. Remy - IUT de Provence, site d'Arles 43

Les classes : implmentation des fonctions membres ratio.cpp :


#include "ratio.h" // <- permet de connatre la // dclaration de la classe // dans ratio.cpp void Ratio::affecter(int numerateur,int denominateur) { num=numerateur; den=denominateur; } double Ratio::valeur_reelle() { return ((double) num ) / den; } void Ratio::inverser() { int tmp=num; num=den; den=tmp; } void Ratio::ecrire() { cout << num << / << den ; }

Rappel : la dfinition permet de fabriquer les choses constituant la classe.


E. Remy - IUT de Provence, site d'Arles 44

27/03/08

O placer les dfinitions des fonctions membres dune classe A ?

Le plus souvent, on les place dans le fichier A.cpp , comme on vient de le voir. On utilise pour cela le spcificateur de porte :: afin de dire de quelle classe la fonction est membre. Plus rarement, et dans le cas de fonctions courtes, directement dans le A.h , et sans le ::, un peu comme en JAVA. Pourquoi cette habitude ? On a plutt intrt garder le fichier A.h le plus petit possible afin de rendre rapide sa compilation (lors de son inclusion dans les fichiers .cpp ). De plus, une fois que le fichier A.cpp a t compil en un A.o , il nest pas ncessaire de le recompiler tant quon ne modifie pas A.cpp ; on gagne ainsi du temps. Permet aussi de ne rendre publique que le .h tout en conservant un certain degr de secret dans un .o compil livr au client.

27/03/08

E. Remy - IUT de Provence, site d'Arles

45

Les classes : instances

Une variable r cre avec ce type Ratio est appele instance de la classe :

main.cpp :
#include "ratio.h" // <- permet de connatre main() // la dclaration de la classe { // dans main.cpp Ratio r; r.affecter(3,4); cout <<"r = "; r.ecrire(); cout <<" = "<<r.valeur_reelle()<<endl; }

Produit lcran: r = 3/4 = 0.75


27/03/08 E. Remy - IUT de Provence, site d'Arles 46

Compilation de programmes modulaires


ratio.h :
class Ratio { private: int num, den; public: void affecter(int ,int); double valeur_reelle(); void ecrire(); void inverser(); }; #include <iostream> using namespace std; #include "ratio.h"

ratio.cpp :
#include <iostream> using namespace std; #include "ratio.h"

main.cpp

void Ratio::affecter(int _num,int _den) { num=_num; den=_den; } double Ratio::valeur_reelle() { return ((double) num ) / den; } void Ratio::inverser() { int tmp=num; num=den; den=tmp; } void Ratio::ecrire() { cout << num << '/' << den ; }

main() { Ratio r; r.affecter(3,4); cout <<"r = "; <<"r r.ecrire(); cout <<" = "<<r.valeur_reelle()<<endl; "<<r.valeur_reelle()<<endl;

g++ -Wall -c ratio.cpp g++ -Wall c main.cpp g++ -Wall -o exemple.exe main.o ratio.o
27/03/08 E. Remy - IUT de Provence, site d'Arles

Produit ratio.o Produit main.o Produit exemple.exe


47

Les classes : dclaration de constructeurs

En plus dutiliser affecter(), on peut crire un (voire plusieurs) constructeur(s) pour la classe. Un constructeur est charg de crer une instance de la classe.
Un constructeur porte toujours le mme nom que la classe. Il peut avoir des paramtres, et ventuellement des valeurs par dfaut. Il na jamais de type de retour. La dclaration a lieu dans le .h .

ratio.h :
class Ratio { private: int num, den; public: Ratio(int n,int d); Ratio(int n); };
27/03/08

E. Remy - IUT de Provence, site d'Arles

48

Les classes : dfinition de constructeurs

Les dfinitions se font dans le .cpp comme pour les autres fonctions membres.
#include "ratio.h" Ratio::Ratio(int n,int d) { num = n; den = d; } Ratio::Ratio(int n) { num = n; den = 1; }

ratio.cpp :

Ou encore mieux, avec un seul constructeur profitant du mcanisme de valeur par dfaut dans le fichier .h : ratio.cpp : ratio.h : #include "ratio.h"
Ratio(int n=0,int d=1); Ratio::Ratio(int n,int d) { num = n; den = d; }
49

27/03/08

E. Remy - IUT de Provence, site d'Arles

Les classes : instanciation 2 (le retour)

On peut dsormais utiliser nos constructeur et crire plus simplement :


main.cpp :

Ratio Ratio Ratio

r1(1,3); r2(4); r3;

// // //

r1 r2 r3

= = =

1/3 4/1 = 4 0/1 = 0

27/03/08

E. Remy - IUT de Provence, site d'Arles

50

Les classes : liste dinitialisation des donnes membres

Un meilleur moyen daffecter des valeurs lors de la construction aux donnes membres de la classe est la liste dinitialisation :

#include "ratio.h" Ratio::Ratio(int n,int d) ratio.cpp : : num(n),den(d) // liste dinitialisation {}

Les constructeurs de num et den sont appels au moment de la construction de Ratio, avant le constructeur de Ratio. Les listes dinitialisation permettent dutiliser le constructeur de chaque donne membre, et ainsi dviter une affectation aprs coup. Important : lordre dinitialisation des membres doit correspondre celui de leurs dclarations dans le fichier .h.

27/03/08

E. Remy - IUT de Provence, site d'Arles

51

Les classes : deux constructeurs particuliers

Deux constructeurs sont toujours ncessaires dans toute nouvelle classe : Le constructeur par dfaut Le constructeur de copie Ils sont tellement importants que si vous ne les crivez pas, le compilateur tentera de le faire votre place mais moins de savoir exactement ce que vous faites il vaut mieux les crire soi-mme.

27/03/08

E. Remy - IUT de Provence, site d'Arles

52

Les classes : le constructeur par dfaut


Par dfinition, le constructeur par dfaut dune class Ratio est : Ratio::Ratio(); // Avec aucun paramtre ! Rle : il cre une instance non initialise quand aucun autre constructeur fourni nest applicable : Ratio r1; Ratio tab_ratio[10]; A vous de choisir en fonction du besoin, si vous devez initialiser ou non certaines des donnes membres ou allouer de la mmoire, etc. En utilisant des valeurs par dfaut pour tous les paramtres d'un constructeurs, on ralise en mme temps le constructeur par dfaut.
VOUS INTERDIT DE COMPTER SUR CELUI FAIT AUTOMATIQUEMENT PAR LE COMPILATEUR : IL EST EN EFFET SOUVENT FAUX (TROP SIMPLISTE)... CEST DONC POUR VOUS EVITER DES FAUTES QUE JE RAJOUTE CETTE CONTRAINTE.
E. Remy - IUT de Provence, site d'Arles

JE VOUS OBLIGE A ECRIRE TOUJOURS CE CONSTRUCTEUR, ET JE

27/03/08

53

Les classes : le constructeur de copie


Son rle : fabriquer une nouvelle

instance de la class Ratio qui est une copie identique trait pour trait l'instance original qu'on lui donne en paramtre. Cette copie doit galement tre indpendante de l'original. Constructeur dclench par un passage de paramtre par valeur d'un Ratio : void f(Ratio r) {...} // Cre une copie locale f() Ratio r1(3,4); f(r1); ou par une copie explicite de la forme : Ratio r1(3,4); Ratio r2(r1); // r2 copie de r1 vaut 3/4 Le constructeur de copie pour une classe Ratio est de la forme :
Ratio::Ratio(const Ratio& original)// reoit une rfrence constante : num(original.num),den(original.den) // liste d'initialisation { }

LA ENCORE, JE VOUS OBLIGE A ECRIRE SYSTEMATIQUEMENT CE CONSTRUCTEUR. EN EFFET, LE CONSTRUCTEUR DE COPIE EST UTILISE (ENTRE AUTRES) LORS DU PASSAGE DE PARAMETRE DE TYPE Ratio PAR VALEUR, DONC PRESQUAU MOINDRE APPEL DE FONCTION !

27/03/08

E. Remy - IUT de Provence, site d'Arles

54

Constructeur de copie et destructeur : un exemple complet (1/3)


La classe VecteurN

a pour but de stocker un ensemble de n coordonnes relles o n est un entier (strictement positif) qu'on prcise au moment de l'excution du programme. La classe VecteurN a besoin d'un tableau de coordonnes dont on ne connat pas la taille au moment de la compilation car cela va dpendre de la dimension choisie par l'utilisateur. On est donc oblig de faire de l'allocation dynamique (c'est--dire utiliser new).
class VecteurN { private: unsigned int nbcoord; double *coord; // tableau des coordonnes VecteurN(); // astuce !... public: VecteurN(unsigned int taille); VecteurN(const VecteurN& original); ~VecteurN(); };
Exceptionnellement, on mettra le
27/03/08

constructeur par dfaut (celui sans paramtre) en priv car on ne veut pas qu'on puisse crer un vecteur sans prciser sa dimension !
E. Remy - IUT de Provence, site d'Arles 55

Constructeur de copie et destructeur : un exemple complet (2/3)


#include "VecteurN.h" void saisie(VecteurN & va) // par rfrence : pas de copie ! {... // va et v1 sont synonymes ici } // destruction du nom va void affiche(VecteurN vb) // passage par valeur : cre copie {... // grce au constructeur de copie ! } // destruction de la copie vb int main() { unsigned int taille; cin>>taille; // choix de l'utilisateur VecteurN v1(taille); // n=taille coordonnes saisie(v1); // pas de copie : v1 et va sont la mme chose ! affiche(v1); // vb est cre comme copie de v1 return 0; } // Destruction de v1
27/03/08 E. Remy - IUT de Provence, site d'Arles 56

Constructeur de copie et destructeur : un exemple complet (3/3)


VecteurN::VecteurN() { cerr<<"N'utilisez pas ce constructeur !"<<endl; exit(1);} VecteurN::VecteurN(unsigned int taille) : nbcoord(taille),coord(0) { coord = new double[nbcoord]; } // allocation du tableau VecteurN::VecteurN(const VecteurN& original) : nbcoord(original.nbcoord),coord(0) { coord = new double[nbcoord]; // allocation du tableau for(unsigned int i=0;i<nbcoord;i++) // puis coord[i]=original.coord[i]; // copie } VecteurN::~VecteurN() { delete [] coord; }
27/03/08

// destruction du tableau
E. Remy - IUT de Provence, site d'Arles 57

Les classes : destructeur

Le destructeur est la mthode membre appele lorsquune instance de classe cesse dexister en mmoire. Son rle est de librer toutes les ressources qui ont t acquises lors de la construction (typiquement librer la mmoire alloue dynamiquement pour la classe dans le constructeur).

class Ratio { ~Ratio(); };

Mme nom que la classe, prcd de ~ Aucun paramtre Et donc pas de surcharge possible Pas de valeur de retour
58

27/03/08

E. Remy - IUT de Provence, site d'Arles

Les classes : fonctions daccs aux membres privs (1/2)


Interdire la modification directe de num et den depuis lextrieur de la classe tout en autorisant leurs lecture ? Solution : limiter laccs en donnant une fonction publique de lecture dun membre priv : ratio.h :

class Ratio { private: int num,den; public: int numerateur() { return num; } int denominateur() { return den; } };
27/03/08 E. Remy - IUT de Provence, site d'Arles 59

Les classes : fonctions daccs aux membres privs (2/2)


Comment viter de modifier la classe par erreur durant le droulement de la fonction daccs ? Solution : considrer linstance de classe comme constante vis--vis de la fonction : ratio.h : ratio.cpp :

#include "ratio.h" class Ratio { private: int Ratio::numerateur() const int num,den; { return num; } public: int numerateur() const; int Ratio::denominateur() const { return den; } int denominateur() const; };
27/03/08 E. Remy - IUT de Provence, site d'Arles 60

Pointeurs sur des fonctions (1/5)


Permet de donner une fonction f1 en paramtre dune autre fonction f2 afin que f2 puisse appeler f1. Exemple : calcul approch de lintgrale de f(x) entre a et b
double Integ(type_de_f f,double a,double b) { mthode des rectangles utilisant f }

Ide : les fonctions ont une adresse en mmoire, comme tous les autres objets C++, et elles ont aussi un type qui est constitu du nombre et du type de ses paramtres ainsi que du type de sa valeur de retour (le prototype). Pointeur sur une fonction !

27/03/08

E. Remy - IUT de Provence, site d'Arles

61

Dclarer un pointeur sur une fonction (2/5)


Dclaration dune fonction : double f(double x); // Il existe une fonction f qui prend // un double et renvoie un double. Dfinition dun pointeur sur une fonction : double (*pf)(double x); // Il existe un pointeur pf sur une // fonction qui prend un double et renvoie un double. Remarques : pf est donc une variable et pas une fonction !

ladresse dune fonction tait dj utilise avec call en cours dArchitecture des Ordinateurs.

27/03/08

E. Remy - IUT de Provence, site d'Arles

62

Utilisation dun pointeur sur une fonction (3/5)


Si pf contient un pointeur correctement initialis vers une fonction existante, on peut appeler la fonction :
double y = (*pf) (15.3); // appeler la fonction pointe par pf en lui passant en paramtre la valeur 15.3, et en stockant son rsultat dans y.

27/03/08

E. Remy - IUT de Provence, site d'Arles

63

Initialisation dun pointeur sur une fonction (4/5)


Le nom dune fonction C++ est synonyme de son adresse, exactement comme pour les tableaux, donc ne pas utiliser loprateur de prise dadresse & ! Exemple : double carre(double x) { return x*x; } double (*pf)(double x); // dclaration de pf pf = carre; // affectation cout<<(*pf)(3); // utilisation : affiche 9. Attention : Il est impratif que les types correspondent !
27/03/08 E. Remy - IUT de Provence, site d'Arles 64

Pointeur sur une fonction (5/5) : exemple


double carre(double x) { return x*x; } void echant(double a,double b, int n, double (*pf)(double x) ) { for(int i=0;i<n;i++) { double x = a+(b-a)*i/(n-1); cout<<"f("<<x<<")="<<(*pf)(x)<<endl; } } echant(1,3,3,carre); echant(0,3.1415,10,sin); // fonction prdfinie
27/03/08 E. Remy - IUT de Provence, site d'Arles 65

Chanes de caractres C (1/2)


Rappel : le type caractre char permet le stockage dune valeur numrique entre 0 et 255 (un octet) reprsentant le code ASCII dune lettre. Une suite de caractres contigus en mmoire termine par un caractre nul '\0' forme une chane de caractres donc un tableau de char. Deux possibilits pour manipuler une chane : Soit elle nexiste pas encore, et on doit rserver de la mmoire pour elle : char str1[50]; // allocation statique char *str2=new char[50]; // alloc. dynamique Soit elle existe dj et on peut la manipuler en connaissant uniquement son adresse : char *str3; // pointeur sur le premier char de la chane

27/03/08

E. Remy - IUT de Provence, site d'Arles

66

Chanes de caractres C (2/2)

Lecture et criture simplement avec >> et << :


char str[200]; cin>>str; // lit une suite de lettres jusqu un // espace, et les place dans les cases // de str[], puis rajoute un '\0' final. cout<<str; // affiche le contenu de str[] lcran // et sarrte grce au '\0'.

Remarque : impossible de savoir lavance combien la personne va taper de lettres do risque derreur grave cause de ceci. CEPENDANT, quand on sait : Bien penser dfinir suffisamment de place pour la suite de lettres PLUS le zro terminal.
char str1[4]="pipo"; char str2[5]="pipo"; char str3[]="pipo";
27/03/08

// incorrect : 4 cases // correct : 4+1 cases // le plus simple !


67

E. Remy - IUT de Provence, site d'Arles

Quelques fonctions prdfinies utiles pour les chanes C


Rajouter #include <cstring> dans len-tte du fichier string length :


int strlen(char *s)

Nombre de lettres de s, sans compter le '\0' final. string copy :


char* strcpy(char *dest,char *source) Recopie les lettres contenues dans source vers dest (et renvoie ladresse du premier char de dest).

string compare :
int strcmp(char *s1,char *s2)

Renvoie 0 (faux) si s1 et s2 contiennent les mmes lettres, un entier ngatif si s1 est avant s2 dans lordre alphabtique, et un entier positif sinon.
27/03/08 E. Remy - IUT de Provence, site d'Arles 68

Quelques fonctions membres des instances prdfinies cin et cout


Ne pas oublier de mettre #include <iostream> dans len-tte du fichier. bool cin.get(char& c) lit un seul caractre et le place dans c. char c; cin.get(c); // renvoie true si la lecture a russi cin.getline(char *s, int nmax) lit une ligne de moins de (nmax-1) lettres, et la place dans s. Si la ligne est plus longue, ce qui dpasse est perdu (mais ne provoque pas de faute de mmoire). char str[80]; cin.getline(str,80); // lit toute une ligne et en place au maximum les 79 premiers char dans str. cout.put(c); crit le caractre c. cin.putback(c); remet le caractre c dans cin. Il sera donc le prochain caractre lu par un cin.get(c);

27/03/08

E. Remy - IUT de Provence, site d'Arles

69

Chanes C++ : la classe string (1/2)

Evite les problmes de dbordement de tableau et de '\0', mais avec un coup de traitement (en temps et en mmoire) plus lev. Rajouter #include <string> dans len-tte de fichier. Dclaration triviale :
string string string string string s1; // s1 contient 0 caractre s2="New York"; // s2 contient 8 caractres s3(60,'*'); // s3 contient 60 astrisques s4=s3; // s4 contient 60 astrisques s5(s2,4,2); // s5 contient Yo

Lecture et criture triviales :


string s; cin >> s; cout << s;

27/03/08

E. Remy - IUT de Provence, site d'Arles

70

Chanes C++ : la classe string (2/2)

Lecture dune ligne complte :

string s; getline(cin,s); // lit une ligne et la place dans s


Accs un caractre dune chane comme avec les chanes C :


char c=s[3]; // le 4me caractre de s va dans c

Accs (en lecture seule) la chane C contenue dans le string : Longueur de la chaine :

const char *sc=s.c_str(); // sc est ladresse du dbut dun tableau de char qui ne peuvent tre modifis.

string s="pipo"; int longueur = s.length(); // -> 4

Affectation, concatnation, et comparaison de chanes directement entre instances avec les oprateurs standards (=,+,<,>,<=, ==, etc.) :
if(s1==s2) s3=s1+"p";

27/03/08

E. Remy - IUT de Provence, site d'Arles

71

(Lecture depuis/criture vers) un fichier sur disque


Ne pas oublier de mettre #include <fstream> dans len-tte du fichier. Ecriture : Dclarer une instance de la classe ofstream (output file stream) : ofstream ofile("fichier.txt"); Sen servir la place de cout : char *str="hello"; ofile << str << endl; Lecture : Dclarer une instance de la classe ifstream (input file stream) : ifstream ifile("fichier.txt"); Sen servir la place de cin pour lire (par exemple) des entiers et calculer leur somme : int som=0,i; while(ifile >> i) som+=i;

27/03/08

E. Remy - IUT de Provence, site d'Arles

72

(Lecture depuis/criture vers) un fichier sur disque : exemples

Cette portion de programme cre un fichier de texte et crit un compte rebours dedans :
{ ofstream ofile("decompte.txt"); for(int i=10;i>0;i--) ofile << i << endl; ofile << "boum!" << endl; } // la fermeture du fichier se fait toute seule // lors de la destruction de ofile.

Cette portion de programme ralise la recopie caractre par caractre dun fichier dans un autre :

{ char c; ifstream ifile("source.txt"); ofstream ofile("destination.txt"); while(ifile.get(c)) // tant quon arrive lire un char ofile.put(c); // on le copie dans ofile. }

Il est important de refermer le fichier aussitt que possible en provoquant la destruction de linstance de ifstream ou de ofstream.
E. Remy - IUT de Provence, site d'Arles 73

27/03/08

Flux de chanes : istringstream et ostringstream

Compromis entre un string et un istream ou ostream : Lire et crire dedans avec >> et << comme avec istream ou ostream, Obtenir un string C++ pour une manipulation ultrieure. Utiliser la mthode str() pour obtenir le string rsultant. Exemple :

#include <iostream> #include <sstream> // stringstream ! #include <string> { string s("pipo"); int n=10; float pi=3.14; ostringstream oss; oss<<s<<','<<n<<','<<pi; cout <<"resultat : "<<oss.str()<<endl; }
27/03/08

A lcran :
resultat : pipo,10,3.14

E. Remy - IUT de Provence, site d'Arles

74

Oprateur ->

Loprateur -> est strictement quivalent une combinaison des oprateurs * et . , et est bien plus simple utiliser !
class A { int i; // et tout ce qui est ncessaire A A* f(); }; A* pa=; (*pa).i = 10; (*(*pa).f()).i=11; A* pa=; pa->i = 10; pa->f()->i=11;
75

27/03/08

E. Remy - IUT de Provence, site d'Arles

Fonctions et/ou classes amies


Problme : Comment faire pour quune fonction f() et/ou une classe B puisse accder aux donnes membres prives dune classe A ? Solution : dclarer dans la classe A que f() et/ou B sont amies de A :

class A { private: int i; // et tout ce qui est ncessaire A friend class B; // B est autorise. friend void f(A& a); // f() est autorise aussi. }; class // // }; B { tout ce qui appartient B, et qui peut se servir des donnes membres prives de A, comme si elle tait A.

void f(A& a) { a.i=10; } // on peut changer i, mme dans f().


27/03/08 E. Remy - IUT de Provence, site d'Arles 76

Le pointeur spcial dinstance : this

Dans certaines conditions particulires, il est ncessaire de disposer dun moyen de dsigner depuis une fonction membre, non pas les donnes membres, mais linstance elle-mme de la classe sur laquelle la mthode membre est appele. Le mot cl this permet de dsigner ladresse de linstance sur laquelle la fonction membre a t appele.
class A { A* f(); }; A* A::f() { return this; } A a; A *pa; pa = a.f();

Cet exemple est une faon atrocement complique de faire pa = &a; !!! Remarque : this est donc dun type diffrent suivant les contextes o il est utilis.

27/03/08

E. Remy - IUT de Provence, site d'Arles

77

Surcharge doprateurs

But : Ecrire simplement des oprations sur des classes rajoutes au C++. Exemple : addition de deux rationnels. 45 oprations : + - * / = += -= *= /= [] () == != <= < >= > << >> etc. (cf. tableau des priorits). Presque toutes ces oprations peuvent tre (re)dfinies sur les nouvelles classes afin dtendre les possibilits du langage C++. Seuls :: , . et .* ne peuvent tre redfinis. Le nom de la fonction surcharger est operatorxx() o xx est le symbole dusage de loprateur. La signature de loprateur change suivant de quel oprateur il sagit. Il est impratif de la respecter. (Il faut par exemple chercher dans un manuel pour obtenir son type exact). Surcharge avec fonction membre ou fonction non-membre amie suivant les cas.

27/03/08

E. Remy - IUT de Provence, site d'Arles

78

Surcharge doprateurs : cas 1, par des fonctions membres


class Ratio { // comme vue prcdemment public: Ratio& operator=(const Ratio& source); // affectation r1 = r2; Ratio& operator*=(const Ratio& source); // produit r1*= r2; }; Ratio& Ratio::operator=(const Ratio& source) { num=source.num; den=source.den; return *this; } Ratio& Ratio::operator*=(const Ratio& source) { num*=source.num; den*=source.den; return *this; }

Le typage de retour Ratio& et le return *this; permettent dutiliser ces expressions comme R-Value : Ratio r1(),r2(),r3(); r1=r2=r3; // equivalent r2=r3; r1=r2; Toujours return *this; la fin des diffrents oprateurs d'affectations , car il faut renvoyer une rfrence sur l'instance qu'on vient de modifier, c'est--dire (*this).
27/03/08 E. Remy - IUT de Provence, site d'Arles 79

Surcharge doprateurs : cas 2, par des fonctions non-membres amies


class Ratio { // comme vue prcdemment public: friend Ratio operator*(const Ratio& friend bool operator==(const Ratio& friend ostream& operator<<(ostream& friend istream& operator>>(istream& };

r1, const Ratio& r2); r1, const Ratio& r2); os,const Ratio& r); is,Ratio& r);

Ratio operator*(const Ratio& r1, const Ratio& r2) { Ratio r(r1.num*r2.num,r1.den*r2.den); return r; } bool operator==(const Ratio& r1, const Ratio& r2) { return (r1.num*r2.den) == (r2.num*r1.den); } ostream& operator<<(ostream& os,const Ratio& r) { os <<r.num<<'/'<<r.den; return os; } istream& operator>>(istream& is,Ratio& r) { is>>r.num; is>>r.den; return is; }
27/03/08 E. Remy - IUT de Provence, site d'Arles 80

Quelques signatures doprateurs usuels


A& A::operator=(const A&); A& A::operator+=(const A&); // idem pour -= *= /= etc. friend bool operator==(const A&,const A&); // idem pour < != etc. friend A operator+(const A&,const A&); // idem pour - * / etc. friend ostream& operator<<(ostream& os,const A&); friend istream& operator>>(istream& is,A&); A A::operator++(); // pr-incrmentation (idem pour --) A A::operator++(int); // post-incrmentation (idem pour -) // ATTENTION : ici le int ne sert rien, part // diffrencier les deux fonctions ! Cest un des // (relativement rares) cas de mauvaise conception du // langage C++.

Ici A:: marque qu'il s'agit d'une fonction membre de la classe A, et friend que c'est une fonction externe amie de la classe A. Attention : dfinition OBLIGATOIRE de l'operator= pour les mmes raisons vues pour le constructeur par dfaut et le constructeur de copie : il serait cr automatiquement sinon !
27/03/08 E. Remy - IUT de Provence, site d'Arles 81

Cas particulier de l'operator[]


class VecteurN { private: unsigned int nbcoord; double coord[]; // pour pouvoir renvoyer le i-me lment de coord public: double& operator[](int); // modification (L-value) double operator[](int) const; // consultation (R-value) }; VecteurN v(3); // 3 coordonnes pour ce vecteur v[1]=3.15; // utilise le 1er operator[] : modifie b double c2 = v[2] // utilise le 2me operator[] : ne modifie pas b

Deux surcharges complmentaires : Soit l'accs en lecture et criture du membre ni de B, mais sans garantie de constance du B ; Soit l'accs en lecture seule du membre ni de B, mais avec la garantie de constance du B.
E. Remy - IUT de Provence, site d'Arles 82

27/03/08

Lecture depuis un fichier grce >>

La lecture dune srie dobjets pour lequel un oprateur >> a t dfini se fait trs simplement :

#include <fstream> #include "VecteurN.h" VecteurN tab_vec[suffisament_grand]; unsigned int i=0; { // Ce bloc sert rendre la dure de vie de ifs la plus // courte possible, afin de librer le fichier le plus // vite possible ! ifstream ifs("fichier.txt"); while(ifs>>tab_vec[i]) i++; } // fin de vie difs, et fermeture du fichier
27/03/08 E. Remy - IUT de Provence, site d'Arles 83

Fonctions inline

Permet de mettre une fonction dans la dfinition de classe (le .h), tout en demandant au compilateur de substituer les appels cette fonction par son code. Na de sens que si la fonction est TRES courte/rapide ! Usage classique : les fonctions accesseurs, de faon garder la proprit de lecture seule tout en conomisant le cot dappel de la fonction :
Vous crivez :
Truc t; int j = t.valeur_i();

Truc.h :
class Truc { int i; public: inline int valeur_i() { return i; } };

... qui signifie :


Truc t; int j = t.i;
84

27/03/08

E. Remy - IUT de Provence, site d'Arles

Spcificateur extern (1/2)


On sais que les variables globales sont communes tout un programme. Idem, pour les fonctions Mais, elles ne sont connues que dans les fichiers .cpp dans lesquelles elles ont t dfinies et/ou dclares. Problme : Comment faire connatre dans un .cpp lexistence dune variable globale dfinie dans un autre fichier .cpp ? Rponse : Il suffit de rajouter une dclaration (et PAS une dfinition) de cette variable dans len-tte du deuxime fichier .cpp . En gnral, on crit une bonne fois pour toute ces dclarations dans un fichier .h que lont peut ainsi facilement inclure dans len-tte. Les dclarations de fonctions sont juste constitues des prototypes de fonctions (leur premire ligne de dfinition). Les dclarations de variables sont constitues de leur dfinition prcde du mot cl extern qui permet de ne pas crer la variable, mais juste dire que cet objet existe dj ailleurs.

27/03/08

E. Remy - IUT de Provence, site d'Arles

85

Spcificateur extern (2/2) : exemple

On veut crer deux variables (var1 et var2) et deux fonctions (fct1 et fct2) utilisables nimporte o dans les fichiers bout1.cpp et bout2.cpp.
variables.h :
extern int var1; extern double var2; int fct1(int a); double fct2(double b); // // // // Dclaration Dclaration Dclaration Dclaration de de de de var1 var2 fct1 fct2

bout1.cpp :
#include "variables.h" int var1=10; // Dfinition de var1 int fct1(int a) // Dfinition { var1+=a; var2+=var1; return de fct1 var1; } #include "variables.h"

bout2.cpp

double var2=11; // Dfinition de var2 double fct2(double b) // { var2+=2.3*var2; return Dfinition de fct2 var2; }

27/03/08

E. Remy - IUT de Provence, site d'Arles

86

Spcificateur static

Malgr le nom, aucun rapport avec lallocation statique ! Plusieurs notions suivant le contexte dapplication. Peut sappliquer une variable, une fonction, une donne membre, ou encore une fonction membre.

27/03/08

E. Remy - IUT de Provence, site d'Arles

87

static en dehors dune classe ou

dune fonction
truc.cpp :
static static {} int int a=10; f()

machin.cpp :
int main() { a=10; // impossible ! int i=f(); // impossible ! }

Limiter la visibilit dun identificateur au seul fichier o il est dfini. Dans lexemple, la variable globale a et la fonction f() ne peuvent tre utiliss que dans le fichier truc.cpp. Mme avec extern, impossible d'accder a ou f ! Il devient possible de crer dans ce fichier une autre variable/fonction globale ayant le mme nom !

27/03/08

E. Remy - IUT de Provence, site d'Arles

88

static dans une fonction

(membre ou non)
unsigned compte() { static unsigned int n=0; return n++; } int main() { cout<<compte()<<endl; // O cout<<compte()<<endl; // 1 cout<<compte()<<endl; // 2 }

Une des utilisations les plus frquentes. La valeur persiste entre deux appels la fonction. Linitialisation de la variable est donc trs importante ! La variable se comporte un peu comme une variable globale qui ne serait visible que dans sa fonction .

27/03/08

E. Remy - IUT de Provence, site d'Arles

89

Donne static dans une classe


A.h : class A { static unsigned int compte; public: A(); unsigned int combien(); }; A.cpp : unsigned int A::compte=0; A::A() { ++compte; } unsigned int A::combien() { return compte; } int main() { A a1,a2,a3; cout<<a1.combien(); // 3 ! }
27/03/08

Une autre utilisation frquente La donne est attache la classe elle-mme et pas aux instances : il ny a quun seul compte pour toutes les instances de A ! Importance de linitialisation, qui se fait dans le .cpp grce au spcificateur de porte !

E. Remy - IUT de Provence, site d'Arles

90

Fonction static dans une classe


A.h : class A { public: static void reset(unsigned int n=0); }; A.cpp : void A::reset(unsigned int n) { compte=n; } int main() { A a1,a2,a3; cout<<a1.combien(); // 3 ! A::reset(); A a4; cout<<a1.combien(); // 1 ! }
27/03/08

Une utilisation peu frquente. La fonction, bien que faisant partie de la classe, ne peut pas tre appele sur une instance de cette classe. Elle est appele globalement, avec le spcificateur de porte. Attention : ne pas mettre static dans le .cpp !

E. Remy - IUT de Provence, site d'Arles

91

Mot cl typedef

Mot cl provenant du langage C Permet de donner un nouveau nom un type existant. Syntaxe : typedef <type> <nom>; Exemples :

typedef int entier; entier i=10; typedef entier *pointeur_sur_entier; pointeur_sur_entier pi=&i; // attention, plus dtoile maintenant ! typedef entier tab1Oentiers[10]; tab10entiers tab={1,2,3,}; typedef double (*fct)(double); fct f1=sin,f2=fabs; // f1 est ladresse de sinus, f2 de valeur absolue typedef fct (*f)(entier,fct []); // Compliqu ! f est ladresse dune // fonction qui prend un entier et un tableau de pointeur sur des // fonctions de R dans R, et qui renvoie ladresse dune fonction de // R dans R.
27/03/08 E. Remy - IUT de Provence, site d'Arles 92

Arguments de la ligne de commande


Lors du lancement dun programme, il est courant de lui donner des paramtres. Exemple : la ligne de commande g++ -Wall -c pipo.cpp contient elle-mme 3 paramtres. Un programme C ou C++ reoit ses paramtres sous forme de chanes C en paramtre de la fonction main. int argc est le nombre darguments, char *argv[] est un tableau de chanes C (cest--dire des char*, ladresse de la premire lettre de chaque mot). Remarque : argv[0] dsigne le nom du programme. Le programme suivant crit lcran le nom du programme suivi des paramtres qui lui ont t donns :

#include <iostream> using namespace std; int main(int argc,char *argv[]) { for(int i=0;i<=argc;i++) cout<<"argv["<<i<<"]="<<argv[i]<<endl; return 0; }
27/03/08 E. Remy - IUT de Provence, site d'Arles 93

Composition

Composition (ou encapsulation ou agrgation) : utiliser des instances dune ou plusieurs classes dj dfinies comme donnes membres lors de la dfinition dune nouvelle classe.
class A {}; class B { string nom; // avec une // classe prdfinie }; class C { A a1,a2; // avec des classes B* pb; // introduites par // lutilisateur };
27/03/08

Lide est : un C est compos de deux A et dun pointeur sur un B . Exemple : Une personne a un nom et un prnom qui sont des strings.
94

E. Remy - IUT de Provence, site d'Arles

Hritage

Lhritage (ou spcialisation, ou drivation) : ajouter des proprits une classe existante pour en obtenir une nouvelle plus prcise. Lide est : un B est un A avec des choses en plus . Exemple : Un tudiant est une personne, et a donc un nom et un prnom. De plus, il a un numro INE.
95

class A { public: void f(); }; class B : public A { public: void g(); }; A a; B b; a.f(); // legal b.g(); // legal b.f(); // legal a.g(); // illegal : a na pas de g().
27/03/08

E. Remy - IUT de Provence, site d'Arles

Proprit issue de lhritage : conversions automatiques

Si B hrite de A, alors toutes les instances de B sont aussi des instances de A, et il est donc possible de faire : A a; B b; a=b; Proprit conserve lorsquon utilise des pointeurs : A *pa; B *pb=; pa=pb; car pointer sur un B cest avant tout pointer sur un A. Evidement, linverse nest pas vrai : A a; B b; b=a; // ERREUR ! Pareil pour les pointeurs : A *pa=; B *pb; pb=pa; // ERREUR ! car pointer sur un A nest pas pointer sur un B.

27/03/08

E. Remy - IUT de Provence, site d'Arles

96

Droits daccs aux membres


3 classes de membres : public, protected et private. Cas de lhritage public.
class A { public: int a; void fa(); private: int c; protected: int b; }; class B : public A { public: void fb(); };
27/03/08

a est visible depuis nimporte o. c nest visible que dans A:: et donc pas dans B:: ni dans ::, cest--dire c est utilisable dans fa() mais pas dans fb() ni dans main(). b est intermdiaire, visible dans A:: et dans B:: mais pas dans ::, cest--dire b est utilisable dans fa(), dans fb(), mais pas dans main().

E. Remy - IUT de Provence, site d'Arles

97

Hritage : vocabulaire
class A {}; class B : public A {};

A est la classe parente de B (en anglais superclass). B est la classe fille/drive de A. B hrite/descend de A.

Hritage : difficults non abordes

Une classe C++ (mais pas JAVA) peut hriter des proprits de plusieurs classes parentes (hritage multiple). Exemple : un hydravion est un avion et aussi un bateau. Utilisation rare et dlicate ! Possible dhriter autrement que publicquement : contrle de laccs aux membres hrits. Usage utile mais plus rare.
E. Remy - IUT de Provence, site d'Arles 98

27/03/08

Droits daccs aux membres


Cas de lhritage private.
class A { public: int a; void fa(); private: int c; protected: int b; }; class B : private A { public: void fb(); };

Seule fb() est publique dans B. Les donne et mthodes publiques de A deviennent prives B. Les donnes et mthodes protges et prives de A deviennent inaccessibles mme dans B. Utile pour simplifier une classe. Exemple : partir d'une superclass de gestion de liste trs gnrale, on tire une sous-classe de gestion de pile. Le dveloppeur profite des mthodes dj crites de la liste mais en ne laissant que l'accs empiler() et depiler().

27/03/08

E. Remy - IUT de Provence, site d'Arles

99

Surcharger des membres

class A { public: int val; }; class B : public A { public: double val; };

Si un membre (donne ou fonction) de B a le mme nom quun membre de A, alors il le cache. Il est nanmoins possible dutiliser A:: pour spcifier de quel membre on parle. Exemple : A a; B b; a.val est un int b.val est un double b.A::val est un int

27/03/08

E. Remy - IUT de Provence, site d'Arles

100

Constructeur dune classe drive

Comme une instance de B est avant tout une instance de A, dans le constructeur de B, on explicite la faon de crer ce A dans la liste dinitialisation :
A::A(int _val1) : val1(_val1) {} B::B(int _val1,int _val2) : A(_val1),val2(_val2) {}

class A { int val1; public: A(int _val1=10); }; class B : public A { int val2; public: B(int _val1,int _val2); };
27/03/08

Evidement, le constructeur de A est utilis avant de faire la partie {}. Ce dtail est souvent important !

E. Remy - IUT de Provence, site d'Arles

101

Ordre de construction et destruction


class A { public: A() { cout<<"A::A()"; } ~A() { cout<<"A::~A()"; } }; class B : public A { public: B() { cout<<"B::B()"; } ~B() { cout<<"B::~B()"; } }; class C : public B { public: C(int n){ cout<<"C::C("<<n<<")"; } ~C() { cout<<"C::~C()"; } }; { C c(10); }
27/03/08 E. Remy - IUT de Provence, site d'Arles

Les constructeurs sont appels dans lordre logique de construction : pour faire un B, il faut dj faire un A . Les destructeurs, dans lordre inverse. On obtient donc :
A::A() B::B() C::C(10) C::~C() B::~B() A::~A()
102

Etudions un cas
class A { public: void f() { cout<<"A::f()"<<endl; } }; class B : public A { public: void f() { cout<<"B::f()"<<endl; } }; main() { A a; B b; A *p; p=&a; p->f(); // p=&b; p->f(); // // } crit A::f() crit A::f() aussi.

Le choix de la fonction est conditionn par le type de p connu au moment de la compilation : puisque p est un A* alors il utilise A::f(). Comment faire en sorte que le deuxime appel crive B::f() ?
E. Remy - IUT de Provence, site d'Arles 103

27/03/08

Fonctions virtuelles
class A { public: virtual void f() { cout<<"A::f()"<<endl; } }; class B : public A { public: void f() { cout<<"B::f()"<<endl; } };

main() { A a; B b; A *p; p=&a; p->f(); // crit A::f() p=&b; p->f(); // crit B::f()! }

Le choix de la fonction est maintenant conditionn par le type exact de lobjet point par p connu au moment de lexcution : puisque p nous emmne sur un B* alors on utilise B::f(). Dsavantages ! On a : Les instances de A et de B prennent plus de place en mmoire (taille dun pointeur au moins) ; Lappel f() est plus lent (le temps dune indirection de pointeur supplmentaire, au moins).
27/03/08 E. Remy - IUT de Provence, site d'Arles 104

Polymorphisme

Le C++ permet que des objets de types diffrents rpondent diffremment un mme appel de fonction : cest le polymorphisme. En pratique : dclarer virtual la fonction concerne dans la classe la plus gnrale de la hirarchie dhritage (dans lexemple prcdant A). Toutes les classes drives qui apportent une nouvelle version de f() utiliseront leur fonction elles. Il reste videment possible dappeler la fonction f() de A en spcifiant p->A::f(), mais ce moment l pourquoi avoir utilis une fonction virtuelle !?
E. Remy - IUT de Provence, site d'Arles 105

27/03/08

tudions un autre cas


class A { int *p; public: A() { p=new int[2]; cout<<"A()"; } ~A() { delete [] p; cout<<"~A()"<<endl; } }; class B : public A { int *q; public: B() { q=new int[1023]; cout<<"B() et q="<<q; } ~B() { delete [] q; cout<<"~B()"; } }; main() { for(int i=0;i<8;i++) { A *pa= new B(); delete pa; } }

Affiche ceci :
A() A() A() A() A() A() A() A() B() B() B() B() B() B() B() B() et et et et et et et et q=0x5821c q=0x5921c q=0x5a21c q=0x5b21c q=0x5c21c q=0x5d21c q=0x5e21c q=0x5f21c ~A() ~A() ~A() ~A() ~A() ~A() ~A() ~A()

Fuite de mmoire ! (memory leak)


106

27/03/08

E. Remy - IUT de Provence, site d'Arles

solution : destructeur virtuel


class A { int *p; public: A() { p=new int[2]; cout<<"A()"; } virtual ~A() { delete [] p; cout<<"~A()"<<endl; } }; class B : public A { int *q; public: B() { q=new int[1023]; cout<<"B() et q="<<q; } ~B() { delete [] q; cout<<"~B()"; } }; main() { for(int i=0;i<8;i++) { A *pa= new B(); delete pa; } }
27/03/08

Affiche ceci :
A() A() A() A() A() A() A() A() B() B() B() B() B() B() B() B() et et et et et et et et q=0x5821c q=0x5821c q=0x5821c q=0x5821c q=0x5821c q=0x5821c q=0x5821c q=0x5821c ~B() ~B() ~B() ~B() ~B() ~B() ~B() ~B() ~A() ~A() ~A() ~A() ~A() ~A() ~A() ~A()

Le bon destructeur est appel.


107

E. Remy - IUT de Provence, site d'Arles

Fonctions virtuelles pures Classes abstraites (1/2)

Quand une fonction est commune toutes une srie de classe drivant toutes dune classe A, cela signifie souvent que cette fonction est attacher la classe A elle-mme. Exemple : figures gomtriques

Forme virtual Dessiner() virtual ~Forme() Triangle Dessiner() Rectangle Dessiner() Carr Dessiner() Cercle Dessiner()

Mais dans ce cas l, souvent on ne sais pas programmer ce que doit faire cette fonction dans le contexte de A : on veux juste sassurer de sa prsence dans toutes les classes filles, sans pouvoir la prciser dans la classe parente.

27/03/08

E. Remy - IUT de Provence, site d'Arles

108

Fonctions virtuelles pures Classes abstraites (2/2)


class Forme { public: virtual void dessiner()=0; // dessiner fait partie de Forme, mais on ne }; // sais pas comment la programmer pour Forme ! class Cercle : public Forme { vecteur3D pos; double rayon; public: void dessiner() { tracer_cercle(pos,rayon); } };

Oblige tous les descendants contenir une mthode dessiner(). Forme::dessiner() nest pas dfinie ; elle est dite virtuelle pure. La classe Forme ne peut pas tre instancie ; elle est dite abstraite. Une classe fille peut ne pas apporter de dfinition dessiner() ; elle est alors aussi abstraite (non-instanciable) son tour, etc. Une classe dans laquelle il ny a plus une seule fonction virtuelle pure est dite concrte et devient instanciable.
27/03/08 E. Remy - IUT de Provence, site d'Arles 109

Les espaces de noms (1/2)

On voudrait par exemple faire cohabiter une fonction sin trs rapide mais faisant un calcul approch, avec la fonction sin de la bibliothque standard qui effectue un calcul prcis mais (relativement) lent. En C, la solution, consiste donner un nom avec un prfixe : fast_sin et sin peuvent cohabiter dans le mme programme. Le C++ introduit la notion despace de nom pour amliorer le procd : sin est dclar dans la bibliothque standard de C++, et son vrai nom est donc std::sin. Nous plaons notre fonction rapide dans un espace de nom que nous choisissons, par exemple fast_maths. Le vrai nom de cette fonction sera alors fast_maths::sin. Pour viter de donner systmatiquement le nom complet quand on ne se sert que dune seule des deux fonctions, on peut crire :
using fast_maths::sin; // sin signifiera toujours fast_maths::sin using namespace fast_maths; // ds que C++ ne connat pas un // symbole, il le cherche dans fast_maths.

27/03/08

E. Remy - IUT de Provence, site d'Arles

110

Les espaces de noms (2/2)


Comme on utilise quasiment tout le temps les fonctions de la bibliothque standard, on utilise presque tout le temps using namespace std; pour se simplifier la vie. Placement et utilisation dun symbole C++ dans un espace de nom, exemple :
fast_maths.h : namespace fast_maths { double sin(double); } main.cpp : #include "fast_maths.h" using fast_maths::sin; double y=sin(3.14); fast_maths.cpp : #include "fast_maths.h" double fast_maths::sin(double d) { }

On peut si ncessaire crer des espaces de noms embots les uns dans les autres
E. Remy - IUT de Provence, site d'Arles 111

27/03/08

Gestion derreurs (1/7)


Les langages rcents (C++, JAVA, etc) apportent une faon lgante de grer lapparition puis le traitement des erreurs qui surviennent au moment de lexcution des programmes. Constatation : le lieu du programme o se produit lerreur nest gnralement pas celui o il est possible de prendre des mesures pour la traiter. Dcouper le traitement derreur en deux parties : Son dclenchement : instruction throw Son traitement : deux instructions insparables try et catch Mcanisme regroup sous le nom de gestion des exceptions . Permet de raliser proprement des tentatives successives visant viter lerreur avant dabandonner. Par exemple, si cest une forme dallocation de mmoire qui provoque lchec, il est possible de tenter de librer certains objets peu utiles avant de recommencer lallocation, et si cela ne suffit pas, alors seulement provoquer un chec.

27/03/08

E. Remy - IUT de Provence, site d'Arles

112

Gestion derreurs : vieille/mauvaise mthode (2/7)

Avant lapparition des exceptions, en C par exemple, on crivait en gnral quelque chose de cette forme l :
double inverse(double x) { if(x==0.0) { fprintf(stderr, "Division par zero !!!\n"); exit(1); // ARRET du programme en force ! } return 1/x; }

Pas satisfaisant car provoque larret du programme et ne permet pas de contourner lerreur. Lidal serait de dlocaliser le traitement de lerreur dans la fonction qui appelle inverse().
E. Remy - IUT de Provence, site d'Arles 113

27/03/08

Gestion derreurs : classes drives de std::exception (3/7)

Dcrire l'erreur l'aide d'une classe drive de std::exception :

class exception { public: exception() throw(); exception(const exception& ) throw(); exception& operator=(const exception& ) throw(); virtual ~exception() throw(); virtual const char* what() const throw(); // message d'erreur }; Nom bad_alloc bad_cast bad_typeid bad_exception out_of_range En-tte new <new> dynamic_cast <typeinfo> typeid <typeinfo> Spcification de l'exception <exception> at() <stdexcept> bitset<>::operator() <stdexcept> invalid_argument Constructeur de bitset <stdexcept> overflow_error bitset<>::to_ulong() <stdexcept> ios_base::failure ios_base::clear() <ios> Dclench par Parent exception exception exception exception logic_error logic_error runtime_error exception

27/03/08

E. Remy - IUT de Provence, site d'Arles

114

Gestion derreurs : nouvelle/bonne mthode (4/7)


#include <stdexcept> // necessaire pour connatre std::range_error double inverse(double x) { if(x==0.0) throw range_error("Division par zero !!!\n"); // expression de type else // std::range_error return 1/x; } void exemple() { double x; cout<<"Donnez un rel"; cin>>x; try { cout << "1/"<<x<<"="<<inverse(x)<<endl; } catch(range_error& re) { cerr<<"ERREUR : "<<re.what()<<endl; } }
27/03/08 E. Remy - IUT de Provence, site d'Arles 115

Gestion derreurs : essais multiples (5/7)


void exemple2() { double x; bool recom; do { recom=false; cout<<"Donnez un rel"; cin>>x; try { cout << "1/"<<x<<"="<<inverse(x)<<endl; } catch(range_error& re) { cerr<<"ERREUR: "<<re.what()<<endl<<"Recommence !<<endl; recom=true; } } while(recom); }
27/03/08 E. Remy - IUT de Provence, site d'Arles 116

Gestion derreurs : mcanisme (6/7)


Au moment o on constate lerreur : 1. On cre une instance dun type choisi, si possible drive de std::exception ou d'un de ses sous-classes. 2. On initialise cette instance de manire donner le plus dinformations possibles sur lerreur qui sest produite. 3. On envoie cette instance avec throw. 4. Linstance remonte la pile des fonctions appelantes, jusqu trouver un catch qui attrape ce type choisi. 5. Le bloc correspondant du catch est excut. 6. Si aucun bloc catch nest rencontr alors cest le gestionnaire dexception par dfaut qui sera utilis (celui l existe toujours!), et en gnral, il provoque la fin du programme avec un message derreur signalant quune exception est survenue (mais sans plus de dtail puisquil na aucune raison de savoir comment traiter cette exception l). Il est possible de mettre plusieurs catch pour des types diffrents sur un mme try.

27/03/08

E. Remy - IUT de Provence, site d'Arles

117

Gestion derreurs : exceptions (7/7)

Un bon programme devrait driver ses propres classes dexception de la classe std::exception ou dune des ses classes drives hlas linformation sur les classes prdfinie est difficile trouver : le plus simple consiste aller lire les fichier include du compilateur ! Attention bien utiliser un passage par rfrence pour les instances de std::exception et ses drives sinon la fonction virtuelle what() ne sera pas appele sur le bon type ! Il est possible (si cela a un sens) de redclencher la mme exception dans un catch aprs avoir ralis une action : try {} catch(type t) { action(s) ; throw ; } Dans ce cas, elle continue sa remonte dans la pile jusqu trouver un nouveau catch valable. Il est aussi possible dattraper nimporte quel type en indiquant la place du type attraper : catch() { } .

27/03/08

E. Remy - IUT de Provence, site d'Arles

118

Listes chanes

Structure de donnes permettant de mmoriser un nombre dlments qui nest pas connu lavance (contrairement aux tableaux). Principe de fonctionnement tudi sur un exemple : la classe ListInt.

LP 27/03/08

E. Remy - IUT de Provence, site d'Arles

119

Introduction la programmation gnrique

Considrons les deux fonctions suivantes :

void swap(int& a,int& b) { int temp=a; a=b; b=temp; } void swap(string& a,string& b) { string temp=a; a=b; b=temp; }

Elles utilisent le mme principe, seul le type des deux paramtres (et celui de tmp) changent. Cette double criture est une perte de temps, une source derreur, et rvle que la mthode dchange ne dpend pas du type de ce quon change. Il est possible dcrire en C++ une seule fois la fonction change de manire ce quelle soit utilisable pour changer toute paire de variables dun mme type : grce aux patrons de fonctions

27/03/08

E. Remy - IUT de Provence, site d'Arles

120

Patrons de fonctions
swap.h : template <typename T> void swap(T& a,T& b) { T temp=a; a=b; b=temp; } truc.cpp : #include "swap.h" int m=10,n=12; swap(m,n); string s("pipo"),t("toto"); swap(s,t); Triangle t1,t2; swap(t1,t2);

Vocabulaire : patrons/modles/templates. Dfinitions (et pas seulement dclaration !) des patrons dans les fichiers .h . Instanciation dans les fichiers .cpp . Lexemple est valable pour tout T o T::operator=() existe. Le compilateur cre (instancie) autant de fonctions diffrentes que ncessaire. Dans lexemple ci-dessus, il cre 3 fonctions swap() diffrentes : swap<int>, swap<string> et swap<Triangle>. Attention, la partie <???> fait partie du nom de la fonction !
E. Remy - IUT de Provence, site d'Arles 121

27/03/08

Patrons de classes
Tablo.h :
template <typename T,unsigned int n> class VecnD { T t[n]; public: T& operator[](int _n); const T& operator[](int _n) const; };

truc.cpp :
#include "Tablo.h" VecnD<int,10> t; // lgal const unsigned int max=100; VecnD<char,max> tc; // lgal unsigned int max2=20; VecnD<Triangle,max2> tt; // illegal // car max2 nest pas constant.

Mme ide, mais pour gnrer des classes et pas des fonctions. Ici on cre des vecteurs de dimension fixe mais quelconque. 2D, 3D, etc. Les coordonnes de ces vecteurs peuvent tre d'un type quelconque. Dfinition du patron dans le .h et instanciation dans les .cpp . Le compilateur cre/instancie autant de classes diffrentes que ncessaire. Ici aussi, la partie <???> fait partie du nom de la classe. En particulier, on retiendra que VecnD nest pas un nom de classe, mais VecnD<int,5> oui. Les paramtres de types doivent tre connus au moment de la compilation. Les paramtres qui ne sont pas des types (ici n) doivent tre des expressions constantes dont la valeur est connue au moment de la compilation.
27/03/08 E. Remy - IUT de Provence, site d'Arles 122

Patrons : Dfinition des mthodes membres


template <typename T> class Tablo { public: T f() { return ; } }; template <typename T> class Tablo { public: T f(); }; template <typename T> T Tablo<T>::f() { return ; }

Toujours dans le .h , mais


Soit directement dans la classe, Soit aprs la classe en spcifiant le contexte dapplication avec X<T>::f() o on rajoute le(s) paramtre(s) du template.
E. Remy - IUT de Provence, site d'Arles 123

27/03/08

Bibliothque standard de patrons Standard Template Library (STL)


Contient une srie de patrons prts lemploi :


Squences :
vector<> : tableau homogne autoredimensionnable ; deque<> (stack<>+queue<>) : piles et files dattente ; list<> : listes set<> : ensemble non ordonn ; multiset<> : comme set<> mais avec doublons ; map<> (et multimap<>) : dictionnaires/tableaux associatifs ; basic_string<> : base du type string (puisquen fait, on a typedef basic_string<char> string;
E. Remy - IUT de Provence, site d'Arles 124

Conteneurs associatifs :

Conteneurs spciaux :

27/03/08

Le patron vector<>

Tableau homogne autoredimensionnable Exemple : vector<int> tab(8); // semblable int tab[8]; Comme pour un tableau, accs possible avec [ ] : tab[2]=10; Accs au nombre dlments avec size() : cout<<tab.size()<<endl; Accs au premier et dernier lments : int& premier=tab.front(); int& dernier=tab.back(); Ajout en fin avec ventuel ajout de nouvelles cases : tab.push_back(11); Suppression du dernier lment : tab.pop_back();

27/03/08

E. Remy - IUT de Provence, site d'Arles

125

Itrateurs

quivalent dun pointeur sur une case dun tableau mais pour dsigner une case dun conteneur de la STL. Chaque classe conteneur apporte limplmentation dun itrateur pour son parcours. Exemple de parcours dun vecteur grce un itrateur :
vector<int> v(8); // initialisation de v for(vector<int>::iterator it=v.begin();it!=v.end();++it) cout<<*it;

Attention, it nest pas ncessairement un vrai pointeur. Il est probable quen fait ce soit quelque chose de plus abstrait mais pour lequel loprateur dindirection * ait t redfini La STL fournit un grand nombre dalgorithmes sur des conteneurs abstraits, et utilisant la notion ditrateurs : Algorithmes de tri (efficaces et complexes, genre QuickSort) Algorithmes de recherche (dichotomie, ) Etc. tudier la documentation de la STL pour en savoir plus !
27/03/08 E. Remy - IUT de Provence, site d'Arles 126

Identification dynamique du type

Oprateur typeid renvoyant une rfrence sur une instance de la classe type_info Possible d'obtenir le nom de la classe par exemple Oprateur dynamic_cast<type_cible>(source) Ncessite que source ait au moins une fct. virtuelle. Renvoie NULL si chec Exemple : B b; A* pa=&b; B* pb=dynamic_cast<B*>(pa);

LP 27/03/08

E. Remy - IUT de Provence, site d'Arles

127

Ancien C++ vs. C++ normalis


La directive #include <iostream.h> dicte le vieux comportement au compilateur VisualC++ par exemple. Les instances cin, cout, cerr et tous les autres noms (de variables, de classes, etc) dclars maintenant dans lespace de nom std taient avant dans la porte globale ! Les variables dclares dans les parenthses des boucles avaient une porte un niveau plus grande que laccolade fermante de cette mme boucle :

{ for(int i=0;i<10;i++) // i dfini partir dici { // i est toujours dfini dans la boucle } // En vieux C++ , i est encore dfini ! } // jusquici
27/03/08 E. Remy - IUT de Provence, site d'Arles 128

Le langage C
Pourquoi programmer en C (1989 ISO) plutt quen C++: Plus rapide compiler (mais moins de vrifications !) ; Trs lgrement plus rapide lors de lexcution, car fonctionnalits bien plus basiques ! Pour la compatibilit avec lancien : parce quon doit rajouter des fonctionnalits un code assembleur, Fortran ou C existant ; Pour la compatibilit avec dautres langages : on veut sassurer que nimporte quel autre langage pourra utiliser ce quon crit (y comprit les langages sans classes : assembleur, Fortran, C, Pascal normalis). Cest le cas par exemple lorsquon crit une bibliothque de fonctions (comme par exemple, la bibliothque JPEG qui permet de lire et dcrire des images dans ce format sans avoir le coder/dcoder soi-mme et qui sera aborde en deuxime anne).

27/03/08

E. Remy - IUT de Provence, site d'Arles

129

Premires diffrences

Illgal davoir plusieurs noms de fonctions/variables/types identiques mme avec des signatures diffrentes : le C ne permet pas la surcharge. Pas de capacit crire des operatorxx . La dfinition de variable na plus un statut dinstruction et ne peut donc plus tre place nimporte o dans un programme : elle doit tre imprativement mise en dbut dun bloc quitte crer un bloc pour cette seule raison. A la place des instances de stream nommes cin, cout, cerr, on trouve les variables globales de flots stdin, stdout et stderr (du type FILE*). Evidement, pas de string mais que des char* Les commentaires // nexistent plus, et seuls ceux /**/ sont normalement tolrs par le compilateur. Le passage par rfrence nexiste pas en C. Seuls existent les passages par valeur et par adresse.

27/03/08

E. Remy - IUT de Provence, site d'Arles

130

printf

Permet dafficher une variable en lcrivant vers le flot de sortie standard stdout. Appel avec un format et la liste des expressions afficher tel que dfini par le format. Exemple :

int i=10; double f=3.14; char str[200]="pipo"; char c='p'; printf("entier=%d reel=%g chaine=%s car=%c\n",i,f,str,c); /* affiche : */ /* entier=10 reel=3.14 chaine=pipo car=p puis passe la ligne. */

Attention : aucun contrle sur le nombre ni le type des expressions ! La syntaxe exacte dune chane de format est donne par la page de man de printf (cf. Google man printf ) normment de rglages diffrents possibles !
E. Remy - IUT de Provence, site d'Arles 131

27/03/08

fprintf, sprintf, et snprintf


Sur le mme principe, La fonction fprintf permet dcrire dans un fichier pralablement ouvert avec fopen(). La fonction sprintf permet dcrire dans une chane (tableau de char) le rsultat au lieu de lenvoyer sur un flot. Ne plus l'utiliser car dbordement de tableau. La variante snprintf permet de sassurer que le rsultat ne dpassera pas la taille (prcise en paramtre) du tableau de char.

27/03/08

E. Remy - IUT de Provence, site d'Arles

132

scanf clavier et de la stocker dans une scanf permet de lire une valeur depuis le
int i; float f; char str[200]; char c; scanf("%d %g %s %c",&i,&f,str,&c);

variable dont on prcise ladresse en mmoire et dont le type doit correspondre avec ce qui est lu. Exemple :

Attention : il faut donner ladresse de chaque variable et pas la variable ellemme ! Le passage par adresse est obligatoire ! Attention : le nombre et le type des paramtres doit imprativement correspondre ce qui est annonc dans le format, sous peine dobtenir de graves erreurs dadresse lexcution. (scanf ne vrifie pas les adresses o on lui dit daller crire en mmoire). Le compilateur n'est pas en mesure de vrifier que les arguments sont cohrents : c'est vous de le faire ! La liste des formats est presque la mme que celle de printf avec de lgres variations (cf. photocopie). Attention au pige : %g, %f, %e et %E qui correspondent un double dans printf, correspondent un float * dans scanf !!!
E. Remy - IUT de Provence, site d'Arles 133

27/03/08

fscanf et sscanf

fscanf permet de lire une valeur depuis un flot (de fichier par exemple). fscanf(FILE *, format ,); sscanf permet danalyser une chane la place dun flot, par exemple pour y lire un entier puis une sous-chane. sscanf(char *, format ,);

Lecture/criture caractre par caractre


char getchar() lit un caractre depuis stdin void putchar(char c) permet dcrire un caractre c vers stdout char fgetc(FILE *f) lit un caractre depuis un FILE *f void fputc(char c, FILE *f) crit un caractre c vers un FILE *f

27/03/08

E. Remy - IUT de Provence, site d'Arles

134

Flots de fichiers

Ouverture dun fichier avec FILE* fopen(char *nom,char* mode), en prcisant son nom et son mode douverture (lecture : "r" ou criture : "w", "b" pour un fichier binaire) Renvoie ladresse dun flot (FILE *) utilisable avec fprintf(), fscanf(), fgetc() ou fputc() en fonction du mode douverture choisi. Fermeture avec void fclose(FILE *f), en prcisant en seul paramtre le flot f fermer. Rappel : il est IMPERATIF de bien refermer TOUT ce qui a t ouvert

27/03/08

E. Remy - IUT de Provence, site d'Arles

135

criture vers un fichier sur disque

Cette portion de programme cre un fichier de texte et crit un compte rebours dedans :
{ char *name="decompte.txt"; FILE *f; if((f=fopen(name,"w")) // teste la valeur de retour { int i; for(i=10;i>=0;i--) fprintf(f,"%2d\n",i); fclose(f); } else fprintf(stderr,"Impossible decrire dans \"%s\"!",name); }

27/03/08

E. Remy - IUT de Provence, site d'Arles

136

Oprateur sizeof

Permet dobtenir la taille (en nombre doctets) en mmoire dun type ou dune variable de ce type. Exemple :
printf("taille dun caractere : %d",sizeof(char)); /* affiche toujours 1 si le compilateur respecte la norme : un char est un entier 8 bits. */ printf("taille dun entier : %d",sizeof(int)); /* affiche habituellement 4 : int est un entier 32 bits. */

Ncessaire pour lallocation dynamique de mmoire en langage C

27/03/08

E. Remy - IUT de Provence, site d'Arles

137

Allocation de mmoire : malloc


Mcanisme plus basique quen C++, mais comparable. On utilise la fonction malloc(size_t size) qui permet de trouver un bloc de size octets libres et den connatre ladresse (valeur de retour). Exemple, allocation dynamique dun entier et dun tableau de dix entiers, avec lquivalent C++ droite :
int *pi; int *ti; pi = malloc(sizeof(int)); ti = malloc(sizeof(int)*10); int *pi; pi = new ti = new int *ti; int; int[10];

En C

En C++

Si le programme ne peut allouer la zone, malloc renvoie NULL, et il ne faut alors videment pas utiliser la zone !!! Comme toujours, il faut absolument librer tout ce qui a t allou une fois quon nen a plus lutilit
E. Remy - IUT de Provence, site d'Arles 138

27/03/08

Libration de mmoire : free


Permet de rendre la zone obtenue avec malloc. Exemple :


/* suite */ free(pi); free(ti); /* suite */ delete pi; delete [] ti;

En C

En C++

Remarque : comme delete NULL, free(NULL) ne fait rien et donc n'est pas faux, mais Librer une zone de mmoire non-alloue par contre plantera systmatiquement votre programme ! Donc, toujours initialiser vos pointeurs avec NULL avant de faire quoi que ce soit.
E. Remy - IUT de Provence, site d'Arles 139

27/03/08

Structures

Les struct du C ressemblent aux class du C++, mais Elles rassemblent des donnes uniquement (pas de fonctions membres). Pas de droit daccs : tout y est public . Pas dhritage. Le mot cl struct fait partie du nom du type lors de la dfinition dune variable de ce type. Exemple :
fiche.h :
struct Fiche { char nom[50]; int age; };

Main.cpp :
#include "fiche.h" main() { struct Fiche f; /* dfinition */ scanf("%s %d" ,f.nom,&f.age); }
E. Remy - IUT de Provence, site d'Arles 140

27/03/08

Bibliothques (1/2)

Une bibliothque est un ensemble de donnes et de fonctions C ou de donnes, de fonctions et de classes C++, qui ne forment pas seules un programme complet mais fournissent un ensemble de services potentiellement utiles et livrs cls en mains un autre programmeur dans lcriture de son programme. Exemple : en deuxime anne, on utilisera : la bibliothque JPEG qui permet de lire et dcrire des images compresses au standard JPEG ; lApplication Programming Interface de MS-Windows qui permet de faire des programmes avec des fentres, des menus et des boutons ; les Microsoft Fundation Classes qui sont des classes C++ pour faciliter la cration dapplications fentres en cachant lAPI.

27/03/08

E. Remy - IUT de Provence, site d'Arles

141

Bibliothques (2/2)

Comment compiler un programme utilisant des fonctions dune bibliothque ? Besoin du extern "C" {} pour dclarer des variables/fonctions C et les utiliser en C++. Option I Options L et -l Comment fabriquer une bibliothque avec ar ?

27/03/08

E. Remy - IUT de Provence, site d'Arles

142

Directives de compilation

#ifdef et #if #endif pour diriger la compilation, et viter les inclusions multiples. Symboles __FILE__ et __LINE__ CPLUSPLUS, etc. #include <...> // d'abord les includes systmes ! using namespace std; #include "..." // aprs seulement vos includes !

27/03/08

E. Remy - IUT de Provence, site d'Arles

143

The End
27/03/08 E. Remy - IUT de Provence, site d'Arles 144