Beruflich Dokumente
Kultur Dokumente
Historique
Langage C Programmation structure B. Kernighan & D. Ritchie 1973 ANSI/ISO 1989 (et 1999)
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
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
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
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
output : Ex1.exe
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
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
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
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
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
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
11
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 ) {
27/03/08
De la forme :
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 }
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
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
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
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
19
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;
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;
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
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
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
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
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
#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
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;
27/03/08
26
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.
Attention, il est illgal de prendre l'adresse d'autre chose qu'une L-Value (sinon pas de zone de stockage) !
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
28
Exercices
noncer le type prcis et quand cest possible les valeurs des variables suivantes :
Les oprations suivantes sont elles lgales ou non ? Pourquoi ? Et que signifient-elles ?
27/03/08
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
30
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
31
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
32
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
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
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
int somme(int a[], int n) { int som=0; for(int i=0;i<n;i++) som += a[i]; return som; }
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
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
38
Alloc. dynamique
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
27/03/08
40
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
41
Rappel : la dclaration permet de connatre les types des choses constituant la classe, et ainsi de lutiliser.
27/03/08
42
27/03/08
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
45
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; }
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
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
48
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
// // //
r1 r2 r3
= = =
27/03/08
50
Un meilleur moyen daffecter des valeurs lors de la construction aux donnes membres de la classe est la 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
51
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
52
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
27/03/08
53
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
54
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
// destruction du tableau
E. Remy - IUT de Provence, site d'Arles 57
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).
Mme nom que la classe, prcd de ~ Aucun paramtre Et donc pas de surcharge possible Pas de valeur de retour
58
27/03/08
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
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
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
61
ladresse dune fonction tait dj utilise avec call en cours dArchitecture des Ordinateurs.
27/03/08
62
27/03/08
63
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
66
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
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
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
69
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
27/03/08
70
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.
Affectation, concatnation, et comparaison de chanes directement entre instances avec les oprateurs standards (=,+,<,>,<=, ==, etc.) :
if(s1==s2) s3=s1+"p";
27/03/08
71
27/03/08
72
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
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
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
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.
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
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
78
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
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
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
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
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; } };
27/03/08
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
85
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
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
87
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
88
(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
89
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 !
90
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 !
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
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
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
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
96
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().
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.
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
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
99
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
100
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 !
101
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
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()
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()
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
108
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
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
110
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
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
112
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
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
114
27/03/08
117
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
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
119
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
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
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
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
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
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
127
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
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
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
27/03/08
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 ,);
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
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
135
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
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. */
27/03/08
137
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
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
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
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
143
The End
27/03/08 E. Remy - IUT de Provence, site d'Arles 144