You are on page 1of 19

Une courte introduction C++

Karl Tombre cole des Mines de Nancy

Version 1.0 Octobre 1999

1 Un peu dhistoire
Le langage C++ a deux grands anctres : Simula, dont la premire version a t conue en 1967. Cest le premier langage qui introduit les principaux concepts de la programmation objet. Probablement parce quil tait en avance sur son temps, il na pas connu lpoque le succs quil aurait mrit, mais il a eu cependant une inuence considrable sur lvolution de la programmation objet. Dvelopp par une quipe de chercheurs norvgiens, Simula-67 est le successeur de Simula I, luimme inspir dAlgol 60. Conu dabord des ns de modlisation de systmes physiques, en recherche nuclaire notamment, Simula I est devenu un langage spcialis pour traiter des problmes de simulation. Ses concepteurs faisaient aussi partie du groupe de travail I FIP 1 qui poursuivait les travaux ayant donn naissance Algol 60. Simula-67 est avec Pascal et Algol 68 un des trois langages issus des diffrentes voies explores au sein de ce groupe. Son nom fut chang en Simula en 1986. Comme son prdcesseur Simula I, Simula permet de traiter les problmes de simulation. En particulier, un objet est considr comme un programme actif autonome, pouvant communiquer et se synchroniser avec dautres objets. Cest aussi un langage de programmation gnral, reprenant les constructions de la programmation modulaire introduites par Algol 60. Il y ajoute les notions de classe, dhritage et autorise le masquage des mthodes, ce qui en fait un vritable langage objets. Le langage C a t conu en 1972 aux laboratoires Bell Labs. Cest un langage structur et modulaire, dans la philosophie gnrale de la famille Algol. Mais cest aussi un langage proche du systme, qui a notamment permis lcriture et le portage du systme Unix. Par consquent, la programmation oriente systme seffectue de manire particulirement aise en C, et on peut en particulier accder directement aux fonctionnalits du noyau Unix. C possde un jeu trs riche doprateurs, ce qui permet laccs la quasi-totalit des ressources de la machine. On peut par exemple faire de ladressage indirect ou utiliser des oprateurs dincrmentation ou de dcalage. On peut aussi prciser quon souhaite implanter une variable dans un registre. En consquence, on peut crire des programmes presque aussi efcaces quen langage dassemblage, tout en programmant de manire structure. Le concepteur de C++, Bjarne Stroustrup, qui travaillait galement aux Bell Labs, dsirait ajouter au langage C les classes de Simula. Aprs plusieurs versions prliminaires, le langage a trouv une premire forme stable en 1983, et a trs rapidement connu un vif succs dans le monde industriel. Mais ce nest quassez rcemment que le langage a trouv sa forme dnitive, conrme par une norme. C++ peut tre considr comme un successeur de C. Tout en gardant les points forts de ce langage, il corrige certains points faibles et permet labstraction de donnes. De plus, il permet la programmation objet. Dautres langages, et en particulier Java, se sont fortement inspirs de la syntaxe de C++. Celle-ci est de ce fait devenue une rfrence. Nous supposons en particulier que les lves qui ont dj appris Java ne seront pas dpayss par ce langage. Cependant, nous voulons mettre en garde contre plusieurs fausses ressemblances : si la syntaxe est la mme ou trs proche, plusieurs concepts sous-jacents sont diffrents. Nous nous efforcerons de signaler ces piges potentiels.

2 Types de base et constantes


En C++, les types de base sont : bool : boolen 2, peut valoir true ou false, char : caractre (en gnral 8 bits), qui peuvent aussi tre dclars explicitement signs (signed char) ou non signs (unsigned char), int : entier (16 ou 32 bits, suivant les machines), qui possde les variantes short [int] et long [int], tous trois pouvant galement tre declars non signs (unsigned), float : rel (1 mot machine),
1. International Federation for Information Processing. 2. La prsence dun type boolen explicite est assez rcente ; auparavant, les entiers taient interprts comme des boolens suivant leur valeur nulle ou non-nulle, et par compatibilit C++ continue accepter des valeurs entires la place de valeurs boolennes.

double : rel en double prcision (2 mots machines), et sa variante long double (3 ou 4 mots machine), void qui spcie un ensemble vide de valeurs. Les constantes caractres scrivent entre quotes simples : a G 3 * [ Certains caractres de contrle scrivent par des squences prdnies ou par leur code octal ou hexadcimal, comme par exemple : \n \t \r \135 \ \x0FF Les constantes entires peuvent scrire en notations dcimale, hexadcimale (prcdes de 0x 3 ) ou octale (prcdes de 0 4 ). Pour forcer la constante tre de type entier long, il faut ajouter un L la n, de mme le sufxe u indique une constante non signe : 12 -43 85 18642 54L 255u 38ul 0xabfb 0x25D3a 0x3a 0321 07215 01526 Les constantes relles scrivent avec point dcimal et ventuellement en notation exponentielle : 532.652 -286.34 52e+4 42.63E-12 12.73 -28.15e4

Les constantes de type chanes de caractres (voir plus loin) scrivent entre double-quotes : "Home sweet home" "Franais, je vous ai compris."

3 Oprateurs et expressions
C++ offre un jeu trs tendu doprateurs, ce qui permet lcriture dune grande varit dexpressions. Un principe gnral est que toute expression retourne une valeur. On peut donc utiliser le rsultat de lvaluation dune expression comme partie dune autre expression. De plus, le parenthsage permet de forcer lordre dvaluation. Les oprateurs disponibles sont les suivants :

3.1 Oprateurs arithmtiques


+ * / % addition soustraction multiplication division (entire ou relle) modulo (sur les entiers)

3.2 Oprateurs relationnels


> >= <= < comparaisons == != galit et ingalit ! ngation (oprateur unaire) && ET relationnel || OU relationnel
3. zro-X. 4. zro.

3.3 Laffectation
= affectation Il faut bien noter que le signe = est loprateur daffectation, et non de comparaison ; cela prte parfois confusion, et entrane des erreurs difciles discerner. noter aussi que laffectation est une expression comme une autre, cest--dire quelle retourne une valeur. Il est donc possible dcrire : a = b = c+2; ceci revenant affecter b le rsultat de lvaluation de c+2, puis a le rsultat de laffectation b = c+2, cest--dire la valeur quon a donne b. Remarquez lordre dvaluation de la droite vers la gauche.

3.4 Oprateurs dincrmentation et de dcrmentation


++ incrmentation -- dcrmentation Ces oprateurs, qui ne peuvent tre appliqus que sur les types scalaires, peuvent semployer de deux manires : en principe, sils prxent une variable, celle-ci sera incrmente (ou dcrmente) avant utilisation dans le reste de lexpression ; sils la postxent, elle ne sera modie quaprs utilisation. Ainsi : a = 5; b = 6; c = ++a - b; donnera c la valeur 0, alors que a = 5; b = 6; c = a++ - b; lui donnera la valeur -1. Faites cependant attention dans les expressions un peu complexes o on rutilise la mme variable plusieurs fois : lordre dvaluation nest pas garanti, et lexpression peut donc avoir des rsultats diffrents suivant la machine utilise. Par exemple, le rsultat de lexpression suivante est indni : t[++a] = a;

3.5 Oprateurs logiques


Ce sont les oprateurs permettant deffectuer des oprations au niveau des bits (masquages). & AND. Exemple : a & 0x000F extrait les 4 bits de poids faible de a. | OR. Ainsi, b = b | 0x100 met 1 le 9me bit de b. ^ XOR. << SHIFT gauche. a = b << 2 met dans a la valeur de b o tous les bits ont t dcals de 2 positions vers la gauche. >> SHIFT droite. ~ complment 1 (oprateur unaire).

3.6 Modier la valeur dune variable


Nous avons dj vu laffectation, lincrmentation et la dcrmentation. Il arrive trs souvent quon calcule la nouvelle valeur dune variable en fonction de son ancienne valeur. C++ fournit pour cela un jeu doprateurs combins, de la forme <variable> <op>= <expr> o <op> est un oprateur. Une telle expression est quivalente lexpression : <variable> = <variable> <op> <expr> += a += b quivaut a = a + b; noter : a++ a += 1 a = a + 1 -= idem, de mme que *=, /=, %=, =, =, &=, |= et^=. 3

3.7 Expressions conditionnelles


expr1? expr2 : expr3 est value de la manire suivante : si expr1 alors expr2 sinon expr3 fsi Cela est pratique par exemple pour calculer le maximum de 2 nombres sans passer par une fonction : z = (a > b) ? a : b; Cette construction pourrait bien sr sexprimer avec une structure conditionnelle de la forme sialorssinon, mais lcriture sous forme dexpression conditionnelle est plus compacte.

3.8 Conversions de types


On dsire souvent changer le type du rsultat retourn par une expression. Pour cela existe le mcanisme de cast 5 : (<nom de type>) expression retourne une valeur dont le type est celui qui est indiqu dans la premire parenthse, et qui est obtenue en convertissant le rsultat de lexpression dans le type spci. Pour nir ce paragraphe, notons aussi que lappel une fonction est une expression comme une autre. Enn, une expression peut dans certains cas tre une suite de plusieurs expressions indpendantes spares par des virgules ; voir cet gard ce qui sera dit par la suite sur la structure itrative par exemple (cf. 4.3). Nous donnons ci-dessous un tableau rcapitulatif des oprateurs de C++, classs dans lordre dcroissant des priorits. Certains de ces oprateurs nont pas t mentionns ci-dessus, mais sont dcrits dans la suite du polycopi. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Fonction/Slection/Porte Unaire Multiplicatif Additif Dcalages Relationnels Ingalit/Egalit ET logique XOR logique OU logique ET relationnel OU relationnel Affectation Conditionnel Exceptions Virgule () [] . -> :: * & - ! ~ ++ -- typeid sizeof casts new delete * / % + << >> < > <= >= == != & ^ | && || = <op>= ? : throw ,

5. Dans ce polycopi, nous expliquons lancien systme de conversion de types, qui est encore largement en vigueur. Il faut nanmoins savoir quun nouveau mcanisme de cast, utilisant les oprateurs static_cast, dynamic_cast, const_cast et reinterpret_cast, a ofciellement remplac lancien systme dans la norme dnitive de C++. Cet ancien systme reste nanmoins utilisable, et la plupart des programmes existants lemploient encore largement.

4 Structures dun programme C++


Contrairement Java, toutes les fonctions ne sont pas incluses dans une classe en C++. En ce sens, C++ hrite de son prdcesseur une structure modulaire, et on peut trs bien concevoir un programme C++ compos dun grand nombre de modules, ventuellement compils sparment. Chaque module est alors compos de fonctions, et ventuellement de dclarations de variables globales. Dans lensemble des modules, une fonction particulire, ayant pour nom main(), doit obligatoirement exister, et de manire unique. On lappelle souvent le programme principal, par abus de langage. Il serait srement plus correct de dire que cest le point dentre lexcution du programme. Ceci tant dit, il est fortement conseill de ne pas multiplier les fonctions hors classe ; dans bien des cas, seule la fonction main, et ventuellement quelques fonctions annexes des ns utilitaires, ont vocation tre dnies hors dune structuration en classes. De mme, nous dconseillons fortement lemploi de variables globales ; comme en Java, il est beaucoup plus judicieux, lorsque cela est ncessaire, dutiliser des variables de classe regroupes dans une classe ad hoc. Chaque fonction a la syntaxe suivante : typeRetour nomDeLaFonction(spcification des paramtres formels) { suite de dclarations de variables locales et dinstructions } Les paramtres formels doivent tre spars par des virgules, et sont typs. Prcisons ces notions en voyant une petite fonction : int moyenne(int a, int b) { int c = (a+b)/2; return c; } Remarque : comme en Java, on peut passer la fonction main des paramtres correspondant aux paramtres dappel du programme.

4.1 Instructions et blocs


Chaque instruction est termine par un point-virgule. noter que le point-virgule est une terminaison dinstruction et non un sparateur dinstruction. En particulier, pour quune expression soit considre comme une instruction, elle doit tre termine par un ; mme si elle est la dernire dun bloc. Un bloc est une suite dinstructions dlimites par une accolade ouvrante { et une accolade fermante }. lintrieur de tout bloc, on peut aussi dnir des variables locales ce bloc : if (n > 0) { int cumul = 0; for (int i=0 ; i < n ; i++) .... .... } Attention linstruction vide ; qui est source potentielle derreurs difciles dtecter, comme dans : /* Exemple dune instruction vide involontaire */ for ( ... ) ; // Ici le point-virgule indique une instruction vide // excuter chaque itration ; ce ntait pas // forcment le souhait du programmeur Vous avez peut-tre remarqu que jai lchement prot de loccasion pour introduire les deux types de commentaires valides en C++. Les portions de code comprises entre /* et */ sont des commentaires, de mme que celles comprises entre // et la n de la ligne.

4.2 Structures conditionnelles


La condition sexprime de la manire suivante : if (<expression>) <instruction-1> [else <instruction-2> ] o lexcution de la branche alors ou de la branche sinon va dpendre de lvaluation de <expression> : si le rsultat est vrai, on excutera <instruction-1>, sinon on effectuera <instruction-2>. De manire tout fait classique, sil y a plusieurs instructions dans la partie alors ou la partie sinon, on mettra un bloc. Quand il y a plusieurs conditions imbriques et quil y a ambigut sur un else, on le rattache au if le plus proche. Une autre instruction conditionnelle se comporte comme un branchement calcul. Par consquent, il ne faut surtout pas oublier de mettre les break aux endroits ncessaires : switch (<expression>) { case <constante-1> : <suite dinstructions> break; case <constante-2> : <suite dinstructions> break; ... case <constante-n> : <suite dinstructions> break; default : <suite dinstructions> } Si on ne met pas de break, lexcution va continuer la suite au lieu de sortir du switch, puisque les diffrentes constantes correspondent seulement des tiquettes de branchement. Il y a parfois des cas o cest leffet souhait ; mais il faut tre prudent !

4.3 Structures itratives


Plusieurs structures itratives existent en C++. Voici la premire : while (<expression>) <instruction> la partie <instruction> pouvant bien sr tre un bloc. Cest la structure tant-que classique. Une autre structure itrative est la suivante : for (<expr1> ; <expr2> ; <expr3>) <instruction> o <expr1>, <expr2> et <expr3> sont des expressions. Souvenez-vous quune expression peut aussi tre une suite dexpressions spares par des virgules. Cest dans cette structure que cela est le plus utilis. Cette construction est quivalente : <expr1>; while (<expr2>) { <instruction>; <expr3>; } Rsumons en disant que <expr1> indique linitialisation avant entre dans la boucle, <expr2> est la condition de poursuite de litration, et <expr3> est la partie quon effectue la n de chaque itration. Une ou plusieurs de ces expressions peuvent tre vides ; en particulier : for ( ; ; ) est une boucle innie ! Une dernire variante de la structure itrative est : do <instruction> while (<expression>); 6

qui permet deffectuer linstruction (ou le bloc) une premire fois avant le premier test sur la condition darrt. Nous avons dj vu lemploi de break dans les structures conditionnelles. En fait, break permet plus gnralement de sortir prmaturment et proprement dune structure de contrle. Ainsi, on peut lutiliser galement dans une itration pour sortir sans passer par la condition darrt. Donnons en exemple une boucle qui lit un caractre en entre (par une fonction getchar()) et qui sarrte sur la lecture du caractre & : for ( ; ; ) if ((c = getchar()) == &) break; Cette fonction peut bien sr scrire plus simplement : while ((c = getchar()) != &) ; // le point-virgule ici est // linstruction vide ! Une autre instruction particulire qui peut tre utile dans les itrations est continue, qui permet de se rebrancher prmaturment en dbut ditration. Enn, signalons que C++ permet aussi de faire goto ; mais comme nous sommes des informaticiens bien levs qui ne disent jamais de gros mots, nous nen parlerons pas...

5 Fonctions et variables
Thoriquement, toute fonction retourne une valeur, qui peut tre utilise ou non. Toutefois, un type particulier, void, permet dindiquer quune fonction ne retourne pas de valeur, ou plutt que la valeur retourne ne doit pas tre prise en compte. Le passage de paramtres peut se faire par valeur ou par rfrence. Le passage dune rfrence se note par le caractre &. En voici un exemple avec une fonction qui change les valeurs de deux variables : void swap(int& a, int& b) { int tmp = a; a = b; b = tmp; } ... int x, y; ... swap(x, y); Une rfrence peut galement tre dclare constante, par exemple pour passer la rfrence dun objet de grande taille, tout en interdisant laccs en criture dans la fonction. Avec un passage par valeur, lobjet serait dupliqu dans la pile dexcution. En supposant lexistence dun type Matrice dcrivant une matrice, on peut par exemple crire : void print(const Matrice& m) { // le compilateur interdit toute tentative // de modification de la variable m dans // le corps de la fonction print } Une fonction peut tre dclare en ligne, comme dans lexemple suivant : inline int max(int x, int y) { return (x > y ? x : y); } La qualication inline indique au compilateur quil est prfrable de remplacer chaque appel la fonction par le code correspondant. Cette qualication nest quindicative, et nest en particulier pas prise en compte si elle est irralisable, cest--dire si le compilateur a besoin de connatre ladresse de la fonction. Une fonction peut galement tre surcharge ; la discrimination est alors faite sur le nombre et le type des paramtres effectifs. 7

Les variables dun programme C++ peuvent avoir plusieurs classes de stockage : automatiques : cest loption par dfaut pour toute variable interne dune fonction. Lallocation se fait dans la pile dexcution. externes ou globaux : ce sont les variables dnies lextrieur de toute fonction, et qui sont donc globales. Si on fait rfrence dans une fonction une variable dnie dans un autre module (en compilation spare), on prcisera quelle est externe par le mot-cl extern. NB : Nous dconseillons fortement lutilisation de variables externes. statiques : une variable globale statique (mot-cl static) est une variable dont le nom nest pas export ldition de liens, et qui reste donc invisible hors du module o elle est dnie. Une variable interne une fonction qui est dclare statique est une variable rmanente : sa porte de visibilit est rduite la fonction, mais elle nest initialise que la premire fois o la fonction qui la dclare est appele ; ensuite, sa valeur persiste dun appel de la fonction lautre. Le mot-cl static permet galement de dnir les variables et mthodes de classe (cf. 7.6). registres : on peut demander quune variable de type entier, caractre ou pointeur soit implante dans un registre, ce qui est souvent utile quand on veut aller vite. Les indices dans les tableaux et les pointeurs en mmoire sont souvent de bons candidats pour tre dclars comme registres. Attention : seule une variable automatique peut tre de type registre. De plus, le mot-cl register, employer dans ce cas, ne donne quune indication au compilateur ; on ne garantit pas que la variable sera bien en registre, le compilateur nayant sa disposition quun nombre limit de registres. vous de donner les indications les plus intelligentes... Les dclarations de variables peuvent en plus tre agrmentes de lun des deux mots cls suivants : const : la variable dsigne en fait une constante ; aucune modication nest autorise dans le programme. volatile : un objet dclar volatile peut tre modi par un vnement extrieur ce qui est contrl par le compilateur (exemple : variable mise jour par lhorloge systme). Cette indication donne au compilateur lui signale que toute optimisation sur lemploi de cette variable serait hasardeuse.

6 Pointeurs
Les pointeurs sont des variables contenant des adresses. Ils permettent donc de faire de ladressage indirect. Ainsi : int* px; dclare une variable px qui est un pointeur sur un entier. La variable pointe par px est note *px. Inversement, pour une variable int x; on peut accder ladresse de x par la notation &x. Ainsi, je peux crire : px = &x; ou x = *px; Voici une autre manire dcrire la fonction swap() qui change deux entiers, cette fois-ci en passant par des pointeurs : swap(int* px, int* py) { int temp; // variable temporaire temp = *px; *px = *py; *py = temp; } 8

et pour changer deux paramtres on appellera : int a,b; swap(&a,&b); Attention : un des piges les plus classiques en C++ est celui du pointeur non initialis. Le fait davoir dclar une variable de type pointeur ne suft pas pour pouvoir drfrencer ce pointeur. Encore faut-il quil pointe sur une ( case ) mmoire valide. Pour reprendre lexemple prcdent, si jcris ( ) int* px; *px = 3; jai de trs fortes chances davoir une erreur lexcution, puisque px ne dsigne pas une adresse mmoire dans laquelle jai le droit dcrire. Ce nest quaprs avoir crit par exemple px = &x; comme dans lexemple ci-dessus que linstruction *px = 3; devient valide.

6.1 Les tableaux


On dclare un tableau de la manire suivante : int a[10]; Il y a une trs forte relation entre un pointeur et un tableau. Dans lexemple prcdent, a est en fait une constante de type adresse ; en effet, a est ladresse du dbut du tableau. Par consquent, on peut crire les choses suivantes : int* pa, a[10]; pa = &a[0]; ou pa = a; Mais attention, il y a des diffrences dues au fait que a est une adresse constante alors que pa est une variable. Ainsi, on peut crire pa = a; mais il nest pas valide dcrire a = pa; Quand on veut passer un tableau en paramtre formel dune fonction, il est quivalent dcrire : void funct(int tab[]) ou void funct(int* tab) car on passe dans les deux cas une adresse. Remarque : comme en Java, les indices, qui correspondent des dplacements, commencent toujours 0. Voyons maintenant comment on peut utiliser cette quivalence entre pointeurs et tableaux pour parcourir un tableau sans recalculer systmatiquement ladresse du point courant. Le problme est de calculer la moyenne dune matrice 200 200 dentiers. int tab[200][200]; long int moyenne=0; register int* p = tab; for (register int i=0 ; i < 200 ; i++) for (register int j=0 ; j < 200 ; j++ , p++) moyenne += *p; moyenne /= 40000; 9

Remarque : on peut crire cela de manire encore plus efcace en protant du fait quon utilise p pour lincrmenter en mme temps. Par ailleurs, une seule boucle suft, et il est inutile dutiliser des compteurs : int tab[200][200]; long int moyenne=0; register int* p = tab; register int* stop = p + 200 * 200; for ( ; p < stop ; ) /*on ne fait plus p++ ici*/ moyenne += *p++; /*on accde la valeur pointe par p, puis on lincrmente*/ moyenne /= 40000; Mais attention : le programme devient ainsi peu prs illisible, et je dconseille dabuser de telles pratiques, qui ne sont justies que dans des cas extrmes, o loptimisation du code est un impratif. Notez aussi quil est exclu de raliser des ( affectations globales ) sur les tableaux, autrement que par le ( ) mcanisme des pointeurs (pas de recopie globale).

6.2 Allocation dynamique de mmoire


Lallocation et la libration dynamique de mmoire sont ralises par les oprateurs new et delete. Une expression comprenant lopration new retourne un pointeur sur lobjet allou. On crira donc par exemple : int* pi = new int; Pour allouer un tableau dynamique, on indique la taille souhaite comme suit : int* tab = new int[20]; Contrairement Java, C++ na pas de mcanisme de ramasse-miettes ; cest donc vous de librer la mmoire dynamique dont vous navez plus besoin (voir aussi la notion de destructeur pour les classes 7.1) : delete pi; delete [] tab;

6.3 Arithmtique sur les pointeurs


Comme le montre lexemple du 6.1, un certain nombre doprations arithmtiques sont possibles sur les pointeurs, en particulier lincrmentation. Tout dabord, on peut leur ajouter ou leur soustraire un entier n. Cela revient ajouter ladresse courante n fois la taille dun objet du type point. Ainsi, dans un tableau, comme nous lavons vu, linstruction p++ (qui est la mme chose que p = p+1) fait pointer p sur la case suivante dans le tableau, cest--dire que ladresse est incrmente de la taille (en octets) du type point. On peut comparer deux pointeurs avec les oprateurs relationnels. Evidemment, cela na de sens que sils pointent dans une mme zone (tableau par exemple). Enn, on peut soustraire deux pointeurs. Le rsultat est un entier indiquant le nombre de ( cases ) de la ( ) taille du type point entre les deux pointeurs. L encore, cela na de signication que si les deux pointeurs pointent dans la mme zone contigu.

6.4 Complments sur les pointeurs


On pourrait encore dire beaucoup sur les pointeurs. Nous nous contentons ici de signaler quelques points que le lecteur intress par la potique de C++ pourra approfondir dans la littrature approprie : C++ propose deux manires de reprsenter les chanes de caractres : celle hrite de C et le type string de la bibliothque standard C++. Nous vous conseillons bien entendu dutiliser ce dernier. 10

Mais comme vous risquez dtre parfois confronts des chanes de caractres ( lancienne ) (cest( ) -dire la mode C), sachez que ce sont des tableaux de caractres termins par le caractre nul (de code 0, et not comme lentier 0 ou le caractre \0). On peut bien sr utiliser des tableaux de pointeurs, des pointeurs de pointeurs, des pointeurs de pointeurs de pointeurs, etc. Bref, vous voyez ce que je veux dire... On peut mme manipuler des tableaux de fonctions, des pointeurs de fonctions, ce qui permet dappeler plusieurs fonctions diffrentes en se servant du mme pointeur.

7 Classes et instances
De manire classique, la classe regroupe des variables dinstance et des mthodes, ainsi que dventuelles variables et mthodes de classe. Contrairement Java, on distingue en C++ la dnition de la classe de sa mise en uvre. La premire regroupe la dclaration des variables et les signatures des mthodes ; elle se met dans un chier header, qui est inclus quand on veut faire rfrence linterface de cette classe dans une autre classe ou dans un programme. Dans ce chier header, on ne met a priori pas les corps des mthodes, sauf celles qui sont inline. Illustrons cela en dclarant une classe dobjets postaux, ayant quatre variables dinstance : poids, valeur, recommande et tarif : class ObjetPostal { protected: int poids; int valeur; bool recommande; public: // Variable dinstance publique -- je sais, ce nest pas bien ! int tarif; // Constructeur ObjetPostal(int p = 20); // Mthodes inline bool aValeurDeclaree() { return (valeur > 0); } int poidsObjet() { return poids; } void recommander() { recommande = true; } }; Comme en Java, les variables dinstance et les mthodes peuvent tre prives, protges ou publiques. La diffrence entre donnes protges et donnes prives est que seules les premires restent accessibles dans les sous-classes de la classe. Les trois variables poids, valeur et recommande sont protges : elles ne sont accessibles que par les mthodes dnies dans la classe ObjetPostal et dans celles de ses sousclasses ventuelles. La variable tarif est publique : elle est accessible par nimporte quelle instance de nimporte quelle classe 6 . Les mthodes dont la dnition est incluse dans la dclaration de la classe, comme aValeurDeclaree, recommander et poidsObjet, sont implantes par des fonctions inline pour un gain defcacit lexcution. Le fait quelles soient dnies lintrieur de la dclaration de classe suft les rendre inline, sans ncessit de mot cl particulier. La fonction ObjetPostal(int), de mme nom que la classe, est un constructeur de la classe. Elle est simplement dclare ici, et sera dnie ailleurs. Nous y reviendrons au 7.1. La classe ObjetPostal peut tre utilise comme un nouveau type dans le programme : ObjetPostal* z = new ObjetPostal(200); ... delete z;
6. Le fait que jai choisi de rendre cette variable dinstance publique pour les besoins de la dmonstration ne signie pas que cette pratique est recommander, loin de l. Une rgle gnrale, qui souffre trs peu dexceptions, est de toujours cacher les dtails dimplantation, donc de rendre les variables dinstance prives ou protges.

11

Attention : la variable z est ici un pointeur sur linstance, et non linstance elle-mme. Dans le corps dune mthode, les variables dinstance de la classe sont dsignes simplement par leur nom. Laccs aux variables et mthodes dautres objets se fait classiquement par la notation pointe, ou par la notation ( che ) dans le cas dun pointeur : ( ) ObjetPostal op; ... op.recommander(); ... ObjetPostal* z = new ObjetPostal(200); ... if (z->aValeurDeclaree()) { ... }

7.1 Constructeurs et destructeurs


Toute classe peut comporter une ou plusieurs fonctions publiques particulires portant le mme nom que la classe et appeles les constructeurs. Elles prcisent comment doit tre cre ou plutt initialise une instance de la classe, en donnant en particulier les valeurs initiales de certaines variables dinstance. Revenons sur le constructeur ObjetPostal dclar prcdemment dans la classe de mme nom. Dans le cas prsent, seule cette fonction est dnie hors du chier header, dans le chier de dnition qui porte le nom de la classe et typiquement le sufxe .C ou .cpp : #include <ObjetPostal.h> // inclusion de la dclaration

ObjetPostal::ObjetPostal(int p) { poids = p; valeur = 0; recommande = false; } noter que dans la dclaration de la classe, le paramtre p a la valeur par dfaut 20 ; lappel du constructeur sans paramtre est donc quivalent son appel avec la valeur 20. noter aussi lutilisation de loprateur de rsolution de porte::, ncessaire ds quon nest plus ( dans ) la dnition de la classe, pour rattacher ( ) la fonction sa classe dappartenance. En fait, il nest jamais ncessaire dappeler explicitement un constructeur pour crer une instance. Cest le compilateur qui se charge de choisir le constructeur utiliser, en fonction des paramtres dinstanciation. Si aucun constructeur ne sapplique, un constructeur par dfaut est appel, qui initialise les variables des valeurs nulles. Il est cependant fortement recommand de toujours prvoir un constructeur, en tout cas ds que la classe nest pas triviale. Conformment ce qui vient dtre dit, la dclaration : ObjetPostal x; dans une mthode ou un programme, cre un objet postal dont le poids est de 20 (valeur par dfaut). En revanche, la dclaration ObjetPostal x(140); cre une instance de la classe ObjetPostal de poids 140 grammes. En fait, un constructeur comme ce dernier, avec un seul paramtre, tient lieu de fonction de conversion implicite de type. Par exemple, la dclaration suivante est valide : ObjetPostal x = 30; Elle est traduite par lapplication de la fonction de conversion dun entier en objet postal, quivalente la dclaration suivante : ObjetPostal x(30); Ce mcanisme de conversion implicite reste nanmoins limit aux constructeurs ayant un seul argument, ou pour lesquels les autres arguments ont tous des valeurs par dfaut. La place mmoire occupe par une instance locale est automatiquement restitue quand la variable qui la dsigne cesse dexister, cest--dire la sortie du bloc de programme dans lequel la variable est dnie. 12

Cependant, il arrive quun constructeur effectue une allocation dynamique de mmoire, typiquement pour une des variables dinstance. Pour restituer la place ainsi alloue quand lobjet doit disparatre, il faut dnir un destructeur, dclar comme une fonction portant le nom de la classe prcd du caractre ~. Ce destructeur est appel automatiquement quand lobjet cesse dexister. Supposons par exemple quun sac postal est caractris par une capacit maximale, un nombre dobjets contenus et un tableau dobjets postaux dont la taille est xe dynamiquement. La place ncessaire pour ce tableau tant alloue par le constructeur, elle doit tre restitue par un destructeur : // Dans le fichier SacPostal.h class SacPostal { private: int nbelts; // nombre dobjets dans le sac int capacite; // capacit du sac ObjetPostal* sac; // le tableau reprsentant le sac public: SacPostal(int); // le constructeur ~SacPostal(); // le destructeur // et les autres methodes... }; // Dans le fichier SacPostal.cpp SacPostal::SacPostal(int cap) { capacite = cap; nbelts = 0; // sac vide sac = new ObjetPostal[cap]; // allocation du tableau } SacPostal::~SacPostal() { delete [] sac; // restitution de la place // occupe par le tableau sac } La dclaration dune variable courrierDeLyon de type SacPostal peut se faire comme suit : SacPostal courrierDeLyon(250); Le constructeur SacPostal::SacPostal(int) est automatiquement appel et un tableau de 250 objets postaux est allou dynamiquement. Le compilateur engendre aussi un appel automatique au destructeur SacPostal::~SacPostal() quand la variable courrierDeLyon cesse dexister, cest--dire pour lexemple donn la sortie du bloc dans lequel elle est dnie. Les constructeurs et destructeurs peuvent aussi tre appels explicitement, lorsquon fait de lallocation dynamique de mmoire, comme dans lexemple suivant : SacPostal* ps = new SacPostal(55); ... delete ps; // constructeur appel // destructeur appel

7.2 Les amis


Avec la notion damis, C++ donne le moyen dafner plus nement le contrle des droits daccs que par les simples notions de variables publiques ou prives. Par exemple, si la classe SacPostal est dclare amie de la classe ObjetPostal, toutes ses instances sont autorises accder aux variables prives dObjetPostal : class ObjetPostal { friend class SacPostal; ... 13

}; Cette ( amiti ) peut tre plus slective et se limiter une ou plusieurs fonctions prcises. Supposons quen ( ) fait seule la mthode affranchir de la classe SacPostal ait besoin daccder aux champs privs de ObjetPostal. Seule cette mthode est alors dclare amie, la place de la classe : class ObjetPostal { friend void SacPostal::affranchir(); ... }; ... // Et dans la dfinition de la classe SacPostal SacPostal::affranchir() { ObjetPostal* x; ... if (x->poids < 20) // Laccs poids est autoris car la x->tarif = 2; ... // mthode est amie de la classe ObjetPostal } Associes aux notions de donnes publiques, protges et prives, les classes et les fonctions amies permettent de contrler de manire ne les protections et les accs aux variables dinstance.

7.3 Lhritage
C++ permet de raliser de lhritage multiple entre classes ; nous nous limiterons cependant dans ce polycopi lexpos de lhritage simple. Une sous-classe, appele classe drive, hrite classiquement des attributs de sa superclasse : class Colis : public ObjetPostal { protected: int volume; }; class Lettre : public ObjetPostal { protected: bool urgent; }; class CourrierInterne : public Lettre { public: CourrierInterne(int p) : (p) { tarif = 0; } }; Lors de la cration dune instance dune classe donne, tous les constructeurs de la hirarchie dhritage de la classe sont automatiquement activs, du plus gnral au plus particulier. Ainsi, la dnition du constructeur de CourrierInterne indique les valeurs donner aux paramtres du constructeur de la superclasse, savoir celui dObjetPostal. Grce lexpression : (p) qui suit la dnition du constructeur CourrierInterne(int), le constructeur ObjetPostal(int) est donc appel avec la valeur de p avant la mise zro du champ tarif. Ce mcanisme est similaire lemploi de super en Java. Dans une classe, les attributs hrits deviennent privs par dfaut, mme sils taient publics dans la superclasse. Toutes les autres classes, y compris ses sous-classes, ne peuvent y accder directement. Cependant les attributs publics hrits restent publics si lhritage est dit public grce au mot cl public, comme dans les exemples prcdents. En pratique, lhritage est public dans la grande majorit des cas, et ce nest que lorsquon souhaite hriter de limplantation tout en masquant linterface de la classe quon fait de lhritage ( priv ) Pensez donc mettre le mot cl public ! ( ). 14

7.4 Liaison dynamique


En C++, la liaison dynamique nest pas systmatique, contrairement Java. Pour assurer cette liaison dynamique quand elle est souhaite, on utilise le mcanisme des fonctions virtuelles. Ainsi, pour que la mthode affranchir de la classe ObjetPostal puisse tre rednie dans les sous-classes et invoque uniformment et dynamiquement sur une collection dobjets postaux divers, instances de ces diffrentes sous-classes, elle doit tre dclare comme virtuelle (mot-cl virtual) dans la classe ObjetPostal : class ObjetPostal { friend void SacPostal::affranchir(); protected: int poids; int valeur; bool recommande; public: int tarif; // Constructeur ObjetPostal(int p = 20); // Destructeur virtuel -- voir ci-aprs virtual ~ObjetPostal() {} // rien de spcial faire ici // Mthodes inline bool aValeurDeclaree() { return (valeur > 0); } int poidsObjet() { return poids; } void recommander() { recommande = true; } // Mthode affranchir, implantation par dfaut // Sera redfinie dans les sous-classes virtual void affranchir() { tarif = 0; } ... }; Alternativement, on peut dcider de ne pas donner dimplantation par dfaut la mthode affranchir, en crivant : class ObjetPostal { ... virtual void affranchir() = 0; Cela fait de la classe ObjetPostal une classe abstraite, qui ne peut tre instancie. Pour tre instanciables, ses sous-classes doivent obligatoirement dnir une mthode affranchir. Il est utile de savoir quun destructeur peut aussi tre dclar virtuel. Supposons que les classes Colis, Lettre et CourrierInterne soient munies de constructeurs et de destructeurs spciques. Si une instance de SacPostal peut contenir des objets postaux de toutes sortes, le destructeur de la classe SacPostal doit appeler un destructeur spcique pour chaque objet contenu dans le sac. La faon de faire la plus lgante consiste dclarer virtuel le destructeur de la classe ObjetPostal dans la dnition de la classe, comme dans lexemple ci-dessus. La fonction SacPostal::~SacPostal() scrit alors : SacPostal::~SacPostal() { delete [] sac; } ce qui a pour effet dappeler successivement le destructeur de chaque lment du sac. Le destructeur de la classe ObjetPostal tant virtuel, cest bien le destructeur spcique chaque objet du sac qui est appel par linstruction delete.

7.5 Laccs la supermthode


Laccs une mthode masque peut se faire en C++ par un appel direct cette mthode, grce loprateur de rsolution de porte. Si par exemple laffranchissement dun courrier par avion est le mme que celui 15

dune lettre, augment de quelques oprations spciques, on peut crire : class Lettre : public ObjetPostal { protected: bool urgent; public: void affranchir() { tarif = 2 + (urgent ? 1 : 0); } }; class ParAvion : public Lettre { public: void affranchir(); }; void ParAvion::affranchir() { // affranchissement ordinaire Lettre::affranchir(); // 7F de supplement pour courrier aerien tarif += 7; }

7.6 Variables de classe


Les variables de classe sont dclares avec le mot-cl static. Par exemple, la classe Lettre peut tre munie de la variable de classe tarifLettre, indiquant le tarif daffranchissement par dfaut : class Lettre : public ObjetPostal { protected: bool urgent; public: static int tarifLettre; }; ... // Dans la dfinition de la classe Lettre (Lettre.cpp par exemple) // Dclatation et initialisation de la variable de classe int Lettre::tarifLettre = 3;

8 La surcharge doprateurs
C++ autorise la surcharge des oprateurs. Par exemple, dnissons un oprateur + permettant dajouter le contenu de deux sacs postaux dans un nouveau sac. Comme cet oprateur doit accder aux champs privs de ses oprandes, il est dclar ami de la classe SacPostal 7 : class SacPostal { private: int nbelts; int capacite; ObjetPostal* sac; public: SacPostal(int); ~SacPostal(); friend SacPostal operator+(SacPostal&, SacPostal&); };
7. On aurait aussi pu dnir loprateur + dans la classe SacPostal.

16

... SacPostal operator+(SacPostal& sac1, SacPostal& sac2) { // cration dun gros sac de capacit ad hoc SacPostal grosSac(sac1.capacite + sac2.capacite); // nombre dlments de ce gros sac grosSac.nbelts = sac1.nbelts + sac2.nbelts; // mettre les lments de sac1 dans grosSac int i = 0; for ( ; i < sac1.nbelts ; i++) grosSac.sac[i] = sac1.sac[i]; // puis mettre les lments de sac2 dans grosSac for (register int j = 0 ; j < sac2.nbelts ; i++, j++) grosSac.sac[i] = sac2.sac[j]; return grosSac; } Le nouvel oprateur semploie ensuite de manire transparente sur les instances de la classe SacPostal : SacPostal sacSeichamps = 200, sacVandoeuvre = 1500; ... SacPostal sacNancy = sacSeichamps + sacVandoeuvre; ...

9 La bibliothque dentres/sorties
Nous ne prtendons pas couvrir dans ce polycopi les trs nombreuses fonctionnalits couvertes par la bibliothque standard C++. Cependant, il nous semble utile de donner quelques indications sur les entres/sorties. Pour utiliser la bibliothque, il faut inclure son chier de dclarations : #include <iostream> Les oprations standards dentre et de sortie sont fournies par trois ots (streams), dsigns par les variables suivantes : cin dsigne le ot dentre standard (typiquement, votre clavier), cout dsigne le ot de sortie standard (typiquement, la fentre dexcution sur votre cran), cerr dsigne le ot standard des messages derreur. Les oprateurs << et >> sont rednis pour permettre des critures et lectures aises : #include <iostream> #include <string> cout << "Bonjour, comment vous appelez-vous ? "; string nom; cin >> nom; if (nom.string_empty()) { cerr << "erreur : nom vide" << endl; } else { cout << nom << ", donnez-moi maintenant votre ge : "; int age; cin >> age; cout << "Ouah, vous ntes plus tout jeune !" << endl; } 17

On notera au passage lemploi de string, la bibliothque de manipulation de chanes de caractres C++, et de la constante endl, qui indique le passage la ligne. Bien entendu, la bibliothque iostream fournit de nombreuses autres fonctionnalits dentre/sortie, et la bibliothque fstream fournit les fonctionnalits de manipulation de chiers. Nous nous sommes contents ici de donner quelques rudiments vous permettant dcrire vos tout premiers programmes...

10 Pour en savoir plus


Ce polycopi ne fournit quune introduction trs succincte au langage C++. Nous avons volontairement pass sous silence un grand nombre de caractristiques, dont notamment lhritage multiple, la gestion des exceptions, les namespaces, les nouveaux mcanismes de conversion, lidentication dynamique de type, et la riche bibliothque standard de C++, avec notamment les chanes de caractres. Les notions de templates et dalgorithmes gnriques font lobjet dun cours spar. Pour approfondir vos connaissances, nous vous conseillons la lecture dun des bons ouvrages consacrs C++, et nous recommandons tout particulirement les deux livres suivants : Bjarne Stroustrup. The C++ programming Language, 3rd Edition. Addison-Wesley, 1997. La rfrence de base, par lauteur du langage. Stanley B. Lippman, Jose Lajoie. C++ Primer, 3rd Edition. Addison-Wesley, 1998. Trs complet (1200 pages), un des meilleurs livres pour apprendre C++, mon avis. Une traduction franaise existe, mais pour linstant, ma connaissance, elle nest disponible que pour la 2e dition, moins complte et pas jour par rapport la norme.

18

You might also like