Sunteți pe pagina 1din 205

C++

Les fondamentaux du langage [Nouvelle dition]

BriceArnaudGURIN

Rsum
Ce livre sadresse tout dveloppeur dsireux dapprendre le langage C++, dans le cadre de ses tudes ou pour consolider son exprience professionnelle. Le premier chapitre prsente les bases de la syntaxe du langage ainsi que lorganisation des programmes. Le chapitre suivant est une transition vers C++, il explicite les notions cls pour crer ses premires applications : structures, pointeurs, bibliothques standard Le troisime chapitre dtaille la programmation oriente objets et les mcanismes spcifiques au langage (hritage, modles de classes). Vient ensuite ltude de la STL (Standard Template Library), prsente travers ses mcanismes les plus importants : les chanes, les structures de donnes et les algorithmes. Le chapitre 5 ouvre C++ sur ses univers, le framework MFC et lenvironnement .NET C++ CLI. Comme illustration des capacits de C++ crer tout type dapplications, louvrage propose un exemple complet de tableur graphique ou encore un grapheur 3D. Louvrage se termine par un chapitre consacr loptimisation et aux mthodes de conception oriente objet (UML). Le code source des exemples du livre est disponible en tlchargement sur www.editions-eni.fr. Les chapitres du livre : Avant-propos - Introduction - De C C++ - Programmation oriente objet - La bibliothque Standard Template Library - Les univers de C++ - Des programmes C++ efficaces

L'auteur
Ingnieur ESIEA, Brice-Arnaud GURIN est responsable des dveloppements logiciels chez LexisNexis, acteur majeur dans l'information juridique, conomique et financire, et les solutions de gestion pour les professionnels. Ses comptences en dveloppement, son dsir de partager ses connaissances l'ont naturellement conduit l'criture d'ouvrages consacrs la ralisation d'applications (.NET, PHP, C++) et la conduite de projets.

Ce livre numrique a t conu et est diffus dans le respect des droits dauteur. Toutes les marques cites ont t dposes par leur diteur respectif. La loi du 11 Mars 1957 nautorisant aux termes des alinas 2 et 3 de larticle 41, dune part, que les copies ou reproductions strictement rserves lusage priv du copiste et non destines une utilisation collective, et, dautre part, que les analyses et les courtes citations dans un but dexemple et dillustration, toute reprsentation ou reproduction intgrale, ou partielle, faite sans le consentement de lauteur ou de ses ayants droit ou ayant cause, est illicite (alina 1er de larticle 40). Cette reprsentation ou reproduction, par quelque procd que ce soit, constituerait donc une contrefaon sanctionne par les articles 425 et suivants du Code Pnal. Copyright Editions ENI Ce livre numrique intgre plusieurs mesures de protection dont un marquage li votre identifiant visible sur les principales images.

- 1-

Classesetinstances
Lobjectif poursuivi par Bjarne Stroustrup tait, rappelonsle, dimplmenter sur un compilateur C les classes dcrites par le langage Simula. Ces deux derniers langages tant radicalement opposs dans leur approche, il fallaitidentifierunedoublecontinuit,notammentductduC. Ilfutaisderemarquerquelaprogrammationseraitgrandementsimplifiesicertainesfonctionspouvaientmigrer lintrieur des structures du C. De ce fait, il ny aurait plus de structure passer ces fonctions puisquelles sappliqueraientvidemmentauxchampsdelastructure. Toutefois,ilfallaitconserverunmoyendedistinguerdeuxinstancesetcestpourcetteraisonquelonamodifila syntaxedeloprateurpoint : Programmationfonctionnelle Programmationorienteobjet

struct Point { int x,y ; } ; void afficher(Point p) { printf("%d,%d\n",p.x,p.y); }

struct Point { int x,y; void afficher() { printf("%d,%d\n",x,y); } } ;

Point p1; afficher(p1);

Point p1; p1.afficher();

Cette diffrence dapproche a plusieurs consquences positives pour la programmation. Pour commencer, le programmeur na plus effectuer un choix parmi les diffrents modes de passage de la structure la fonction afficher(). Ensuite, nous allons pouvoir oprer une distinction entre les lments (champs, fonctions) de premierplanetdesecondplan.Ceuxdepremier planserontvisibles,accessibleslextrieurdelastructure.Les autresserontcachs,inaccessibles. Ce procd garantit une grande indpendance dans limplmentation dun concept, ce qui induit galement une bonnestabilitdesdveloppements.

1.Dfinitiondeclasse
Uneclasseestdoncunestructurepossdantlafoisdeschampsetdesfonctions.Lorsquelesfonctionssont considreslintrieurduneclasse,ellesreoivent lenomdemthodes. Lensembledeschampsetdesmthodesestdsignsousletermedemembres.Nousnerecommandonspasla dsignationdeschampslaidedutermeattribut,carilpeutprendreunsenstrsparticulierenlangageC++ managouenlangageC#. Pour le lecteur qui passe de Java C++, il faut faire attention terminer la dclaration dune classe par le caractrepointvirgule,uneclassetantlacontinuit duconceptdestructure : class Point { int x,y ; // deux champs // une mthode void afficher() { printf("%d,%d\t",x,y);

- 1-

} } ; // point-virgule Laclassesuitglobalementlesmmesrglesquelastructurepourcequiestdesonutilisation:instanciation/ allocation,copie,passageunefonction...

a.Lesmodificateursdaccs
Nousallonsprsentoprerunedistinctionentrelesmthodes(fonctions)accessiblesdepuislextrieurdela classeetcellesquinontquuneutilitalgorithmique.Demme,certainschampssontexposslextrieurde laclasse,leuraccsenlectureetenmodificationestautoris,alorsquedautresdoiventtreprotgscontre desaccsintempestifs. Cettedistinctionlaisseunegrandelatitudedanslorganisationduneclasse,lapartiecachepouvantvoluer sansrisquederemettreenquestionleresteduprogramme,lapartieaccessibletantaucontraireconsidre commestable. LelangageC++offreplusieursniveauxdaccessibilitetlapalettedepossibilitsestassezlarge,sibienque certainslangagesquiluisuccdentdanslordredespublicationsnelesretiennentpastous. Modificateur Accessibilit Complte. Le membre champ ou mthode est visible lintrieur de la classecommelextrieur. Trsrestreinte.Lemembrenestaccessiblequlintrieurdelaclasse. Restreintelaclassecouranteetauxclassesdrives. Restreinteunelistedefonctionsidentifiescommetantamies.

public private protected friend

Donnons maintenant un exemple de visibilit. Sachant que la visibilit par dfaut dans une classe est private,nousallonschoisirpourchaquemembreunmodificateurdaccs:

class Point { private: int x,y; public: void afficher() { printf("%d,%d\t",x,y); } void positionner(int X,int Y) { x=X; y=Y; } int couleur; } ;

Openmirrors.com

Avantdutilisercetteclasse,dressonsuntableaudesvisibilitspourchaquemembredelaclassePoint: x y afficher positionner private(cach) private(cach) public public

- 2-

Openmirrors.com
couleur public

Leschamps xet ysontbienprsentspourchaqueinstance,maisilsnesontlisibles/modifiablesquepardes mthodesappartenantlaclasse,comme afficher() etpositionner().Lesautresfonctions,mmesi elles appartiennent des classes, nont pas daccs direct xet y. Elles doivent passer par des mthodes publiquesdelaclassePoint. Lechampcouleurestluipublic,cequisignifiequilestcompltementexpos. Essayonsmaintenantdappliquercesrglesdevisibilitlaclassesusdcrite. Point p; p.x=3; // erreur, x est priv p.y=2; // erreur, y est priv p.positionner(89,2); // ok, positionner() est publique p.afficher(); // ok, afficher() est publique p.couleur=0x00FF00FF; // ok, couleur est public Lorsquelescommentairesindiquenterreur,celasignifiequelecompilateurrelvera lalignecommentantpas valide. Le message circonstanciel peut avoir la forme "champ inaccessible" ou "champ priv", voire "champ invisible".Parfoislecompilateurestplusexpliciteenindiquantquelaporteprive(private) dunmembrene convientpaslusagequelonenfait. Pour dcouvrir des exemples de champs protected ou de fonctions amies, nous devrons attendre encore quelquespages. Pour le programmeur qui dbute, le choix dune porte nest pas facile oprer. Et il faut viter de suivre le conseil suivant qui se rvle trop strotyp, un peu grossier : tous les champs sont privs ( private) et toutes les mthodes sont publiques. Cette rgle peut convenir dans certains cas, mais sans doute pas dans tous les cas de figure. Dabord, elle favorise trop la notion dinterface au dtriment de lalgorithmie (la programmationfonctionnelle).Oruneclassenesersumepasuneinterface,sansquoilencapsulation la runiondunestructureetdefonctionsnauraitaucunintrt.Lesdtailsdimplmentationontbesoindtre cachspourvoluerlibrementsansremettreenquestionleresteduprogramme,fortbien.Cequinousconduit dclarercertainesmthodes avecunniveaudevisibilitinfrieurpublic.Demme,certainschampsontun typage particulirement stable, et si aucun contrle nest ncessaire quant leur affectation, il ny a aucune raisondelesdclarerdemanire prive. Bienquelechoixdunevisibilitpuissetredcidparleconcepteurquisaidera dunsystmedemodlisation commeUML,leprogrammeurquiincomberait cetteresponsabilitpeutsaiderdutableausuivantpourdcider quellevisibilitchoisir.Enlabsencededrivation(hritage),lenombredecasdefigureestassezlimit,etil faut bien reconnatre que les programmes contenant un nombre lev de drivations ne sont pas lgion, surtoutsanslemploidUML. champalgorithmique champdestructure champenlectureseule

privateouprotected public private ou protected getXXX()publique private ou protected setXXX()publique


avec une mthode

champencritureseule

avec une mthode

champenlectureetcritureaveccontrledes accs champ"constante"declasse

private ou protected, avec deux mthodes publiquesgetXXX()etsetXXX()


champ public, statique et sans doute dclar const

mthode caractrisant accessiblesunobjet

les

oprations

Publique. La mthode fait alors partie de linterface


- 3-

Mthodedestineporterlalgorithmie

privateouprotected

Nousconstatonsdanscetableauquelesfonctionsamiesnyfigurentpas.IlsagitdunconceptpropreC++, donctrspeuportable,quiasurtoutdelutilitpourlasurchargedesoprateurslorsquelepremierargument nestpasdutypedelaclasse(reportezvouslapartieAutresaspectsdelaPOOSurchagesdoprateurssur lasurchargepourdautresdtails). galement,ilestfaitplusieursfoismentionduterme"interface".Uneinterfaceestuneclassequinepeuttre instancie. Cest un concept. Il y a deux faons denvisager linterface, selon quon la dduit dune classe ordinaireoubienquelonconstruituneclasseordinairepartirduneinterface.Danslepremiercas,ondduit linterfaceduneclasseencrantunelisteconstituedesmthodespubliquesdelaclasse.Leconceptestbti partirdelaralisationconcrte.Danslesecondcas,oncreuneclassedrivant(hritant)delinterface.Le langage C++ na pas de terme spcifique pour dsigner les interfaces, bien quil connaisse les classes abstraites. On se reportera la section Hritage Mthodes virtuelles et mthodes virtuelles pures sur les mthodesvirtuellespurespourterminerltudedesinterfaces. Quoi quil en soit, cest une habitude en programmation oriente objet (POO) de dsigner lensemble des mthodespubliquesduneclassesousletermedinterface.

b.Organisationdelaprogrammationdesclasses
IlexisteavecC++uneorganisationparticuliredelaprogrammationdesclasses.Letypeestdfinidansun fichierdentte.h,commepourlesstructures,alorsquelimplmentationestgnralementdportedansun fichier source .cpp. Nous nous rendrons compte par la suite, en tudiant les modules, que les notations correspondantesrestenttrscohrentes. En reprenant la classe Point, nous obtiendrons deux fichiers, point.h et point.cpp. la diffrence du langageJava,lenomdefichiernadimportancequedanslesinclusionsetlesmakefiles.Commeilesttoujours explicitparleprogrammeur,aucunerglesyntaxiquenimposedenomsdefichierpouruneclasse.Toutefois, parsouciderigueuretdecohrence,ilestdusagedenommerfichier dentteetfichiersourcepartirdunom de la classe, en sefforant de ne dfinir quune classe par fichier. Donc si la classe PointColore vient complternotreprogramme,elleseradclaredansPointColore.hetdfiniedansPointColore.cpp. VoicipourcommencerladclarationdelaclassePointdanslefichierpoint.h:

class Point { private: int x,y; public: void afficher(); void positionner(int X,int Y); int couleur; } ; Nousremarquonsquelesmthodessontuniquementdclareslaidedeprototypes(signaturecloseparun pointvirgule). Cela suffit aux autres fonctions pour les invoquer, peu importe o elles sont rellement implmentes. Ensuite,nousimplmentons(dfinissons)laclassePointdanslefichierpoint.cpp:

void Point::afficher() { printf("%d,%d\t",x,y); } void Point::positionner(int X,int Y) { x=X; y=Y;

- 4-

} Danscettecriture,ilfautcomprendrequelidentifiantdelafonctionafficher(...) serapportelaclasse Point(sonespacedenoms,enfait,maiscelarevientaumme).Cestlammetechniquepourlamthode positionner(). Nous avions dj rencontr loprateur de rsolution de porte :: lorsque nous voulions atteindre une variableglobalemasqueparunevariablelocalelintrieurdunefonction.Envoiluneautreutilisationetce nestpasladernire. Denosjours,lescompilateursC++sonttrsrapidesetilnyaplusdediffrencedeperformancesutiliserla dfinitioncompltedelaclasselorsdeladclarationoubienladfinitiondportedansunfichier .cpp.Les dveloppeurs Java prfreront vraisemblablement la premire version qui concide avec leur manire dcrire une classe, mais lapproche dporte du C++, en plus dtre standard, offre comme avantage une lisibilit accrue si votre classe est assez longue. En effet, seule la connaissance des champs et de la signature des mthodesestimportantepourutiliseruneclasse,alorsquelimplmentationestlachargedeceluiquiacr laclasse.

2.Instanciation
Sil est une rgle imprative en science des algorithmes, cest bien celle des valeurs consistantes pour les variables.Ainsilcrituresuivanteestuneviolationdecettergle: int x,y; y = 2*x; Lavariablexntantpasinitialise,ilnestpaspossibledeprdirelavaleurdey.Bienquecertainscompilateurs initialisent les variables 0, ce nest ni une rgle syntaxique, ni trs rigoureux. Donc, chaque variable doit recevoirunevaleur avantdtreutilise,etsipossibledssacration. Dailleurs, certains langages commeJava ou C# interdisent lemploi du fragment de code cidessus, soulevant uneerreurbloquanteetinterrompantleprocessusdecompilation. Quesepassetilprsentlorsquuneinstancedeclasse(structure)estcre?Unjeudevariablestoutneuf est disponible, nattendant plus quune mthode vienne les affecter, ou bien les lire. Et cest cette dernire ventualitquiposeproblme.Ainsi,considronslaclassePointetlinstanciationdunnouveaupoint,puisson affichage. Nous employons linstanciation avec loprateur new car elle est plus explicite que linstanciation automatiquesurlapile. Point*p; // un point qui nexiste pas encore p=new Point; // instanciation p->afficher(); // affichage erron, trop prcoce Enprincipe,lamthode afficher()comptesurdesvaleursconsistantespourleschampsdecoordonnesx et y. Mais ces champs nont jusqu prsent pas t initialiss. La mthode afficher() affichera nimporte quellesvaleurs, violantnouveaularglenoncecidessus. Comment alors initialiser au plus tt les champs dune nouvelle instance pour viter au programmeur une utilisationinopportunedesesclasses?Toutsimplementenfaisantconciderlinstanciationetlinitialisation.Pour arriver ce rsultat, on utilise un constructeur, mthode spciale destine initialiser tous les champs de la nouvelleinstance. Nous complterons ltude des constructeurs au chapitre suivant et en terminons avec linstanciation, qui se rvleplusvoluequepourlesstructures. Pourcequiestdelasyntaxe,laclasseconstituantunprolongementdesstructures,cesdeuxentitspartagent lesmmesmcanismes:
q

rservationparmalloc()

- 5-

Openmirrors.com
q q

instanciationautomatique,surlapile instanciationlademandeavecloprateurnew.

Pourlesraisonsquionttvoquesprcdemment,larservationdemmoireaveclafonctionmalloc()est proscrire,carelleninvoquepasleconstructeur.Lesdeuxautresmodes,automatiqueetparloprateur new sonteux,parfaitement applicablesauxclasses. Point p, m; // deux objets instancis sur la pile Point* t = new Point; // un objet instanci la demande

3.Constructeuretdestructeur
Maintenantquenousconnaissonsmieuxlemcanismedelinstanciationdesclasses,nousdevonsdfinirunou plusieurs constructeurs. Il est en effet assez frquent de trouver plusieurs constructeurs, distingus par leurs paramtres(signature),entranantlinitialisationdesnouvellesinstancesenfonctiondesituationsvaries.Silon considre la classe chaine, il est possible de prvoir un constructeur dit par dfaut, cestdire ne prenant aucunargument,etunautreconstructeurrecevantunentierspcifiantlatailledutamponallouerpourcette chane.

a.Constructeur
Vous laurez compris, le constructeur est une mthode puisquil sagit dun groupe dinstructions, nomm, utilisantunelistedeparamtres.Quellessontlescaractristiquesquiledistinguentdunemthodeordinaire? Toutdabordletypederetour,carleconstructeurneretournerien,pasmme void.Ensuite, leconstructeur portelenomdelaclasse.CommeC++estsensiblelacasseildiffrencielesmajusculesetlesminusculesil fautfaireattentionnommerleconstructeurPoint()pourlaclassePointetnonpoint()ouPaint(). Comme toute mthode, un constructeur peut tre dclar dans le corps de la classe et dfini de manire dporte,enutilisantloprateurdersolutiondeporte. class Chaine { private: char*buffer; int t_buf; int longueur; public: // un constructeur par dfaut Chaine() { t_buf = 100; buffer = new char[t_buf]; longueur = 0; } // un autre constructeur, dfini hors de la classe Chaine(int taille); } ; Chaine::Chaine (int taille) { t_buf = taille; buffer = new char[t_buf]; longueur = 0; } Vous aurez bien not le type de retour du constructeur : aucun type nest spcifi. Le choix dune implmentation au sein mme de la classe ou alors hors delle est le vtre, il suit la mme logique que celle applicableauxmthodes ordinaires.
- 6-

Openmirrors.com

Openmirrors.com
b.Lepointeurthis
Il nest pas rare quun constructeur reoive un paramtre rpercuter directement sur un champ. Dans lexemple prcdent, largument taille a servi initialiser le champ t_buf. Pourquoi alors sembarrasserdun nomdiffrentsilesdeuxvariablesdsignentlammequantit? causeduphnomnedemasquage,rpondrezvousjustetitre: Chaine::Chaine (int t_buf) { t_buf = t_buf; // erreur buffer = new char[t_buf]; longueur = 0; } La ligne normalement charge dinitialiser le champ

t_buf affecte largument t_buf en lui attribuant sa

proprevaleur.Laffairesecompliquelorsquonapprend queloprateurdersolutiondeportenepermetpas de rsoudre lambigut daccs une variable de plus haut niveau comme nous lavons fait pour le cas des variables globales. Il faut alors recourir au pointeur this, qui dsigne toujours ladresse de linstance courante: Chaine::Chaine (int t_buf) { this->t_buf = t_buf; // correct buffer=new char[t_buf]; longueur=0; } Lepointeur thisesttoujours Type*, o Typereprsentelenomdelaclasse.Dansnotreconstructeurde classeChaine,thisestunpointeursurChaine,soitunChaine*. Signalons au passage que this peut atteindre nimporte quelle variable de la classe, quelle que soit sa visibilit, mais pas les champs hrits de classes suprieures lorsquils sont privs. Par ailleurs, il est inutile dutiliserthislorsquilnyapasdquivoqueentreunparamtre,unevariablelocaleetunchampdelaclasse. Enfin, this peut tre utilis pour transmettre ladresse de lobjet courant une fonction ou une mthode. Commeapplicationtrsindirecte,onpeutdemander auconstructeurdafficherladressedunouvelobjet: printf("Adresse %x\n",(int) this);

c.Destructeur
Puisqueleconstructeuraunrledinitialisation,ilsemblaitlogiquedeprvoirunemthodechargederendre desressourcesconsommesparunobjetavantsadestruction. Rappelonsqueladestructiondelobjetintervientsoitlademandeducompilateur,lorsqueleflotdexcution quitte la porte dune fonction ou dun bloc ayant allou automatiquement des objets, soit lorsque le programmereoitlordrededestructionaumoyendeloprateurdelete.

void destruction() { Point* p; Point m; // instanciation automatique p = new Point; // instanciation manuelle printf("deux objets en cours"); delete p; // destruction de p return; // destruction de m implicite }

- 7-

Openmirrors.com
Le destructeur ne reoit jamais darguments, ce qui est tout fait normal. Comme il peut tre invoqu automatiquement,quellesvaleurslecompilateurfourniraitilcettemthode? Comme le constructeur, il ne renvoie rien, pas mme

void et porte le nom de la classe. En fait, il est

reconnaissableparlaprsencedusymbole~(tilde)plac avantsonnom: ~Chaine() { delete buffer; } Commepourleconstructeur,lasyntaxenexigepasdedoterchaqueclassedundestructeur.Saprsenceest dailleursliedventuellesrservationsdemmoireouderessourcessystmemaintenuesparlobjet. Laprsenceduconstructeurnestpasnonplusimposeparlasyntaxe,maisnousavonsvuquesonabsence conduisaitunnonsensalgorithmique. Enfin, comme toute mthode, le destructeur peut tre dfini dans le corps de la classe ou bien de manire dporte,lasyntaxeressemblantalorscela: Chaine::~Chaine() { delete buffer; }

d.Destructeurvirtuel
Bienquenousneconnaissionspasencoreladrivation(etlhritage),nilesmthodesvirtuelles,vousdevez savoirquelesdestructeursonttoutintrttrevirtuelssilaclasseesthrite.Sanstropanticipersurnotre tude,prenezencomptelefaitquelesconstructeurssonttoujoursvirtuels.Nousillustreronsleconceptdes mthodesvirtuellesetdesdestructeursvirtuelsunpeuplusloindanscechapitre.Entretemps,voicilasyntaxe dedclarationdunteldestructeur: virtual ~Chaine() { delete buffer; } Certains environnements de dveloppement comme Visual C++ (Microsoft) dclarent systmatiquement un constructeuretundestructeurvirtuelpouruneclasse.

4.Allocationdynamique
Intressonsnousmaintenantcetypedeprogrammationappelprogrammationdynamique.Commelesclasses constituentunprolongementdesstructures,ilesttoutfaitpossiblededfinirunpointeurdetype Classe* pourconstruiredesstructuresdynamiques,tellesleslistes,lesarbresetlesgraphes.Nousvousproposonsun exempledeclasseListepourillustrerceprincipeenmmetempsquelesmthodesstatiques. Dautrepart,vousdevezfaireattentionlorsquevousallouezuntableaudobjets. Imaginonslescnariosuivant: Chaine*chaines; chaines=new Chaine[10]; Quelestlefonctionnementattenduparloprateur new?Allouer10foislatailledelaclasse Chaine?Doitil aussiappelerchaqueconstructeurpourchacunedes10chanesnouvellementcres? Pourdterminersoncomportement,ajoutonsunmessagedanslecorpsduconstructeur:

- 8-

class Chaine { private: char* buffer; int t_buf; int longueur; public: // un constructeur par dfaut Chaine() { printf("Constructeur par dfaut, Chaine()\n"); t_buf = 100; buffer = new char[t_buf]; longueur=0; } Chaine (int t_buf) { printf("Constructeur Chane(%d)\n", t_buf); this->t_buf = t_buf; buffer = new char[t_buf]; longueur = 0; } } ; int main(int argc, char* argv[]) { Chaine*chaines; chaines=new Chaine[10]; return 0; } Testons le programme et analysons les rsultats : le constructeur est bien appel 10 fois. Comment ds lors appelerledeuximeconstructeur,celuiquiprendunentiercommeparamtre? Vous ne pouvez pas combiner la slection dun constructeur avec paramtre et la spcification dun nombre dobjets allouer avec loprateur new. Il faut procder en deux tapes, puisque lcriture suivante nest pas valide: Chaines = new Chaine(75)[10]; Optezalorspourlatournuresuivante,mmesielleparatpluscomplexe: Chaine** chaines; chaines = new Chaine*[10]; for(int i=0; i<10; i++) chaines[i] = new Chaine(75); Attention, vous aurez remarqu le changement de type de la variable chaines, passe de Chaine* Chaine**. Ce changement diffre linstanciation de lallocation du tableau, devenu un simple tableau de pointeurs.

5.Constructeurdecopie
Nousavonsdcouvertlorsdeltudedesstructuresquelacopieduneinstanceversuneautre,paraffectation, avait pour consquence la recopie de toutes les valeurs de la premire instance vers la seconde. Cette rgle restevraiepourlesclasses. Chaine s(20); Chaine r; Chaine x = s; // initialisation de x avec s
- 9-

r = s;

// recopie s dans r

Nousdevonsgalementnousrappelerquecettecopieposedesproblmeslorsquelastructure(classe)possde des champs de type pointeur. En effet, linstance initialise en copie voit ses propres pointeurs dsigner les mmeszonesmmoirequelinstancesource.Dautrepart,lorsqueledestructeurestinvoqu,leslibrations rptitiondesmmeszonesmmoireaurontprobablementuneffetdsastreuxpourlexcutionduprogramme. Lorsquunblocatlibr,ilestconsidrcommeperduetlibrepourunerallocation,doncilconvientdenepas insister. Les classes de C++ autorisent une meilleure prise en charge de cette situation. Il faut tout dabord crire un constructeur dit de copie, pour rgler le problme dinitialisation dun objet partir dun autre. Ensuite, la surcharge de loprateur (cf. Autres aspects de la POO Surcharge doprateurs) se charge dliminer le problmedelarecopieparaffectation. class Chaine { // ... la dclaration ci-dessus public: Chaine(const Chaine&); // constructeur de copie Chaine& operator = (const Chaine&); // affectation copie } ; Commenonsparleconstructeurdecopie: Chaine::Chaine(const Chaine & ch) { t_buf = ch.t_buf; longueur = ch.longueur; buffer = new char[ch.t_buf]; // ch rfrence for(int i=0; i<ch.longueur; i++) buffer[i]=ch.buffer[i]; } Leconstructeurdecopieprendcommeuniqueparamtreunerfrenceversunobjetdutypeconsidr. Poursuivonsaveclasurchargedeloprateurdaffectation: Chaine& Chaine::operator=(const Chaine& ch) { if(this != &ch) { delete buffer; buffer = new char[ch.t_buf]; // ch rfrence for(int i=0; i<ch.longueur; i++) buffer[i] = ch.buffer[i]; longueur = ch.longueur; } return *this; } Ilfautfaireattentionnepaslibrerparerreurlammoirelorsqueleprogrammeurprocdeuneaffectation dunobjetversluimme: s = s; Pouruneclassemuniedecesdeuxoprations,lesrecopiesparinitialisationetparaffectationnedevraientplus poserdeproblmes.Naturellement,leprogrammeuralachargededcrirelalgorithmeapproprilarecopie.

- 10 -

Hritage
1.Drivationdeclasse(hritage)
Maintenant que nous connaissons bien la structure et le fonctionnement dune classe, nous allons rendre nos programmesplusgnriques.Ilestfrquentdedcrireunproblmegnralavecdesalgorithmesappropris, puisdeprocderdepetitesmodificationslorsquuncassimilairevienttretrait. Laphilosophieorienteobjetconsistelimiteraumaximumlesmacros,lesinclusions,lesmodules.Cettefaon daborder les choses prsente de nombreux risques lorsque la complexit des problmes vient crotre. La programmation oriente objet sexprime plutt sur un axe gnrique/spcifique bien plus adapt aux petites variationsdesdonnesdunproblme.DesmthodesdemodlisationsappuyantsurUMLpeuventvousguider pourconstruiredesrseauxdeclassesadaptsauxcirconstancesdunprojet.Maisilfautgalementsappuyer surdeslangagessupportantcetteapproche,etC++enfaitpartie.

a.Drivationdeclasse(hritage)
ImaginonsuneclasseCompte,composedeslmentssuivants:

class Compte { protected: int numero; double solde;

// numro du compte // solde du compte

static int num; // variable utilise pour calculer le prochain numro static int prochain_numero(); public: char*titulaire;

// titulaire du compte

Compte(char*titulaire); ~Compte(void); void crediter(double montant); bool debiter(double montant); void relever(); }; Nouspouvonsmaintenantimagineruneclasse CompteRemunere,spcialisantlefonctionnementdelaclasse Compte. Il est ais de concevoir quun compte rmunr admet globalement les mmes oprations quun compte classique, son comportement tant lgrement modifi pour ce qui est de lopration de crdit, puisquun intrt est vers par lorganisme bancaire. En consquence, il est fastidieux de vouloir rcrire totalement le programme qui fonctionne pour la classe Compte. Nous allons plutt driver cette dernire classepourobtenirlaclasseCompteRemunere. Laclasse CompteRemunere,quantelle,reprendlensembledescaractristiquesdelaclasseCompte:elle hritedeseschampsetdesesmthodes.OnditpoursimplifierqueCompteRemunerehritedeCompte.

class CompteRemunere : public Compte { protected: double taux; public: CompteRemunere(char*titulaire,double taux); ~CompteRemunere(void); void crediter(double montant); };

- 1-

Vousaurezremarququeseuleslesdiffrences(modificationsetajouts)sontinscritesdansladclarationde laclassequihrite.Touslesmembressonttransmisparlhritage: public Compte. VoicimaintenantlimplmentationdelaclasseCompte:

#include "compte.h" #include <string.h> #include <stdio.h> Compte::Compte(char*titulaire) { this->titulaire=new char[strlen(titulaire)+1]; strcpy(this->titulaire,titulaire); solde=0; numero=prochain_numero(); } Compte::~Compte(void) { delete titulaire; } void Compte::crediter(double montant) { solde+=montant; } bool Compte::debiter(double montant) { if(montant<=solde) { solde-=montant; return true; } return false; } void Compte::relever() { printf("%s (%d) : %.2g euros\n",titulaire,numero,solde); } int Compte::num=1000; int Compte::prochain_numero() { return num++; } Mispartlutilisationdunchampetdunemthodestatiques,ilsagitdunexerciceconnu.Passonslaclasse CompteRemunere:

#include ".\compteremunere.h" CompteRemunere::CompteRemunere(char*titulaire, double taux) : Compte(titulaire) { this->taux=taux; } CompteRemunere::~CompteRemunere(void) { } void CompteRemunere::crediter(double montant)
- 2-

{ Compte::crediter(montant*(1+taux/100)); } LappelduconstructeurdelaclasseComptedepuisleconstructeurdelaclasseCompteRemunereseratrait dansunprochainparagraphe.Concentronsnouspluttsurlcrituredelamthodecrediter. Cettemthodeexistedjdansla classedebase, Compte.Maisilfautremarquer quelecrditsuruncompte rmunr ressemble beaucoup au crdit sur un compte classique, sauf que lorganisme bancaire verse un intrt.Nousdevrionsdoncappelerlamthodedebase,crediter,situedanslaclasse Compte.Hlas,les rglesdevisibilitetdeportefontquellenestpasaccessibleautrementquenspcifiantlenomdelaclasse laquelleonfaitrfrence : void CompteRemunere::crediter(double montant) { // porte la plus proche => rcurrence infinie crediter(montant*(1+taux/100)); // criture correcte Compte::crediter(montant*(1+taux/100)); } Les programmeurs Java et C# ne connaissant pas lhritage multiple, on a substitu le nom de la classe de baseparunmotclunique,respectivementsuperetbase. Pourtudierlefonctionnementduprogrammecomplet,nousfournissonslemoduleprincipal,main.cpp:

//#include "compte.h" // inutile car dj prsent dans compteremunere.h #include "compteremunere.h" int main(int argc, char* argv[]) { Compte c1("Victor Hugo"); c1.crediter(100); c1.relever(); c1.debiter(80); c1.relever(); CompteRemunere r1("Jean Valjean",5); r1.crediter(100); r1.relever(); return 0; } Lexcutiondonnelesrsultatssuivants:

Lecrditde100eurossurlecomptedeJeanValjeanabientrmunrhauteurde5 %.

- 3-

Openmirrors.com
b.Hritagepublic,protgetpriv
IlexisteunespcificitdulangageC++:ilestpossiblederestreindrelaccsauxmembresdelaclassedrive encontrlantlapublicitdelhritage. class Derive : public Base { ... }; Le mot cl public peut tre remplac par private ou protected. Le tableau suivant numre les cas daccessibilit des champs dune classe de base depuis une classe drive, en fonction de leur niveau de protectioninitialetdutypededrivation. Visibilitdanslaclassede base public protected private public protected private public protected private Typededrivation Visibilitdanslaclasse drive public protected inaccessible private private private private protected protected

public public public private private private protected protected protected

Nousvousrecommandonstoutefoisdenepasabuserdecetypedeconstruction,carleslangagesJavaetC# nedisposentquedelhritagepublic.

c.Appeldesconstructeurs
Nous savons quuneclassedrivantduneautreclassehritedelensembledeseschamps,soientilsprivs. Cesttoutfaitnormal,caruneclasseesttoujours dcritecommeuntout.Elleabesoindelensembledeses champspouradossersonalgorithmie,aussiuneclassenestjamaisdcriteaveclarrirepensedeladriver par la suite. Ce motif de conception est rserv aux classes abstraites, cestdire contenant au moins une mthodevirtuellepureetpeudalgorithmie. Decefait,ilfauttrouverunmoyendinitialiserlensembledeschamps,ycompris ceuxprovenantdelaclassede base,privsounon.Cetravaildinitialisationadjtralislaidedunoudeplusieursconstructeurs. Dautre part, cest bien la classe hritant qui est instancie, et son constructeur appel pour initialiser ses propreschamps.Ilparatdslorslgitimedechercher appelerauplusttunconstructeurdanslaclassede base,oulesconstructeursdesclassesdebaseencasdhritagemultiple. LeconcepteurdeC++avoulusoulignerquecetappeldevaitfigureravantlexcutiondelapremireinstruction duconstructeurdelaclassehritant,aussiatilprvuunesyntaxeparticulire: CompteRemunere::CompteRemunere( char*titulaire, double taux) : Compte(titulaire) {

- 4-

Openmirrors.com

this->taux=taux; } LeconstructeurdelaclasseCompteestappelavantlaffectationduchamptaux.Encasdhritagemultiple, onsparelesconstructeurspardesvirgules : ConstructeurHerite(params) : Constructeur1(params), { } Constructeur2(params)

Vous aurez not la possibilit de transmettre au constructeur de la classe suprieure des paramtres reus danslaclassehritant,cequiesttoutfaitlgitime. Si votre classe de base na pas de constructeur, ou si elle en possde un par dfaut (sans argument), le compilateur peut raliser cet appel automatiquement. Toutefois, labsence de constructeur ou labsence de choix explicite dun constructeur constituent une faiblesse dans la programmation, et probablement dans la conception.

2.Polymorphisme
Le polymorphisme caractrise les lments informatiques existant sous plusieurs formes. Bien quil ne sagisse pas proprement parler dune caractristique ncessaire aux langages orients objet, nombre dentre eux la partagent.

a.Mthodespolymorphes
Pour dfinir une mthode polymorphe, il suffit de proposer diffrents prototypes avec autant de signatures (listesdarguments).Lafonctiondoittoujoursporterlemmenom,autrementelleneseraitpaspolymorphe.Le compilateur se base sur les arguments transmis lors de linvocation dune mthode, en nombre et en types, pourdterminerlaversionquildoitappeler. class Chaine { ... void concat(char c); void concat(Chaine s); void concat(int nombre); } : Faitesattentionlorsquunemthodepolymorpheestinvoquelaidedunevaleurlittralecommeparamtre, ilyaparfoisdesambigutsdlicatesrsoudre. Letranstypage(changementdetypeparcoercition)apparat alorscommelunedessolutionslesplusclairesquisoit. DansnotreexempledeclasseChaine,nousallonsinvoquerlamthodeconcat()aveclavaleurlittrale65. Si65estlecodeASCIIdelalettreB,cestaussiunentier.Quelleversionchoisitlecompilateur? Chaine s="abc"; s.concat(65); Suivantlaversionappele,nousdevrionsobtenirsoitlachane"abcA"soitlachane"abc65". Nouspouvonsrsoudrelquivoquelaideduntranstypageadaptauxcirconstances : s.concat( (char) 65 ); Noussommescertainscettefoisquecestlaversionrecevantuncharquiestappele.Ilvautmieuxtreleplus explicitepossiblecarlecompilateurC++estgnralementtrspermissifettrsimplicitedanssesdcisions!

- 5-

b.Conversionsdobjets
Sil est une mthode qui est frquemment polymorphe, cest certainement le constructeur. Celuici permet dinitialiserunobjetpartirdautresobjets: Chaine x(A); Chaine t(x); Lorsquenousauronstudilasurchargedesoprateurs,nousverronscommenttirerpartidecettemultitude deconstructeursoffertscertainesclasses.

3.Mthodesvirtuellesetmthodesvirtuellespures
LelangageC++estunlangagecompil,etdecefait,fortementtyp.Celasignifie quelecompilateursuitauplus prslestypesprvuspourchaquevariable. Livronsnous lexprience suivante : considrons deux classes, Base et Derive, la seconde drivant naturellement de la premire. La classe Base ne contient pour linstant quune mthode, methode1(), surcharge(rcrite)danslasecondeclasse: class Base { public: void methode1() { printf("Base::methode1\n"); } } ; class Derive : public Base { public: void methode1() { printf("Derive::methode1\n"); } } ; Nousproposonsmaintenantunefonction main()instanciantchacunedecesclassesdanslebutdinvoquerla mthodemethode1().

int main(int argc, char* argv[]) { Base*b; Derive*d; b = new Base(); b->methode1(); d = new Derive(); d->methode1(); return 0; } Lexcutionduprogrammefournitdesrsultatsenconcordanceaveclesinstructions contenuesdanslafonction main():

- 6-

Openmirrors.com

Lecompilateurasuiviletypagedespointeursbetdpourappliquerlabonnemthode. MaintenantconsidronslaclasseBasecommeunsousensembledelaclasseDerive.Ilestlgaldcrireb=d puisque toutes les oprations applicables b sont disponibles dans d, lopration est sans risque pour lexcution.Quellevatrealorslechoixducompilateurlorsquenouscrironsb->methode1()?

printf("--------\n"); b = d; b->methode1(); Lexcutionduprogrammenousfournitlersultatsuivant,lecompilateurayantfinalementoptpourlaversion proposeparBase.

Cersultatesttoutfaitlogique,entenantcomptedufaitquelditiondesliensalieuavantlexcution.Aucun mcanismenest appliqu pour retarder lditiondesliensetattendrelederniermomentpourdterminerquel estletypedsignparlepointeurb. CemcanismeexistedansC++,ilsagitdesmthodesvirtuelles. Compltonsprsentnosclassesparmethode2(),dontleprototypeestmarquparlemotclvirtual:

class Base { public: void methode1() { printf("Base::methode1\n"); } virtual void methode2() { printf("Base::methode2\n"); } } ; class Derive : public Base { public: void methode1() { printf("Derive::methode1\n"); }

- 7-

void methode2() { printf("Derive::methode2\n"); } } ; Vous aurez not quil nest pas ncessaire de marquer la version hritire, lattribut automatiquementtransmislorsdeladrivation. Unnouveaumain()fournitdesrsultatsdiffrents:

virtual

est

printf("--------\n"); b = d; b->methode2(); La seconde mthode tant virtuelle, le compilateur retarde lditeur de liens. Cest lexcution que lon dterminequelestletypedsignparlepointeur b.Lexcutionestpluslentemaislecompilateurappelle"la bonneversion". Enfait,lesmthodesvirtuellesreprsententlagrandemajoritdesbesoinsdesprogrammeurs,aussilelangage Javaatilpriscommepartidesupprimerlesmthodesnonvirtuelles.Decefait,lemotcl virtualadisparu desasyntaxe. LelangageC#estrestplusfidleauC++enprvoyantlesdeuxsystmesmaisenrenforantla syntaxe de surcharge, afin que le programmeur exprime son intention lorsquil drive une classe et surcharge unemthode. Lesmthodesvirtuellessontparticulirementutilespourprogrammerlesinterfaces graphiquesetlescollections. Signalonsquellesfonctionnentgalementavecdesrfrences: Base& br = *d; br.methode1(); br.methode2(); Il arrive frquemment quune mthode ne puisse tre spcifie compltement dans son algorithmie, soit parce que lalgorithmie est laisse aux soins du programmeur, soit parce que la classe exprime un concept ouvert, complterlademande. Les mthodes virtuelles pures sont des mthodes qui nont pas de code. Il faudra driver la classe laquelle ellesappartiennentpourimplmentercesmthodes: class Vehicule { public: virtual void deplacer() =0 ; } ; class Voiture : public Vehicule { public: void deplacer() { printf("vroum"); } } ;

Openmirrors.com

La mthode virtuelle pure est signale par la conjonction de la dclaration virtual et par laffectation dun pointeurnul(=0).Decefait,laclasse Vehicule,abstraite,nestpasdirectementinstanciable.CestVoiture qui est charge dimplmenter tout ou partie des mthodes virtuelles pures reues en hritage. Sil reste au moins une mthode virtuelle pure nayant pas reu dimplmentation, la classe Voiture reste abstraite. Ce nestcertespaslecasdenotreexemple. Vehicule*veh = new Vehicule; // interdit car classe abstraite

- 8-

Openmirrors.com

Openmirrors.com
Voiture *voit1 = new Voiture; // ok voit1->deplacer; veh = new Voiture; // ok Le langage Java ne connaissant pas les pointeurs, il a remplac lcriture =0 ou =NULL par le mot cl abstract.CelasignifiequeleslangagesC++etJava(enC#)peuventexprimerlesmmesnotionsobjet,ce quiestdterminantlorsquelimplmentationsuitunemodlisationcommeUML. Les langages successeurs de C++ proposent galement la notion dinterface : il sagit dune classe abstraite donttouteslesmthodessontabstraites,autrementditvirtuellespuresdanslevocabulaireC++.Linterfacea sans aucun doute t propose pour simplifier lcriture des programmes, mais elles ne constituent pas une nouveaut en tant que telle. C++ est donc capable dexprimer des modlisations faisant intervenir des interfaces.

4.Hritagemultiple
Unesituationinconfortablesestprsenteauxconcepteursdeprogrammesorientsobjet :uneclassedevait spcialiserplusieursconceptssimultanment.Decefait,elledevaitdriverdaumoinsdeuxclasses,ethriterde chacun de ses membres. Lauteur de C++ a pourvu son langage dune syntaxe respectant cette modlisation quelondsignesouslappellationhritagemultiple.

Dansnotreexemple,classeChritelafoisdelaclasseAetdelaclasseB. EntermesC++,nousaurionstraduitcettemodlisationdelafaonsuivante : class A { ... } ; class B { ... } ; class C : public A, public B { ... } ;

- 9-

Openmirrors.com
a.Notationsparticulires
Lapremirenotationconcernenaturellementlappeldesconstructeurs.LaclasseChritantdesclassesAetB, elle doit appeler les deux constructeurs, dans lordre de son choix. En fait, lordre dinitialisation na normalementpasdimportance,uneclassetantuntout,ellenestpasconueenfonctiondautresclasses.Si lordrerevtuneimportanceparticulirepourqueleprogramme fonctionne,lamodlisationestprobablement revoir. C(param) : A(params), B(params) { ... } videmment,lesparamtresapplicablesauxtroisconstructeurspeuventtrediffrentsenvaleur,entypeet ennombre. Par la suite, nous devons distinguer les mthodes qui portent le mme nom dans les classes A et B, et qui seraientinvoquesparunemthodedeC. Imaginonslasituationsuivante : class A { void methode1() { ... } } ; et class B { void methode1() { ... } } ; QuesepassetilsiunemthodedeCappellemethode1(),dontelleaenfaithritdeuxfois ?Enlabsence dunmarquageparticulier,lecompilateursoulve uneerreurcarlappelestambigu : class C : public A, public B { void methode2() { methode1() ; // appel ambigu } } ;

Openmirrors.com

Lorsque de tels cas se prsentent, le programmeur doit faire prcder le nom de la mthode du nom de la classe laquelle il fait rfrence. Cest bien entendu loprateur de rsolution de porte qui intervient ce momentl : class C : public A, public B { void methode2() { A ::methode1() ; // ok } } ; lextrieurdelaclasse,leproblmedemeure,carlecompilateurnesaitpasquelleversionappeler : class A

- 10 -

Openmirrors.com

{ public: int f(int i) { printf("A::f(int=%d)\n",i); return i; } char f(char i) { printf("A::f(char=%c)\n",i); return i; } } ; class B { public: double f(double d) { printf("B::f(double=%g)\n",d); return d; } } ; class AetB : public A, public B { public: char f(char a) { printf("AetB::f(char=%c)\n",a); return a; } bool f(bool b) { printf("AetB::f(boolean=%d)\n",b); return b; } } ; int main(int argc, char* argv[]) { AetB x; x.f(1); // appel ambigu x.f(2.0); // appel ambigu x.f(A); x.f(true); return 0; } Ilexistedeuxmoyenspourleverlambigutdesappelssignalsdanslafonction main().Lepremierconsiste faireprcderlinvocationdefparA::ouB::,commeindiqu :

x.A::f(1000); x.B::f(2.0); Le second moyen consiste utiliser dans le corps de la dclaration de la classe AetB des dclarations dutilisation:using

using A::f; using B::f; Lesecondmoyenestprivilgiercarilaugmentelaqualitdelamodlisation.

b.Consquencessurlaprogrammation
Cette caractristique a fortement augment la complexit des compilateurs, en mme temps que la vie des

- 11 -

programmeurs. Imaginons une classe A drivant en deux classes B et C. Ajoutons notre diagramme une classeDquihritesimultanmentdeBetdeC.ElledevraithriterdeuxfoisdesmembresdeA. Les langagesJava etC# ont supprim lhritagemultiple,enautorisantseulementlhritage dune classe et limplmentationduneinterface.Uneinterfacetantabstraite,ellenecontientnichamp,nicode.Lesdoutes sontparconsquentlevs. LauteurdeC++aluiproposlemcanismedesclassesvirtuelles.

Enprincipe,touslesmembresdelaclasseAsonthritsdeuxfoisparlaclasseD.Lutilisationdeloprateur dersolutiondeporte::contrlelaccscesmembres.Lorsqueleconcepteur(ouleprogrammeur)neveut quunseulhritage, ilpeututiliserlemcanismedeladrivationvirtuelle : class { ... class { ... B } C } : public virtual A ; : public virtual A ;

class D : public virtual B, public virtual C { ... } ; Le compilateur sassurera alors que chaque membre nest hrit quune fois. Toutefois, le programmeur ne pourrapasdiffrencierloriginedesmembreshrits.Cemcanismeestdoncmanieravecprudence.

- 12 -

Openmirrors.com

AutresaspectsdelaPOO
1.Conversiondynamique
a.Conversionsdepuisunautretype
Lesconstructeurspermettentdeconvertirdesobjetspartirdinstances(devaleurs)exprimesdansunautre type. PrenonslecasdenotreclasseChaine.Ilseraitintressantde"convertir"unchar*ouuncharenchane :

#include <string.h> class Chaine { private: char*buffer; int t_buf; int longueur; public: // un constructeur par dfaut Chaine() { t_buf=100; buffer=new char[t_buf]; longueur=0; } Chaine (int t_buf) { this->t_buf=t_buf; buffer=new char[t_buf]; longueur=0; } Chaine(char c) { t_buf=1; longueur=1; buffer=new char[t_buf]; buffer[0]=c; } Chaine(char*s) { t_buf=strlen(s)+1; buffer=new char[t_buf]; longueur=strlen(s); strcpy(buffer,s); } void afficher() { for(int i=0; i<longueur; i++) printf("%c",buffer[i]); printf("\n"); } } ; int main(int argc, char* argv[]) { // Ecriture 1 // Conversion par emploi explicite de constructeur Chaine x;

- 1-

x=Chaine("bonjour"); x.afficher();

// conversion

// Ecriture 2 // Transtypage (cast) par coercition Chaine y=(char*) "bonjour"; // conversion (cast) y.afficher(); // Ecriture 3 // Transtypage (cast), approche C++ Chaine z=char(A); // conversion (cast) z.afficher(); return 0; } Lafonctionmain()illustrelestroisfaonsdeconvertirversuntypeobjet : parlemploidunconstructeurexplicite(1) parlemploiduntranstypage,commeenC(2) enutilisantlasyntaxepropreC++(3).

Nousverronsquelasurchargedeloprateurdeconversionpermetdallerplusloin,notammentpourconvertir unobjetversuntypeprimitif.Enconjonctionaveclasurchargedeloprateurdaffectation,ildevientpossible deraliserpratiquementnimportequeltypedeconversion,sansavoirrecourirdesmthodesspcialises (ToInt,ToString,ToChar,FromInt,FromString...).

b.Oprateursdeconversion
LelangageC++disposededeuxoprateursdeconversionspcifiques : const_cast<type> expressionconst_cast Convertituneexpressioncommeleferait(type)expressionsiletypedeexpression diffreuniquementparles modificateursconstouvolatile.

reinterpret_cast<type> expressionreinterpret_cast Convertitunpointeurenunautrepointeursanstropsesoucierdelacohrencedelopration. Le premier oprateur, const_cast, sert oublier provisoirement lutilisation du modificateur const. Les fonctions constantes, marques const aprs leur prototype, engagent ne pas modifier les champs de la classe.Toutefois,cettesituationpeuttreparfoisgnante.const_castautoriselaconversiondunpointeur declasseversuneclasseidentique,maisdbarrassedesmarqueursconst.

class CCTest { public: void setNombre( int ); void afficheNombre() const; private: int nombre; }; void CCTest::setNombre(int num) { nombre = num; } void CCTest::afficheNombre() const {

- 2-

Openmirrors.com

printf("Avant: %d",nombre); const_cast< CCTest * >( this )->nombre--; printf("Aprs %d",nombre); } int main() { CCTest X; X.setNombre( 8 ); X.afficheNombre(); } Cest dans la mthode afficheNombre que linterdiction a t provisoirement leve. Signalons galement quecetoprateurnepermetpasdirectementdeleverlinterdictiondemodificationsurdeschampsconst. Quant loprateur reinterpret_cast, il se rvle potentiellement assez dangereux et ne devrait tre utilisquepourdesfonctionsdebasniveau,commedanslexempleducalculdunevaleurdehachagebase surlavaleurentiredunpointeur : unsigned short Hash( void *p ) { unsigned int val = reinterpret_cast<unsigned int>( p ); return ( unsigned short )( val (val >> 16)); }

c.Conversionsentreclassesdrives
Deux oprateurs supplmentaires contrlent les conversions de type, tout particulirement lorsquilsagit de classesdrives.Lorsdeltudedesmthodes virtuelles,nousavonsenvisagdeuxclasses,BaseetDerive laseconde drivantdelapremire. Loprateurstatic_castsassurequilexisteuneconversionimpliciteentrelesdeuxtypesconsidrs.Dece fait,ilestdangereuxdelutiliserpourremonter unpointeurdanslahirarchiedesclasses,cestdiredutiliser un pointeur Derive* pour dsigner un Base*. Finalement, loprateur static_cast, qui donne la pleine mesure de son action lors de la compilation, est beaucoup plus utile pour raliser des conversions propres entredesnumrationsetdesentiers. enum Couleurs { rouge, vert, bleu }; int conv_couleur(Couleurs coul) { return static_cast<int> ( coul ); } Pour utiliser loprateur

dynamic_cast, il faut parfois activer un commutateur du compilateur, comme

ENABLE_RTTI(RunTimeTypeInformation)pourlecompilateurMicrosoft.Ildevientalorstrivialdenvisagerdes conversionspourlesquellesundoutesubsistequantautyperellementmanipul:dynamic_cast.

void conie_sans_risque(Base*b) { Derive*d1 = dynamic_cast<Derive*>(b); } Silargumentdsigneeffectivementunobjetdetype Base,sapromotiondirecte en Derive (par coercition) est assez risque. Loprateur dynamic_cast renverra NULL, voire lvera une exception. Toutefois, la dtection devraattendrelexcutionpourdterminerletypeexactreprsentparb.

2.Champsetmthodesstatiques

- 3-

a.Champsstatiques
Unchampstatiqueestunevariableplacedansuneclassemaisdontlavaleurestindpendantedesinstances decetteclasse :ellenexistequenunseuletuniqueexemplaire.Quelleestlopportunitdunetellevariable ? Toutdabord,ilexistedessituationspourlesquellesdesvariablessontplacesdansuneclasseparsoucide cohrence. Envisageons la classe Mathematiquesetlavariable PI.Ilestlogiquederattacher PIdanslevocabulaire mathmatique,autrementditdeclasserPIdansMathematiques.Maislavaleurde PIestconstante,etelle nesauraitdetoutefaonvarierpourchaqueinstancedecetteclasse.Pourvoquercecomportement,ilat choisidemarquerlechampPIcommetantstatique. Deuximement,lesmthodesstatiques(cf.partiesuivante)nontpasaccsauxvariablesdinstance,cest direauxchamps.Siunemthodestatiqueabesoindepartagerunevaleuravecuneautremthodestatique,il ne lui reste plus qu adresser une variable statique. Attention, il sagit bien dun champ de la classe et non dune variable locale statique, disposition qui a heureusement disparu dans les langages de programmation actuels. class Mathematiques { public : static double PI ; } ; double Mathematiques ::PI=3.14159265358 ; Vousaureznotqueladclarationdunchampstatiquenquivautpassonallocation.Ilestncessairede dfinirlavariablelextrieurdelaclasse,enutilisant loprateurdersolutiondeporte ::pourrellement allouerlavariable. Comme autre exemple de champ statique, reportezvous lexemple prcdent de la classe Compte. Le prochain numro de compte, un entier symbolisant un compteur, est incrment par une mthode ellemme statique. Soulignons quil y a une forte similitude entre un champ statique, appartenant une classe, et une variable dclaredansunespacedenoms,quedaucunsappellent module.

b.Mthodesstatiques
Lesmthodesstatiquesreprennentlemmeprincipequeleschampsstatiques.Ellessontdclaresdansune classe,carellesserapportentlasmantiquedecetteclasse,maissonttotalementautonomesvisvisdes champs de cette classe. Autrement dit, il nest pas ncessaire dinstancier la classe laquelle elles appartiennentpourlesappliquer,cequisignifiequonnepeutlesappliquerdesobjets. Restonspourlinstantdansledomainemathmatique.Lafonction cosinus(angle)peuttreapproche partirdundveloppementlimit,fonctionmathmatiquetrssimple : cosinus(angle) = (anglei/!i) pouri=2*k,kvariantde0n. NousendduisonspourC++lafonctioncosinus : doublecosinus(doublea) { return1a*a/2+a*a*a*a/24+a*a*a*a*a*a/720 } Nous remarquons que cette fonction est la fois totalement autonome, puisquelle ne consomme quun argumenta,etquelleserapporteaudomainemathmatique.Nousdcidonsdenfaireunemthodestatique :

- 4-

Openmirrors.com

classMathematiques { public: doublecosinus(doublea) { return1a*a/2+a*a*a*a/24+a*a*a*a*a*a/720 } } Maintenant, pour appeler cette mthode, nous navons qu lappliquer directement la classe Mathematiquessansavoirbesoindecreruneinstance : doubleangle=Mathematique::PI/4 printf("cos(pi/4)=%g",Mathematiques::cosinus(a))

Lersultatapproche

2/2,soit0,707.Lemicroprocesseurnesyprendpasautrement pourfournir

lavaleurduncosinuslafonction cos()disponibledans<math.h>.Certainsprocesseursnumriques factorisentlavariableaetutilisentunetabledepoidspouraugmenterlarapiditducalcul.

Nousdonnonsmaintenantunexempledestructuredynamique,laliste,quialafaveurdesenseignementsen informatique.Cettestructurestockedeslmentsdenaturediverseentiers,chanes...etsatailleaugmente aufuretmesuredesbesoins.

On utilise en gnral les fonctions suivantes pour manipuler les listes : cons() qui sera notre constructeur, suite()et est_vide()quitestelanullitduneliste.Lesapplicationsdeslistessontinnombrables,etleur apprentissageestunpluspourtoutprogrammeur. TraditionnellementtraiteenlangageC,lalistegagnetreimplmenteenC++,notammentparlemploide mthodes statiques. Voici donc limplmentation propose. Nous commenons par le fichier Liste.h, qui possdeplusieursmembresstatiques. #pragmaonce #include<stdio.h> classListe {
- 5-

public: char*element Liste*_suite staticconstListe*LISTE_VIDE Liste() { _suite=const_cast<Liste*>(LISTE_VIDE) } Liste(char*e,Liste*suite) { _suite=suite element=e } Liste*suite() { return_suite } staticboolest_vide(Liste*l) { returnl==LISTE_VIDE } voidafficher() { Liste::afficher(this) } staticvoidafficher(Liste*l) { if(Liste::est_vide(l)) return printf("%s,",l>element) Liste::afficher(l>suite()) } } Lafonction est_videtanttotalementautonome,ilestlogiquedeladfinirstatique.Laffichageduneliste estgrandementfacilitparlcrituredunefonctionrcursiveprenantunelistecommeparamtre.Decefait,la fonction ne sapplique aucune instance en particulier et la mthode correspondante devient ellemme statique.Lexpositiondecettemthodercursiveavecleniveau publicouprivestunchoixquiappartientau programmeur. Nous trouvons ensuite dans le fichier Liste.cpp la dfinition du champ statique LISTE_VIDE. Notez au passagelacombinaisondesmodificateursstaticetconst,combinaisonfrquentesilenest. #include".\liste.h" constListe*Liste::LISTE_VIDE=NULL LadfinitiondportedecettevariablenepouvaitenaucunemanirefigurerdanslefichierListe.h,carson inclusion par plusieurs modules cpp aurait entran sa duplication entre chaque module, rendant ainsi impossibleldition desliens. Lafonctionmain()creunexempledelistepuisappellelamthodeafficher() : #include"Liste.h" intmain(intargc,char*argv[]) { Liste*liste=newListe("bonjour",

- 6-

Openmirrors.com

newListe("les", newListe("amis", const_cast<Liste*>(Liste::LISTE_VIDE)))) liste>afficher() return0 } Au passage, vous aurez not lemploi de loprateur const_cast pour accorder prcisment les types manipuls.

3.Surchargesdoprateurs
LeconcepteurdeC++avouluquesonlangagesoitleplusexpressifpossible dfinirdesclassesetcrerde nouveauxtypesestunechose,conserveruneprogrammationsimpleetclaireenestuneautre.Lasurchargedes oprateurs, cestdire leur adaptation des classes dfinies par le programmeur va justement dans cette direction. Un oprateur est en fait assimilable une fonction. Cette fonction runit un certain nombre doprandes et value un rsultat dun type particulier. Pour quun oprateur soit surcharg, il doit figurer dans une classe commenimportequellemthode.

a.Syntaxe
En fait, il est laiss une trs grande libert au programmeur dans le type des arguments applicables aux oprateurssurchargs.Lerespectdelacommutativit,despriorits,delasmantiquemmedunoprateur peuventtreremisencausesansquelecompilateurnetrouveyredire. Lasyntaxeestlasuivante: classClasse { type_retouroperatorop(type_opoperande) { ... } } Cet oprateur, op, est destin figurer entre une instance de la classe Classe et un oprande de type type_op.Lafonctionestlibredemodifierlinstancelaquelleelleestappliqueetpeutrenvoyerunrsultat denimportequeltype,mmevoid. Pour illustrer cette notation, reprenons lexemple de la classe Chaine vu prcdemment. Ajoutons la surchargedeloprateur+danslebutdeconcatnerununiquecaractre: Chaine&operator+(charc) { buffer[longueur++]=c return*this }

- 7-

Ilestassezjudicieuxderenvoyerlarfrencedelobjetcourant,cequipermet denchanerlesoprations: Chainec("bonjour") c=c+V c+o Chained d=c+u+s //enchanement d.afficher()//affichebonjourVous Vous aurez not que notre oprateur + effectue une modification de linstance laquelle elle est applique, alors quen termes mathmatiques additionner deux variables ne modifie ni lune, ni lautre. Il aurait t possible la place de construire une nouvelle chane et de se contenter dvaluer la concatnation. Nous gagnerionsainsiensouplessemaisperdrionsvraisemblablementenperformance. Pratiquement tous les oprateurs sont disponibles pour la surcharge, lexception notable de ceux figurant dansletableausuivant: . Accsauxchamps Rsolutiondeporte Tailleduntype Formeprdicative(voirif) AdressagerelatifdeschampsAdressagerelatif

:: sizeof ? : .*

Ces exceptions faites, le programmeur a donc une grande libert dans les smantiques proposes par les oprateurs, condition toutefois de conserver le nombre doprandes (binaire, unaire), la prcdence, de respecterlassociativit.Leprogrammeurnepeutpasnonplusdterminerladressedunoprateur(pointeur defonction),niproposerdesvaleurspardfautpourlesargumentsapplicables. Les oprateurs peuvent tre implments sous forme de mthode ou de fonctions, ces dernires ayant tout intrttredclaresamies(friend).Lintrtdecetteapprocheestquilestpossibledinverserlordredes oprandesetdaugmenterainsilevocabulaireduneclasseexistante. Cette technique est dailleurs mise profit pour la surcharge de loprateur std::ostream.

<< applicable la classe

b.Surchargedeloprateurdindexation
Nous proposons son implmentation car il convient parfaitement lexemple de la classe Chaine. Cet oprateur est utile pour accder la nime donne contenue dans un objet. Dans notre cas, il sagit dnumrerchaquecaractrecontenudansunechane: intlength() { returnlongueur } charoperator[](intindex) { returnbuffer[index] } Laffichagecaractreparcaractredevientalorstrivial: for(inti=0i<d.length()i++) printf("%c",d[i])

- 8-

Openmirrors.com

c.Surchargedeloprateurdaffectation
Loprateurdaffectationestpresqueaussiimportantqueleconstructeurdecopie.Ilestfrquentdedfinirles deuxpouruneclassedonne. Chaine&operator=(constChaine&ch) { if(this!=&ch) { deletebuffer buffer=newchar[ch.t_buf] //chrfrence for(inti=0i<ch.longueuri++) buffer[i]=ch.buffer[i] longueur=ch.longueur } return*this }

d.Surchargedeloprateurdeconversion
Loprateur de conversion ralise un travail symtrique aux constructeurs prenant des types varis comme argumentspourinitialiserlenouvelobjet.Danslecasdelaclasse Chaine,laconversionversun char*est absolumentncessairepourgarantirunebonnetraductionavecleschanesgresparlelangageluimme. Faitesattentionlasyntaxequiesttrsparticulire: operatorchar*() { char*out out=newchar[longueur+1] buffer[longueur]=0 strcpy(out,buffer) returnout } Lapplicationestbeaucoupplussimple: char*s=(char*)d printf("d=%s",s) Cetoprateursertconvertirpartranstypage(cast)unobjetdetypeChaineversletypechar*.

4.Fonctionsamies
Lesfonctionsamiesconstituentunrecoursintressantpourdesfonctionsnappartenantpasdesclassesmais devantaccderleurschampsprivs. Sagissant de fonctions et non de mthodes, elles nadmettent pas de pointeur this, aussi reoiventelles gnralementuneinstancedelaclassecommeparamtre, oubieninstancientelleslaclasseenquestion. classChaine { ... friendvoidafficher(Chaine&) } voidafficher(Chaine&ch) { for(inti=0i<ch.longueuri++)

- 9-

Openmirrors.com

printf("%c",ch.buffer[i]) } Dansladclarationdelaclasse Chaine,nousavonsvolontairementplacladclarationdamitisansprciser sil sagissait dun bloc public, priv ou protg. Puisque la fonction afficher() nest pas un membre, lemplacementdesadclarationnaaucuneimportance. Lesfonctionsamiesprsententunrelintrtpoursurchargerdesoprateursdontlepremierargumentnest paslaclassequelonestentraindedcrire.Lexemplehabituelconsistesurchargerloprateurdinjection,<<, appliqulaclasseostream: #include<iostream> usingnamespacestd classChaine { private: intt_buf char*buffer intlongueur public: friendostream&operator<<(ostream&out,Chaine&) ... } ostream&operator<<(ostream&out,Chaine&ch) { for(inti=0i<ch.longueuri++) out<<ch[i]//<<surchargpourostreametchar returnout } intmain(intargc,char*argv[]) { Chainec("bonjour") cout<<c<<"\n" //coutestunobjetdetypeostream } Signalons encore que les relations damiti ne sont pas hritables et quil est possible de dfinir des relations damitientremthodes.Cesusagessonttoutefois limiterpouraugmenterlaportabilitdesprogrammesvers deslangages neconnaissantpasceconcept.

5.Adressagerelatifetpointeursdemembres
LelangageC++ainaugurunenouvelletechniquedeprogrammation,laprogrammation ditedistribue.Cette techniqueconsisteappelersurunemachinedistantedesmthodesappliquesunobjetdontonneconnat quelinterface.Rappelonsnousquelinterfacequivautlensembledesmthodespubliquesduneclasse. Danspareillesituation,lanotiondepointeurausensonouslavonstudijusquprsentestinapplicable.Un pointeur dsigne une adresse appartenant la machine courante, voire au processus courant. Pour invoquer une mthode, ou accder un champ distance, un numro de membre serait plus appropri, car il est indpendant de lemplacement mmoire de lobjet. Bien entendu, au dernier moment, ce dplacement sera combinuneadressedebase,physiquecellel,pouraccderaumembreconsidr. Cette adresse de base nest gnralement pas connue du demandeur, il lobtient partir dun service de nommage,souventaumoyenduneURLapproprie.

- 10 -

Openmirrors.com

De nombreux middlewares orients objet, tels DCOM, Corba, RMI, ou .NET Remoting travaillent de cette faon. Beaucoupdentreeuxsontdailleursimplments enC++. LespointeursdemembresdeC++permettentjustementdexprimerlaccsunmembreentantquindexetnon entantquadressephysique.

a.Notations
Troisoprateurssontdvouslapriseenchargedeladressagerelatif,termedsignantenfaitlespointeurs demembres:
q

loprateur&(adresserelativedunmembre) loprateur.*(indirectionsuruneadresserelative) loprateur->*(indirectionsuruneadresserelative).

Lepremieroprateurretrouveladresserelativedunmembre,ensuivantlalogique habituelle: Chaine::*index_buffer=&Chaine::buffer LetypedupointeurestdoncClasse::*. Lesautresoprateurscombinentlindexunerfrenceouuneadressedobjet, encoreunefoisensuivant lalogiquehabituelle: Chainech ch.*index_buffer=newchar[100] Pourlespointeursrelatifsdemthode,lasignatureestaussiconformeauxpointeursdefonctionsusuels: int(Chaine::*pf_length)(void) pf_length=&Chaine::length

b.Constructiondunmiddlewareorientobjet

- 11 -

Nousproposonsdappliquerlespointeursdemembreslasimulationdunmiddlewareservantdistribuernos applications. Dans notre cas, tout fonctionnera lintrieur dun seul processus mais le raisonnement est aismentgnralisableuneinfrastructurepluscomplexe.

Un middleware est un ensemble de services systmes et rseau destin faire communiquer des logiciels fonctionnant sur des machines diffrentes. Il existe des middlewares orients messages (Mom) dont MQSeries (IBM) et MSMQ sont les plus connus. Il sagit ici dtudier un middleware orient objet(Moo).

NousallonscomplterleschmaprsentenintroductiondecettepartielaideduneclasseAdaptateur.Il sagit de la couche intermdiaire (middleware) charge de relayer les invocations du demandeur travers le "rseau" pouratteindrelevritableobjet,dsignsouslenomdobjetdistant. Pourledemandeur,lescnarioestlesuivant: 1.accsauservicedenommage(uneseuleinstanciation). 2.localisationdelobjetdistantcontrelafournitureduneURL. 3.accsauxmthodesdistantes. Pourillustrernotreexemple,nousproposonsuneinterfaceIObjetDistantpossdantdeuxmthodes,lune se contentant de renvoyer la chane de caractres "Bonjour tout le monde" et la seconde, plus labore, additionnantdeuxnombresentierstransmisenparamtres.

InterfaceIObjetDistant
VoicipourcommencerlinterfaceIObjetDistant,ellenecontientquedesmthodesvirtuellespures: #pragmaonce //Fichier:IObjetDistant //Propos:Cetteinterfacecontientlalistedesmthodes // pouvanttreappelesdistance. // Poursimplifier,touteslesmthodesontla // mmesignature. classIObjetDistant { public: virtualvoid*hello_world(intnb_params,void*params)=0 virtualvoid*somme(intnb_params,void*params)=0 }

ImplmentationObjetDistant
Nouspoursuivonsavecsonimplmentation,quiignoretotalementquelleestamenetre ObjetDistant distribue,cestdireinstancieetinvoquedistance: #pragmaonce //Fichier:ObjetDistant.h //Propos:Implmentationdelaclasse(interface)IObjetDistant #include"IObjetDistant.h" classObjetDistant:publicIObjetDistant { public: void*hello_world(intnb_params,void*params) void*somme(intnb_params,void*params)

- 12 -

Openmirrors.com

} Puis: #include".\objetdistant.h" //laclasseObjetDistantignoretotalementquellevatredistribue, //cestdireappeledistance. void*ObjetDistant::hello_world(intnb_params,void*params) { return"Bonjourtoutlemonde"//char*enfait } void*ObjetDistant::somme(intnb_params,void*params) { int*local_params=(int*)params //pourrenvoyerlersultat,unreinterpret_castauraittpossible //maispeurigoureux. int*resultat=newint[1] *resultat=local_params[0]+local_params[1] return(void*)resultat }

Adaptateur
Nousentronsensuitedanslevifdusujetaveclaclasse Adaptateur,quiutilise despointeursdemembres plusieursniveaux. Dans le cas dune relle distribution, il suffira de prvoir deux classes (skeleton et ladressedelobjetl_objetentrelesdeuxmachines. #pragmaonce //Fichier:Adaptateur.h //Propos:Classepermettantlinvocationscurisedemthodes // surlobjetdistant. #include"IObjetDistant.h" classAdaptateur:publicIObjetDistant { private: IObjetDistant*l_objet void*invoquer_par_numero( void*(IObjetDistant::*p_fonction)(int,void*), intnb_params,void*params) public: Adaptateur(IObjetDistant*adr) void*hello_world(intnb_params,void*params) void*somme(intnb_params,void*params) } Limplmentation suit naturellement. Notez comment les mthodes hello_world() contententdappelerlesvritablesimplmentationsaumoyendepointeursdemembres: #include".\adaptateur.h" Adaptateur::Adaptateur(IObjetDistant*adr) { l_objet=adr //mmoriseladressephysiquedelobjetdistant }
- 13 -

stub) translatant

et

somme()

se

void*Adaptateur::invoquer_par_numero( void*(IObjetDistant::*p_fonction)(int,void*),intnb_params,void*params) { //Lepointeurdemembreestutilisici. //Lappelcombineladressephysique,l_objet, //aveclindexdelafonctionappeler,p_fonction. return(l_objet>*p_fonction)(nb_params,params) } void*Adaptateur::hello_world(intnb_params,void*params) { returninvoquer_par_numero( &IObjetDistant::hello_world,//indexdelafonction nb_params,params) } void*Adaptateur::somme(intnb_params,void*params) { returninvoquer_par_numero( &IObjetDistant::somme, //indexdelafonction nb_params,params) }

Servicedenommage
Leservicedenommageestassezbasique.Ilassuresesresponsabilitsdenregistrementdobjet(publication) avec celles dun serveur dobjets (instanciation). Dans une situation relle de distribution, lURL dsignant lobjet ne se rsume pas un seul segment indiquant le nom. Figure souvent aussi ladresse du serveur o lobjetestinstanci. #pragmaonce //Fichier:Nommage.h //Propos:Systmedenregistrementetdelocalisation //dobjetsIObjetDistant // Poursimplifier,laclassepublieautomatiquementunobjet #include"IObjetDistant.h" #include"Adaptateur.h" classNommage { private: IObjetDistant*objet char*url public: voidpublier(IObjetDistant*objet,char*url) IObjetDistant*trouver(char*url) Nommage() ~Nommage() } Limplmentation est plus intressante que la dclaration car elle substitue, la demande du client, lobjet distantparsaversiondistribue(Adaptateur) : #include".\nommage.h" #include"objetdistant.h" #include<string.h> Nommage::Nommage() { //procdelapublicationdunnouvelobjet printf("Instanciationdunobjetsouslenomobjet1\n") publier(newObjetDistant,"objet1") }

- 14 -

Openmirrors.com

Nommage::~Nommage() { //dtruitlobjet deletethis>objet this>url=NULL } voidNommage::publier(IObjetDistant*objet,char*url) { printf("Publicationdunobjetsouslenom%s\n",url) this>objet=objet this>url=url } IObjetDistant*Nommage::trouver(char*url) { printf("Recherchedunobjetsouslenom%s\n",url) if(!strcmp(this>url,url)) returnnewAdaptateur(this>objet) printf("Objetpastrouv\n") returnNULL }

Programmeclient(demandeur)
La fonction main() fait office du demandeur dans notre schma. Elle est conforme au scnario voqu ci dessus: #include"nommage.h" intmain(intargc,char*argv[]) { //Utiliseleservicedenommageadhoc Nommagenommage //recherchedunobjetdistantcontreuneurl IObjetDistant*objet_distant=nommage.trouver("objet1") //appeldunemthodedistance char*s=(char*)objet_distant>hello_world(0,NULL) printf("s=%s\n",s) //appelduneautremthode,distancetoujours inttab[2]={3,8} int*r=(int*)objet_distant>somme(2,(void*)tab) printf("3+8=%d\n",*r) return0 } Aprstousceseffortsdeprogrammation,nousproposonslesrsultatsdelexcutionduprogramme:

- 15 -

6.Laprogrammationgnrique
Le langage C est trop proche de la machine pour laisser entrevoir une relle gnricit. Au mieux, lemploi de pointeursvoid*(parailleursintroduitsparC++)permetletravailsurdiffrentstypes.Maiscetteimprcisionse payecher,tantdupointdevuedelalisibilitdesprogrammes,quedelarobustesseoudesperformances. Lesmacrosnesontpasnonplusunesolutionviablepourlimplmentationdunalgorithmeindpendammentdu typage. Les macros dveloppent une philosophie inverse aux techniques de la compilation. Elles peuvent convenirdanscertainscassimplesmaisleurutilisationrelveleplussouventdubricolage. Reste que tous les algorithmes ne sont pas destins tre implments pour luniversalit des types de donnesC++.Pouruncertainnombredetypes,onsemetalorspenserentermesdefonctionspolymorphes. Finalement, les modles C++ constituent une solution bien plus lgante, trs simple et en mme temps trs sredemploi.Letypededonneestchoisiparleprogrammeurquivainstanciersonmodlelademande.Le langageC++proposedesmodlesdefonctionsetdesmodlesdeclassesetnousallons traitertourtources deuxaspects.

a.Modlesdefonctions
Tous les algorithmes ne se prtent pas la construction dun modle. Identifier un algorithme est une prcaution simple prendre car un modle bti mauvais escient peut diminuer les qualits dune implmentation. Pour lalgorithmie standard, la bibliothque S.T.L. a choisi dtre implmente sous forme de modles. Les conteneurs sont des structures dont le fonctionnement est bien entendu indpendant du type dobjet mmoriser. Pourleschanes,lechoixestplusdiscutable.LaS.T.L.proposedoncunmodledeclassespourdeschanes indpendantes du codage de caractre, ce qui autorise une ouverture vers des formats trs varis. Pour le codagelepluscourant,lASCII,laclassestringestuneimplmentationspcifique(voirlapartiesurlesmodles spcialiss). Ledomainenumriqueestparticulirementdemandeurdemodlesdefonctionsetdeclasses.Leprogrammeur peutainsichoisirentreprcision(long doubleoudouble)etrapidit(float)sansremettreenquestion lensemble de son programme. Certains algorithmes peuvent mme travailler avec des reprsentations de nombresplusspcifiques. Unesyntaxespcifiqueexplicitelaconstructiondunmodledefonction.Ilsagitdunprfixeindiquantlaliste des paramtres fournir linstanciation du modle. Les paramtres sont frquemment crits en majuscule pour les distinguer des variables dans le corps de la fonction, mais il ne sagit aucunement dune contrainte syntaxique. template<classT>Tcosinus(Ta) { inti Tcos=1 Tpa=a*a intpf=2 Tsigne=1 for(i=2i<=14i+=2) { cos+=signe*pa/pf signe=signe pa=pa*a*a pf=pf*(i+1) pf=pf*(i+2) } returncos }

- 16 -

Openmirrors.com

Lemodledoitavanttoutselirecommeunefonctiondontletypeestparamtrable.Imaginonsqueletypesoit double,nousobtenonslasignaturesuivante : doublecosinus(doublea) Dans le corps de la mthode, le type T scrit la place du type des variables ou des expressions. Ainsi les variablescos,paetsignepourraienttredesdoubleoudesfloat. Ilvasansdirequelalistedesparamtresdumodle,spcifieentrelessymboles <et>peuttrediffrente delalistedesparamtresdelafonction,ainsiqueleprouvelexemplesuivant: template<classT>voidvecteur_add(T*vect,intn,Tvaleur) { for(inti=0i<ni++) vect[i]+=valeur } Pour utiliser un modle de fonction, il faut simplement appeler la fonction avec des arguments qui correspondentceuxquelleattend.Celaapoureffetdedvelopperlemodledansuneversionadapteaux types des arguments transmis, arguments qui peuvent naturellement tre des valeurs littrales, des expressionsouencoredesvariables,enrespectantlesrgleshabituellesdappeldefonction. Nous proposons un premier exemple avec notre modle de fonction cosinus. Dans cet exemple, nous comparonsladureducalculdunnombreimportantdecosinus,ainsiquelaprcisionducalcul.Suivantletype de largument pass la fonction cosinus float ou long double le compilateur produira plusieurs versionspartirdummemodle. intmain(intargc,char*argv[]) { time_t start,finish double result,elapsed_time intN=30000000 //30000000cosinusenfloat time(&start) floata1=3.14159265358F/2 floatr1 for(inti=0i<Ni++) r1=cosinus(a1) time(&finish) elapsed_time=difftime(finish,start) printf("%dcosinusenfloat,r=%f\n",N,r1) printf("Dureducalcul: %6.0fsecondes.\n",elapsed_time) //30000000cosinusenlongdouble time(&start) longdoublea2=3.14159265358/2 longdoubler2 for(i=0i<Ni++) r2=cosinus(a2) time(&finish) elapsed_time=difftime(finish,start) printf("\n%dcosinusenlongdouble,%g\n",N,r2) printf("Dureducalcul: %6.0fsecondes.\n",elapsed_time) return0 } Nousconstatonsdesdiffrencessensiblesdansladureetlaqualitducalcul.

- 17 -

Openmirrors.com

Munisdecesinformations,leconcepteuretleprogrammeurpeuventchoisirletypededonneleplusadapt leurcahierdescharges. Nous proposons maintenant une application du modle

vecteur_add().

Un diteur de code source

performantguideleprogrammeurdanslasaisiedesargumentspassslafonction: Voilmaintenantlecodecomplt: doublevaleurs[]={3,9,8.2,5} vecteur_add(valeurs,4,3.0) Letroisimeparamtredelafonctiondoitimprativementtredutypecorrespondant autableaudevaleurs, dansnotrecaspourdesraisonsdhomognitdescalculs,maisaussipourdesraisonssyntaxiques. Fournirunevaleurlittraleambigupeutprovoquerunavertissement,voireuneerreurdecompilation: vecteur_add(valeurs,4,3) //3littraledentier Sachantquecetappeldoittreleplusexplicitepossible,nousendduisonsquelemodledefonctionpeut tresurcharg,autrementdit,polymorphe. Lasurchargedunmodledefonctionconsistedcrireuneautrefonctionportantlemmenommaisrecevant desparamtresdiffrents. template<classT>voidvecteur_add(T&vect,Tvaleur) { vect+=valeur } Lesparamtresdumodlepeuventgalementvarierduneversionlautre. lappeldelafonction,lecompilateurdcidequelleestlaversionlaplusapproprie. Nousobtenonsfinalementlecodesuivantpourinstanciernotrenouveaumodle : vecteur_add(valeurs[0],(double)3) Lorsquune optimisation de limplmentation est possible pour un type donn, il est dusage de spcialiser le modle. Ainsi, nous pourrions crire une version spcifique de cosinus() sappuyant sur la bibliothque mathmatiquelorsqueletypeestdouble: template<double>doublecosinus(doublea) { returncos(a) } En prsence du modle gnral template<class

T> T cosinus(T a)etdelaversionspcialise,le

- 18 -

Openmirrors.com

compilateurprendralaversionspcialisesiletypeconsidrcorrespondexactement. La classe string de la S.T.L. est un exemple de modle spcialis de classe basic_string appliqu aux char.Ilvasansdirequelesmodlesdeclassesontaussilapossibilitdtrespcialiss.

b.Modlesdeclasses
La spcification dun modle de classe suit exactement la mme logique quun modle de fonction. Nous donnons ici lexemple dune classe, Matrice, qui peut tre implmente avec diffrents types, au choix du programmeur. template<classT>classMatrice { protected: intnb_vecteur,t_vecteur T*valeurs public: //constructeur Matrice(intnb,intt):nb_vecteur(nb),t_vecteur(t) { valeurs=newT[nb_vecteur*t_vecteur] } //rfrencesurletypeT T&at(intvecteur,intvaleur) { returnvaleurs[vecteur*t_vecteur+valeur] } //transtypagededoubleversT voidrandom() { intvect,val for(vect=0vect<nb_vecteurvect++) { for(val=0val<t_vecteurval++) at(vect,val)=(T)(((double)rand()/RAND_MAX)*100.0) } } //affichage voidafficher() { intvect,val cout.precision(2) cout<<fixed for(vect=0vect<nb_vecteurvect++) { for(val=0val<t_vecteurval++) { cout.width(6) cout<<at(vect,val) cout<<(val<t_vecteur1?",":"") } cout<<endl } } } LesmembresdelaclasseMatriceillustrentdiffrentsaspectsdesmodlesdeclasse. La classe contient un champ de type T*, comme nous avions pu dclarer prcdemment une variable locale danslecorpsdelafonctioncosinus.

- 19 -

Lamthodeat()renvoieunerfrenceversuntypeT,notT&bienentendu. LeparamtredumodleTpeuttreutilispoureffectuerdesconversionsdetype(transtypage)lamthode random()explicitelefonctionnementdelanotation(T)pourinitialiserdemanirealatoirechaquevaleurde lamatrice. Quantlafonction afficher(),elleestmoinstrivialequellenyparat.Sinousdevionsutiliser printf() pour afficher sur la console nos valeurs, nous devrions tester le type rellement utilis afin de choisir un formateur appropri, %f ou %g par exemple. Il se trouve que loprateur << est surcharg pour les types primitifsdeC++,maispasprintf. Sanslaclasse ostream,nousaurionsdautresfaonsdersoudreceproblme remplacer ostream,cequi peut se rvler fastidieux (et inutile). Nous pourrions galement crer un modle de fonction afficher(), externelaclasse,puislespcialiser.Oubiennouspourrionsoprerunespcialisationpartielledelaclasse Matrice.Maissurcepoint,certainscompilateurssemontrentpluscoopratifsquedautres.Enattendantla solution indique au paragraphe consacr aux modles de fonction, nous nous contentons de la version actuelledelamthodeafficher()quifonctionnetrsbienaveclestypesusuels. Si nous avions choisi une dfinition dporte de mthode, elle deviendrait un modle de fonction. Il conviendraitalorsdefairefigurerlesparamtresdumodle danslenomdelaclasse: template<classT>T&Matrice<T>::at(intvecteur,intvaleur) { returnvaleurs[vecteur*t_vecteur+valeur] } Linstanciation se passe presque comme pour une fonction, si ce nest quil faut fournir explicitement le type associaumodle. Matrice<double>m(3,4) m.random() m.afficher() Notreaffichageproduitlesrsultatssuivants:

LasyntaxeC++admetdesvaleurspardfautpourladfinitiondemodles : template<classT=float>classMatrice ... Linstanciationdelaclasse(etdumodle)peutalorssefairesansprciserdetype,floattantlavaleurpar dfaut: Matrice<>mf(5,5) //matricedefloat Nous en arrivons au sujet dlicat de la spcialisation partielle. Prcisons dabord que les compilateurs C++ supportentplusoumoinsbiencemcanisme.Danscertainscas,lacompilationnaboutitpasoubienlecode gnresterron,cequipeutservlerparticulirementgnant. Parmi les compilateurs les plus rguliers, citons le GNU C++, le compilateur Microsoft VC++ et la dernire version fournie par la socit Intel. Dautres sont videmment conformes 100 % la norme C++ mais les produitscitsontfaitlapreuvedeleurrigueurdanslaversionindique.Attention,carcelanapastoujourst
- 20 -

Openmirrors.com

lecas,notammentpourleVC++(avant2003)etlecompilateurdIntel. Nousallonscommencerparcrerunmodledefonctionpourlamthode afficher(), cestdireutiliserla dfinitiondporte: template<classT>voidMatrice<T>::afficher() { intvect,val cout.precision(2) cout<<fixed for(vect=0vect<nb_vecteurvect++) { for(val=0val<t_vecteurval++) { cout.width(6) cout<<at(vect,val) cout<<(val<t_vecteur1?",":"") } cout<<endl } } Cetteversionconstituelaversiondesecours,laplusgnrale.Ilsepeutquellesoitimpossibleconstruire, maisdansnotrecas,coutsecomporteassezbienaveclensembledestypesusuels. Nouspoursuivonsparlafournitureduneversionspcifiquelaffichagedesfloat.Onnotequelemodlede fonctionaperdusonparamtremaisqueletypedeclasse,Matrice<float>,estindiqu: template<>voidMatrice<float>::afficher() { intvect,val cout.precision(2) cout<<fixed printf("Affichagespcifiquefloat\n") for(vect=0vect<nb_vecteurvect++) { for(val=0val<t_vecteurval++) { printf("%f",at(vect,val)) printf((val<t_vecteur1?",":"")) } printf("\n") } } Commepourlesmodlesdefonctionsspcialiss,lecompilateurutiliseradeprfrencecetteversionpourune matricedefloatetlaversionlaplusgnraledanslesautrescas.

- 21 -

Introduction
Linventeur de C++, Bjarne Stroustrup, a travaill sur des projets de dveloppement trs importants. Les premires applications de C++ ont t ralises dans le domaine des tlcommunications, domaine qui rclame des outils de haut niveau. Certes les classes favorisent labstraction, mais un programme nest pas compos uniquementdinterfacesetdimplmentations. la conception du logiciel, le dveloppeur doit dterminer la limite de rutilisation des ralisations prcdentes. Derrireletermerutilisation,onentendsouventlefaitdecopier/collercertainespartiesdecodesource.Quels sontleslmentsquiseprtentlemieuxcetteopration?Lesclasses,naturellement,lemodleorientobjet tant construit autour du concept de rutilisation. Mais on trouve galement des structures de donnes, des fonctionsspcialises,despicesalgorithmiquesdenaturesdiverses,ntantpasencoreparvenueslamaturit ncessairelaformationdeclasses. Lesmodulesdcriventundcoupageassezphysiquedesprogrammes.Ainsi,unfichierdecodesource.hou.cpp peutil tre considr comme module. Toutefois, lassemblage de modules nest pas une chose aise, surtout lorsquils proviennent de ralisations prcdentes. Il peut survenir des conflits de noms, de fonctions, des diffrences de reprsentation de types, ainsi quune dstructuration du programme, tous les membres dun moduleapparaissantaummeplanquelesautres. CestpourcesraisonsqueC++proposelesespacesdenomsilsagitdensemblesdevariables,defonctions,de classes et de sousespaces de nom, membres obissant des rgles de visibilit. Ainsi le programmeur pourra organiser et ordonner son dveloppement, fruit dun travail nouveau et dune agrgation de codes sources anciens. Langage de haut niveau, C++ incite aussi prendre du recul sur les dveloppements achevs. On ne peut que stonner que les mmes algorithmes se retrouvent dans tous les logiciels. Un algorithme, cest un lment caractris, au comportement dtermin. Pourquoi ne pas le formaliser laide dune ou de plusieurs classes ? Cestprcismentundesrlestenusparlabibliothquestandard,laStandardTemplateLibrary. dire vrai, la S.T.L. constitue tout aussi bien un ensemble doutils pour le programmeur quune preuve du fonctionnementdumodleorientobjet.

Openmirrors.com

- 1-

Organisationdesprogrammes
1.Espacesdenoms
LelangageCneconnatquedeuxniveauxdeporte:leniveauglobal,auquellafonctionmain()appartient,et le niveau local, destin aux instructions et aux variables locales. Avec lapparition de classes, un niveau supplmentairesestinstall,celuidestinlenregistrementdeschampsetdesmthodes.Puislintroductionde ladrivation(hritage)etdesmembresstatiquesaencorenuanclapalettedesniveauxdeporte. Pourlesraisonsvoquesenintroduction,ildevenaitncessairedestructurerlespaceglobal.Pournenretenir quune,lespaceglobalesttroprisqupourlerangementdevariablesetdefonctionsprovenantdeprogrammes anciens.Lesconflitssontinvitables. Onpeutalorspartitionnercetespacegloballaidedespacesdenoms: namespace Batiment { double longueur; void mesurer() { longueur=50.3; } } ; namespace Chaines { int longueur; void calcule_longueur(char*s) { longueur=strlen(s); } } ; Deuxespacesdenoms, Batimentet Chaines,contiennenttouslesdeuxunevariablenomme longueur, dailleurs de type diffrent. Les fonctions mesurer()et calcule_longueur() utilisent toujours la bonne version, car la rgle daccessibilit est galement vrifie dans les espaces de noms : le compilateur cherche toujourslaversionlaplusproche. Pour utiliser lune ou lautre de ces fonctions, la fonction main() doit recourir lopration de rsolution de porte::oubienuneinstructionusing:

int main(int argc, char* argv[]) { Batiment::mesurer(); printf("La longueur du btiment est %g\n",Batiment::longueur); using Chaines::longueur; Chaines::calcule_longueur("bonjour"); printf("La longueur de la chane est %d\n",longueur); return 0; } Nousremarquonsquelappel dunefonctiondclarelintrieur dunespacedenomsressemblebeaucoup celuidunemthodestatique.Cetteanalogieseprolongepourlaccsunevariable,quelonpeutcomparer laccsunchampstatique. Lasyntaxe usingestutilepourcrerdesalias.Avantladclarationusing Chaines::longueur,ilnexiste pour main() aucune variable accessible. Ensuite, jusqu nouvel ordre, longueur est devenu un alias de Chaines::longueur.
- 1-

a.Utilisationcompltedunespacedenoms
Il nest pas rare davoir utiliser tous les membres appartenant un espace de noms. Il est dautant plus inutiledecrerunaliaspourchacundeuxquelopration risquedtrelafoisfastidieuseetvaine.Lasyntaxe using namespace convientbienmieux:

#include <iostream> using namespace std;

// utilise tout lespace de noms std

Cettedclarationestfortedesignification,etilconvientdebienladiffrencierdeladirective #include.Alors que la directive #include inclut le fichier indiqu, iostream en lespce, la directive using namespace credesaliaspourlesmembresdelespacedenomsstd,dclarsdansiostream. Autrementdit,ladirective usingnimportepas,ninclutpas,elleestuniquementdestinesimplifierlcriture. Sanssonemploi,nousdevrionsprfixerchaquelmentparstd. Ainsiestilpluscommodedcrire: cout << "Bonjour\n"; que: std::cout << "Bonjour\n"; Toutefois, en cas de conflit entre deux espaces de noms entirement utiliss, lutilisation de loprateur de rsolutiondeportelvetouteambigut: namespace Chaines { int longueur; void calcule_longueur(char*s) { longueur=strlen(s); } bool cout; } ; ... using namespace Chaines; std::cout << "Pas de doute sur cette ligne\n";

b.Espacedenomsrpartisurplusieursfichiers
Il est tout fait juste de dfinir le mme espace de noms au travers de diffrents fichiers de code source, chacundeuxcontribuantlenrichirdesesmembres. // tri.h namespace Algo { void tri_rapide(int*valeurs); void tri_bulle(int*valeurs); } ; // pile.h namespace Algo

- 2-

Openmirrors.com

{ class Pile { protected: int*valeurs; int nb_elements; int max; public: Pile(); void empiler(int v); int depiler(); bool pile_vide(); } ; } ; ... #include "tri.h" #include "pile.h" using namespace Algo; int main(int argc, char* argv[]) { return 0; } Le compilateur C++ de Microsoft, lorsquil fonctionne en mode manag (avec les extensions .NET), conserve lorganisation des modules au niveau du code objet et de lexcutable. Du mme coup, la diffrence entre la directive#includeetusing namespacedevientdeplusenplustnuelelangageC#adoncfaitlechoix derunirlesdeuxdirectivesenuneseule,using,unpeulamaniredeJavaaveclinstructionimport. Dautrepart,ilesttoutfaitpossibledeconserverlasparationentredclarationetdfinition.Lorganisation ducodesourceen.heten.cppfonctionneainsicommelaccoutume:

// tri.cpp #include "tri.h" void Algo::tri_rapide(int*valeurs) { }

c.Relationentreclasseetespacedenoms
Finalement, classe et espace de noms sont assez proches. Doiton considrer lespace de noms comme une classenecontenantquedesmembresstatiquesoulaclassecommeunmoduleinstanciable?Enralit,une classe C++ engendre son propre espace de noms. Voil qui claire la fois lutilisation de loprateur de rsolutiondeporteetsurtout,quiexpliqueladclarationdeschampsstatiques. Silechampstatiquetaiteffectivement"instanci"aumomentdesadclarationdanslecorpsdelaclasse,ce champ pourrait exister en plusieurs versions : chaque fichier .cpp incluant la dclaration de la classe au traversdun fichier dentte.hprovoquantsaduplication.Ilfautdoncinstanciercechamp,uneetuneseule fois,dansunfichier.cpp,normalementceluiquiporteladfinitiondesmthodesdelaclasse. Lamiseengardevautaussipourlesespacesdenoms.Siunespaceestdfinidansunfichier.h,ilfautveiller ne pas dclarer de variable dans cette partie, autrement la variable pourrait se trouver dfinie plusieurs endroits. Commentdoncindiqueraulecteurlexistencedunevariabledansladclaration.hdunespacedenoms,telle coutetcinpourlespacestd,sachantquellenepeutpastredfiniecetendroit?Larponseestencore lemotcl static.Lemotcl staticprvientlecompilateurquecettevariablenedoitpastreduplique parchaquefichierincluantladfinitiondelespacedenoms. Toutefois,etcontrairementauxclasses,lavariablenapastredfiniedansunfichier.cpp:

- 3-

// pile.h namespace Algo { class Pile { protected: int*valeurs; int nb_elements; int max; public: Pile(); void empiler(int v); int depiler(); bool pile_vide(); } ; static int T_PILE=3; } ; // pile.cpp #include "pile.h" // ne pas dclarer ici la variable Algo::T_PILE

d.Dclarationdesousespacesdenoms
Unespacedenomspeuttrsbiencontenirdautresespacesdenoms,voiredesclassesetdesstructures. namespace Algo { namespace Structures { class Tableau {}; class Pile {}; } ; namespace Fonctions { using namespace Structures; void tri_rapide(Tableau t); } ; } ; Il est absolument ncessaire dutiliser lespace Structures dans lespace Fonctions pour atteindre la dfinitiondelaclasseTableau.lextrieurdelespace Algo,plusieursdirectivespeuventtrencessaires pourutiliserlensembledesmembresdclarsdansAlgo:

using namespace Algo; using namespace Algo::Structures; int main(int argc, char* argv[]) { Tableau t; return 0; }

2.PrsentationdelaSTL
LabibliothquestandardatcredanslebutdaiderleprogrammeurC++produiredeslogicielsdehaut niveau.BienqueleslangagesCetC++soientuniversellementsupportspardesinterfacesdeprogrammation (API) de toutes sortes, la partie algorithmique reste un peu en retrait tant les instructions sont concises et
- 4-

Openmirrors.com

prochesdelamachine.LeschanesdecaractresASCIIZ(terminesparunoctetdevaleurzro)sontunbon exemple de situation pour laquelle le programmeur se trouve dmuni. De nos jours, la question nest plus de savoir comment les chanes sont reprsentes mais plutt comment les utiliser le mieux possible. Il faut conserverdebonnesperformances,certes,maisilfautaussisaccommoderdescodagesinternationaux. Aussi, la gnralisation de la programmation oriente objet a fini par dnaturer le travail des dveloppeurs. Lalgorithmie a t abandonne au profit dune vision abstraite, base dinterfaces. Bjarne Stroustrup avait peuttresentiquecettetransformationdumtierdeprogrammeurinterviendraitrapidement,aussilaS.T.Lft ellerenduedisponibletrsviteaprslapublicationdulangage. La S.T.L. propose un certain nombre de thmes pour aider le programmeur : les chanes de caractres, les entressorties,lesalgorithmesetleursstructuresdedonnes,lecalculnumrique.Sicelanesuffisaitpasvos travaux,rappelezvousquelleatbtieenC++,aussitoutprogrammeurestillibredelacomplteravecses proprescontributions.

- 5-

FluxC++(entressorties)
La S.T.L. gre de nombreux aspects des entressorties. Elle inaugure une faon de programmer pour rendre persistantslesnouveauxtypesdfinislaidedulangageC++. Lquipe qui la conue la fin des annes 80 a eu le souci dtre conforme aux techniques en vigueur et de produireuntravailquirsisteraitaufildesans. Il faut reconnatre que la gestion des fichiers a normment volu depuis lintroduction de la bibliothque standard:lesbasesdedonnesrelationnellesontremplaclesfichiersstructurs,etlesinterfacesgraphiquesse sontimposesfaceauxconsolesorientescaractres. Toutefois, les axes pris pour le dveloppement de la S.T.L. taient les bons. Si lutilisation des flux est un peu tombe en dsutude, leur tude permet dy voir plus clair pour produire une nouvelle gnration dentres sorties. Aussi, le terminal en mode caractres continue son existence, la vitalit des systmes Linux en est la preuve.

1.FluxC++
Pourbiencommencerlapprentissagedesentressorties,ilfautfaireladiffrenceentrefichieretflux.Unfichier estcaractrisparunnom,unemplacement,desdroitsdaccsetparfoisaussiunpriphrique.Unflux stream enanglaisestuncontenu,uneinformationquiestlueoucriteparleprogramme.Cetteinformationpeuttre deplusoumoinshautniveau.labase,ontrouvenaturellementloctet,puisceluicisespcialiseendonnede type entier, dcimal, boolen, chane... Enfin, on peut crer des enregistrements composs dinformations trs diverses.Ilesttoutfaitlogiquedeconsidrerquelaformedecesenregistrementscorrespondlaformation duneclasse,cestdireduntypeausensC++. LesfluxC++quelonappelleparfoisflotsenlanguefranaisesontorganissentroisniveauxlepremier,le plus abstrait, regroupe les ios_base, format dentresortie indpendant de ltat et du formatage. Puis on trouveleniveaubasic_ios,versionintgrantlanotiondeparamtrergional(localeenanglais). Enfin, nous trouvons le niveau basic_iostream, groupe de modles de classes destines supporter le formatagedetouslestypesdebaseconnusparlelangage.Cestceniveauquenoustravaillerons. Lacirculationdesinformationssefaitdanslecadredunsystmedemmoiretampon(buffer),supportparla classebasic_streambuf. LeprogrammeurutilisateurdelaS.T.L.connatgnralementlesfluxstandard, cout, cinet cerr, ainsi que lesclassesistreametostream.

2.Fluxintgrs
Les flux intgrs, cout, cin et cerr, offrent de nombreuses faons dchanger des informations. Les objets cout et cerr sont des instances de la classe ostream tandis que cin est une instance de la classe istream. Cesclassesproposentlesoprateurs<<et>>pourlireoucrirelestypesprimitifs :

cout << "Bonjour"; // char* int age; cin >> age; Nous avons vu quil tait tout fait possible denrichir le registre des types supports en surchargeant cet oprateurlaidedunefonctionamie: friend ostream& operator << (ostream& out,Type & t); friend istream& operator >> (istream& out,Type & t);

Openmirrors.com

- 1-

3.tatdunflux
Lesfluxistreametostreamdisposentdemthodespourcaractriserleurtat.

setstate(iostate) clear(iostate)

Ajouteunindicateurdtat Dfinitlesindicateursdtat

4.Miseenforme
La classe

ios_base propose un certain nombre de contrles de formatage applicables par la suite. Ces

contrlessutilisentcommedesinterrupteurs.Onappliqueunformatagequiresteactifjusqunouvelordre.

skipws left right internal boolalpha dec hex oct scientific fixed showbase showpoint showpos uppercase adjustfield basefield floatfield flags() flags(fmtflags) setf(fmtflags) unsetf(fmtflags)

Sautelespacedanslentre Ajustelechampenleremplissantaprslavaleur Remplitavantlavaleur Remplitentrelesigneetlavaleur Utiliseunereprsentationsymboliquepourtrueetfalse Basedcimale Basehexadcimale Baseoctale Notationavecvirguleflottante Formatvirgulefixedddd.dd Ajouteunprfixeindiquantlabase Imprimeleszrosdroite Indique+pourlesnombrespositifs AfficheEpluttquee Lilajustementduchamp:internal,leftouright Lilabase:dec,hexouoct Lilasortieenflottant:fixedouscientific Litlesindicateurs Dfinitlesindicateurs Ajouteunindicateur Annuleunindicateur

laidedesformateurs,nouspouvonsaffichertemporairementenhexadcimalunevaleurentire:

- 2-

cout << hex << 4096 << "\n"; cout << 8192 << "\n"; // toujours en hexa cout << dec << 4096 << "\n"; // repasse en dcimal La fonction width() peut spcifier lespace destin la prochaine opration dentresortie, ce qui est utile pourlaffichagedenombres.Lamthodefill()fournitlecaractrederemplissage:

cout.width(8); cout.fill(#); cout << 3.1415; LaS.T.L.proposegalementuncertainnombredemanipulateursdeflux,commeflush,quiassurelapurgedu flux,cestdirelevidagedutampondestinationdupriphriquedesortie: cout << 3.1415 << flush;

5.Fluxdefichiers
Pour travailler avec les fichiers dune autre manire que le ferait le langage C, la S.T.L. propose les classes ofstream et ifstream. Il sagit, pour les fichiers, dadaptation par hritage des classes ostream et istream. Voicipourcommencerunprogrammedecopiedefichierutilisantcesclasses: #include <fstream> using namespace std; int main(int argc, char* argv[]) { if(argc<3) return 1; // ouverture des flux ifstream src(argv[1]); if(!src) { cerr << "Impossible douvrir " << argv[1] << endl; return 2; } ofstream dst(argv[2]); if(!dst) { cerr << "Impossible douvrir " << argv[2] << endl; return 2; }

// copie char c; while(src.get(c)) dst.put(c); // fermeture src.close(); dst.close(); return 0; }

Openmirrors.com

- 3-

Openmirrors.com

Les classes possdent la mme logique que leurs anctres ostream et istream. La boucle de copie aurait doncputrecritedelamaniresuivante :

// copie char c; while(! src.eof()) { src >> c; dst << c; } IlexisteunediffrenceimportanteaveclAPIproposeparlelangageC:lesquantitsnumriquessonttraites comme des chanes de caractres. Louverture en mode binaire ne changerait rien ce comportement, ofstreamtantspcialisdansletraitementdescaractres(char).

sortie.open("fichier.bin",ios_base::binary); sortie << x; // toujours la chane "43" sortie.close();

6.Fluxdechanes
Un flux peut tre attach une chane de caractres ( string) plutt qu un priphrique. Cette opration rend le programme plus gnrique et permet dutiliser les squences de formatage pour des messages avant uneimpressiondansunjournal(log),unfichier,uncran... Pourutiliserlesfluxdechanes,ilfautinclurelefichier<sstream>,ainsiquelexposeleprogrammesuivant:

#include <math.h> #include <sstream> #include <iostream> using namespace std; int main(int argc, char* argv[]) { ostringstream out; double pi = 3.14159265358; out.width(8); out.setf(ios_base::scientific); out << "cos(pi/4)= " << cos(pi/4) << endl; // sortie du flux sur une string string res = out.str(); // finalement affichage cout << res; // relecture istringstream in(res); double x; string msg; in >> msg >> x; cout << "msg=" << msg << endl << "x=" << x; return 0; } Lexcution de ce programme est assez intressante. la relecture, le scanner stoppe la chane msg sur le caractreespaceinsraprslespace.Dautrepart,nousretrouvonsbienlavaleurdelavariablex,soitracine de2sur2(0.707):

Openmirrors.com

- 4-

7.Lesparamtreslocaux
Laclasselocaledfinituncertainnombredefacettesdestinestreprisesencomptelorsduformatagepar lesfluxdelaS.T.L.Chaque facettesechargedun type de formatage pour une culture particulire. Imaginons pourlexempleleformatagedunnombreenmonnaiepourunpaysdelazoneEuro,oubienleformatagedela dateenlangueallemande. Cetteclassesertchoisiruncertainnombredefacettesenvuedeleurapplicationsurunfluxparlintermdiaire de la mthode imbue(). Elle contient aussi un certain nombre de mthodes statiques pour comparer deux classeslocales entreelles. PourillustrerlefonctionnementsubtildesclasseslocalesdfiniesparlaS.T.L.,nousallonsconcevoirunefacette pourafficherdesnombresenEuro.Lexemplepourraensuitetreamnagpoursupporterdiffrentssymboles montaires. Pour commencer, nous allons inclure les fichiers correspondant lemploi de classe locale, ainsi que lentte fstream,carnotresystmeutiliselefluxcoutpourlaffichage:

#include <fstream> #include <locale> using namespace std; prsent,nousdfinissonsunestructure(classe) Monnaiepourquecoutfasseladistinctionentrelaffichage dundoublesansunitetlaffichagedunedonnedetypeMonnaie:

struct Monnaie { Monnaie (const double montant) : m(montant) {} double m; }; Vousaurezsansdouteremarqulestyledinitialisationdelavariablem,trscourantenC++(cestcommesilon utilisaitleconstructeurdelavariablem). Nouspoursuivonsparlcrituredunefacettedestineformaterladonnepourlaffichage.Ilestfaciledesous classercettefacetteenvuedelarendreparamtrable. class monnaie_put: public locale::facet { public: static locale::id id; monnaie_put (std::size_t refs = 0) : std::locale::facet (refs) { } string put (const double &m) const { char buf[50]; sprintf(buf,"%g Euro",m); return string(buf);

Openmirrors.com

- 5-

} }; locale::id monnaie_put::id; Suivantlorganisationdevotreprogramme,ladfinitionduchamp monnaie_put::iddevrasefairedansun fichier.cppspar. Nous allons maintenant surcharger loprateur <<poursupporterlinsertion dunobjet Monnaiedansunflux ostream. Sagissant dune structure dont tous les champs sont publics, la dclaration damiti nest pas ncessaire. Nousobtenonsdirectement : ostream& operator<< (ostream& os, const Monnaie& mon) { std::locale loc = os.getloc (); const monnaie_put& ppFacet = std::use_facet<monnaie_put> (loc); os << ppFacet.put(mon.m); return (os); } Ilnenousresteplusquappliquercetteconstructionettesterleprogramme. int main(int argc, char* argv[]) { cout.imbue (locale (locale::classic (), new monnaie_put)); Monnaie m(30); cout << m; // affiche 30 Euro return 0; }

- 6-

Classestringpourlareprsentationdeschanesde caractres
Cestunfaittonnant,lamajoritdestraitsdalgorithmientudiepasleschanesentantquetelles.Lastructure dedonnessenrapprochantleplusresteletableaupourlequelonaimaginunegrandequantitdeproblmes etdesolutions. LelangageCestrestfidlecetteapprocheetconsidreleschanescommedestableauxdecaractres.Ses concepteursontfaitdeuxchoiximportantslalongueurdunechaneestlimitecelleallouepourletableau,et lecodageestceluidescaractresduC,utilisantlatableASCII.Danslamesureoilnexiste pas de moyen de dterminer la taille dun tableau autrement quen utilisant une variable supplmentaire, les concepteurs du langage C ont imagin de terminer leurs chanes par un caractre spcial, de valeur nulle. Il est vrai que ce caractrena pas de fonction dans la table ASCII, mais les chanes du C sont devenues trs spcialises, donc, trsloindelalgorithmiegnrale. LauteurdeC++,BjarneStroustrup,asouhaitpoursonlangageunecompatibilitaveclelangageCmaisaussi uneamliorationducodageprenantencomptediffrentsformatsdecodage,ASCIIounon.

1.ReprsentationdeschanesdanslaS.T.L
Pour la S.T.L., une chane est un ensemble ordonn de caractres. Une chane sapparentedoncfortementau vector, classe galement prsente dans la bibliothque. Toutefois, la chane dveloppe des accs et des traitementsquiluisontpropres,soutenantainsimieuxlesalgorithmestraduitsenC++. Leschanesdelabibliothquestandardutilisentuneclassedecaractrespoursaffranchirducodage.LaS.T.L. fournitlesupportpourlescaractresASCII(char)etpourlescaractrestendus( wchar_t),maisonpourrait trs bien envisager de dvelopper dautres formats destins des algorithmes base de chanes. Le gnie gntique emploie des chanes composes de caractres spcifiques, A, C, G, T. Le codage avec un char est donc trs coteux en terme despace, puisque deux bits suffisent exprimer un tel vocabulaire, dautant plus quelessquencesdegnespeuventconcernerplusieurscentainesdemilliersdebases.Onpeutaussispcifier descaractresadaptsdesalphabetsnonlatins,pourlesquelslatableASCIIestinefficace. Laclassebasic_stringutiliseunvecteur(vector)pourrangerlescaractresenmmoire.Limplmentation ainsiquelesperformancespeuventvarierdunenvironnementlautre,suivantlaqualitdelaprogrammation. Toutefois, la consquence la plus intressante de lutilisation du vecteur est que la longueur des chanes est devenuevariable.VoilunelimitationdulangageCenfindpasse. En contrepartie, laccs aux caractres nest pas aussi direct. Des mthodes spcifiques ont t ajoutes, la classe vectorneproposantpaslencessaire.Unechaneproposegalementdesmthodesspcifiquespour travaillersurdesintervallesdecaractres(extractiondesouschanes,recherche),alorsquelevecteurestplutt destinlaccsindividuelauxlmentsquilcontient. Pourleprogrammeurutilisateurdelabibliothquestandard,lapartiechanedecaractressersumepeuttre laclassestring,destineamliorerlantique char*.Maisilyaaussilematrielncessairepourallerplus loin.

2.Modedemploidelaclassestring
Laclasse stringestunespcialisationdumodle basic_stringpourlescaractreshabituels, char.Les caractristiques exposes par la suite sont galement valides pour dautres formats de chanes issus de basic_string.

a.Fonctionsdebase
Unechaneseconstruitdediffrentesmanires,partirdunelittraledechaneoucaractreparcaractre. Lorsquelachaneestcre,oncherchesouvent accdercertainsdesescaractres.

Constructeurs
- 1-

Openmirrors.com

Diffrents constructeurs sont disponibles pour initialiser une chane de type string. Une chane peut tre initialisepartirdunechaneC( char*),partir duneautrechaneoudunmorceaudechane:

#include <iostream> #include <string> using namespace std; int main(int argc, char* argv[]) { string s1; // chane vide string s2 = ""; // vide aussi string s3 = "Bonjour"; string s4(s3); // copie s3 dans s4 // copie tous les caractres de s3 dans s5 string s5(s3.begin(),s3.end()); string s6(s3,0,s3.length()); // idem cout << s1 << " " << s2 << endl; cout << s3 << " " << s4 << endl; cout << s5 << " " << s6 << endl; return 0; } Les mthodes begin() et end() expriment des positions de souschane. Il ne sagit pas de valeurs entires, mais de rfrences des caractres. Le constructeur utilis pour s6 est donc diffrent de celui employpours5quifonctionneavecdesindicesdecaractres.

Itrateurs
La classe string fournit deux itrateurs destins litration ordinaire et litration inverse. Toutefois, les mthodes spcifiques de la classe string donnent de meilleures implmentations pour les algorithmes spcifiquesauxchanes. Ilestcependantpossibledemployercesitrateurs,begin/end(respectivementrbegin,rend):

#include <algorithm> #include <iostream> #include <string> using namespace std; int main(int argc, char* argv[]) { string s="Bonjour la STL"; string::iterator p=find(s.begin(),s.end(),n); if(p==s.end()) cout << "Il nexiste pas de caractre n dans s" << endl; else cout << "Le caractre n se trouve dans la chane : " << *p; return 0; }

Accsauxlments
Loprateur dindex [] a t surcharg pour la classe string. On accde alors aux lments s[0] s [s.length() -1].Endehorsdecesplages,laccsdclencheuneexceptionout_of_range.

string s="Bonjour la STL"; cout << s[0] << << s[1] << << s[s.length()-1] << endl;

- 2-

Attentioncarlquivalenceentretableauetpointeurnestplusvrifie,laclassestringayantdfiniplusieurs champs.Ainsi,sestdiffrentde&s[0].

b.IntgrationdanslelangageC++
Lachaneestuntypesingulierpourtoutlangagedeprogrammation,aussiimportant quepeutltrelentierou leboolen.Laclasse stringatconuedanslebutderendresonemploileplusnaturelpossible,cest direquelledoitsintgreraussibienquechar*auseindesprogrammesC++.

Affectations
NousavonsvuauchapitresurlaProgrammationorienteobjetquaffectationetconstructeursontdesnotions lies. La classe string possde videmment un constructeur de copie, dont trois arguments sur quatre reoiventdesvaleurs pardfaut. Loprateur=atredfinipourrendrelemploidelaclasselemoinssingulierpossible: string m; m = a; // initialiser m partir de a m = "Bonjour"; // initialise m partir de char* m = s; AttentiontoutefoisdenepascommettrederreurdesmantiquelielaconfusionentretableASCIIetvaleur entire: m = 65L; // erreur de smantique

Eneffet,mseraitinitialiseaveclecaractredecodeASCII65,doncA.etnonaveclachane"65".

Comparaisons
La comparaison de chanes est essentielle pour implmenter les algorithmes. La classe basic_string supportelescomparaisonsentredeuxchanesetentreunechaneetuntableaudecaractres.Lamthode compare()retourne 1,0ou1commeleferaitlafonctionstrcmp(). Deplus,lesoprateurs==,!=,>,<,<=et>=onttsurchargspourcomparerdeschanes.

#include <iostream> #include <string> using namespace std; int main(int argc, char* argv[]) { string a="Piano"; string b="Pianissimo"; cout << boolalpha; cout << a.compare(b) << endl; // affiche 1 cout << a.compare("Forte") << endl; // affiche 1 cout << (a<b) << endl; return 0; } // affiche true

ConversionavecleC

Openmirrors.com

- 3-

Il existe trois mthodes pour appliquer les fonctions de traitement des chanes du C sur des instances de la classestring : data(),c_str()etcopy(). La mthode data() copie les caractres de la chane dans un tableau avant de retourner un pointeur vers cettezone( const char*).Linstancedelaclasse stringalachargedelibrerdelammoireletableau, aussi le programmeur ne doit pas tenter de le faire luimme. Si la chane est modifie pendant la priode dexpositiondelafonctiondata(),descaractresrisquentdtreperdus :

string a="bonjour"; const char*d=a.data(); a[0]=B; char c=d[0]; // erreur, toujours la valeur b Lamthode c_str()ajouteuncaractre 0lafindelachane,faisantconcidercettereprsentationavec leschanesdulangageC: const char*b = a.c_str(); int l_a = strlen(b); Enfin, la mthode copy() recopie le contenu de la chane dans un tampon dont le programmeur a la responsabilitdelibration: char*t=new char[a.length()+1]; // 0 terminal a.copy(t,a.length()); t[a.length()]=0; // 0 terminal // ... delete t;

c.Fonctionsspcifiquesauxchanes
Leschanesdecaractresdelabibliothquestandardoffrentdesmthodesspcifiqueslcrituredecertains algorithmes. Il sagit de fonctions de haut niveau, difficilement programmables en langage C pour certaines dentreelles.

Insertion
Loprateur+=atsurchargpourlajoutduncaractreoudunechanelafindunechane.Cetoprateur alammefonctionquelamthodeappend()quioffreelleunregistredepossibilitsplusvari. La mthode insert() prsente lavantage dintercaler de nouveaux caractres lendroit souhait par le programmeur. string s="Fort"; s+="isimo"; cout << "s=" << s << endl; s.insert(5,"s"); cout << "s=" << s << endl;

// affiche Fortisimo // affiche Fortissimo

Concatnation
Loprateur + a t surcharg pour runir plusieurs chanes en une seule. Il concatne aussi bien des caractresquedeschanes: string tonalite; string ton="la "; ton=ton+b; string maj="majeur"; tonalite=ton+maj;

- 4-

Openmirrors.com
cout << tonalite << endl; // affiche la b majeur

Rechercheetremplacement
Ilexisteplusieursmthodespourrechercherdeslmentslintrieurdunechane:

find rfind find_first_of find_last_of find_first_not_of find_last_not_of

recherchedunesouschane. rechercheenpartantdelafin. recherchedecaractres. recherchedecaractresenpartantdelafin. rechercheduncaractreabsentdanslargument. rechercheduncaractreabsentdanslargumentenpartantdelafin.

Toutescesmthodesrenvoientunstring::size_type,assimilableunentier.

string s="BEADGCF"; string::size_type i1=s.find("A"); cout << "i1=" << i1 << endl; // affiche 2

Si la recherche naboutit pas, find() renvoie renvoiedailleursunrsultatnonsign:

npos, position de caractre illgale. La mthode find()

string::size_type i2=s.find("Z"); cout << "i2=" << i2 << endl; // affiche un nombre trs grand if(i2==string::npos) cout << "recherche de Z non trouv"; Ilnestpasraredecombinerrechercheetremplacement.Cestprcismentlerledelamthodereplace() quedemodifierunesquencedechaneenlaremplaantparuneautresquence: string ton="la bmol majeur"; string::size_type p=ton.find("maj"); ton.replace(p,6,"mineur"); cout << ton << endl; // affiche la bmol mineur Ilfautremarquerquelasquenceremplaantepeutavoirunelongueurdiffrentedelasquencerecouverte.Il existeaussiunemthodeerase()poursupprimeruncertainnombredecaractres:

ton.erase(ton.find("bmol"),5); cout << ton << endl; // affiche la mineur

Extractiondesouschane
Lamthodesubstr()raliseuneextractiondunesouschane.Ilestentenduquellerenvoieunrsultatde typestring.Voiciunexempletrssimpledutilisationdecettemthode:

cout << ton.substr(0,3).c_str() << endl; // affiche la

Openmirrors.com

- 5-

Conteneursdynamiques
Unefonctionessentielledelabibliothquestandardestdefournirdesmcanismespoursupporterlesalgorithmes avec la meilleure efficacit possible. Cet nonc comporte plusieurs objectifs contradictoires. Les algorithmes rclamentdelagnricit,autrementditdesmthodesdetravailindpendantesdutypededonnesmanipuler. LelangageCutilisaitvolontierslespointeurs void*pourgarantirlagnricitmaiscetteapprocheentraneune perte defficacit importante dans le contrle des types, une complication du code et finalement de pitres performances. Lefficacit rclame pour S.T.L.. sobtient au prix dune conception rigoureuse et de contrles subtils des types. Certes, le rsultat est un compromis entre des attentes parfois opposes mais il est assez probantpourtreutilisllaborationdapplicationsdontlasret defonctionnementestimprative. Les concepteurs de la S.T.L. ont utilis les modles de classes pour dvelopper la gnricit. Les modles de classesetdefonctionssonttudisendtailauchapitreProgrammationorienteobjet,etleuremploiestassez simple.Uneclasseestinstanciepartirdesonmodleenfournissantlesparamtresattendus, gnralementle typededonneseffectivementprisencomptepourlimplmentationdelaclasse.Ilfautabsolumentdiffrencier cette approche de lutilisation de macros (#define) qui provoque des rsultats inattendus. Ces macros se rvlenttrspeusresdemploi. Une ide originale dans la construction de la bibliothque standard est la corrlation entre les conteneurs de donnes et les algorithmes sappliquant ces conteneurs. Une lecture compare de diffrents manuels dalgorithmiedbouche surlaconclusionquelesstructuresdedonnessonttoujoursunpeulesmmes,ainsique les algorithmes sappliquant ces structures. Il ntait donc pas opportun de concevoir des structures isoles, commeunepileouunelistesanspenserlaprs,lapplicationdalgorithmesmoinsspcifiques.Lesconcepteurs delaS.T.L.ontsuvitercetcueil.

1.Conteneurs
Les conteneurs sont des structures de donnes pour ranger des objets de type vari. Les conteneurs de la S.T.L.respectentlesprincipalesconstructionslabores enalgorithmie. Classe Description TableaudeTunedimension.Structurederfrence. ListedoublementchanedeT. Filedattente(queueenanglais)deTdoubleaccs. Filedattentepriorit. PiledeT. TableauassociatifdeT. TableauassociatifdeT. EnsembledeT. EnsembledeT. Tableaudeboolens,ensembledebits. Entte

vector list deque priority_queue stack map multimap set multiset bitset

<vector> <list> <deque> <deque> <stack> <map> <map> <set> <set> <bitset>

Laclassevectorestunpeuunestructurederfrence.Bienquelesautresclassesnesoientpasbasessur cellecipourdesraisonsdeperformances, vectorouvrelavoieauxautresconteneurs.Cettepremirepartie explicite son fonctionnement, tandis que la partie suivante est consacre aux squences, cestdire aux conteneursdeplushautniveau,maisreprenantlalogique duvecteur.

a.Insertiondlmentsetparcours

- 1-

Lesdeuxprincipalesfonctionsattenduesdesconteneurssontdepouvoiragrgerdenouveauxlmentsetde parcourirleslmentsrfrencsparleconteneur. Linsertion est une opration dont le droulement est gnralement propre au type de conteneur considr. Par exemple, linsertion dans une file dattente priorit est sensiblement diffrente de linsertion dans un tableauassociatif. Le parcours, lorsquil est complet, est frquemment impliqu dans linspection de la collection dobjets que reprsente le conteneur. Ce parcours est galement envisageable pour explorer les rsultats fournis par un algorithmedeslectionouderecherche.

b.Itrateurs
Lorsqueleprogrammeureffectueunerecherche,untri,uneslectionoudautresoprationsdecetypesurun conteneur, il na pas besoin de rendre son programme dpendant du conteneur utilis. En effet, le parcours duneslectionsuruntableauassociatifousurunvecteurestidentiquedunpointdevuealgorithmique.Les itrateursontjustementpourmissiondefournirunmoyengnralpourparcourirlesdonnes. Voicimaintenantunexempledeconstructiondunvecteurdechanesetdeparcourslaidedunitrateur: #include #include #include #include <iostream> <string> <vector> <iterator>

using namespace std; int main(int argc, char* argv[]) { vector<string> tab; vector<string>::iterator iter; tab.insert(tab.begin(),"Mozart"); tab.insert(tab.begin()+1,"Beethoven"); tab.insert(tab.begin()+2,"Schubert"); for(iter=tab.begin(); iter!=tab.end(); iter++) cout << (*iter) << " "; cout << endl; return 0; } Unitrateursutilisedonclamaniredunpointeurdontlarithmtiqueestpropreauconteneurconsidr. Lesitrateursserventauparcoursdeslmentsdunconteneurindpendammentdutypereldeconteneur. Chaqueconteneurpossdedesfonctionsmembrespourdterminerlesbornes(dbutetfin)delasquence dlments.

c.Oprationsapplicablesunvecteur
Levecteurestuntableaudynamique,unecollectiondlmentsdummetype.Letableaupeutgalementtre considrcommeunepile,structuretrsimportantepourlcrituredeprogrammes.Lesmthodespush_back (),pop_back()etback()sontjustementdestinesaccomplircerle.

vector<int> v; v.push_back(1756); v.push_back(1770); v.push_back(1797); int last=v.back();

// empile 1756 // empile 1770 // empile 1797 // interroge le dernier lment, soit 1797

- 2-

Openmirrors.com

v.pop_back();

// dpile 1797

vector<int>::iterator i; for(i=v.begin(); i!=v.end(); i++) cout << *i << " "; Levecteurpossdegalementuncertainnombredemthodespourlinsertion et leffacementdedonnes desemplacementsprcisalorsquelesfonctionsdepilesagissentlextrmitdelasquence. vector<string> opus; opus.insert(opus.begin(),"K545"); opus.insert(opus.begin()+1,"K331"); opus.erase(opus.begin()); vector<string>::iterator o; for(o = opus.begin(); o != opus.end(); o++) cout << *o << " "; //affiche seulement K331 Enfin le vecteur possde une fonction swap() qui change les valeurs entre deux vecteurs, opration trs utilepourletrideslments.

2.Squences
a.Conteneursstandard
Laplupartdesconteneurssuiventlemodledevector,sauflorsquecet"hritage"dnaturelerleaccompli parleconteneur.Nouspouvonsdoresetdjdgageruncertainnombredecaractristiquesetdoprations communes.

Typesdemembres

value_type allocator_type size_type difference_type iterator const_iterator reverse_iterator const_reverse_iterator reference const_reference

Typedlment Typedugestionnairedlment Typedesindices,descomptesdlments Typedediffrenceentredeuxitrateurs espcedevalue_type* typedeconstvalue_type* itrateurenordreinverse,value_type* constvalue_type* value_type& constvalue_type&

Itrateurs

begin() end()

Pointesurlepremierlment Pointesurllmentsuivantledernierlment

- 3-

rbegin() rend()

Pointesurledernierlment(premierenordreinverse) Pointesurllmentsuivantledernierenordreinverse(avantlepremier)

Accsauxlments

front() back() [] at()

Premierlment Dernierlment Indexenaccsnoncontrl,nonapplicableauxlistes Indexenaccscontrl,nonapplicableauxlistes

Oprationssurpileetfile

push_back() pop_back() push_front() pop_front()

Ajoutelafindelasquence Supprimeledernierlment Insreunnouveaupremierlment(applicablelistetdeque) Supprimelepremier(uniquementlistetdeque)

Oprationsurlistes

insert(p) erase(p) clear()

Insreavantp Supprimep Effacetousleslments

Oprationsassociatives

operator[](k) find(k) lower_bound(k) upper_bound(k) equal_range(k)

Accdellmentdeclk. Cherchellmentdeclk. Cherchelepremierlmentdeclk. Cherchelepremierlmentdeclsuprieurek. Cherchetousleslmentslower_boundetupper_bounddeclk.

b.Squences
Lessquencesdsignentlesconteneursquisuiventlemodlede vector.Ontrouvecommesquencesles classesvector,listetdeque.

list

- 4-

Openmirrors.com

Openmirrors.com
Une liste est une squence optimise pour linsertion et la suppression dlments. Elle dispose ditrateurs bidirectionnelsmaispasdunaccsparindex,lafoispourrespecterlaformedesalgorithmesspcifiquesaux listesetpourloptimisationdesperformances. La classe list est probablement implmente laide de listes doublement chanes, mais le programmeur napasbesoindeconnatrecedtail. En supplment des oprations applicables aux squences gnrales, la classe list propose les mthodes splice(),merge()etsort().

splice() merge() sort()

Dplaceleslmentsdunelisteversuneautre,sanslescopier Fusionnedeuxlistes Trieuneliste

Dautresmthodessontparticulirementutilestellesreverse(),remove(), Nousdonnonsunexempledemiseen uvrepourcertainesdecesmthodes: #include <iostream> #include <list> #include <algorithm> using namespace std; bool initiale_b(string s) { return s[0]==B; } int main(int argc, char* argv[]) { // une liste de chanes list<string> liste; // quelques entres liste.push_back("Beethoven"); liste.push_back("Bach"); liste.push_back("Brahms"); liste.push_back("Haydn"); liste.push_back("Rachmaninov"); // retourner la liste liste.reverse(); // supprimer tous les compositeurs commenant par B liste.remove_if(initiale_b); // afficher la liste list<string>::iterator i; for(i=liste.begin(); i!=liste.end() ; i++) { string nom=*i; cout << " " << nom.c_str(); } return 0; }

remove_if()etunique().

deque
La classe deque ressemble beaucoup la classe lextractionauxextrmitsdelasquence.

list, sauf quelle

est optimise pour linsertion et

- 5-

Pour les oprations dinsertion portant sur le milieu de la squence, lefficacit est en de de ce quoffre la liste.

c.Adaptateursdesquences
partir des squences vector, list et deque on conoit de nouvelles squences stack, queue et priority_queue. Toutefois, pour des raisons de performances, les classes correspondantes refondent limplmentation.Ilnesagitdoncpasdesousclassementmaispluttduneadaptation.Ceciexplique leurnom dadaptateursdesquences. Lesadaptateursdesquencenefournissentpasditrateurs,carlesalgorithmesquileursontapplicablesnen ontpasbesoin.

Stack
Unepileestunestructurededonnesdynamiqueoilnestpaspossibledaccder unlmentsansretirer sessuccesseurs.OnappellecettestructureLIFOLastInFirstOut.

Linterface

stack reprend les mthodes qui ont toujours du sens pour une pile, savoir back(), push_back()etpop_back().Cesfonctionsexistentaussisouslaformedenomsplusconventionnellement utilissencrituredalgorithme:top(),push(),pop().
Queue
Les files dattente sont galement appeles piles FIFO First In First Out. Leur fonctionnement est un peu diffrentdespilesclassiquespuisquoninsredesvaleurslafin(empilement)etquelonretirelesvaleursau dbut.

- 6-

Openmirrors.com

Laclassequeueproposelesmthodesdinterfacefront(),back(),push()et pop().Nousproposonsun petitprogrammedeparcoursdegraphecritlaideduneclassequeue. Legrapheservantdexempleestungrapheorientcomposde4sommets:

Nous avons choisi dimplmenter ce graphe laide dune matrice dadjacence, structure qui permet de conserverunalgorithmetrslisible: #define N 4 bool**init_graphe() { bool**graphe; graphe=new bool*[N]; graphe[0]=new bool[N]; graphe[0][0]=false; graphe[0][1]=true; graphe[0][2]=true; graphe[0][3]=true; // true lorsquil y a une adjacence

- 7-

graphe[1]=new bool[N]; graphe[1][0]=false; graphe[1][1]=false; graphe[1][2]=true; graphe[1][3]=true; graphe[2]=new bool[N]; graphe[2][0]=false; graphe[2][1]=true; graphe[2][2]=false; graphe[2][2]=true; graphe[3]=new bool[N]; graphe[3][0]=false; graphe[3][1]=false; graphe[3][2]=false; graphe[3][3]=false; return graphe; } Voicimaintenantlastructurededonnesquisupporteleparcoursditenlargeurdabord : une file dattente. Commeilsagitdeparcourirungraphepourlequelplusieursn udspeuventtreadjacentsdautresn uds, untableaudeboolensconserveltatdevisitedechaquesommet. queue<int> file; bool*sommets_visites; Nouspoursuivonsparlecodedesfonctionsdestinesauparcoursdugraphe : void visite(bool**graphe,int n,int sommet) { if(sommets_visites[sommet]) return; sommets_visites[sommet]=true; cout << "Visite du sommet" << (sommet+1) << endl; // cherche toutes les adjacences non visites for(int i=0; i<n; i++) if(graphe[sommet][i] && ! sommets_visites[i]) file.push(i); } void parcours_largeur(bool**graphe,int n) { sommets_visites=new bool[n]; for(int i=0; i<n; i++) sommets_visites[i]=false; // sommet pas visit file.push(0); // part du sommet 1

while(! file.empty()) { int s=file.front(); file.pop(); visite(graphe,n,s); } } Notreexemplesetermineparlafonctionmain()etparsonexcution.

int main(int argc, char* argv[]) { bool**graphe; graphe=init_graphe();

- 8-

Openmirrors.com

parcours_largeur(graphe,N); return 0; } Lexcutionassurebienquechaquesommetaccessibledepuislesommet1estvisituneetuneseulefois:

Filedattentepriorit
Il sagit dune structure de donnes importante pour la dtermination du meilleur chemin dans un graphe. LalgorithmeA*(AStar)utilisejustementdesfilesdattentepriorit.Parrapportunefiledattenteordinaire, unepondrationacclreledpilementdlmentsaudtrimentdautres. priority_queue<int> pq; pq.push(30); pq.push(10); pq.push(20); cout << pq.top() << endl; // affiche 30

d.Conteneursassociatifs
Lesconteneursassociatifssonttrsutileslimplmentationdalgorithmesdetoutessortes.Cesstructuresde donnes sont si pratiques que certains langages/environnements les proposent en standard. La classe principalesappellemap,onlatrouveparfoissouslenomdetableauassociatif,oudetabledehachage.

Map
La classe map est une squence de cls et de valeurs. Les cls doivent tre comparables pour garantir de bonnesperformances.Lorsquelordredeslmentsestdifficiledterminer,laclasse hash_mapfournirade meilleuresperformances. Lesitrateursdelaclassemapfournissentdesinstancesdelaclassepairregroupant unecletunevaleur.

#include <iostream> #include <map> using namespace std; int main(int argc, char* argv[]) { map<int,string> tab; tab[1]="Paris"; tab[5]="Londres"; tab[10]="Berlin"; map<int,string>::iterator paires; for(paires=tab.begin(); paires!=tab.end(); paires++) { int cle=paires->first; string valeur=paires->second; cout << cle << " " << valeur.c_str() << endl; } return 0;

- 9-

Multimap
Lemultimapestunetablemapdanslaquelleladuplicationdeclestautorise.

Set
Unjeu(set)estunetablemapnegrantquelescls.Lesvaleursnontaucunerelationentreelles.

Multiset
Ilsagitdunsetpourlequelladuplicationdeclestautorise.

3.Algorithmes
Unconteneuroffredjunrsultatintressantmaislabibliothquestandardtiresaforcedunautreaspect:les conteneurssontassocisdesfonctionsgnrales,desalgorithmes.Cesfonctionsutilisentpresquetoutesdes itrateurspourharmoniserlaccsauxdonnesduntypedeconteneurlautre.

a.Oprationsdesquencesansmodification
Ilsagitprincipalementdalgorithmesderechercheetdeparcours.

for_each() find() find_if() find_first_of() adjacent_find() count() count_if() mismatch() equal() search() find_end() search_n()

Excutelactionpourchaquelmentdunesquence. Recherchelapremireoccurrencedunevaleur. Recherchelapremirecorrespondancedunprdicat. Recherchedansunesquenceunevaleurprovenantduneautre. Rechercheunepaireadjacentedevaleurs. Comptelesoccurrencesdunevaleur. Comptelescorrespondancesdunprdicat. Recherche les premiers lments pour lesquels deux squences diffrent. Vaut true si les lments de deux squences sont gaux au niveaudespaires. Recherche la premire occurrence d une squence en tant que soussquence. Recherche la dernire occurrence dune squence en tant que soussquence. Recherchelanimeoccurrencedunevaleur.

Nousproposonsunexemplederecherchedevaleurdansunvecteurdechaneschar*:

#include <iostream> #include <algorithm>

- 10 -

Openmirrors.com

#include <vector> using namespace std; int main(int argc, char* argv[]) { vector<char*> tab; tab.push_back("Sonate"); tab.push_back("Partita"); tab.push_back("Cantate"); tab.push_back("Concerto"); tab.push_back("Symphonie"); tab.push_back("Aria"); tab.push_back("Prlude"); vector<char*>::iterator pos; char*mot="Concerto"; pos=find(tab.begin(),tab.end(),mot); cout << *pos << endl; // affiche Concerto return 0; }

b.Oprationsdesquenceavecmodification

transform() copy() copy_backward() swap() iter_swap() replace() replace_if() replace_copy() replace_copy_if() fill() fill_n() generate() remove() remove_if() remove_copy() remove_copy_if() unique() unique_copy() reverse() reverse_copy()

Applicationduneoprationsurtousleslmentsdunesquence. Copieunesquence. Copieunesquenceenordreinverse,partirdesondernierlment. changedeuxlments. Idemsurlabaseditrateurs. Remplaceleslmentsparunevaleurdonne. Remplace les lments par une valeur donne lorsqu un prdicat est vrifi. Copie une squence en remplaant les lments par une valeur donne. Idemsurlabasedunprdicat. Remplacechaquelmentparunevaleurdonne. Limiteloprationfillauxnpremierslments. Remplacetousleslmentsparlersultatduneopration. Supprimeleslmentsayantunevaleurdonne. Idemsurlabasedunprdicat. Copieunesquenceensupprimantleslmentsdunevaleurdonne. Idemsurlabasedunprdicat. Supprimeleslmentsadjacentsetgaux. Copieunesquenceensupprimantleslmentsadjacentsetgaux. Inverselordredeslmentsdunesquence. Copieunesquenceenordreinverse.

- 11 -

rotate() rotate_copy() random_shuffle()

Opreundcalagecirculairedeslments. Idemsurlabasedunecopie. Dplaceleslmentsdemanirealatoire.

c.Squencestries

sort() stable_sort() partial_sort() partial_sort_copy() nth_element() lower_bound() upper_bound() equal_range() binary_search() merge() inplace_merge() partition() stable_partition()

Trieleslments. Trieenconservantlordredeslmentsgaux. Trielapremirepartiedunesquence. Idemsurlabasedunecopie. Placelenimelmentlemplacementappropri. Recherchelapremireoccurrencedunevaleur. Rechercheladernireoccurrencedunevaleur. Rechercheunesoussquencedunevaleurdonne. Rechercheunevaleurdansunesquencetrie. Fusionnedeuxsquencestries. Fusionnedeuxsoussquencesconscutivestries. Dplaceleslmentsautourdunevaleurpivot. Identique lalgorithme prcdent, mais conserve l ordre des lmentsgaux.

d.Algorithmesdedfinition

includest() set_union() set_intersection() set_difference()

Vrifiesiunesquenceestunesoussquenceduneautre. Raliseuneuniontriedeplusieurssquences. Intersectiontrie. Construitunesquencedlmentstrisdanslapremire,maispas dansladeuxime.

e.Minimumetmaximum

min_element() min() max_element() max()

Lapluspetitevaleurdansunesquence. Lapluspetitedesdeuxvaleurs. Laplusgrandevaleurdansunesquence. Laplusgrandedesdeuxvaleurs.

- 12 -

Openmirrors.com

4.Calculnumrique
LelangageC++estparticulirementattrayantpourlaclartdesonexcution,dontladurepeuttreprvue. Lestypeshabituelsducharaudouble sontgalementconnusdautreslangages,commeleCoulePascal, etilsassurentunegrandepartiedescalculsncessaireslexcutiondunprogramme. Il existe des situations pour lesquelles ces types ne sont plus adapts, soit la prcision nest pas suffisante mme avec un double soit la rapidit des calculs est mise mal par une volumtrie trop consquente. Les logicielsncessitantdimportantscalculsenhauteprcisionontdumalsatisfaireuneoptimisationavancedes performancesavecunereprsentationsatisfaisantedesvaleursnumriques. Dressons un rapide tat des lieux des possibilits de calcul numrique en C++ avant dintroduire la partie correspondantedanslabibliothquestandard.

a.Limitesdesformatsordinaires
Lentte

<limits>

offre un certain nombre de classes paramtres pour dterminer les valeurs

caractristiquesdunformattelshortoudouble.Quelleestlapluspetitevaleur,quelestlepluspetitincrment (epsilon)... Leprogrammesuivantnousdonnedesinformationssurletypedouble: #include <iostream> #include <limits> using namespace std; int main(int argc, char* argv[]) { cout << "Plus petite valeur (double)=" << numeric_limits<double>::min() << endl; cout << "Plus grande valeur (double)=" << numeric_limits<double>::max() << endl; cout << "Plus petite valeur absolue significative (double)=" << numeric_limits<double>::epsilon() << endl; cout << "Reprsentation dune quantit qui nest pas un nombre (double)=" << numeric_limits<double>::signaling_NaN() << endl; return 0; }

b.Fonctionsdelabibliothque
Les enttes de la bibliothque du C, <cmath> et <math.h> fournissent le support pour les fonctions mathmatiquesusuelles,applicablespourlaplupartautypedouble.Cetypeatchoisidunepartparcequil correspond un format standardis (IEEE 754), et dautre part car les microprocesseurs savent travailler directementavecceformat,sansprendreplusdetempsquele floatquioffreluiunemoinsbonneprcision. Attentiontoutefoiscertainesimplmentationslogiciellesquipeuventfairevarierlgrementcesformats. IlpeuttreintressantdeconsulterlexcellentouvragedeLaurentPadjasek,Calculnumriqueenassembleur (Sybex)pourdcouvrirtouteslessubtilitsducodageIEEE754. NousproposonsmaintenantunelistedesprincipalesfonctionsdelabibliothquemathmatiqueduC.

abs(),fabs() ceil() floor() sqrt()

valeurabsolue pluspetitentiernoninfrieurlargument plusgrandentiernonsuprieurlargument racinecarre


- 13 -

pow() cos(), sin(), tan() acos(), asin(), atan() atan2(x,y) sinh(), cosh(), tanh() exp(), log(), log10() mod()

puissance fonctionstrigonomtriques arc cosinus, arc sinus, trigonomtriquesinverses atan(x/y) fonctionstrigonomtriqueshyperboliques exponentielleetlogarithmes partiefractionnaire arc tangente, fonctions

c.Initiativescomplmentaires
Puisque C++ a t conu pour pouvoir crer des nouveaux types de donnes, certains travaux non officiellementreconnusparlalibrairiestandardfournissentdetrsbonsrsultatsdansbeaucoupdedomaines commelingnierie,lecalculfinancierolecalculnumrique.CitonspourlexemplelestravauxdAlainReverchon etdeMarcDucamp,synthtissdanslouvrageOutilsmathmatiques pourC++(ArmandColin).

d.Fonctionsdelabibliothquestandardetclassevalarray
La bibliothque standard sest surtout attache proposer un mcanisme qui assure la jointure entre loptimisationdescalculsetleprogrammeproprementdit.Lesmicroprocesseursadoptentletravaillachane etenparalllepouroptimiserlescalculs,conceptquelondsignesousletermedecalculvectoriel. NoustrouvonsdanslaS.T.L.lastructure valarraydontlerleestjustementdeprparerloptimisationdes calculstoutenconservantuneassezbonnelisibilitduprogramme. Cetteclassepossdeparmisesconstructeursunesignaturequiaccepteuntableauordinaire,initialisationqui assureunebonneintgrationaveclexistant. Le programme suivant cre un valarray partir dune srie de valeurs doubles fournies dans un tableau initialisenextension.Uneseuleopration v+=10corresponduneadditioneffectuesurchaquevaleurdu vecteur. Suivant les implmentations et les capacits du microprocesseur, cette criture raccourcie ira de pair avecuneexcutiontrsrapide.IlfautdailleursremarquerquelesprocesseursRISCtirentpleinementpartide cetypedopration. #include <iostream> #include <valarray> using namespace std; int main(int argc, char* argv[]) { double d[]={3, 5.2, 8, 4 }; valarray<double> v(d,4); v+=10; // ajoute 10 chaque lment return 0; } La bibliothque standard offre galement le support des matrices (slice_array) formes partir de valarrayetaprsunetapedestructurationappeleslice.

- 14 -

Openmirrors.com

LenvironnementWindows
1.LesprogrammesWin32
La plateforme Win32 a trs vite t supporte par C++ car Microsoft a lanc ds 1992 lun des premiers environnements intgrs pour ce langage il sagissait de Visual C++ et cette poque, de nombreux architectes taient convaincus de lintrtdunlangageobjetprochedulangageCpourcrerdesapplications fonctionnantdansdesenvironnementsgraphiques. Toutefois, la programmation dune application fentre, sans framework, nest pas une tche aise. Il nen demeure pas moins que la plateforme Win32 propose plusieurs formats dapplications:applications"console", servicesWindows,bibliothquesdynamiques(DLL)etbienentendulesapplicationsgraphiquesfentres. VisualC++2010disposedunassistantpourdmarrerunprojetdetypeWin32:

Lassistantdonnelechoixpourleformatdfinitifduprojet:applicationconsoleougraphique(Windows),DLL, bibliothquestatiqueOnpeutgalementraccorderleprojetauxframeworksATLetMFC,maisnousverronsce dernierunpeuplusloin.

- 1-

Les enttes prcompils rduisent le temps de compilation, car les volumineux fichiers denttes standards comme windows.h (plus de 100000 lignes), sont analyss avant de dmarrer la phase de compilation. En consquence pour le programmeur, tous les fichiers de code .cpp doivent rappeler le fichier dentte stdafx.h:

// chap5_win32.cpp : dfinit le point dentre pour lapplication console. // #include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[]) { return 0; } Lamacro_TCHARdsigneenfaitletypewchar_tquiestassimilableuncaractreunicode. LesapplicationsWin32deVisualC++bnficientdeslmentsdelanormeC++ANSIsyntaxe,STL,etautres bibliothquesstandardmaisaussidesbibliothquesspcifiquesWindows. // chap5_win32.cpp : dfinit le point dentre pour lapplication console. // #include "stdafx.h" #include <string> // STL #include <Windows.h> // Windows int _tmain(int argc, _TCHAR* argv[]) { return 0; }

2.LeframeworkMFC
- 2-

Openmirrors.com

Trs rapidement la programmation dune application graphique fentre en mode C sest avre contre productive.Microsoftapropospoursapartunframeworkdedveloppement,dsignpartirdestroislettres qui explicitent la nouvelle bibliothque de classes, Microsoft Foundation Classes. Lenvironnement MFC est cependantbiendavantagequuneAPI,cestuncadredapplicationstructurantetcouvranttouslesaspectsdu dveloppementdapplicationsfentres: Organisation gnrale du programme sparationdesdiffrentsdapplication Crationdetypesdedocumentsstructurs et Utilisation du design pattern MVC grce larchitectureDocumentVuedeMFC. Srialisation et mcanismes de dfinition de formatsdedocuments TemplatesdapplicationsSDI(simpledocument), MDI(multiplesdocuments),Explorateur Modle objet pour tirer le meilleur parti de GDI (GraphicDeviceInterface) Bibliothques de classes composantsActiveX. spcifiques et

Prsentationsstandardisesdesapplications

Priseenchargeavancedugraphisme

Support des bases de donnes, du rseau, de formulairesHTML

LesMFCsesonttoffesaufildesannesetrestenttrspopulairesauprsdesdveloppeursC++mmeside nouveauxenvironnementsontfaitleurapparition.

a.CrationduneapplicationMFC
Accessible depuis le menu Fichier Nouveau projet, Visual C++ dispose dun assistant pour crer des applicationsMFC:

La premire tape de lassistant propose plusieurs formats dapplications : SDI comme le blocnotes,MDI comme les anciennes versions de Word avec plusieurs documents ouverts et un menu fentre Le style du
- 3-

projet correspond la prsentation par dfaut. Enfin les MFC peuvent tre employes sous forme duneDLL (lexcutable est plus compact mais il faut penser dployer les bons fichiers) ou comme une bibliothque statique.

Vient ensuite le choix de supporter les documents composs par exemple intgrer des objets dans un documentOffice:

- 4-

Openmirrors.com

VisualC++etMFCcrentuneextensionpropreauxdocumentsdelapplication.Decettefaonundoubleclic surunfichierdepuislexplorateurWindows lancelapplicationappropriecarlextensionatenregistrepar lesystme.

LesapplicationsMFCbnficientaussidusupportdesbasesdedonnesetplusieursniveauxsontproposs:

ltapesuivante,ondcidedustyledelafentreprincipaleetdutypedemenuquevarecevoirlemenu.Il

- 5-

estnoterquelesMFCproposentunmenudetyperubanOfficemmehorsdeWindows7.

Lavantderniretapeestgalementconsacredesfonctionnalitsoptionnellesetavances.

La dernire tape de lassistant est trs importante pour dfinir les classes composant lapplication : le programmeurdoitchoisirleurnometleurmodledebaseparexempleCViewouCScrollView.

- 6-

Openmirrors.com

Aprslassistant,uneapplicationbasiquecorrespondantauxparamtreschoisis estgnreparVisualStudio. Onpeutdjlexcuterpourcontrlerlersultat:

b.LarchitectureDocumentVue
LarchitectureDocumentVueguideleprogrammeurpourstructurersonapplication, ensparantclairementles responsabilitsdechaqueclasse.Pourdonnerunaperudecedesignpattern,nousallonsprendrelexemple
- 7-

delimplmentation dunebotededialogueservantmodifierlesparamtresdeprojection3Ddunefigure dessinerlcran. LapremiretapeconsisteconstruirelabotededialoguedepuislditeurderessourcesdeVisualC++.On procdedepuislexplorateurderessourcesetlaboteoutils.

En deuxime tape, les menus contextuels Ajouter une classe puis Ajouter une variable servent respectivement associer une classe hritant de CDialog puis associer chaque contrle une variable type:

Voildabordpourladfinitiondelaclasse:

- 8-

Openmirrors.com

Vientensuitelajoutdesvariables.OnremarqueraleprfixecaractristiquedesapplicationsMFCsurlenomde lavariable,m_:

Labotededialogueseraaffichepartirdunmenuspcifique:

- 9-

Par proximit, nous dcidons dimplmenter le gestionnaire dvnements commandant laffichagedelabote dedialoguedanslaclassedrivantdeCDocument:

Avant de dfinir ce gestionnaire dvnements, nous ajoutons dans la classe document deux variables contrlant la perspective de la figure dessiner. Elles seront videmment dites travers la bote de dialogue:

- 10 -

Openmirrors.com

class CFractaleDoc : public Cdocument { // Attributes public: double m_l, m_L; Commecesvariablessontutilisesdsledmarragedelapplication,leurinitialisationalieudanslaprocdure OnNewDocumentappeleparleframework :

BOOL CfractaleDoc::OnNewDocument() { if (!Cdocument::OnNewDocument()) return FALSE; // valeurs "initiales" des paramtres de projection // sagissant dune application SDI, la commande Fichier / Nouveau // rappelle cette procdure m_l = 1.1; m_L = 1.2; return TRUE; } Nouspoursuivonsparlimplmentationdugestionnairedvnementdestinafficherlabotededialogue: void CfractaleDoc::OnEditionParams3d() { // instancie la bote de dialogue CDialog3D dlg; // lecture des valeurs modifier dlg.m_l = m_l; dlg.m_L = m_L; // affichage modal dlg.DoModal(); // mise jour des valeurs dans la classe Document m_l = dlg.m_l; m_L = dlg.m_L; // actualisation du trac de la figure InvalidateRect(NULL,NULL,true); } Pour terminer, voici comment les paramtres sont lus dans la classe Vue drivant de CView. La procdure OnDrawestappeleparleframeworklorsquilfautactualiserlazoneclientedelafentre,autantenaffichage quenimpression : void CFractaleView::OnDraw(CDC* pDC) { ... l = ((CfractaleDoc*)GetDocument())->m_l; L = ((CfractaleDoc*)GetDocument())->m_L; ... }

c.Programmationgraphique
Windows et les MFC apportent au programmeur tous les outils ncessaires pour dfinir des applications graphiquesdehautniveau.Lecodesuivantdonneunaperudelamiseen uvredunaffichagelabor.On serendracomptequelpointlesapplicationsgraphiquespeuventsavrerdlicatesprogrammer:

- 11 -

Openmirrors.com
void CFractaleView::OnDraw(CDC* pDC) { // associer la palette au DC CPalette* pOldPal; // ancienne pOldPal = pDC->SelectPalette(&m_Palette, true); pDC->RealizePalette() ; // MX et MY dterminent la qualit de limage int MX,MY; MX = 1500; MY = 1500; // dimensionner lcran RECT rc; GetClientRect(&rc); // changer de repre et de systme de coordonnes if(!pDC->IsPrinting()) { // dessiner une image de fond CBrush brosse; CBitmap image; image.LoadBitmap(IDB_IMAGE); brosse.CreatePatternBrush(&image); pDC->FillRect(&rc,&brosse); pDC->SetMapMode(MM_ISOTROPIC); pDC->SetViewportExt(rc.right,-rc.bottom); // rapport de coordonnes pDC->SetViewportOrg(rc.right/2,rc.bottom/2); // fixer lorigine } pDC->SetWindowExt(2*MX,2*MY); // plan de 1000 points CRect zone_invalide; // zone rafrachir CPoint test; // un point pDC->GetClipBox(&zone_invalide); zone_invalide.NormalizeRect(); // crer une police police=new Cfont; police->CreatePointFont(14*10,"Arial",pDC); pDC->SelectObject(police); CRect pos; CSize ttexte; char msg[500]; sprintf(msg,"Graphique vu en 3D, %dx%d points",MX,MY); ttexte=pDC->GetTextExtent(msg); pos.left=-(ttexte.cx)/2; pos.right=pos.left+ttexte.cx; pos.top=-MX*0.75; pos.bottom=-MX; if(!pDC->IsPrinting()) { pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(RGB(248,172,122)); pDC->DrawText(msg,&pos,0); int dec = 15; pos.left += dec; pos.top -= dec; pos.right += dec;

- 12 -

Openmirrors.com

pDC->SetTextColor(RGB(250,234,96)); pDC->DrawText(msg,&pos,0); } else { pDC->SetTextColor(RGB(0,0,0)); pDC->DrawText(msg,&pos,0); }

// ----------------------------------------------------// vue en 3D double a1,a2,a3,b1,b2,b3; // coefficients de projection 3D double l,L; // latitude et longitude // point de vue l = 1.1; L = 1.20; l = ((CfractaleDoc*)GetDocument())->m_l; L = ((CfractaleDoc*)GetDocument())->m_L; // a1 a2 a3 b1 b2 b3 ces coefficients sont calculs pour une projection orthogonale = -sin(L); = cos(L); = 0; = -sin(l)*cos(L); = -sin(l)*cos(L); = cos(l);

// ----------------------------------------------------// trac dune figure mathmatique en 3D int i,j; // coordonnes dans lespace logique int n; // nombre ditration int X,Y; // coordonnes projetes (X,Y)=proj(x,y,z) double cote,x,y; // coordonnes mathmatiques int coul; // couleur des points for(i=-MX;i<MX;i+=80) for(j=-MY;j<MY;j++) { x = (double)i/MX*2; y = (double)j/MY*2; cote = 100 * sin(x)*sin(y)*exp(x*x - x*y +y*y); coul = 1000 + cote; if(coul>4000) coul = 1500; if(coul<0) coul = 800; // projection sur un plan X= a1*i + a2*j + a3*cote; Y= b1*i + b2*j + b3*cote; // test dimpact test.x=X; test.y=Y; if(PtInRect(&zone_invalide,test)) // le point est-il invalide ? pDC->SetPixel(test,PALETTEINDEX(coul)); // 40*100=4000

- 13 -

Openmirrors.com
} for(i=-MX;i<MX;i++) for(j=-MY;j<MY;j+=80) { x = (double)i/MX*2; y = (double)j/MY*2; cote = 100 * sin(x)*sin(y)*exp(x*x - x*y +y*y); coul = 1000 + cote; if(coul>4000) coul = 1500; if(coul<0) coul = 800; // projection sur un plan X= a1*i + a2*j + a3*cote; Y= b1*i + b2*j + b3*cote; // test dimpact test.x=X; test.y=Y; if(PtInRect(&zone_invalide,test)) // le point est-il invalide ? pDC->SetPixel(test,PALETTEINDEX(coul)); // 40*100=4000 } // ------------------------------------------------------------// librer GDI pDC->SelectPalette( pOldPal,true ) ; m_Palette.DeleteObject() ; pDC->SelectStockObject(ANSI_VAR_FONT); delete police; } Voicilersultatdelexcutiondeceprogramme:

Openmirrors.com

- 14 -

Openmirrors.com

3.LesActiveX
MicrosoftaconulescomposantsCOM(aussiappelsActiveX)lorsquelaplateformeWindowsseststructure en serveur dapplication. Il sagissait de crer des composants transactionnels capables de servir plusieurs applications, le systme dexploitation se chargeant de contrler linstanciation, la rpartition de charge, la scurit Une de leurs caractristiques intressantes est leur implmentation en C++ langage objet lexcution rapide et un systme dinterface crit en IDL interface description language facilitant la mise jourdelimplmentation. Depuis une quinzaine dannes, il existe des millions de composants de ce type et Microsoft cherche abandonner cette architecture au profit de .NET. Les ActiveX sont encore trs utiliss dans les applications de type Visual Basic (non .NET) et paradoxalement par des applications Web qui trouvent l un moyen abordable daugmenterleregistredefonctionnalitsdesnavigateurs.

- 15 -

Lenvironnement.NET
1.LecodemanagetlamachinevirtuelleCLR
Comme Java, .NET fait partie de la famille des environnements virtualiss. Le compilateur C++ manag ne produit pas du code assembleur directement excut par le microprocesseur mais un code intermdiaire, luimme excut par une machine "virtuelle", la CLR Common Language Runtime. Cette couche logicielle reproduit tous les composantsdunordinateurmmoire,processeur,entressortiesCettemachinedoitsadapterausystmedexploitationquilhberge,etsurtoutoptimiserlexcutiondu code.

Ilyalunediffrenceimportanteentre.NETetJava.AlorsqueMicrosoftavidemmentfaitlechoixdeseconcentrersurlaplateformeWindows,dautresditeurs ontportJavasurleursOSrespectifsUnix,AIX,MacOS,LinuxLasocitNovellacependantrussileportagede.NETsurLinux,cestleprojetMono.

CetenvironnementvirtualisauneconsquencetrsimportantesurleslangagessupportslestypesdedonnesetlesmcanismesobjetssontceuxdelaCLR.Comme Microsofttaitdansunedmarchedunificationdesesenvironnements,plusieurslangagessesontretrouvsligibleslaCLR,quitteadapterunpeuleurdfinition.Le projetdeSuntaitpluttunecrationexnihilo,etdecefaitseullelangageJavaarellementtpromusurlamachinevirtuelleJVM.

2.LesadaptationsdulangageC++CLI
LetermeCLIsignifieCommonLanguageInfrastructurecestlensembledesadaptationsncessairesaufonctionnementdeC++pourlaplateforme.NET/CLR.

a.LanormeCTS
Lepremierchangementconcernelestypesdedonnes.EnsuccesseurdeC,leC++standardabassatypologiesurlemotmachine(int)etlecaractrede8bits(char). Confrontdesproblmesdestandardisationetdinteroprabilit,Microsoftachoisidunifierlestypesdedonnesentresesdiffrentslangages. Lestypesprimitifs,destinstreemployscommevariableslocales(boucles,algorithmes),sontdestypes"valeurs".Leurzonenaturelledestockageestlapile(stack), etleurdfinitionappartientlespacedenomsSystem:

wchar_t signed char unsigned char double float int long unsigned int unsigned long short bool void

System::Char System::SByte System::Byte System::Double System::Float System::Int32 System::Int64 System::UInt32 System::UInt64 System::Int16 System::Boolean System::Void

Caractreunicode Octetsign Octetnonsign Dcimaldoubleprcision Dcimalsimpleprcision Entier32bits Entier64bits Entier32bitsnonsign Entier64bitsnonsign Entiercourt16bits Boolen Procdure

Voici un exemple utilisant la fois la syntaxe habituelle de C++ et la version "longue" pour dfinir des nombres entiers. Cette dernire ne sera utilise quen cas dambigut,maisilsagitenfaitdelammechose. int entier = 4; // alias de System::Int32 System::Int32 nombre = entier; Lesstructuresmanages(ref

struct)sontcomposesdechampsreprenantlestypescidessus.Ellessontcressurlapileetnesontpashritables.

ref struct Point { public: int x,y; } ; int main(array<System::String > args) { Point p; // initialisation automatique sur la pile : pas de gcnew p.x = 2; p.y = 4; return 0; } Commetouslestypesvaleurs,lesstructuresmanagesnautorisentpasleffetdebordmoinsquunerfrenceexplicitenaittpasse(voircidessouslesrfrences suivies). Par opposition lesclasses manages ( ref class) sont cres sur le tas et correspondent davantage au fonctionnement des classes (structure) du C/C++ standard. videmment,ellessonthritables,manipulesparrfrences,etinstanciesparloprateurgcnew.

ref class Personne { public: String nom;

Openmirrors.com

- 1-

int age; } ; int main(array<System::String > args) { Personne pers = gcnew Personne(); pers->nom = "Marc"; pers->age = 35; return 0; } Naturellement,lesstructuresetlesclassesmanagessupportentladfinitiondemthodes,C++estbienunlangageobjet! Leslangages.NETsontfortementtyps,envitantautantquepossiblelagnricit"fourretout"duvoid*issuduC,oudu VariantdeVB.Enopposition,lanormeCTS prvoit que lensemble des types de donnes valeurs ou rfrence hritent dun comportement commun System::Object. Nous dcouvrirons un peu plus loin commentcettecaractristiqueestexploitedanslesclassesdecollectionsdobjets.

b.LaclasseSystem::String
Parmilestypesintrinsques.NETontrouvelaclasse System::Stringpourreprsenterleschanes.EllenestpasaussiintgreaulangageC++CLIquedansC#, maiscependantelleoffreexactementlesmmesservices.Microsoft ladeplusamnagepourfaciliterlamanipulationetlaconversionavecchar*: Voici un premier exemple de construction de chanes. On remarquera lemploi facultatif du symbole L devant les littrales de chane, ainsi que le symbole dont la significationseraexpliqueunpeuplustard. char* c = "Bonjour String chaine1 = String chaine2 = String chaine3 = C++"; gcnew String(c); // construction partir dun char* L"C++ CLI cest formidable"; // Littrale de chane .NET "Un univers dcouvrir"; // idem

Console::WriteLine(chaine1); Console::WriteLine(chaine2 + ". " + chaine3);

Nouspoursuivonsavecquelquesexemplesdemploidemthodesetdepropritsdelaclasse System::String.Cetextraitdecodeemploieunenouvelleinstruction, for eachquiraliseuneitrationsurlensembledeslmentsdunecollection.CommedanslecasdelaSTL,unechaneestassimileunecollectiondecaractres:

// manipuler les chanes String prefixe = "Bon"; if(chaine1->StartsWith(prefixe)) Console::WriteLine("chane1 commence par {0}", prefixe); // itrer sur les caractres avec une boucle for each for each(wchar_t c in chaine1) { Console::Write(" " + c); } Console::WriteLine(); // itrer classiquement avec un for for(int i=0; i<chaine2->Length; i++) Console::Write( chaine2[i] ); Console::WriteLine();

LaclasseSystem::Stringcontientdenombreusesmthodesetpropritsquetoutprogrammeuraintrtdcouvrir.Beaucoupdentreellesseretrouventgalement danslaclassestringdelaSTL. Voici maintenant comment obtenir un char* partir dune String. Lopration repose sur le concept de marshalling, cestdire de mise en rang de linformation. Pourquoi estce si difficile ? Les char* nont pas beaucoup dintrt dans le monde .NET qui propose des mcanismes plus volus. Mais ces mmes char* sont indispensablesdanslemondeWin32ountrsgrandnombredlmentsdelAPIlesutilisent.Lepassagedunmondelautre,latransformationdesdonnessappellele marshalling.CestdonclaclasseSystem::Runtime::InteropServices::Marshall quiestchargede"convertir"uneStringenchar*:

// conversion vers char* (utile pour les accs natifs Win32) char* cetoile = static_cast<char *> (Marshal::StringToHGlobalAnsi(chaine1).ToPointer()); // utilisation de la chane // ... // librer le buffer Marshal::FreeHGlobal(safe_cast<IntPtr>(cetoile)); Nous constatons que le programmeur est responsable dallouer et de librer le buffer contenant la chane exprime en char*. La prsentation du garbage collector va donnerdesinformationscomplmentairescesujet.

c.Legarbagecollector

- 2-

Une caractristique des environnements virtualiss comme Java ou .NET est lemploi dun ramassemiettes ou garbage collector en anglais. Il sagit dun dispositif de recyclage et de compactage automatique de la mmoire. Autant le fonctionnement de la pile est assez simple systmatiser, autant les algorithmes de gestion du tas diffrentdunprogrammelautre.Or,leslangagescommeleCetleC++mettentenavantlemcanismedespointeursquisupposent unetotalelibertdansladressage delammoire. Quelestleprincipalbnficedelafonctiongarbagecollector?Enramassantlesmiettes,onvitelemorcellementdelammoireetdecefaitonoptimisesonemploietsa disponibilit.Ceciestpourtantdifficilementcompatibleaveclemploidespointeursetleurarithmtiquequinepeuttreprditelacompilation.Leslangagesnouvellement crspourlesenvironnementsvirtualisscommeJavaetC#ontdoncfait"disparatre"lanotiondepointeurauprofitdesseulesrfrences,puisquunerfrencenesten principepas"convertible"enadressemmoire.

C# comme C++ CLI disposent malgr tout des pointeurs, mais leur utilisation est strictement encadre pour ne pas perturber le fonctionnement du garbage collector.

Le ramassemiettes est activ priodiquement par la CLR lorsque la mmoire vient manquer ou quune allocation consquente est sollicite. Le programmeur peut galementanticiperetdemanderluimmesondclenchement.Leprincipeestdecompterlenombrederfrencesactivessurleszonesmmoiresallouesetdelibrer toutesleszonesquinesontplusrfrences.Dansunsecond tempslegarbagecollectorpeutdciderdedplacerdesblocsmmoirespourcompacterlesespacesallous et rendre disponibles de plus grands blocs. Les rfrences correspondantes sont actualises avec les nouvelles adresses, ce qui a pour consquence de rendre les pointeursinutilisables.

d.Constructionetdestructiondobjets
LelangageC++CLIoffredeuxvisages:lepremierestunegestion"classique"delammoiresuruntasspcifique,avecconstructeursetdestructeursinvoqusparles oprateurs new et delete (ou automatiquement si lobjet est instanci sur la pile). Le second repose sur la gestion "manage" de la mmoire, avec une utilisation de rfrencessurletasduCLRrendantpossiblelusagedugarbagecollector. Cest au moment de la dfinition de la classe que lon opte pour lun ou lautre des modes. Si la classe est dfinie laide du mot cl ref, il sagit du mode manag. Autrementcestleschmaclassique. tudionslaclassesuivante: // la prsence du mot cl ref indique // quil sagit dune classe manage ref class Personne { public: String nom; // rfrence vers un objet manag de type String int age; Personne() { nom = nullptr; // nullptr est une rfrence manage et non // une adresse age = 0; } Personne(String { // this est this->nom = this->age = } } ; Lemotcl nullptrreprsenteunerfrencemanageetnonuneadresse.DansluniversclassiqueNULL(littralement(void*)0)reprsenteuneadressequinepeut treutilise.Danslecasdesclassesmanages,cestuneconstantedontleprogrammeurnapasconnatrelavaleurpuisquilnyapasdarithmtiquedespointeurs.Par commodit, this devient lautorfrence de la classe et nest pas un pointeur. Cependant Microsoft a choisi loprateur -> pour accder aux membres partir dune rfrencemanageafindenepastroubler lesprogrammeursexprimentsdansleC++classique. Nous aurons galement remarqu lutilisation du symbole caret qui dsigne la rfrence vers un objet manag. On parle aussi de handle dobjet cet effet. Voici prsentcommentonprocdepourinstancieruneclassemanage : int main(array<System::String > args) { nom,int age) donc une rfrence manage et non un pointeur nom; age;

Openmirrors.com

- 3-

// dclaration dune rfrence Personne objet1; // une rfrence vers un objet manag // instanciation et invocation du constructeur objet1 = gcnew Personne("Corinne", 25); // utilisation de lobjet Console::WriteLine("objet1: nom={0} age={1}", objet1->nom, objet1->age); // suppression explicite de la rfrence objet1 = nullptr; return 0; } En fait dans notre cas la ligne objet1 = nullptr est facultative, car la variable objet1 est automatiquement dtruite en quittant la fonction main(). Le garbage collectorestsystmatiquementavisquelecomptederfrence associeobjet1doittrediminuduneunit.Sicecomptedevientnul,alorslegarbagecollectorsait quilpeutdtruirelobjetetlibrerlammoirecorrespondante. Enconsquencedelemploiduramassemiettes,leprogrammeurnelibrepasluimmesesobjetspuisquecestledispositifautomatiquequisencharge.Ilyadoncune diffrencefondamentaledanslapprochepourlibrerlesressourcesdemandesparunobjet.CommeJava,lesconcepteursdeC++CLIontchoisiletermedefinaliseur, sortededestructeurasynchrone. LelangageC++CLIproposelesdeuxsyntaxes,ledestructeur(not~)etlefinaliseur(not!).

ref class Personne { public: String nom; int age; ... ~Personne() { Console::WriteLine("Destructeur"); } !Personne() { Console::WriteLine("Finaliseur"); } } ; Ledestructeurserainvoquseulementsileprogrammeurdtruitexplicitementlobjetaumoyendelinstructiondelete.

int main(array<System::String > args) { // dclaration dune rfrence Personne objet1; // une rfrence vers un objet manag // instanciation et invocation du constructeur objet1 = gcnew Personne("Corinne", 25); // utilisation de lobjet Console::WriteLine("objet1: nom={0} age={1}", objet1->nom, objet1->age); // destruction explicite de lobjet -> seul le destructeur est appel delete objet1; // suppression explicite de la rfrence objet1 = nullptr; return 0; }

Le finaliseur sera invoqu si lobjet est dtruit par le garbage collector (ce qui dans notre cas se produit au moment o la variable objet1 disparat de la porte de la fonction)etconditionqueledestructeurnaitpastinvoqu. int main(array<System::String > args) { // dclaration dune rfrence Personne objet1; // une rfrence vers un objet manag // instanciation et invocation du constructeur objet1 = gcnew Personne("Corinne", 25); // utilisation de lobjet Console::WriteLine("objet1: nom={0} age={1}", objet1->nom, objet1->age); // pas destruction explicite de lobjet // delete objet1; // suppression explicite de la rfrence objet1 = nullptr; return 0; }

- 4-

e.Larfrencedesuivi%etlehandle
prsent,concentronsnoussurlesrfrencesdesuivi%(trackingreference)etsurlehandleauquelnousallonslargirlusage. Nouscommenonsparquelquesfonctionsutilisantcesnotations: // le symbole % signifie la rfrence (alias) void effet_de_bord_reference(int %nombre, int delta) { nombre += delta; } // les handles sont comme des "pointeurs" pour les valeurs int calcul_par_reference(int r1, int r2) { int v1 = *r1; // drfrencement int v2 = *r2; // drfrencement return v1 + v2; } Larfrencedesuivi%agitcommelesymbole &enC++classique.Ellecreunaliassurunevariabledetypevaleurouobjet.Lehandle secomportepluttcommeun pointeurencesensquilintroduituneindirectionquilconvientdvaluer(drfrencer). Voyonsmaintenantcommentsontutilisescesfonctions: // a est la rfrence dun entier (et non lentier lui-mme) int a = 10; // le drfrencement * donne accs la valeur a = *a + 2; effet_de_bord_reference(*a, 1); // il faut drfrencer x pour lappel Console::WriteLine("a = {0}", a); Cepremierexempleaugmentelavaleur"pointe"parxjusqu13.Onremarqueralanalogieaveclanotation"valeurpointe"*duC++standard.Lalignelaplusdifficile estsansdoutecelledeladclaration: int adonnelarfrencedunentierquelonpeututiliserpar*a.EnC++classique,linstructionint& r=3nestpascorrecte, etdemmequeint* pnalloueaucunentier. LesecondexempleestbeaucoupplusprochedeC++classique: // un entier cr sur la pile int b = 20; // une rfrence (alias) de la valeur b int %c = b; // b et c dsignent la mme valeur // modification "directe" de b effet_de_bord_reference(b, 2); // modification "directe" de c alias b effet_de_bord_reference(c, 3); Console::WriteLine("b = {0} / c = {1}", b, c); Ensortiedecetexemple,betcreprsentelammevaleursoit20+2+3=25. lappeldunefonctionexigeantunhandle,lecompilateurlefournitluimme.Danslexemplequisuit,aestdclarint

alorsquebestdclarint.

int somme = calcul_par_reference(a, b); Console::WriteLine("somme = {0} ", somme); Onauraremarqudanslimplmentationdecettefonctionquilconvientdedrfrencerchaqueparamtrehandlepouraccderlavaleur.

Considronsmaintenantlaclassecomplexeetdeuxfonctions: ref class Complexe { public: double re,im; Complexe() { re = im = 0; } } ; // le handle vers un objet donne laccs aux champs

Openmirrors.com

- 5-

void effet_de_bord_objet(Complexe comp) { comp->re = 5; // -> agit comme un drfrencement comp->im = 2; } // la rfrence sur un handle est comme un pointeur de pointeur void effet_de_bord_objet_ref(Complexe % comp) { comp = gcnew Complexe(); comp->re=10; comp->im=20; } LapremirefonctionreoitcommeparamtreunhandlesurunComplexecestsuffisantpouraccderdirectementauxchampsdelobjet.Danscecasonpeutconsidrer ->commeunoprateureffectuantundrfrencement.Ensortiedelafonction,leschampsreetimgalentrespectivement5et2.

// modification par la fonction des champs de lobjet effet_de_bord_objet(comp); Console::WriteLine("re = {0} im ={1}",comp->re,comp->im); Leparamtredelasecondefonctionestunerfrencedehandle,sortedepointeurdepointeur.Ellepermetdesubstituerlobjetdsigncommeparamtreparunautre: // substitution par la fonction de lobjet rfrenc effet_de_bord_objet_ref(comp); Console::WriteLine("re = {0} im ={1}",comp->re,comp->im); Bienquilsoitimpossiblededterminerlavaleurdelarfrencecomp(ladressedelobjet),onpourraitadmettrequecelleciatchangeparlafonctionpuisquecestun autreobjetquiestassocicetterfrence.

f.Lepointeurinterneetleszonespingles
Defaonlimiterlesmodificationssurlesalgorithmesfaisantappelauxpointeurs,MicrosoftaintgrcemcanismedanslefonctionnementdelaCLR.Unpointeurinterne estlquivalentstrictdunpointeurC++,maissurunezonemanage.OnserappelletoutefoisqueleGCalafacultdedplacerdesblocsmmoires,cequirendlutilisation dupointeurinterneunpeupluslentequelespointeursnatifs,puisquelaCLRestchargedactualisercespointeurs. LazonepingleestunezonemmoirerendueinamovibleauxyeuxduGC.Ongagneainsienrapiditpuisquelepointeurpinglenapastreactualis,ceciauxdpens dumorcellementdelammoire. Voiciunpremierexemplepourillustrerlefonctionnementdupointeurinterne.Ilsagitdinitialiserunpointeurinternepartirdeladresseduntableaudedouble.titrede comparaison, nous rappelons que la syntaxe pour instancier un tableau natif est inchange (dclaration int* pi).Linstanciation de tableaux manags est par contre assez diffrente puisquelle utilise loprateur gcnew et utilise la classe System::Array. Linitialisation du pointeur interne se pratique en demandant ladresse du premier lment du tableau &pd[0]. Les oprations daccs par pointeur suivent les mmes rgles quen C++ classique sauf quen arrireplan le GC peut dplacer la mmoiresansaffecterlexcutionduprogramme,mmeavecdelarithmtiquedespointeurs. // un tableau de 50 entiers // cest un pointeur classique non manag int* pi = new int[50]; // un tableau de 10 double // cest une rfrence (handle) manage array<double> pd = gcnew array<double>(10); pd[0] = 1; // un pointeur interne sur le tableau de double interior_ptr<double> pid = &pd[0]; while(++pid < &pd[0] + pd->Length) { *pid = *(pid-1) + 1; // cest beau larithmtique des pointeurs ! } // vrification 1,2,3...10 for(int i=0; i< pd->Length; i++) Console::Write("{0} ", pd[i] ); Console::WriteLine();

Danslecasdupointeurpingle,ilfautprocderenplusieurstemps.Primo,onfixelammoireeninitialisantunpointeurpinglepartirduneadresse.Silsagitdunchamp, cesttoutlobjetquisetrouvefixenmmoire.Secundo,unpointeurnatifestinstancipartirdupointeurpingleetlesoprationsparpointeursontlieu.Tertio,lazone estnouveaurendueaucontrlecompletduGCenaffectantlepointeurpingleuneautrerfrenceoubiennullptr.

// dclarer un pointeur pingle fixe la mmoire correspondante pin_ptr<double> pin = &pd[0]; // obtention dun pointeur "natif" pendant la dure de la manipulation double * manip = pin; while(++manip < &pd[0] + pd->Length) { *manip = *(manip-1) * 2; // cest beau larithmtique

- 6-

des pointeurs ! } // vrification 1,2,4,8... for(int i=0; i< pd->Length; i++) Console::Write("{0} ", pd[i] ); // libration de la zone : elle peut nouveau bouger pin = nullptr;

g.Lestableauxetlesfonctionsnombrevariabledarguments
Les tableaux manags se dclarent et sinstancient laide dune syntaxe particulire. En effet, lune des limitations des tableaux C++ classique reste leur trs grande similitude avec les pointeurs, ce qui les rend parfois risqus demploi. La plateforme .NET leur confre des proprits trs utiles comme la longueur (Length) ou lnumration.Devenusindpendantsdespointeurs,ilssontgalementdesobjets"commelesautres"queleGCpeutdplaceraugrdescompactagesdelammoire. Voiciunpremierexempledanslequelunechaneestdcoupeautourdesparateurs espaceoupointvirgule,etlesmotsaffichslesunsaprslesautresaumoyendune bouclefor each.

// un tableau de char array<wchar_t> sep = { , ; }; // un tableau de chanes String phrase = "Bienvenue dans C++ CLI"; array<String> tabs = phrase->Split(sep); // parcourir le tableau for each(String mot in tabs) Console::WriteLine(mot);

Linstanciationpargcnewquenousavionsutilisprcdemmentpermetdeprciserlatailledutableau,maisaussisesdimensions.Iciunematricede3x3,en2dimensions donc: // un tableau dimension double array<float,2> matrice = gcnew array<float,2>(3,3); matrice[0,0] = 1; matrice[0,1] = 2; matrice[0,2] = 3; matrice[1,0] = 4; matrice[1,1] = 5; matrice[1,2] = 6; matrice[2,0] = 7; matrice[2,1] = 8; matrice[2,2] = 9; // affichage des dimensions Console::WriteLine("Longueur de la matrice = {0} ", matrice->Length); Console::WriteLine("Longueur de 1re dim = {0} ", matrice->GetLength(0)); Console::WriteLine("Longueur de 2me dim = {0} ", matrice->GetLength(1));

Par voie de consquence, les fonctions nombre variable darguments de C/C++ pouvaient tre rendues plus sres grce des tableaux dobjets prfixs par ... et suivantledernierparamtreformeldelafonction: /// <summary> /// journalise un vnement (message paramtr {0}...) /// </summary> void log(String message, ... array<System::Object> p );

h.Lesproprits
PasdeplateformeMicrosoftsanspropritnivnement:cesontdeslmentsindispensableslaprogrammationgraphiquetoutdabordmaisbienplus,gnralement, si lon y regarde de plus prs. Une proprit est un type particulier de membre qui sapparente un champ auquel on dfinit des accesseurs pour prciser son comportement: Lectureseule critureseule

Openmirrors.com

- 7-

Formatagedesvaleursenlectureouencriture Contrledecohrenceenlectureouencriture Calculdunevaleurenlectureouencriture

LesapplicationsdespropritssontinnombrablesetlaquasitotalitdelAPI .NETreposesurleuremploipluttquesurdeschampspublics. ref class Utilisateur { protected: String login, password; bool isLogged; public: Utilisateur() { isLogged = false; } // une proprit en lecture / criture property String Login { String get() { return login; } void set(String value) { login = value; } } // une proprit en criture seule property String Password { void set(String value) { password = value; } } void Authenticate() { isLogged = (login=="..." && password=="xxx"); } // une proprit en lecture seule property bool IsLogged { bool get() { return isLogged; } } } ; int main(array<System::String > args) { Utilisateur user1 = gcnew Utilisateur(); user1->Login = "abc"; user1->Password = "098"; user1->Authenticate(); Console::WriteLine("Utilisateur {0}, authentifi {1}", user1->Login, user1->IsLogged); return 0; }

i.Lesdlgusetlesvnements
Les dlgus de C++ CLI succdent aux pointeurs de fonction peu srs du langage C. Nous laurons compris, tous les lments faiblement typs et donc potentiellement dangereux du monde classique ont t revisits en .NET. Les dlgus de C++ CLI ont les mmes rles que les pointeurs de fonctions : ils servent dfinirdesfonctionsgnriques,desfonctionsauxiliaires Lexemplecidessousmontrecommentutiliserundlgupourdfinirunefonctiongnriquedecomparaisonentre2nombres: // un dlgu est un type de fonction delegate int del_compare(int a,int b); // voici une fonction qui respecte la signature du dlgu int comparer_entier(int n1,int n2) { return n1-n2; } // cette fonction emploie un dlgu int calcule_max(int a,int b,del_compare f_comp) { if(f_comp(a,b)>0)

- 8-

// appel de la fonction au travers du dlgu return a; else return b; } int main(array<System::String > args) { // dclaration et instanciation du dlgu del_compare pcomp; pcomp = gcnew del_compare(&comparer_entier); // utilisation du dlgu int max = calcule_max(34,22,pcomp); Console::WriteLine("Max = {0}",max); return 0; }

Lerledesdlgusneselimitepaslalgorithmiepuisquilssontgalementemployspourdmarrerdestraitementssurdesthreadssparsetlimplmentationdes vnements. Un vnement est un signal dclench par un objet (ou une classe) qui peut tre capt (on dit gr) par une ou plusieurs mthodes. Lexemple habituel est celui de lvnementClicksurunobjetButton:

this->button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click); Lamthodebutton1_Clickestchargedafficherunmessagelcran:

Openmirrors.com

Dans cet exemple, le dlgu standard .NET est System::EventHandler. Mais nous verrons dans lexemple du tableur comment dclarer ses propres signatures dvnementsaumoyendedlgusspcifiques.

j.Lesmthodesvirtuelles
LefonctionnementhabitueldeC++,langagefortementtypavecphasedditiondesliensestceluidemthodesnonvirtuellesencasdhritagedeclasses.Toutefoiscest leprincipeinversequisestimposaufildesansetdelamonteenpuissancedesapplicationsgraphiques.LeprogrammeurC++CLIdoitdonctretrsassezprcisquant la mise en uvre du polymorphisme. Le fait de dclarer une mthode comme tant virtual exigera des surcharges au niveau des sousclasses, quelles prcisent overrideou new.Danslepremiercas,lamthoderestevirtuelleetlepolymorphismeestappliquendterminantdynamiquementletypedelobjet.Danslesecondcas, lepolymorphismeestrompuetcestladclarationdelobjetquiprime. Voiciunpremierexempleutilisantoverride:

ref class Compte { protected: double solde; int num_compte; public: Compte(int num_compte) { this->num_compte = num_compte; solde = 0; } virtual void Crediter(double montant) { if(montant>0) solde += montant; else throw gcnew Exception("Erreur"); } void Afficher() { Console::WriteLine("Compte n{0} Solde = {1}",num_compte,solde); } }; ref class Livret : public Compte { protected: double taux; public:

Openmirrors.com

- 9-

Livret(int num_compte) : Compte(num_compte) { taux = 0.0225; } virtual void Crediter(double montant) override { if(montant>0) solde += montant*(1+taux); else throw gcnew Exception("Erreur"); } } ; int main(array<System::String > args) { Compte c1 = gcnew Compte(1); c1->Crediter(100); c1->Afficher(); Livret l2 = gcnew Livret(2); Compte c2 = l2; c2->Crediter(100); c2->Afficher(); return 0; } Danscetexemple,lobjetc2dsigneenfaitunLivret,donclecompteestcrditavecintrt:

Enremplaantoverrideparnew,laCLRauraitsuiviladclarationdec2,soitCompteetnonLivret,etlecomptenauraitpasperudintrts.

k.Lesclassesabstraitesetlesinterfaces
ParanalogieaveclesmthodesvirtuellespuresdeC++classique,lelangageC++CLIproposelesmthodesabstraites.Ilsagitdemthodesdontladfinitionnapast donneetquidoiventtreimplmentesdansdessousclassesconcrtesavantdtreinstanciables: ref class Vehicule abstract { public: virtual void Avancer() abstract; }; ref class Voiture : public Vehicule { public: virtual void Avancer() override { Console::WriteLine("Vroum"); } }; Nousmesuronslatrsgrandeproximitaveclesmthodesvirtuellespuresde C++,maisaussilerespectdesmcanismesobjetsde.NET.Lemotcl abstract estplus adaptquelepointeurNULLdeC++,tandisquelemotcloverridesoulignebienlemploidupolymorphisme. Le langage C++ CLI connat aussi les interfaces, lesquelles sapparentent des classes totalement abstraites, mais sont surtout un moyen pour .NET de contourner lhritagemultipleauseinduframework. Les interfaces de classes se dfinissent sans recourir aux superclasses puisquelles sont totalement abstraites cest donc la forme new qui prvaut dans les classes dimplmentation: interface class Icompte { void Crediter(); void Afficher(); };

ref class Compte : Icompte { public: virtual void Crediter() new { //... } virtual void Afficher() new { //... } };

3.Leframework.NET
Microsoft propose un framework trs complet pour supporter le dveloppement de toutes ses applications. .NET Framework est limplmentation de la CLR pour Windows accompagn dun immense rseau de classes organises en namespaces et en assemblages. Tous les thmes de la programmation sont abords : rseau, graphisme, entressorties,internet,scurit,XML,programmationsystmeNousnendonneronsquunaperupouraiderleprogrammeurdmarreraveccetenvironnement.

- 10 -

UnassemblageestuneDLLouunEXEquicontientdestypesclasses,numrations,dlgus,structuresrpertorisdansdesespacesdenoms.

a.Lesrfrencesdassemblages
Pouraccderauxclassesduframework,leprogrammedoitdabordrfrencerlesassemblagesquilesexposent,puisfacultativementinclurelesnamespacesaumoyende linstructionusing namespace. CestaumoyendelacommandeProjetPropritsquelonmodifielalistedesrfrencesaccessibles. Dansnotreexemplecidessous,ilyadj5assemblagesrfrencs:

b.LespacedenomsSystem::Windows::Forms
Cetespacecorrespondlaprogrammationdapplicationsgraphiques(fentres)sousWindows.Ildfinitlesfentres,leurstyle,lescontrles boutons,listes,cases cocher VisualC++proposeunconcepteurvisueldcransgnrantducodeC++:

Openmirrors.com

- 11 -

VoiciunextraitducodegnrparVisualC++: #pragma once namespace chap5_clr_winform_framework { using using using using using using namespace namespace namespace namespace namespace namespace System; System::ComponentModel; System::Collections; System::Windows::Forms; System::Data; System::Drawing;

/// <summary> /// Description rsume de Form1 /// </summary> public ref class Form1 : public System::Windows::Forms::Form { private: System::Windows::Forms::MenuStrip menuStrip1; protected: private: System::Windows::Forms::ToolStripMenuItem fichierToolStripMenuItem; private: System::Windows::Forms::ToolStripMenuItem nouveauToolStripMenuItem; private: System::Windows::Forms::ToolStripSeparator toolStripMenuItem1; private: System::Windows::Forms::StatusStrip statusStrip1; private: System::Windows::Forms::ListBox listBox1; private: /// <summary> /// Variable ncessaire au concepteur. /// </summary> System::ComponentModel::Container components; #pragma region Windows Form Designer generated code /// <summary> /// Mthode requise pour la prise en charge du concepteur /// - ne modifiez pas /// le contenu de cette mthode avec lditeur de code. /// </summary> void InitializeComponent(void) { this->menuStrip1 = (gcnew System::Windows::Forms::MenuStrip()); this->fichierToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->nouveauToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem()); this->toolStripMenuItem1 = (gcnew System::Windows::Forms::ToolStripSeparator()); ... this->listBox1 = (gcnew System::Windows::Forms::ListBox()); this->menuStrip1->SuspendLayout(); this->SuspendLayout(); // // menuStrip1 // this->menuStrip1->Items->AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem >(2) {this>fichierToolStripMenuItem, this->testToolStripMenuItem}); this->menuStrip1->Location = System::Drawing::Point(0, 0); this->menuStrip1->Name = L"menuStrip1"; this->menuStrip1->Size = System::Drawing::Size(366, 24); this->menuStrip1->TabIndex = 0; this->menuStrip1->Text = L"menuStrip1"; // // fichierToolStripMenuItem // this->fichierToolStripMenuItem->DropDownItems>AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem >(6) {this->nouveauToolStripMenuItem, this->toolStripMenuItem1, this->ouvrirToolStripMenuItem, this->enregistrerToolStripMenuItem, this->toolStripMenuItem2,

- 12 -

this->quitterToolStripMenuItem}); this->fichierToolStripMenuItem->Name = L"fichierToolStripMenuItem"; this->fichierToolStripMenuItem->Size = System::Drawing::Size(50, 20); this->fichierToolStripMenuItem->Text = L"&Fichier"; Lorsquelonsouhaiteprendreenchargeunvnementparticuliercommeunclicsurunmenuouunbouton,cestencoreVisualC++quicrelecodecorrespondant. Ilnya plusquimplmenterlegestionnairedvnementdemanireapproprie: private: System::Void quitterToolStripMenuItem_Click(System::Object System::EventArgs e) { this->Close(); }

sender,

c.LespacedenomsSystem::IO
CetespacedenomsrnovetotalementlanciennefaondaccderauxfichiershritedulangageC.Ilyamaintenantplusieurstypesdeclassesspcialises:

FileInfo,DirectoryInfo FileStream Stream StreamReader StreamWriter XmlTextReader XmlTextWriter StringReader StringWriter MemoryStream

Oprationsgnralessurlesfichiersetlesrpertoireslistes,dplacement Fluxfichierpourlireetcrire Classeabstraitepourlireoucrire(marcheaussiaveclerseau) Classespcialisantunstreamdanslalecturedecaractresetdechanes Classespcialisantunstreamdanslcrituredecaractresetdechanes LecturecontinuedunfluxXML(aussiappeleAPISAX) critureauformatXML Fluxcapabledeliredansunechane Fluxcapabledcriredansunechane Fluxmmoire

Voiciunpetitexempledcrituredansunfichier: private: System::Void enregistrerToolStripMenuItem_Click(System::Object sender, System::EventArgs e) { // utilise le contrle de slection de fichier if(saveFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::Cancel) return; // nom du fichier slectionn String filename = saveFileDialog1->FileName; // ouverture dun flux fichier en criture FileStream fs = gcnew FileStream(filename, FileMode :: Create, FileAccess::Write); // adaptation dun flux texte sur le flux fichier StreamWriter sw = gcnew StreamWriter(fs); // criture dune chane dans le flux sw->Write("Ecriture dans un fichier"); sw->Flush(); // purge du flux texte bufferis // fermeture des flux sw->Close(); fs->Close(); }

d.LespacedenomsSystem::Xml
CetespacedenomsproposedetrsnombreusesclassespourmanipulerdesdonnesXMLlaidedesAPISAX,DOM,XSL,XPath,XMLschmasLapriseenchargedeXML estdonctrscomplteetfaitfigurederfrence.LexemplequisuitmontrecommentchargerunextraitXMLetlanalyser: System::Void xMLToolStripMenuItem_Click(System::Object sender, System::EventArgs // un extrait XML String s ="<villes><ville>Paris</ville><ville>Nantes </ville><ville>Annecy</ville></villes>"; // instanciation dun objet DOM XmlDocument doc = gcnew XmlDocument(); // initialisation de larbre DOM avec lextrait XML doc->LoadXml(s); // parcours de larbre XmlNode n = doc->DocumentElement->FirstChild; while( n != nullptr) { String v = n->InnerText; listBox1->Items->Add(v); // prochain noeud collatral n = n->NextSibling; } } e) {

Openmirrors.com

- 13 -

e.LespacedenomsSystem::Data
Cet espace de noms est constitu des lments daccs dconnect aux donnes relationnelles : DataSet, DataTable, DataView, DataRelation, DataRow, DataColumnLexemplesuivantinstancieunetableDataTableetlassocielasourcededonnesduncomposantDataGridView:

System::Void dataToolStripMenuItem_Click(System::Object // cration dune table et dfinition de 2 colonnes DataTable dt = gcnew DataTable(); dt->Columns->Add("Utilisateur"); dt->Columns->Add("Login"); // une ligne de donnes DataRow dr;

sender, System::EventArgs

e) {

// la ligne est instancie selon le modle de la table dr = dt->NewRow(); dr["Utilisateur"] = "Jean Valjean"; // 1re valeur dr["Login"] = "jval"; // 2me valeur dt->Rows->Add(dr); // rattachement la table // autre faon dindexer les colonnes dr = dt->NewRow(); dr[0] = "Cosette"; dr[1] = "cosy"; dt->Rows->Add(dr); // encore une ligne dr = dt->NewRow(); dr[0] = "Thnardier"; dr[1] = "bistr"; dt->Rows->Add(dr); // "affichage" dataGridView1->DataSource = dt; }

f.LespacedenomsSystem::Collections
Les structures de donnes de lespace de noms System::Collections ont fait partie de la premire version de .NET Framework. Il sagit de collections faiblement types,quiexploitentpourbeaucouplhritagedelaclasseSystem::Object partouslestypesmanags. Onretrouvedanscetespacedenomsdespiles,desfiles,deslistes(tableauxdynamiques).VoicijustementunexempledemploidelaclasseArrayList.Ondcouvreici lesavantagesetlescontraintesdelapprochefaiblementtype. Ilesttrsaisdattachertoutessortesdobjetsdansuneliste,maisparfois plusdlicatderetrouverleur type: System::Void arrayListToolStripMenuItem_Click(System::Object sender, System::EventArgs // un tableau liste contient des objets - valeurs ou rfrences ArrayList tab = gcnew ArrayList(); tab->Add("Toulouse"); tab->Add((wchar_t)P); tab->Add(123); // ajout dun objet rfrence // ajout dun objet valeur // ajout dun objet valeur e) {

- 14 -

// cr 2 objets valeurs pour faciliter la dtermination du type wchar_t c = 0; Object ochar = c; int i=0; Object oint = i; // numration de la collection for each(Object o in tab) { String v; v = o->ToString(); // version textuelle de lobjet // teste le type de lobjet if(dynamic_cast<String >(o) != nullptr) v = v + " est une String"; // pour les types valeurs dynamic_cast ne sapplique pas if(o->GetType()->Equals(ochar->GetType())) v = v + " est un char"; if(o->GetType()->Equals(oint->GetType())) v = v + " est un entier"; // affichage listBox1->Items->Add(v); } } Lexcutionduprogrammeindiquebienquetouslestypesonttidentifis:

videmment, si le type de la collection est homogne, il est plus simple dutiliser systmatiquement loprateur dynamic_cast voire loprateur de transtypage par coercition.

g.LespacedenomsSystem::Collections::Generic
Apparusavecladeuximeversionde.NETFramework,lesconteneursgnriquesontlargementsimplifilcrituredesprogrammespuisquilssontparamtrscommeavec laSTLavecuntypespcifiquedobjet.

Comparer<T> Dictionnary<T> LinkedList<T> List<T> Queue<T> SortedList<T> Stack<T>

Classedebasepourimplmenterdesalgorithmesdetrisgnriques Tabledehachagegnrique Listegnriquedoublementchane Listegnrique Filedattentegnrique(aussiappelepileFIFO) Listegnriquedontleslmentspeuventtretris Pilegnrique(aussiappelepileLIFO)

Lexemplesuit: System::Void genericToolStripMenuItem_Click(System::Object // cette liste ne contient que des String List<String> liste = gcnew List<String>(); // ajout dobjets daprs le type indiqu liste->Add("Bordeaux"); liste->Add("Pau"); liste->Add("Laval"); // le parcours numre les String for each(String s in liste) listBox1->Items->Add(s); } sender, System::EventArgs e) {

Openmirrors.com

- 15 -

h.LeportagedelaSTLpourleC++CLI
MicrosoftadbutleportagedelaSTLsur.NETmaisilsemblequelexprienceneserapasmenesonterme.Ilyadegrandesdiffrencesdanslapproche etdansla gestiondesthreads.cejour,laSTLpourC++CLI2010necomportequequelquesclassesalorsqueleframework.NETestdoresetdjtrscomplet.Audel de la frustration,cestsansdoutecertainsprogrammesquisontporter(rcrire)surcettenouvellebibliothque.

4.Lesrelationsaveclesautreslangages:C#
Notre dmarche nest pas ici de comparer C++ CLI et C#, mais plutt dillustrer les possibilits dchanges et de partage entre les langages CLS Common Language Specification. TousceslangagesC++CLI,C#,VB.NET,F#...partagentunsoclecommun,laCLRquiexcuteuncodeintermdiaireMSILindpendant.Chaqueprogrammeurpeutainsi choisirsonlangagesansseproccuperdesproblmesdinteroprabilitquiavaientlargementnuiaudveloppementdapplicationspendanttantdannes.Lechoixestalors plusunequestiondaptitude,deprfrence, dequantitdecodeexistant,cestbeaucouppluspositifcommeapprochequedeprocderparlimination. NousproposonsicilexempleduneDLLcriteenC++etduneapplicationgraphiqueprogrammeenC#.CommenonsparlaDLLquinestconstituequeduneclasse: public ref class Utilitaire { public: static property DateTime Heure { DateTime get() { return DateTime::Now; } } static double cosinus(double a) { return 1 - a*a / 2; } }; AprsavoircrunprojetdapplicationconsoleC#,nousajoutonsunerfrencelaDLL,cequiestlaconditionpourqueleprogrammeC#puisseatteindrelestypespublics dfinisprcdemment:

LesystmedaidelasaisieintellisensedeVisualStudioretrouvebienlestypesetlesmthodespublics:

- 16 -

Ilnyaplusqutester: class Program { static void Main(string[] args) { DateTime d = chap5_cpp_csharp_lib.Utilitaire.Heure; Console.WriteLine(d.ToLongTimeString()); } }

5.UneapplicationenC++pour.NET:letableur
NousproposonsmaintenantdtudierlaralisationduntableurgraphiqueenC++CLI.Lebutesticidexpliquerlamiseen uvredemcanismesdeC++CLIautravers dune application consquente, sans rentrer dans lanalyse fonctionnelle. Pour ceux qui souhaitent aller plus loin, lensemble du code source est document et fourni en tlchargementdefichierscomplmentairespourcetouvrage.

a.Architecturedutableur
Larchitecturereposesurdeuxassemblagesdistincts:linterfacegraphiqueCLIMulticubeetlunitdecalculCLITableur.LepremierassemblageestunexcutableWinform tandisquelesecondestunebibliothquedeclasses.DLL.

Lexcutable est principalement constitu de formulaires (dcran) WinForm qui dfinissent lapparence et le comportement de lapplication graphique. On y trouve le formulaire principal Form1, la bote de dialogue de formatage de cellule FormFormat et la bote de dialogue d propos FormAbout. La fonction principale main() instancie et affiche Form1 qui contrle lexcution de lapplicationjusqu sa fermeture. La programmation est vnementielle et cest au travers de menus et de barre doutilsquelessentieldesoprationssontdclenches.Commenousleverronsunpeuplusloin,desvnementsparticulierssontgalementgrsauniveaudelagrille dedonnepourrecalculerlafeuillelorsquecelaestncessaire. Lesecondassemblageestindpendantdelinterfacegraphique.Pourviterlesinterdpendances,nousavonsutilisdesvnementspournepastomberdanslepigede linterface graphique qui commande un calcul, lequel doit ensuite tre affich sur linterface graphique. La classe Feuille est un tableau de Cellule, cette classe reprsentantunevaleur,uneformule,untype,unformatdaffichage,unstyleDansuntableur,lesformulesnepeuventpastoujourssvaluerdegauchedroiteetde haut en bas, un ordonnanceur est intgr la feuille de calcul pour dterminer lordre adquat dans lequel les oprations dactualisation auront lieu. Il manque encore lvaluateurdeformulesquicalculedesexpressionsbasedeconstantes,defonctionsintrinsques(cosinus,somme,moyenne)ouderfrencesdescellules.

b.Lafeuilledecalcul
Avant dexpliciter la classe Feuille, commenons par la classe Cellule. Une cellule est une formule dont lvaluation donne une valeur. Cette valeur est ensuite formateetaffichesurlagrillededonneenrespectantuncertainstyle.LextraitquisuitdonneunaperudecetteclasseoplusieursmcanismespropresC++CLI sontutiliss:proprits,numrations,ainsiquelescommentaires///destinsfabriquerladocumentationduprojetauformat XML. /// <summary> /// Cellule reprsente une valeur ou une formule formate /// </summary> public ref class Cellule

Openmirrors.com

- 17 -

{ private: /// <summary> /// Valeur "calcule" dune cellule (membre priv) /// </summary> String value; /// <summary> /// Formule (membre priv) /// </summary> String formula; /// <summary> /// Style gras (membre priv) /// </summary> bool isBold; /// <summary> /// Style italique (membre priv) /// </summary> bool isItalic; /// <summary> /// Style soulign (membre priv) /// </summary> bool isUnderlined; /// <summary> /// Style couleur de fond (membre priv) /// </summary> Color backgroundColor; /// <summary> /// Style couleur davant plan (membre priv) /// </summary> Color foreColor; /// <summary> /// Style police (membre priv) /// </summary> String fontName; /// <summary> /// Style taille de la police (membre priv) /// </summary> float fontSize; /// <summary> /// Style alignement (membre priv) /// </summary> DataGridViewContentAlignment alignment; public: /// <summary> /// Formatages possible de la valeur /// </summary> enum class FormatType { AUTOMATIQUE, NOMBRE, POURCENTAGE, DATE, TEXT, PERSONNALISE };

private: /// <summary> /// Formatage de la valeur (membre priv) /// </summary> FormatType format; /// <summary> /// Chane de formatage (membre priv) /// </summary> String formatString; public: /// <summary> /// Indique si la valeur peut tre reconnue comme nombre /// </summary> bool IsNumber();

/// <summary> /// Constructeur /// </summary> Cellule(); /// <summary> /// Srialise la cellule au format XML /// </summary> XmlNode ToXml(XmlDocument doc, int col, int row); /// <summary> /// Dsrialise la cellule partir dun noeud XML /// </summary> void FromXml(XmlNode n); } ; LaclasseFeuillecontientuntableau2dimensionsreprsentantlamatricedeCellule.LencoredesmcanismesdeC++CLIsontemploys,commelesdlguset lesvnements.Nousverronsunpeuplusloincommentlinterfacegraphiquesy"connecte".Noustrouvonsaussidespropritsfacilitantlestranslationsdundomaine lautre,parexemple,pourretrouverlenomcourtdunfichier. /// <summary>

- 18 -

/// Feuille de calcul /// </summary> public ref class Feuille { public: /// <summary> /// Fonction appele quand une cellule est mise jour /// </summary> delegate void del_cellUpdated(int col, int row, String value); /// <summary> /// Dclench lorsquune cellule est mise jour (calcule) /// </summary> event del_cellUpdated OnCellUpdated; /// <summary> /// Fonction appele lorsque le style est modifi /// </summary> delegate void del_cellStyleUpdated(int col, int row); /// <summary> /// Dclench lorsque le style est modifi /// </summary> event del_cellStyleUpdated OnCellStyleUpdated; // taille de la feuille /// <summary> /// Nombre de lignes max /// </summary> const static int ROWS = 100; /// <summary> /// Nombre de colonnes max /// </summary> const static int COLS = 52; private: int rows; int cols; /// <summary> /// Cellules composant la feuille de calcul /// </summary> array<Cellule,2> cells; public: /// <summary> /// Accs index aux cellules /// </summary> Cellule Cells(int col,int row); /// <summary> /// Constructeur /// </summary> Feuille(); private: /// <summary> /// Evalue le contenu dune cellule /// </summary> String evalue(Cellule cell, int col, int row); /// <summary> /// Appel pour capturer la valeur dune cellule /// </summary> void ev_OnGetCellValue(String name, double & value); /// <summary> /// Identifie les rfrences des cellules contenues dans une plage /// </summary> array<Cellule> GetRange(String range); /// <summary> /// Instance dvaluateur /// </summary> Evaluator ev; /// <summary> /// Erreurs /// </summary> StringBuilder errors; public: /// <summary> /// Recalcule la feuille aprs dtermination du plan /// </summary> void recalc(); private: /// <summary> /// Chemin et nom de fichier (membre priv) /// </summary> String filename; /// <summary> /// Nom du document (membre priv) /// </summary> String fileshortname; /// <summary> /// Indique si une modification a eu lieu depuis la dernire sauvegarde (membre priv) /// </summary> bool isModified;

Openmirrors.com

- 19 -

/// <summary> /// Enregistre le document vers le fichier filename /// </summary> void SaveToFilename(); /// <summary> /// Produit un flux XSLX /// </summary> String GetExcelStream(); public: /// <summary> /// Constructeur applicatif /// </summary> void Init(); /// <summary> /// Indique si le document a t modifi depuis la dernire sauvegarde /// </summary> property bool IsModified { bool get() { return isModified; } } /// <summary> /// Obtient ou dfinit le nom/chemin du document /// </summary> property String Filename { String get() { return filename; } void set(String value) { this->filename = value; FileInfo f = gcnew FileInfo(filename); this->fileshortname = f->Name; } } /// <summary> /// Obtient le nom du document /// </summary> property String Fileshortname { String get() { return fileshortname; } } /// <summary> /// Enregistre le document /// </summary> void Save(); /// <summary> /// Enregistre sous un autre nom /// </summary> void Save(String filename);

/// <summary> /// Charge le document /// </summary> void Load(String filename);

/// <summary> /// Enregistre une copie au format XLSX /// </summary> void SaveCopyExcel(String filename); }; CommedtaildimplmentationdelaclasseFeuille,nousavonschoisilamthodeGetRangequiemploiedeslistesgnriques:

/// <summary> /// Identifie les rfrences des cellules contenues dans une plage /// </summary> array<Cellule> Feuille::GetRange(String range) { array<String> names = range->Split(gcnew array<wchar_t> { : }); System::Collections::Generic::List<Cellule> res = gcnew System::Collections::Generic::List<Cellule>(); Evaluator ev = gcnew Evaluator(); int col1 = 0, col2 = 0; int row1 = 0, row2 = 0; ev->CellNameToColRow(names[0], col1, row1); ev->CellNameToColRow(names[1], col2, row2); for (int i = col1; i <= col2; i++) for (int j = row1; j <= row2; j++) { res->Add(cells[i, j]);

- 20 -

} return res->ToArray(); } LamthodeSaveToFilenamemeten uvrequantellelAPIXMLde.NETquenousavonstudiprcdemment:

/// <summary> /// Enregistre le document vers le fichier filename /// </summary> void Feuille::SaveToFilename() { XmlDocument doc = gcnew XmlDocument(); XmlNode root = doc->CreateElement("document"); doc->AppendChild(root); for(int col=0; col<cols; col++) for (int row = 0; row < rows; row++) { if (cells[col, row]->Value != "" || cells[col, row]->Formula != "" { XmlNode n = cells[col, row]->ToXml(doc,col,row); root->AppendChild(n); } } StringBuilder sb = gcnew StringBuilder(); XmlTextWriter writer = gcnew XmlTextWriter(gcnew StringWriter(sb)); writer->Formatting = Formatting::Indented; doc->WriteTo(writer); writer->Flush(); FileStream fs = gcnew FileStream(filename,FileMode::Create,FileAccess::Write); StreamWriter sw = gcnew StreamWriter(fs); sw->Write(sb->ToString()); sw->Flush(); fs->Close(); isModified = false; }

c.Lordonnanceurdecalcul
Lordonnanceur qui dtermine dans quel ordre les formules des cellules doivent tre values, repose sur limplmentation dun graphe. Cette structure de donne nexistant pas nativement, nous lavons cre en C++. Ici il y a pas de mcanisme propre C++ CLI si ce nest lemploi des structures de donnes de System::Collection::Generic (List, Stack),ainsiquedestructuresnontypescommeHashtable.

/// <summary> /// Reprsente un sommet au sein dun graphe /// </summary> public ref class Sommet { public: /// <summary> /// Nom du sommet /// </summary> String name; /// <summary> /// Sommets du graphe adjacents /// </summary> List<Sommet> adjacents; public: Sommet() { adjacents = gcnew List<Sommet>(); name = ""; } }; /// <summary> /// Reprsente un graphe dirig /// </summary> public ref class Graphe { private: /// <summary> /// Ensemble des sommets du graphe (membre priv) /// </summary> List<Sommet> g; public: /// <summary> /// Constructeur /// </summary> Graphe() { g = gcnew List<Sommet>(); } public: /// <summary> /// Ajoute un sommet au graphe, avec ses dpendances (sommets adjacents) /// </summary> void AjouterSommet(String name, array<String> dependances) { Sommet adj;

Openmirrors.com

- 21 -

Sommet s; if ((s = GetSommetByName(name)) == nullptr) { // le sommet nexiste pas encore s = gcnew Sommet(); s->name = name->ToUpper(); g->Add(s); } // ajoute ses dpendances for (int i = 0; i < dependances->Length; i++) if ((adj = GetSommetByName(dependances[i])) != nullptr) { s->adjacents->Add(adj); } else { // cr un nouveau sommet adjacent adj = gcnew Sommet(); adj->name = dependances[i]->ToUpper(); g->Add(adj); s->adjacents->Add(adj); } } public: /// <summary> /// Identifie le sommet appel name /// </summary> Sommet GetSommetByName(String name) { for (int i=0;i<g->Count;i++) { Sommets = g[i]; if (s->name == name) return s; } return nullptr; }

/* * Tri topologique */ private: Hashtable visites; void init_visites() { visites = gcnew Hashtable(); } System::Collections::Generic::Stack<Sommet> pile; Sommet depart; public: /// <summary> /// Effectue un tri topologique partir des adjacences entre sommet (dpendances inverses) /// </summary> array<String> TriTopologique() { pile = gcnew System::Collections::Generic::Stack<Sommet>(); List<String> plan = gcnew List<String>(); hasError = false; errors = gcnew StringBuilder(); for (int i=0;i<g->Count;i++) { Sommets = g[i]; init_visites(); depart = s; // le tri est bas sur un parcours en profondeur dabord parcours_profondeur(s); } while (pile->Count != 0) plan->Add(pile->Pop()->name); return plan->ToArray(); } private: void parcours_profondeur(Sommet s) { if (s == nullptr) return; if (visites[s->name] != nullptr /*&& visites[s->name] is bool*/) return; else visites[s->name] = true; pile->Push(s); for(int i=0;i<s->adjacents->Count;i++) { Sommet adj=s->adjacents[i]; if (adj == depart) { // dj visit : erreur, graphe cyclique hasError = true; errors->Append("Rfrence circulaire sur

- 22 -

" + depart->name + " : " + s->name + "\n"); return; } parcours_profondeur(adj); } } };

d.Zoomsurlvaluateur
Lvaluateurtantuneclasseassezlongue,nousavonsrecouruauxrgionspourdlimitercertainespartiesducode:

Lamthodesuivanteestassezimportante:elleutiliseunvnementpoursolliciter unevaleurissueduneclassedeniveausuprieur: void Evaluator::cellule(String name, Variant f) { double value = 0; // un vnement cherche la valeur de la cellule sans ajouter une rfrence Cells OnGetCellValue(name, value); f->nValue = value; } Sans vnement, il aurait fallu transmettre une rfrence de Cells lobjet Evaluator ce qui aurait cr une rfrence circulaire. Ces anomalies perturbent le fonctionnementduGCetprovoquentdesfuitesmmoires(lammoireestalloueetnepeuttrerelche).

e.Linterfacegraphique
LaprsencedunconcepteurvisuelpourC++estquasimentindispensablesillonsouhaitecrerdesinterfacesdehautniveau.

Openmirrors.com

- 23 -

Ceconcepteurlaissetoutelalibertauprogrammeurpourpositionnerlescontrlessuruncran,modifierlespropritsdapparenceetdecomportement,etenfincrerle codevnementiel. Dcouvronslegestionnairedvnementdg_CellValueChanged:

Cetvnementestdclenchlorsquelecontenudunecelluledelagrillededonneavolu.Lamthodedterminesilsagitduneformule(commenantpar=comme dansExcel)oudunevaleur,puisdemandelerecalculdelafeuille: #pragma region dg_CellValueChanged void dg_CellValueChanged(Object sender, DataGridViewCellEventArgs e) { Object vo = dg->Rows[e->RowIndex]->Cells[e->ColumnIndex]->Value; String value = vo != nullptr ? vo->ToString() : ""; if (value->StartsWith("=")) sheet->Cells(e->ColumnIndex, e->RowIndex)->Formula = value; else { sheet->Cells(e->ColumnIndex, e->RowIndex)->FormattedValue = value; } // il faut recalculer puisque les valeurs ont chang sheet->recalc(); } #pragma endregion Cestlapartieintressanteduprogrammedupointdevuedesvnements:lerecalculvaentranerlactualisationdecertainescellules.Ilvadabordviterdepasserune rfrence de la grille la classe Feuille, voire lobjet valuateur, puis dsactiver la dtection de lvnement CellValueChanged car cest le programme et non lutilisateurquiactualiselaffichage. Lamthodesuivantecontrlejustementlapriseenchargedelvnement: #pragma region SetCellValueHandler void SetCellValueHandler(bool handle) { if (handle) this->dg->CellValueChanged += gcnew System::Windows::Forms::DataGridViewCellEventHandler(this,&Form1: :dg_CellValueChanged); else this->dg->CellValueChanged -= gcnew System::Windows::Forms::DataGridViewCellEventHandler(this,&Form1: :dg_CellValueChanged); } #pragma endregion Voicimaintenantladfinitiondelamthoderecalc()quiprovoquelactualisationdescellulessurlagrillededonnes:

void Feuille::recalc() { ev = gcnew Evaluator(); errors = gcnew StringBuilder(); ev->OnGetCellValue += gcnew Evaluator::del_get_cell_value(this,&Feuille::ev_OnGetCellValue);

- 24 -

// cration du plan Graphe graphe = gcnew Graphe(); for (int i = 0; i < cols; i++) for (int j = 0; j < rows; j++) { if (cells[i, j]->Value->StartsWith("=")) { cells[i, j]->Formula = cells[i, j]-> Value; graphe->AjouterSommet( Evaluator::ColRowToName(i, j), Evaluator::GetNames(cells[i, j]-> Formula)); } else { if (cells[i, j]->Formula-> StartsWith("=")) { graphe->AjouterSommet( Evaluator::ColRowToName(i, j), Evaluator::GetNames(cells[i, j]-> Formula)); } } } array<String> plan = graphe->TriTopologique(); errors->Append(graphe->getErrors()); System::Collections::Generic::Stack<String> pile = gcnew System::Collections::Generic::Stack<String>(); // excution du plan for (int p = 0; p < plan->Length; p++) { int col = 0, row = 0; ev->CellNameToColRow(plan[p], col, row); if (cells[col, row]->Formula->StartsWith("=")) { if (pile->Contains(plan[p]->ToUpper())) { // passer } else { pile->Push(plan[p]->ToUpper()); cells[col, row]->Value = evalue(cells[col, row], col, row ); // dclenche un vnement pour actualiser } } } } laffichage sur la grille OnCellUpdated(col, row, cells[col, row]->Value);

Openmirrors.com

- 25 -

Openmirrors.com

Objectifsdecelivre
C++faitfigurederfrenceaupanthondeslangagesinformatiques,cequiesttoutfaitjustifideparsatrs large application. Il est galement rput difficile et rserv des connaisseurs, voire des experts. Cette apprciationestpourtantinfonde. Lobjectifpremierdecelivreestdexpliquerlesfondamentauxdulangageen sappuyantsur lapetitehistoire quiaconduitsonconcepteur,BjarneStroustrup,dfinirC++telquenouslepratiquonsaujourdhui.Lechapitre Introduction prsente les bases de la syntaxe ainsi que lorganisation des programmes, lments qui nont pas cessdvoluerdepuislintroductiondulangage.LechapitreDeCC++accompagnelelecteurjusquauxportes de C++, en passant par ltape langage C. Cela permet daborder en douceur ces notions cls que sont les pointeurs,lesstructuresetlestypesdedonnesdfinisdanslesbibliothquesstandard.Ildevientalorsplusais dapprhenderlesconceptsduchapitreProgrammationorienteobjetainsiqueleursapplications. Langle pratique est le deuxime objectif de ce livre, car les mcanismes les plus abscons ne se rvlent quau travers dexemples concrets et aussi peu artificiels que possible. Le chapitre La bibliothque Standard Template LibraryprsentelabibliothquestandardSTLsouslaformeduncondensdesfonctionslesplusincontournables. Sans tre un guide de rfrence, cela donnera au dveloppeur les points dentre pour btir ses premires applications.LechapitreLesuniversdeC++proposeplusieursapplications denvergure commelegrapheur3D ouletableurpourWindows.Avoirunbutconsquentenprogrammationfavoriseledpassementdusimpleniveau syntaxique, et la dcouverte de frameworks tels que MFC ou .NET donnera un clairage indispensable pour envisagerledveloppementdelogicielscritsenC++. Finalement, C++ savre tre un langage la fois concis et expressif. Le nombre de constructions est potentiellementinfinimaisleslmentsdebasesontrduitsunepoignedemotsclsetquelquesdizaines de rgles de grammaire. Syntaxe contenue mais smantique sans limite (comme XML en fait), lenjeu du programmeur C++ est videmment de matriser la complexit et de conserver la qualit au fil des cycles de dveloppement.LechapitreDesprogrammesC++efficacesproposedespistespourcoderavecefficacit(ctait justement ce quoi Bjarne Stroustrup voulait arriver), mais aussi concevoir des rseaux de classes qui nenfouiraient pas lessentiel derrire des millions de lignes de code. Audel de la STL et de son approche pragmatique,cestbienUMLetlesmthodes danalysebasessurcettenotationquilfautconsidrer.

Openmirrors.com

- 1-

Dpassersesprogrammes
1.OublierlesrflexesdulangageC
LesprogrammeurshabitusaulangageContsansdouteprvudenombreusesmacrosinstructionstraitespar le prprocesseur. Cest notamment le cas des constantes textuelles et de "fonctions" qui rendent bien des services. Le langage C++ propose cependant des alternatives qui amliorent la sret du programme ainsi que sa rapidit. Lesconstantesdfiniesaveclemotcl const,toutdabord,prsententlnormeavantagedtrevrifiespar lecompilateuretnonparleprprocesseur.Encasderreurvisibilit,orthographelecompilateurpeutdonner beaucoupplusdecontexteauprogrammeur. Les fonctions en ligne (inline) permettent de dvelopper des instructions sans construire tout un cadre de pile, avec les consquences que cela peut avoir sur la transmission des arguments et sur les performances. Naturellement,leurmiseen uvredoitsatisfairequelquescontraintesmaisilyadesbnficescertainsles employer. Considronslexemplesuivant,danslequeluneconstantesymboliqueestdfinielattentionduprprocesseur. En cas derreur dcriture dans le main, le compilateur na aucun moyen de prciser dans quel fichier .h elle pourraitnepascorrespondre.Demmenousavonsdcritunemacroquicomparedeuxentiers.Maissagissant dunemacroquirptelundesesarguments,nousconstateronsquedeseffetsdebordapparaissent. Lexempleproposeunealternativechaqueproblme,basesurlasyntaxeC++plussreetplusrapide: #define TAILLE 10 #define COMP(a,b) (a-b>0?1:(a-b==0?0:-1)) const int taille = 10; inline int comp(int a,int b) { if(a-b>0) return 1; else { if(a==b) return 0; else return -1; } } int _tmain(int argc, _TCHAR* argv[]) { // utilisation comme en langage C int* tableau = new int[TAILLE]; int a = 2, b = 3; int z = COMP(a++, b); // quelle est la valeur de a en sortie ? printf("a = %d\n",a); // utilisation comme en C++ A = 2; Z = comp(a++,b); // quelle est la valeur de a en sortie ? printf("a = %d\n",a); return 0; } lexcution,nousvrifionsquelutilisationdelamacroproduituneffetdebordindsirablesurlavariablea:

Openmirrors.com

- 1-

2.Gestiondelammoire
Lune des subtilits dont nous navonspasencoreparlestlallocation puis la libration de tableaux dobjets. ConsidronslaclassePointdontleconstructeurallouedelammoirepourreprsenterdescoordonnes:

class Point { public: int* coordonnees; Point() { coordonnees = new int[2]; printf("Point\n"); } ~Point() { delete coordonnees; printf("~Point\n"); } } ; SinousprocdonslinstanciationduntableaudePoint,ilyauraautantdappels auconstructeurquedobjets crs: Point* tab = new Point[3]; // trois constructeurs appels car 3 points crs Aprsutilisation,ilestconvenabledelibrercetableau.Toutefoislutilisationde deletesurcetobjet tabpeut avoirdescomportementsinattendus.Unepartiedesobjetsneserapasdtruite,oupire,uneerreurdexcution peutsurvenir.Ilconvientpluttdutiliserlinstructiondelete[]:

//delete tab; // erreur, seul un destructeur est appel delete[] tab; // erreur, seul un destructeur est appel

3.Concevoirdesclassesavecsoin
SansvouloirappauvrirlesnombreusesnormeslaborespourC++,nousrecommandonsdadopterunschma typepourdfinirdesclassesenseconcentrantsurleprogramme,maissansoublierlessentiel.Celangageest lafoistrspuissant,trsriche,etreclebeaucoupdepigesdanssesconstructions"nondites". Lapremirechosefaireestdechoisirdesidentificateursquisontexplicitesetparlants,etenmmetempsde respecteruneconventiondenommage.Mlanger m_param, iParam, param,toutappeler champ1, champ2,
- 2-

champ3sontdeserreursvitertoutprix.
Uneclasseestuneentitcomplte,autonome,etleprogrammeurdoitconnatrelavancelesmembresquila composent. Lasecondeproccupationestlavisibilitdecesmembresoublionslesformulestoutesfaites"tousleschamps sont privs et toutes les mthodes sont publiques". La ralit est toujours plus nuance, et les mthodes daccspournepasdirelespropritsviennentrapidementcontredirecettelogiqueunpeusimpliste. La troisime recommandation est lordre dans lequel sont dfinis les membres. On peut commencer par les champs,puislesconstructeurs,puislesmthodespubliques,enfinlesmthodesnonpubliques.Ilnefautpasse priverderpterpourchaquemembresavisibilit,cequivitedeserreursencasdecopiercoller intempestif. Pourchaquemthode,ondoitbienavoirlespritsielleeststatique,virtuelle,abstraitePourchaquechamp,il fautdterminersontype,sonnom,savisibilitsileststatique,constant La dernire recommandation est la documentation. Une bonne classe doit tre accompagne de beaucoup de commentaires.CeuxcinedoiventpasreformulerenmoinsbiencequiestcritenC++,maispluttdvelopper desdtailssupplmentairespourguiderceluiquiparcourtlecode. CommeC++spareladfinitiondelimplmentation,celaconstitueune"optimisation"pourleprogrammeur.Le compilateurne"moulinera"quelesfichiers .cppquisontamensvoluerplusfrquemmentquelesfichiers.h, normalementplusstablescarissusdeltapedeconception.

4.Yvoirplusclairparmilespossibilitsdelhritage
Le langage C++ propose plusieurs modes dhritage. Le plus utilis, juste titre, est lhritage public. quoi peuventbienservirlesautresmodes,commelhritagepriv? Lhritage public spcialise une classe. La classe de base exprime le concept le plus gnral, le plus abstrait, alorsquelessousclassesvontplusdansledtail.Maisilexistetoujoursenprincipeunerelation"estuncas particulierde"entrelaclassedriveetlaclassedebaselelivretestuncasparticulierdecompte.Lavoiture estuncasparticulierdevhicule De ce fait il faut se mfier des constructions syntaxiquement possibles mais smantiquement insenses. La classeChameauSatellite nasansdoutepasgrandeutilit.Uneappletnepeuttreaussiunehorlogeestun gestionnaire de souris. Ces constructions amnent de la complexit et produisent des programmes difficiles maintenir. Fautil en conclure que lhritage multiple est proscrire ? Sans doute pas. Cest un mcanisme indispensable mais qui doit tre envisag avec des prcautions. C++ tant un langage gnral, il permet lhritagemultiple,ilenammeeubesoinpourlaSTL.Maisleprogrammeurdoittreprudentsilestamen recourirluimmecetteconstruction.Lautrepossibilitestdesappuyersurunframeworkquiprvoitlhritage multipledansuncontexteencadr,sansrisque. Et lhritage priv, finalement ? L encore il sagit dun "truc" pour viter lexplosion de code au sein dun programmecomplexe.Touslesmembresdevenantprivs,cenestpaspourcrerdesmthodesvirtuellesque lonrecourtcemode.Cestpourrepiquerducodesanstropsembarrasserdunquelconquepolymorphisme. De nos jours, il existe dautres faons de contrler la quantit de code, et la programmation par aspects AOP (AspectOrientedProgramming)ouvreunevoieprometteuse.

5.AnalyserlexcutiondunprogrammeC++
IlexistedesoutilsdanalysedelogicielsC++(profilers)traquantlesgouletsdeperformances,lesfuitesmmoire et les oprations litigieuses. Sous Unix, lutilitaire purify sert prcisment dtecter les erreurs dimplmentationsdunprogrammeC++. CommeC++estunlangageassezprochedelamachine,onpeutgalementemployersousWindowslutilitaire ProcessViewerpouranalyserlefonctionnementdunprogramme.

Openmirrors.com

- 3-

Laconceptionorienteobjet
1.RelationentrelaPOOetlaCOO(ConceptionOrienteObjet)
La conception dun programme orient objet en C++ est une tape qui demande une certaine maturation. Lessenceduntelprogrammenetientpasseulementlimplmentationdesalgorithmesquilcontient,maisbien lastructuredesclassesetauxrelationsquilesrunissent.Nousnousproposonsdanscettepartiededcrire lesapprochesdemthodeslieslaconceptiondeprogrammeC++.

a.LapprocheinitialedeC++
Avant tout, rappelonsnous que C++ a t cr pour crer des applications destination du domaine des tlcommunications.Celangagenestdoncpasseulementuntravailderecherche,maiscestaussilefruitdun travail dingnierie informatique. Bien entendu, son concepteur Bjarne Stroustrup sest assur quil tait suffisamment gnral pour tre adapt dautres situations. Pour atteindre cet objectif, il a conu trois lmentsessentiels: 1.lelangageC++enluimme. 2.labibliothquestandardS.T.L. 3.unemthodedeconceptionorienteobjetpourC++.

La mthode propose par Bjarne Stroustrup repose videmment sur son exprience de conception dapplications antrieure C++. Elle repose sur un certain nombre de thmes forts, dont certains sont des classiques des mthodes de conception logicielle, oriente objet ou pas. Citons parmi ces thmes la dlimitationdusystme,lacomplexitdesapplicationsparrapportaudegrdabstractiondesoutils,ouencore lescyclesdeconceptionetdeprogrammationperuscommedesactivitsitratives. Leprocessusdedveloppementminimalestconstitudetroistapes: 4.analyse 5.conception 6.implmentation

Ltapequinousproccupeestjustementcelledelaconception.Lauteurdelamthodeorganisedessous processuspartirdudcoupagesuivant: identificationdesclasses,desconceptsetdeleursrelationslesplusfondamentales spcificationdesoprations,cestdiredesmthodes spcificationdesdpendances(descardinalitsdiraitonenUML) identificationdesinterfacespourlesclasseslesplusimportantes rorganisationdelahirarchiedesclasses utilisationdemodlespouraffinerlediagramme.

En conclusion, Bjarne Stroustrup a propos une mthode gnrale, adapte la conception de programmes C++.Ilestvraisemblablequelelangageluimmeatinfluencparlamthodedeconceptionorienteobjet.

b.UMLetC++
Undesintrtsdelapprochedcritecidessusvientdufaitquelleestcompatibleavecunsystmedenotation comme UML. De fait, UML a t cr une poque o C++ tait lun des rares langages de programmation
- 1-

orienteobjetdisponiblespourlindustrie. UML est lacronymedUnified Modeling Language. Il sagit dun formalisme qui unifie les travaux en matire de conception oriente objet dvelopps pour la plupart au cours des annes 70 et 80. Ce formalisme est linitiativedunesocit, Rational,quiaensuiteproposlelogicieldemodlisationRose,ainsiquunemthode partentireappeleRUP( RationalUnifiedProcess). Comme C++ tait lun des principaux langages du march, il a fortement influenc llaborationdUML. Aussi, estil frquent de trouver des logiciels de modlisation UML capables de transformer automatiquement un diagrammedeclassesenprogrammeC++,sanstoutefoispouvoirfournirlimplmentationcorrespondante! Le formalisme UML est constitu de neuf diagrammes. Il nest souvent pas ncessaire dexcuter lensemble des diagrammes pour parvenir une modlisation conforme. Par contre, certains diagrammes ncessitent de syprendreenplusieursfois.Onparlealorsdunprocessusdemodlisationitratif. VoicilalistedesdiagrammesconstitutifsduformalismeUML1: Diagrammedescas dutilisations Diagrammede collaboration Vision fonctionnelle Vision fonctionnelle Pose les limites du systme en listant les acteurs et leursintentions,sanssattardersurle"comment". Les cas dutilisations sont dcrits sous la forme de scnarii textuels avant dtre formalits en diagrammes de collaboration. Cest le dbut du comment. Cest une version "workflow" du diagramme de collaboration plus adapte la description de certainsscnarii. Cest une version technique d un workflow on considredeschangementsdtatauseindeclasses quiprennentprogressivementforme. Semblable au diagramme dtat transition, le diagramme de squence tudie la constitution de classe sous l angle du temps, en faisant progressivementressortirdesmthodes. Ce diagramme ne fait pas partie d UML mais il est indispensable. Les classes mtiers sont souvent structurespartirdestablesSQL. Ce diagramme est un peu la version affine, superposedesprcdentsdiagrammes. Lorsque les classes sont strotypes, pourvues dinterfaces techniques, elles sont appeles composants. Ce diagramme prcise sur quels n uds les composantsdoiventtredploys.

Diagrammedactivit

Vision fonctionnelle

Diagrammetat transition

Vision dynamique

Diagrammedesquence

Vision dynamique

Diagrammedudomaine

Vision"mtier"

Diagrammedeclasses

Visionstatique

Diagrammede composants

Visionsystme

Diagrammede dploiement

Visionsystme

DesoutilstelsqueVisioArchitectouEnterpriseArchitectsontcapablesdereproduirelediagrammedeclasses dunprogramme,ouinversementdegnrerduC++partirdundiagrammedeclasses. CommeUMLetC++ sontdeuxlangagestrsgnraux,ilssentendentparfaitementettoutelapanoplieduC++peuttrecriteen UML. Lediagrammedeclassesuivantmontrecommentmodliserunepartiedutableur CLIMulticubetudiau chapitre Les univers de C++. Cest bien la preuve que le formalisme UML nest aucunement rserv aux langagesC#ouJava.

- 2-

Openmirrors.com

2.Lesdesignpatterns
Les design patterns sont des faons standard daborder des problmes logiciels. Il sagit de constructions de classesrpertoriesquelonadapte(dveloppe)augrdesbesoins. Nous avons vu au chapitre Les univers de C++ que les MFC implmentaient le pattern MVC (Modle Vue Contrleur)traverslarchitecturedocumentvue.Ilexistedetrsnombreuxmodlesetcertainsgnrateursde codepeuvent constituerlossatureduneapplicationenparamtrantdespatternspourunlangagedonn. Pour illustrer les patterns, nous dveloppons lexemple du Singleton. Il sagit de veiller ce quune classe ne puisse tre instancie quune et une seule fois. Un exemple habituel dutilisation de ce pattern concerne les fichiersdeconfigurationquinedoiventpasexisterenplusieursexemplairesauseinduneapplication. class Configuration { private: static Configuration* instance; Configuration() { // Le constructeur priv empche toute instanciation externe la classe } public: static Configuration* getInstance() { if(instance==NULL) { printf("Instanciation de Configuration\n"); instance = new Configuration(); } return instance; } public: bool param1,param2; } ; Configuration* Configuration::instance=NULL; int _tmain(int argc, _TCHAR* argv[]) { // utilisation du singleton Configuration::getInstance()->param1 = true;

- 3-

Openmirrors.com
Configuration::getInstance()->param2 = true; return 0; } Commeleconstructeurestpriv,ilestimpossibledinstancierlaclasse Configurationendehorsdecelleci. Cependant, nous pouvons confier cette instanciation une mthode publique et statique, getInstance(). Cettederniresappuiesurunchampstatique,instance,pourcontrlerluniqueinstanciationdelaclasse.Au premier appel de getInstance(), le champ instance gale NULL et la classe est instancie. Tous les appels successifs travailleront partir de cet unique objet. De cette faon, les paramtres de la configuration param1etparam2nexisterontquenunseulexemplaireauseinduprogramme. Lacopiedcrancidessousmontrequelinstanciationnaeulieuquuneseulefois:

Openmirrors.com

- 4-

Openmirrors.com

quisadressecelivre ?
Ce livre est destin tout dveloppeur dsireux dapprendre le langage C++, dans le cadre de ses tudes, ou pourconsolideruneexprienceprofessionnelle. Avant daborder cet ouvrage, la matrise dun autre langage de programmation est un plus, mais il nest pas ncessairequecelangagesoitapparentaulangage C. TouslesexemplesonttralissaveclaversiongratuitedeVisualC++,etunetrsgrandemajoritdentreeux sont portables tels quels sous Unix et Linux, lexception des exemples du chapitre Les univers de C++ qui pourronttretestsavecquelquesamnagements.

- 1-

Lesnotionscls
1.PrincipalescaractristiquesdulangageC++
LelangageC++estapparuofficiellementen1983,datedesespremiresutilisations horsdulaboratoireAT&T qui la fait natre. Son concepteur, Bjarne Stroustrup, avait dbut ses travaux plusieurs annes auparavant, sansdoutevers1980.LelangageC++ appel jusquelCavecdesclassesatlaborenconservantla plupartdesconceptsdulangageC,sonprdcesseur.Lesdeuxlangagessesontensuitemutuellementfaitdes emprunts. Comme le langage C, C++ adopte une vision trs proche de la machine. Il a t destin en premier lieu lcrituredesystmesdexploitationmaissescaractristiques luiontouvertdautresperspectives. Le langage est form dinstructions trs explicites, courtes, dont la dure dexcution peut tre prvue lavance,aumomentdelcritureduprogramme. Le nombre dinstructions et de notations tant volontairement limit, les interprtations des constructions smantiques sont multiples et cest sans doute ce que le concepteur du langage C++ dsigne sous le terme dexpressivit. Toutefois, Bjarne Stroustrup a veill contourner certains cueils du langage C, et notamment sa tendance tout ramener au niveau de loctet, quantit numrique limite et totalement obsolte dans lhistoire de linformatique, mme en 1980. Pour obtenir ce rsultat, le langage sest enrichi de classes qui dcrivent des types de donnes adapts aux diffrents besoins du programmeur. La visibilit du langage combine labstractiondesclassesfournitdesprogrammesdehautniveau. Les classes (et la programmation oriente objet) nont pas t inventes loccasion de C++. Linventeur du langage sest efforc dadapter des concepts de haut niveau introduits par le langage Simula en 1967, une plateformetrsexplicite,lelangageC. Il rsulte de cette double filiation un langage trs riche, trs puissant, particulirement expressif et en mme tempstrsefficace.Lelangagesestensuiteaffin, asubidesvolutions,destransformations,jusquaustade o le produit de laboratoire est devenu un standard, labellis par lorganisme amricain ANSI. Les travaux de standardisationontdbuten1987poursacheveren1998. Sans attendre cette date tardive, C++ avait fait de nombreux adeptes. En premier lieu, les programmeurs connaissant le langage C sont passs plus ou moins naturellement au langage C++ : nombre de compilateurs C++reconnaissentlelangageC.EtdenombreuxditeursdecompilateursneproposentpasdecompilateurC seul. En second lieu, le langage a servi lcriture de systmes dexploitation ou de parties de systmes dexploitation : le systme quipant le Macintosh, linterface graphique Windows sont cods en C++. Par consquent,leslogicielsprvuspourcessystmesavaienttoutavantagetreprogrammseuxaussienC++. En rsum, on pourrait dire que C++ est tout la fois un langage de haut niveau bas sur des instructions prochesdelamachine.Unquilibresubtilquelesdveloppeursapprcient.

2.Programmationorienteobjet
LemploignralisdemthodesdeconceptionlogiciellestellesquUMLaeuraisondelhistoiredelinformatique. Pourquoilaprogrammationorienteobjet atelletinvente ?Quepermetellededcrireenparticulier ?Nous devonsnouspenchersurlesujetcarunedescaractristiqueslesplusimportantesdulangageC++estsongot prononcpourlaproductiondeclassesetdobjets. Vers1950,lesordinateurstaientprogrammslaidedulangageAssembleur.Celangage,detrsbasniveau puisquil ncessite la fourniture de codes binaires, respecte rigoureusement les consignes de Von Neumann, linventeurdesordinateurssquentiels.Unprogrammeestconstitudunesuitedinstructionsimpratives,que leprocesseurvaexcuterlesuneslasuitedesautres,ainsiquededonnesquelonnosepasencoreappeler "variables".Toutestdoncclair,unprogrammepossdeundbutetunefin.Ilconsommedesentresetfournit unrsultatlorsquesachvesonexcution.

Openmirrors.com

- 1-

Lesmicroprocesseurssurcircuitintgrnexistaientpasencore.Lespremierscircuitsintgrssontapparusla fin des annes soixante et les premiers microprocesseurs intgrs ont t mis sur le march au dbut des annes soixantedix. Jusquel, les ordinateurs taient conus autour dun processeur base de composants discrets,transistorsoulampes. Onsestrapidementrenducomptequecettefaondaborderlesprogrammesposaitdeuxproblmes.Dunepart, le programme tait loin dtre optimal car de nombreuses suites dinstructions se rptaient, pratiquement lidentique.Dautrepart,lesprogrammestaientlaborspardesmathmaticiensquicherchaienttraduiredes algorithmesfaisantappeldesoutilsconceptuelsdebienplushautniveauquelAssembleur. Cest en 1954 que les travaux sur le langage Fortran (Formula Translator) ont dbut. En 1957, John Backus prsentaituncompilateurdestinauxscientifiquesquiavaientbesoindeprogrammerdesalgorithmesstructurs sansavoirconstruireeuxmmeslatraductionenAssembleur.Dautreslangagesontsuivireprenantlemodle deFortran :desinstructionsdecontrle(if,for,while...),desvariables,etfinalementdesprocdures. Uneprocdureestunesuitedinstructionsquiporteunnom.Lepasfranchitaitnorme :pluttquederecrer lidentiqueunesuitedinstructions,onplacelapremireoccurrencedansuneprocdure,puisonremplaceles prochaines occurrences par un appel cette procdure. La programmation procdurale tait ne, bientt appeleprogrammationfonctionnelle,cequipermettaitdereliermathmatiquesetinformatique. De nos jours, presque tous les langages connaissent la notion de procdure : Basic, Pascal, Java, et bien entenduCetC++. La prochaine tape consistait structurer les donnes. En tudiant de prs un programme qui manipule des coordonnes de points dans le plan, on saperoit que chaque point est dfini par deux variables x et y, lune reprsentant labscisse du point et lautre, son ordonne. Les langages de programmation des annes 50 saccommodaient mal de ce type de situation. Pour reprsenter trois points P1, P2, P3 il fallait dclarer six variables, x1, y1, x2, y2, x3, y3. La programmation structure ( base de structures) autorise la dfinition de types rassemblant un certain nombre de variables aussitt dnommes champs. Dans notre exemple, nous imaginons le type Point contenant les champs x et y. Lintrt de cette approche rside dans le fait que le nombre de variables est divis par deux (trois instances de Point : P1, P2 et P3). Beaucoup de langages structursontadoptlanotationP1.xpourdsignerlavaleurduchampxrapporteaupointP1,autrementdit, labscissedeP1. Les programmes btis avec ces langages dcrivent des types structurs, des variables et des procdures (fonctions). Une fonction est une procdure qui prend gnralement des paramtres pour valuer un rsultat. Aprs avoir misaupointlaprogrammationstructure,onsestrenducomptequelesfonctionsprenaientleplussouventdes instances de structures comme paramtres. La syntaxe des langages de programmation salourdissait rapidement,puisquilfallaitdistinguerdenombreuxcasdefigure :passageparvaleur,parrfrence,structure enentre,ensortie... Vint alors lide de runir dans une seule "bote" la structure, des champs et des fonctions sappliquant ces champs. Cette structure devenait une classe. Les variables formes partir dune classe (les instances) prenaientlenomdobjet. Ctaitnenpointdouterlavoiesuivre,lavenir.Laprogrammationorienteobjet taitne,etlelangageSimula,proposen1967futunedesespremiresconcrtisations. Reste que ce type de programmation na pas connu cette poque lengouement quon lui reconnat de nos jours. Les concepts taient peuttre trop novateurs, trop ardus pour linformatique naissante. De plus, les ordinateurs de lpoque, peine transistoriss, avaient le plus grand mal faire fonctionner cette programmationgourmandeenoctets. Onseconcentradoncsurlamiseaupointdelangagesdestinslefficacit,on"descendit"danslchelledela conceptualisation pour revenir des valeurs sres. Le langage C est vraisemblablement un des langages les plusexplicitesquisoit,toujoursprtrevenirlunitfondamentale,loctet. Une quinzaine dannes scoulrent et Bjarne Stroustrup perut le retard pris par de nombreux projets informatiques, faute doutils logiciels adapts. La plupart taient de trs bas niveau, proches de lAssembleur, alorsquelesproblmestraiterdevenaientdeplusenplusconceptuels.Heureusement,lehardwareavaitfait degrosprogrsdepuis1967,aveclintroductiondescircuitsintgrspuisdesmicroprocesseurs,lammoirevive cotaitdemoinsenmoinscheretmmelesdisquesdurstaientlivrsensrieaveclesordinateurspersonnels.

- 2-

Cesconditionsfavorablesrunies,BjarneStroustrupopralagreffedestravauxSimulasuruncompilateurC le langageC++tiresesorigines decetteapproche.

3.Environnementdedveloppement,makefile
a.ChoixdunEDI
Ladonneaunpeuchangcesderniresannes.Auparavant,lenvironnementdedveloppementintgr(EDI) proposaitsonditeurdecodesource,soncompilateur,seslibrairiesetsesutilitaires.Ilsagissaitdunlogiciel propritairequivoluaitenvironunefoisparan. Ondevraitdsormaisparlerdenvironnementdedveloppementintgr.Lintroduction delatechnologieXMLa rendu les fichiers de configuration plus ouverts. Les diteurs de code source sont personnalisables, le compilateur peut tre remplac par un autre, tout est modifiable. Le paroxysme est atteint avec Eclipse qui devient une plateforme de dveloppement pour pratiquement tous les langages existants, par le biais de modulesadditionnels(plugins). C++Builder Embarcadero (repreneur des activits dveloppement de Borland) Licence commerciale VisualC++ Microsoft Licence commerciale mais dition express gratuite. OpenSource Offre un diteur visuel dapplications graphiques, plus les framework MFC et .NET. Une version "libre" est disponible. Des plug ins sont disponibles, notamment pourutiliserlecompilateurIntel. Intgre le compilateur GNU. Pas tellement adapt au dveloppementgraphiquepourWindows. EnvironnementUnixpourWindows. Plateforme ddition de code source, initialement destineJava.DespluginsC++sontdisponibles. PlateformededveloppementcroisWatcom. Lecompilateurseulenlignedecommandeestgratuit,et lEDIestpayant.

DevC++

CygWin Eclipse

OpenSource OpenSource

OpenWatcom

Libre

Cettelisteestnouveauloindtreexhaustive.DenombreuxEDIoffrentungrandconfortlditiondecode source.Ladisponibilitdundbuggeur,unedocumentationenlignedequalitserontdeslmentsquivous aideront choisir. Des magazines de programmation publient rgulirement leur avis sur ce type de produit, testlappui.

b.Constructiondunfichiermakefile
Lefichiermakefilespcifielesoprationsdeconstructiondunprogramme ilestindpendantdulangagede programmation et sappliquetrsbienC++.Certainsenvironnementsdedveloppementlont remplac par leurpropregestionde"projets",avecparfoislapossibilitdeconvertirunmakefileenprojetoudexporterun projetenmakefile. Ilnexisteenprincipequunseulfichier makefileparrpertoire.Lutilitairequipilotelacompilation makeou nmakepeuttrelancenspcifiantlefichierutiliser,maismakefileestceluichoisipardfaut. Unfichiermakefileseconstruitlaidedunditeurdetexte.Ilcontientunensembledecibles.Onrencontre leplusfrquemmentlesciblesalletclean.

Openmirrors.com

- 3-

Lasyntaxeestlasuivante : cible : dpendance1 dpendance2 ... instruction1 instruction2 instruction3 Lesdpendancesetlesinstructionssontoptionnelles.Souvent,lacibleallnapasdinstruction,etlacibleclean napasdedpendance.Voiciunpetitexempledemakefile :

#cibles par dfaut all : lecteur.exe lecteur.exe : lecteur.obj personne.obj link lecteur.obj personne.obj -o lecteur.exe lecteur.obj : lecteur.cpp lecteur.h personne.h cl -c lecteur.cpp -o lecteur.obj personne.obj : personne.h personne.cpp cl -c personne.cpp -o personne.obj

#nettoyage du rpertoire clean : del *.obj del lecteur.exe Donnons une lecture en langue franaise de ce fichier makefile et prcisons tout dabord que les lignes commenantparunsigne#sontdescommentaires,ainsiquilenestlusagedanslesfichiersscriptUnix. Lefichier makefilecommenceparidentifierlacible allquinaquedesdpendances. Cettesyntaxeindique quilfautconstruireleprogramme lecteur.exe. Lorsquelonexcutelacommande makedepuislerpertoire duprojet,lesdatesdesfichierssontcomparesenremontantlesrgleslesunesaprslesautres :
q

si

personne.h ou personne.cpp personne.cppestrecompil

ont une date postrieure

personne.obj,

le fichier

si lecteur.cpp,lecteur.houpersonne.hontunedatepostrieure lecteur.cppestaussirecompil

lecteur.obj,lefichier

silundesmodulesobjetsaunedatepostrieureaufichierlecteur.exe,lacommandelinkestelle aussiexcute.

Silundesmodulesobjetsaunedatepostrieureaufichier lecteur.exe,lacommande linkestelleaussi excute. Ce processus sappellelacompilationspare lesmodulesnesontrecompilsquesicestncessaire.Sous Unix,ondisposedunecommande,touch,quimodifieladatedecertainsfichiers.Celadonnelemmeeffetque douvrirlesditsfichiersdansunditeuretdelesrenregistrersansmodification. Toutefois, afin de sassurer de la fracheur des rsultats, la commande make clean efface tous les modules objets,ainsiquelexcutable. On peut alors enchaner les commandes make cleanet make. Les fichiers intermdiaires ayant t effacs, touslesmodulessontrecompils. Une syntaxe de macros facilite le changement de compilateur. On dfinit un certain nombre de constantes, telles que le nom du compilateur, le nom de lditeur de liens, les commutateurs doptimisation, la liste des modulesobjets...

- 4-

Openmirrors.com

4.OrganisationdunprogrammeC++
Un programme C++ subit une srie de transformations avant dtre excut. La principale transformation seffectuelaidedunoutilquelonappellecompilateur.Lecompilateurfaitluimmeappelunprtraitement confi au prprocesseur. Le prprocesseur rassemble diffrents fichiers contenant des sources compiler ensemble. Le rsultat de la compilation est plac dans un ou plusieurs modules objets, qui sont ensuite runis par un troisimeoutilappelditeurdeliens. Lditeurdeliensformegnralementunexcutablepourlesystmedexploitation,maisilpeutaussicrerune bibliothque(library)destinerutiliserlesmodulesobjetsdansdautreslogiciels.

a.Codessources
LescodessourcesC++sontsaisislaidedunditeurdetexte.Certainsditeursdecodessourcesontune fonction de coloration syntaxique, cestdire quils affichent les mots cls du langage dans une certaine couleur, les chanes littrales dans une autre couleur, les commentaires sont galement distingus par une couleurspcifique,etc.Laffichagedevientalorsplusais,ainsiquelarecherchederreursdansleprogramme. En principe, les codes sources sont saisis dans des fichiers disposant de lextension .cpp. Vous pouvez galementrencontrerdescodessourcesayantlextension .h.Ilsagitdefichiersdenttes(header)destins tre inclus par des fichiers .cpp laide de la directive #include. Le prprocesseur ralise cette "insertion". Lassemblage des fichiers .h dans des fichiers .cpp ne modifie pas ces derniers. Un fichier temporaireestcrpourladuredelacompilation. Ilexistedeuxoriginesdefichiersdenttes lesenttessystmeetlesenttespropresauprogramme. Lesenttessystmecontiennentdesdclarationsdefonctionsutilitaires,tellesquelaffichagededonnes lcranouencorelamanipulationdefichiers.Limplmentationdecesfichiersnestpasfourniesouslaformede codesource,maisleprogrammeurnenapasnonplusbesoin.Ilsecontenteradeliersesmodulesobjetsavec lalibrairiecontenantlesfonctionsenquestionsousuneformecompile. Les enttes propres au programme dfinissent des constantes, des fonctions et des classes qui sont implmentesdansdesfichiers.cpp. La directive #include semploie avec deux syntaxes, suivant que le fichier inclure se trouve dans le rpertoiresystmeoudanslerpertoireduprogramme.

#include <stdio.h>:stdio.hestrecherchdanslerpertoiredesenttessystme #include "personne.h":personne.hestrecherchdanslerpertoireduprogramme.


Quoiquilensoit,lesfichiersdextension .hnesontpasdirectementcompils.Ilssontdestinstreinclus dansunfichier .cppquiseraluicompilpourformerunmoduleobjet.Lemoduleobjetportegnralementle nomdufichier.cpp,aveclextension.obj(ou .osousUnix)maiscelanestnullementunecontrainteouune obligation. tudionsprsentunpetitexemple. Dans cet exemple, le fichier compiler sappelle personne.cpp. Il a besoin de deux fichiers dentte, stdio.hetpersonne.h,quiserontcompilsenmmetempsquelui,aprsavoirtinclusparladirective #include.

Openmirrors.com

- 5-

Ilrsultedelacompilationununiquemoduleobjetappelpersonne.obj. Pourobtenircersultat,lefichierpersonne.cpppourraitressemblercela :

#include <stdio.h> #include "personne.h" int main() { } Peuimportecequecontiennenteffectivementlesfichiersstdio.hetpersonne.h. Lacommandedecompilationseralasuivante(aveclecompilateurgcc) : gcc -c personne.cpp -o personne.obj Autrementdit,nousdemandonsaucompilateurC++ gccdecompilerlefichier personne.cpppourformerle module objet personne.obj. Le commutateur -c demande gcc de ne pas appeler automatiquement lditeur desliens,doncilsecontenteradecompilerpersonne.cpp. Il est noter que le fichier personne.obj nest cr que si la compilation se passe bien. Toute erreur de syntaxe,touteabsencedefichier stdio.hou personne.h provoquera larrtprmaturduprocessus.Le compilateur gnre alors un message qui dcrit les circonstances du problme, ventuellement prcd dun numrodeligneetdunomdufichieranalysolerreursestproduite. Parexemple,nouspourrionsavoirlemessagesuivant: personne.cpp, ligne 2 : le fichier personne.h est introuvable.

- 6-

ParfoislerreuralieudansunfichierdenttequicontientdesinstructionsnerespectantpaslasyntaxeC++: personne.h, ligne 38 : erreur de syntaxe. Il devient alors intressant dutiliser un environnement de dveloppement intgr. Les messages derreurs sontaffichsdansunefentrespciale.Ilsuffitdeslectionnergnralementpardoubleclic un message derreur,etlditeurdetexteouvrelefichiercorrespondant,puisilaffichelaligneosesitueleproblme.

b.Modulesobjets
LesmodulesobjetssontuneformeintermdiaireentrelecodesourceC++etlexcutable.Ledcoupageen modules objets facilite la compilation spare. Lorsque vous modifiez une ligne de programme, cette modificationnapeuttrepasdimpactsurlensembledulogiciel.Enrecompilantuniquementlapartiedecode "autour"delamodification,ongagneuntempsconsidrable. Letermemoduleobjetnarienvoiraveclaprogrammationorienteobjet.Deslangagestelsquelelangage Assembleur, le langage C, le Pascal, le Basic produisent galement des modules objets. Il sagit dun fichier binairersultantdelacompilationduncodesource. Laquestionrevientchoisirunestratgiepourdcouperaumieuxsonprogramme.Trssouvent,lesclasses quenoustudieronsendtaildanscetouvragesontdfiniesdansunfichierdentte .hetimplmentes dansunfichiersource.cpp.Chaquefichiersourceestcompiletdonnelieuunmodule objet. Ilestgalementpossibledecrerdesmodulesobjetspartirdefichierssources .cppregroupantuncertain nombre de fonctions du logiciel. Ainsi, il nest pas rare de trouver un module objet form partir du module main.cpp,danslequelonadfinilafonctionprincipaleduprogrammemain(). Le langage C++ nous autorise dpasser cette construction. Longtemps, les langages de programmation, commeleC,sesontcantonnscetteconstructionomodulefonctionnelquivautfichiersource.C++largit cettevision : Pour le concepteur, il importe plus davoir une vision logique des fonctions quun point de vue physique. Les espacesdenomsonttcrsdanscebut.OrenC++,uneclasseengendresonpropreespacedenoms,il devenait logique de ranger chaque classe dans son propre fichier de code source. Il sagit pourtant dune conventionetnondunecontrainte,aussipouvezvousdfinirautant declassesquevouslesouhaitezdansun mme fichier de code source. Il est enfin possible dimplmenter une classe dfinie dans un fichier .h au travers deplusieursfichierscpp.Toutefois,cesconstructionsnesontpastrsnaturelles,ellesreprsententun risquedecomplicationlorsdelditiondesliens. Nousallonstudierprsentlastructuredunmoduleobjet.Silestvraiquedenombreuxlangagesutilisent unprocessusdeconstructionidentiqueC++compilation,ditiondesliens,excutionlesmodulesobjets sontloindtrecompatiblesentreeux.CestparfoisgalementvraiduncompilateurC++lautre. Les modules objets contiennent le code source compil. Le travail dun compilateur consiste dune part effectuerdesvrifications,dautrepartgnrerducode(binaire,assembleur)enfonctiondesinstructions.Le moduleobjetcontient: ladclarationdesvariablesauniveauassembleur(nom,taille), ladclarationdesfonctions(nom,codebinaire).

Lditeur des liens est charg dassembler (de runir) diffrents modules objets pour construire une librairie (.lib, .dll ou .so)oubienunexcutable(.exesousWindows,pasdextensionsousUnix).Lditiondes liensconsisteorganiserlassemblageenrespectantleformatimposparlesystmedexploitation,dumoins lorsquelacibleestunexcutable. Engnral,touteslesvariablessontregroupesdansunsegmentdedonnes,ettouteslesfonctions(toutes les instructions) sont installes dans un segment de code. Un entte complte lensemble en regroupant diffrentes informations dordre gnral (mtadonnes, point dentre). Lditeur des liens va ensuite remplacerchaquerfrenceunevariableouunefonctionparsonadressedanslassemblageralis.Les

Openmirrors.com

- 7-

nomssymboliquesdisparaissentauprofit duneadresseentirementnumrique. Cette construction dun assemblage appelle certains commentaires. En premier lieu, estil envisageable de mlangerunmoduleobjetobtenupartirduncodesourcecritenlangageCavecunmoduleobjetobtenu partirdeC++ ?Silestvraiquelesdeuxlangagessontproches,notammentencequiconcernelestypesde donnes et leur reprsentation, les modules objets utilisent des conventions diffrentes. Les noms symboliquessontformsdistinctement.Parailleurs,lorsdelappeldunefonction,lesparamtresnesontpas transmisdanslemmeordreenfin,lestechniquesderestaurationdepilesontdiffrentes.Pourrsoudreces difficults,leprogrammeurpeutemployerdesdirectivespropresaucompilateurC++danslebutdeprciserla faondappelerunefonctionsituedansunmodulecompilenC.Cesdirectivessont extern "C"etparfois aussiPASCAL.Nousleretrouveronsunpeuplustardendtail. Pour lier un module objet C++ un module objet crit dans un langage tel que Basic, laffaire se corse. La situation est pourtant plus frquente quon ne limagine. Nombre dapplications sont crites pour partie en Visual Basic, linterface graphique et pour autre partie en C++, les traitements. La premire chose considrer est alors la reprsentation des types de donnes qui varie singulirement dun langage lautre. Deuxtechniquessontalorsenvisageables:utiliseruncompilateuretunditeurdeliensquireconnaissentle formataveclequelonsouhaitetravailler,oubiencrerdesmodulesobjetscommunsplusieurslangagesde programmation. Microsoftalongtempsfavorislapremiretechniquemaissestretrouvdansuneimpassequilaconduitla seconde.LescompilateursVisualBasicarrivaientexploiterdesmodulesC++(composantsCOMouActive X), toutefois, la vie du dveloppeur devenait incroyablement complique lorsquil sagissait dchanger des donnessouslaformedechanesdecaractres,dedates...LesingnieursdeMicrosoftontensuiteconula plateforme .Net, o les modules objets sont identiques dun langage de programmation lautre. Ils ne se sont pas arrts l puisquils en ont profit pour uniformiser les types de donnes entre leurs diffrents langagesenintroduisantlanormeCTS,CommonTypeSpecification.Decefait,leslangagesVB.NET,C#etC++ partagentungrandnombredecaractristiquescommunes. Sous Unix, la situation se prsente mieux car les langages C et C++ reprsentent la majorit des dveloppements.Lesautreslangages,telsTCL/TKouPHPsontenfaitdessurcouchesduC(commeC++ses dbuts),sibienquelaquestiondelinteroprabilitentrelangagesseposetoutsimplementmoinssouvent. RestequentrecompilateursC++,lesmodulesobjetsnontpastoujourslammeorganisation,mmelorsquils sont compils sur la mme plateforme. Nous retrouverons des exemples de situations problmatiques en envisageantlalignementdesmots(unitdedonne)auniveaudeloctet,dudoubleoctetouduquadruple.

c.Librairies(bibliothques)
Lassemblage form par lditeur de liens nest pas toujours un excutable. On peut livrer certains modules objetssouslaformedelibrairiesstatiquesoudynamiques. Une librairie statique se contente de regrouper un certain nombre de modules objets. Sous Windows, on lui donnesouventlextension.lib.Desprogrammesexcutables(contenantdoncunefonction main)utiliseront certaines fonctions de la librairie, aprs ligature. Les fonctions systme telles que laffichage, la gestion des chanes de caractres... sont livres sous forme de librairies statiques. Certains compilateurs proposent plusieurs versions, excution simple thread ou multithread, version internationalement neutre, version spcifiqueaufranais,enfonctiondesplatesformesetdescompilateurs. Onemploieaussideslibrairiesstatiquestiercesdutilitapplicative,tellesqueleslibrairiesdanalyselexicaleet grammaticaleetleslibrairiesmathmatiques. Au temps o la mmoire tait limite, il fallait minimiser la taille des excutables. Pourtant les logiciels devenaientfonctionnellementdeplusenplusriches.Pourrsoudrecettecontradictiondobjectifs,leslibrairies dynamiques ont t inventes. Elles permettent de mutualiser (de partager) du code et parfois aussi des variablesentreplusieursprocessus. Un processus est un programme en cours dexcution. Lorsquun processus appelle une librairie dynamique (.dll sous Windows et .so sous Unix), ldition des liens doit tre opre. Il ne sagit pourtant pas des mmes techniques que celles employes avec un module objet ou une librairie statique. Cest en gnral le systmedexploitationluimmequiassurecerle.

- 8-

Cetteoprationsoulvequelquesremarques.Premirement,lditeurdesliensdoittreprvenuquecertaines fonctionsneserontdisponiblesqulexcution.Ilnedoitpaschercherfonctionnercommelaccoutume.On obtientcersultatauprixdunesyntaxequelquepeualourdie,carnombredeprogrammes C++utiliseront ceteffetdespointeursdefonction. Deuximement,lalocalisationdelalibrairieaucoursdelexcutionprovoqueraunralentissementplusoumoins long : la librairie doit tre charge en mmoire, ventuellement initialise, puis la fonction sera lie au programmeappelant. Leslibrairiesdynamiquesontconnucesdernierstempsunessorinattendu,tantsousUnixquesousWindows, auprixilestvrai,desubstantiellesmodificationsdeleurfonctionnementetdeleurdfinition. Pour finir, ajoutons que le terme librairie est peuttre inappropri, quoique gnralement usit. En effet, le termeanglaislibrarydevraittretraduitparbibliothque.

d.Excutable
Un excutable est le rsultat fourni par dfaut par lditeur de liens. Il suit une construction qui dpend du systme dexploitation. Certains systmes, comme Windows, proposent diffrentes organisations dexcutables: .com, .exe, .dll.SousUnix,leschosesparaissentmoinsvaries,carlattribution du droit dexcution(--x)suffitrendreligibleunfichierlexcution...sousrserveduneconstitutionadapte. Quelle que soit la plateforme, un excutable est form partir dun entte indiquant le point dentre du processus,cestdireladressedelapremireinstructionexcuter.Enprincipe,cettevaleurvariepeu,etles logiciels anti virus mettent profit cette caractristique pour dtecter un virus informatique. Un excutable contamincontientenprincipelesinstructionspropresauviruslafindufichier,afindviterdetoutdcaleret detoutrcrire.Lepointdentreduprocessusestalorsprovisoirementdroutlafindufichier,puisune instruction de saut reprend le cours normal de lexcution. Ainsi, lutilisateur lance son processus, sans se rendre compte immdiatement quun virus sest install. Son programme semble se lancer normalement ensuite, lesstratgiesviralesdivergent! Quoi quil en soit, le format excutable a peu volu depuis sa conception il y a plus de vingt ans. C++ produisant un code assembleur difficile dcompiler, lexcutable est vulnrable face aux intrusions virales, nous venons de le voir, mais aussi face aux dgradations lors de transmissions travers les rseaux informatiques.DesditeurstelsqueMicrosoftontrevuenprofondeurlaconstitutiondun excutable, appel assemblage,aveclatechnologieC++manag(C++pour.NET).Danscetenvironnement,lexcutablepeuttre sign numriquementpourgarantirlafoissonintgritetsonorigine. Lautre faiblesse des excutables C++ rside prcisment dans le manque dinformation sur limpact du fonctionnement.C++permetcertesdcriredessystmesdexploitationetdescompilateurs,maisilestaussi fort apprci pour ses performances et son ouverture sur le monde applicatif. Dans un environnement dexcutiontelquInternet(vialinterfacedeprogrammationCGI),lexcutableC++constitueun risque. Impossible en effet de prdire, la simple vue dun fichier excutable, sil renferme des instructions dangereuses telles que le reformatage du disque dur. Cet cueil plaide en faveur dune excution scurise desprogrammesC++,commecelasefaitaveclesprogrammesJavaouC#. L encore, Microsoft a devanc dautres diteurs en proposant un C++ manag, ce qui a t possible en amnageant quelque peu la syntaxe. On peut tre surpris par la dmarche, mais C++ a des qualits indniables en termes de performances et de consolidation de lexistant, ce qui le rend de plus en plus populaire dans un contexte Internet. On voit ainsi apparatre des librairies C++ pour construire des services web.Lebesoindescuriserlexcutablesefaitalorsnettementressentir.

5.Leprprocesseur
Le prprocesseur est charg deffectuer un premier traitement "textuel" sur les fichiers sources. Cest lui qui dcode les lignes dbutant par le symbole #. Ces lignes servent imbriquer des fichiers de manire conditionnelleetvaluerdesconstantessymboliques(galementappelesmacro). En fait, le prprocesseur est devenu une partie intgrante du compilateur qui le contrle et optimise son application.

Openmirrors.com

- 9-

6.Choixduncompilateur
Lechoixduncompilateurdpenddaborddelaplateformeenvisage.Windows ?Linux ?Macintosh ?Chacune asessupporters. Mais le choix nest pas entirement dtermin par cette question. Les librairies proposes (framework), lenvironnementdedveloppementsontgalementimportantsprendreencompte. Enfin, certains compilateurs se montrent meilleurs que dautres dans des domaines particuliers, tels que loptimisationducodeoulesupportcompletdesmodles(templates). Voiciunecourteliste,loindtreexhaustive,descompilateursC++lesplusutiliss. GNUg++ LicenceGPL Pratiquementincontournablepourcompilerle noyau de Linux. Trs respectueux du standard ANSI. Compilateur assez lent et codepeuoptimis. Code assez optimis. Bon support des templates. Compilateur mode ANSI et mode manag.Net(langagetendu). Compilateur et WindowsetUnix. EDI multiplateforme

MicrosoftC++

Licence commerciale mais ditionexpressgratuite

CompilateurC++ Embarcadero (anciennement Borland) IntelC++

Licencecommerciale

Licencecommerciale

Code assez optimis, notamment lorsque le microprocesseurcibleestdummefabricant. Trsrputdanslemondedelacrationdes jeux,telsqueDOOM.

WatcomC++

Licencecommerciale

Pour le microordinateur, le choix du compilateur sera galement dtermin par le prix et la disponibilit du produit. Lorsquil sagit dune station de travail ou dun miniordinateur, le choix peut se restreindre au compilateur propos par le constructeur (HP, Sun...). On peut alors se tourner vers un projet de portage du compilateurGNUsurcetteplateforme,maisparfoisletravailnestpasencoreachev.

7.Lditeurdeliens
Lditeur de lien soccupe dassembler les modules objets puis de rsoudre les rfrences symboliques en formantunexcutableouunebibliothquedecodebinaire"pur".Cetutilitaireappelenfindechanedpend beaucoupdelenvironnementetdusystmedexploitation.

- 10 -

LesbasesdelaprogrammationC++
NousallonsmaintenantdcouvrircommentC++permetdimplmenterdesalgorithmes.Celangageappartientla familledeslangagesprocduraux,cequisignifiequelesinstructionsdunprogrammesontregroupespourformer desprocduresquelonappelleaussifonctions. UnprogrammeC++utilisedunepartdesvariablespourrangerdesvaleursetdautrepartdesinstructionspour fairevoluercesvaleurs.CenestpaslaspectleplusoriginaldeC++puisquilpartagecettebase"algorithmique" aveclelangageC.Decefait,denombreuxtypesdedonnessontcommunsauxdeuxlangagesetlesinstructions de base sont galement identiques. Ceci facilite lapprentissage du langage C++ et amliore la portabilit ascendante. Signalons aussi que la syntaxe C++ est un peu plus souple que celle du C, notamment en ce qui concerne la dclarationdesvariablesetdesparamtres.Larelecturedesprogrammessentrouvenaturellementamliore. PourlelecteurquidcouvrelaprogrammationorienteobjetavecC++ ilestessentieldassimilerpleinementla programmationfonctionnelle,cestdirebasedefonctions.Connatrelesalgorithmesdebaserecherches,tris estuntrsbonmoyendyparvenir.Laprogrammationorienteobjetestunsurensembledelaprogrammation fonctionnelle,unefaonparticuliredelastructurer,delexploiter.Maislesrglesdebasedemeurentlesmmes. Avanttouteinstructionettoutedclarationdevariable,expliquonslanotationdescommentaires. Lescommentairessontdesannotationsrdigesparceluiquiprogramme.Ilsfacilitentlarelectureetrappellent parfoislerledecertainesvariablesoudecertainsblocsdinstructions. LelangageC++connatdeuxtypesdecommentaires :lescommentairessuruneseuleligneetceuxquioccupent plusieurslignes. Pourlepremiertype,onutiliselabarreobliqueredouble.Lecompilateurquireconnatcettesquence//ignore toutcequisuitjusqulafindelaligne. Lesecondtypeestdlimitparlessquences /*et */,cequiautoriselannotationsurplusieurslignesoubien sur une partie de ligne seulement, comme en langage C. Attention, lutilisation de commentaires imbriqus/* ...... /* ... */ ......*/nestpastoujoursaccepteparlecompilateur. LescommentairespeuventtreplacsdansnimportequelfichiersourceC++.

1.Dclarationdevariables
a.Utilitdesvariables
Ilexisteplusieurssortesdevariables.Selonlemploiquileurestdestin,ondterminera unnom,uneporte, untypeetparfoismmeunevaleurinitiale.Cesontlesalgorithmesquifontressortirlancessitdemployer desvariables. Lorsquevouscrezunevariable,vousdeveztoujourschoisirlenomleplusexplicite quisoit,mmesilestun peulong.Dunepart,lesditeursdecodesourcedisposentpresquetousdunefonctiondecompltion,dautre partlalisibilit dunprogrammeestlobjectifnumroun. Ainsi,pourreprsenterlesdimensionsdunmeuble,prfrezlesnomslargeur,hauteuretprofondeurauxnoms sibyllinsl,LetP. Pourlesboucles,onprfreradesidentificateurs(noms)pluscourts,commei,j,kconditionqueleurporte soitlimite.Ilesthorsdequestiondedclarerunevariableidansuneporteglobale,ceseraitbeaucouptrop dangereuxpourlefonctionnementdesalgorithmes. Ilestgalementimportantdesoulignerquunevariablevoitsavaleurvolueraucoursdutemps.Unevariable peutdoncservirrecueillirunevaleurdisponibleunmomentdonnetmmorisercettevaleurautantde

Openmirrors.com

- 1-

temps que ncessaire. Ce rsultat pourra soit voluer la valeur de variable est modifie soit concourir llaborationduneautrevaleurenvaluantuneexpressionoelleintervient. Variabledordregnral Reprsente une caractristique attache un objet naturel, comme une largeur, un poids ou un prix. L unit mtre, kilogramme ou euro n est pas mmorise par la variable elle mme.Savaleurvoluegnralementpeu. Savaleurvoluergulirementdansuneplagedevaleurs,de1 15parexemple.Permetderaliseruneactionuncertainnombre defois. Sa valeur est actualise en fonction de certains calculs. Une sommeconstitueunbonexempledecetypedevariable.

Variablediscrte

Variableintermdiaire

Lemoyenleplussimplepourfairevoluerlavaleurdunevariableestlaffectation,dsigneparloprateur=. Lavariableestplacegauchedusigne =etlavaleurdroite.Onparledelvalue(leftvalue)etdervalue (rightvalue). aire = largeur * longueur ; Ilexistedautresmoyensquelaffectationpourintervenirsurlavaleurdunevariable,moyensquelondsigne par effets de bord. La porte est une caractristique dterminante pour protger une variable contre une crituremalencontreuse. Seulelavaleurdunevariableestmodifiable.Sontype,saporteetmmesonnomsontdfinisunefoispour touteslorsdesadclaration.

b.Portedesvariables
Unevariableperdsoncontenulorsqueleflotdexcutionquittesaportededclaration.Lorsquilsagitdune porteglobale,elleperdsoncontenulorsquelexcutionduprogrammeprendfin. LadclarationdesvariablesestobligatoiredansunprogrammeC++.Ilnestpaspossibledutiliserunevariable nondclare,lecompilateursoulvedanscecasuneerreur.Seulsleslangagesinterprtsdisposentdecette caractristique,commeBasicouPHP. Ladclarationdunevariablespcifietoutessescaractristiquesenuneseuletape. Lendroitoseffectuecettedclarationdterminelaportedelavariable,cestdirelargiondanslecode oelleadusens.Horsdesaporte,ilpeuttreimpossibledyaccderpourliresavaleuroupourlamodifier. Lavariablepeuttrsbienaussineplusexisterlorsquelleestconsidrehorsdesaporte. Porteglobale Variablediteglobale.Estenprincipeaccessiblepartouteslesfonctions du programme. Nombreuses occasions de modifications concurrentes, donclusagedecetteporteestlimiterlepluspossible. Variable moins globale, car des modificateurs daccs private, public peuvent tre employs pour limiter son usage hors de lespacedenoms. Raisonnementidentiqueceluidelespacedenoms,hormislefaitque lavariableexisteautantdefoisquelaclasseestinstancie. Variable dite locale. Dvolue un usage algorithmique. Niveau de protectionassezlev. Variable trs locale. Raisonnement identique la variable recevant la

Porte dun espace de noms

Porteduneclasse

Portedunefonction

Porte

dun

bloc

- 2-

dinstructions

portedunefonction.

Dans le tableau cidessus, lappellation porte locale signifie que la variable est dclare lintrieur dune fonction. Dans le cas de la porte dun espace de noms, la variable est dclare lintrieurdun espace de noms,etainsidesuite. int v_globale; // variable globale

namespace Formes { double aire; // porte dun espace de noms } class Cube { float arrete; } ;

// porte dune classe (champ)

void compter() { int i; // variable locale } Lorsque lon fait rfrence une variable, le compilateur privilgie toujours celle qui a la dclaration la plus proche.Autrementdit,sideuxvariablesportent lemmenom,lunetantglobaleetlautretantlocaleune fonction,lavariableglobaleseramasqueparlavariablelocalepourlesexpressionssitues lintrieurdela fonction : int i; // variable globale

void compter() { int i; // variable locale i=2; // affecte la variable locale } Pourcontournerceproblme,onpeutemployerloprateurdersolutiondeporte::quiexplicitelaporte laquelleserapportelavariableconsidre : int i; void compter() { int i; // variable locale i=2; // affecte la variable locale ::i=8; // variable globale affecte } Noustrouveronsparlasuitedautresemploisdecetoprateur

::quinexistepasdanslelangageC.

c.Syntaxededclaration
Lasyntaxepourdclarerunevariableesttrsconcise: type nom ; Letypeetlenomsontobligatoires. Le pointvirgule clt la dclaration, il est ncessaire. On trouve parfois aussi une dclaration procdant linitialisationdelavariableavecunevaleur :

Openmirrors.com

- 3-

int prix = 30 ;

// dclare une variable prix de type entier

Ilestgalementpossibledepartagerletypeentreplusieursvariables,danslebutderaccourcirlasquence dedclaration: int largeur, hauteur ; Naturellement,chaquevariablelargeurethauteurestdetypeentier( int).Chacunedentreellespeutrecevoir unevaleurinitiale : int largeur = 30, hauteur = 120 ; Le nom de la variable doit commencer par une lettre ou par le caractre de soulignement. Sont exclus des identificateursdevariablelescaractresassimilsdesoprateurs,telsque- + / . <ainsiquelecaractre $. Ilnestpasjudicieuxdemployerdescaractresaccentus,mmesilecompilateurlesaccepte.Laportabilitdu programmeseraitplusquerduite.

d.Typesdedonnes
Le langage C++ reprend les types primitifs du langage C. Ces types se rpartissent dans diffrentes catgories : types entiers, types dcimaux, caractres. C++ ajoute un type boolen et namliore pas la reprsentation archaque des chanes de caractres. Une classe est plutt propose dans la bibliothque standardSTLmaissonemploinestpasaussignralisquelhabituelchar*. Pour les types dont le codage varie dun compilateur lautre (int par exemple), loprateur sizeof() retournelalargeurdutypeoudelavariableenoctets.

Lestypesentiers
LelangageCatinventunepoqueolinformatiquetaittrsterreterre.Parailleurs,ilatconu pourtirerpartidesinstructionsspcialisesduprocesseuranimantlePDP11(unanctreduVaxdeDigital). Certainesnotationscommelefameux++viennentdirectementdecette"optimisation".Ctvariable,lunitde base reste loctet. Cette quantit est assez bien place pour reprsenter des caractres ASCII (la table de basecontient128symboles)maisaussidesentierscourts,entre128et+127. Lecompilateurutiliselareprsentationencomplmentdeux,cequiluipermetdetraiternombresngatifset soustractionsaveclammearithmtiquequeladdition. Prenonsletype char,quiautoriselareprsentationde2 8 =256valeurs.Cesvaleurssontrpartiesentre 128et+127,encomplmentdeux.Lecomplmentdeuxestenfaitlecomplmentun(inversiondetous lesbits)augmentduneunit.Avecdesvariablesdetypechar,additionnons4et3 :

char x = 4 ; char y = -3 ; char z = x + y ; Pour avoir le codage de la valeur 4, cestdire de la variable x, il suffit de dcomposer en base 2, nous obtenons1x2 2 =4,donc: x = 0000 0100 Pouravoirlecodagedelavaleur3,cestdiredelavariabley,commenonsparcrire3enbase,soit1x21 + 1x20 =3,soit:

- 4-

+y = 0000 0011 Puisinversonstouslesbits,nousobtenonslecomplmentun: !+y = 1111 1100 Ajoutons1,lareprsentationde3,enbinaireetencomplmentdeux,est : y = 1111 1101 prsent,additionnonsxety : x = 0000 0100 y = 1111 1101 Ladditionbitbit,avecpropagationdesretenues,donne: z = 0000 0001 Cequiestconformeaursultatattendu. Leformatencomplmentdeuxatchoisipoursasimplicit.C++proposeuncertainnombredetypesde donnes pour reprsenter des nombres entiers, dont la largeur varie de un huit octets, en fonction du microprocesseur.

char

256valeurs [128+127]

Sertauxoctetsetauxcaractres.

short

32768valeurs [32767 +32768]

Entier court, abrviation de short int . Avec les processeurs32bits,sonemploitenddisparatre,saufpour desraisonsdecompatibilit. Entier. En principe, sa largeur adopte la taille du mot machine,donc32bitsleplussouvent.

int

2 32 valeurs

[2 31 1231 1]

long

2 64 valeurs

Entierlong,abrviationde long int. En principe le double de l int, mais parfois limit 32 bits avec certains compilateurs !

[2 63 1263 1]

Lemploiduqualificateur unsigneddplacelesvaleursreprsentesdanslintervalle[02n 1].Ladclaration dunevariabledevientalors: unsigned char c ; UnedesgrandesdifficultsdeportagedapplicationC++vientdelamultitudedesformatsdereprsentation attachs aux types. Il est vrai quentre un microcontrleur 8 bits et un Power PC, le type int naura pas la mme largeur. Mais cette situation nest pas toujours avre et les compilateurs 16 bits finissent par disparatre. Pourlesvariablesdiscrtesutilisesdanslesbouclesforilpeuttreopportundemployerletype int.En effet,lesmicroprocesseurs32bits,lesplusrpandus,doiventralentirpourtraiterdesquantitsinfrieures.Ils lisentlammoireparblocsdequatreoctets,voiredavantage.Ilyatoutesleschancesquelavariablesoiten fait loge dans un registre du microprocesseur, mais leur nombre est compt, surtout avec des microprocesseurs utilisant un registre dinstructions complexe, les CISC, dont le Pentium fait partie. Par
- 5-

Openmirrors.com

opposition, les processeurs RISC tels que le Power PC disposent de peu dinstructions mais de beaucoup de registres.Ilssontdoncavantagsparlecompilateurpourtraiterlesboucles. Lorsque vous spcifiez une valeur littrale de type entier, le compilateur utilise la lecture en base 10. Donc laffectationx = 123signifiequevousplacezlavaleur123danslavariablex. Comme C++ reprend les caractristiques de C et comme C a t invent de manire concomitante dUnix,le compilateurreconnataussilesbasesoctaleethexadcimale. Enoctal,chaquechiffrevolueentre0et7,puisquelabaseest8.Lecompilateurreconnatlemploideloctal enprfixantlavaleurlittraleparunzro : x=0123quivautenfaitx=1x82 +2x81 +3x80 soit83endcimal. LeformatoctalestsurtoututilepourreprsenterlesdroitsdaccsauxfichiersUnix.Cesdroitsdfinissentla valeur de 3 bits, rwx (read write execute), ce qui donne 8 possibilits et entrane lemploi de la base octale. Lhexadcimal est plus commode pour manipuler la base 2, trs risque pour linterprtation humaine. Dans cettebase,onutiliselesdixchiffres09auxquelsonadjointleslettresA,B,C,D,EetFpourcompterjusqu 15.Lesseizepossibilitsdundigithexadcimalfontquecechiffreestquivalentunquartet, soitundemi octet.Lecompilateurreconnatlemploidelhexadcimalpourunevaleurlittraleparleprfixe0x. Aveca=0xA28,lavariableareoitlavaleur10x162 +2x161 +8x160 ,soita = 600endcimal. Lcriturebinaireestassezaisedduire,puisquechaquechiffrereprsenteunblocdequatrebits : 1010 0010 1000

Lestypesdcimaux
Letermedenombrevirguleflottanteestsansdouteplusappropriqueceluiderelchoisipourreprsenter cenombre.C++disposedestypesfloatetdouble,conformesauformatIEEE754.Ceformatstandardest surtoutemploy parlescoprocesseursarithmtiques. Laprcisionestloindtrebonne,surtoutsilavaleurabsoluedesnombresreprsenterestleve. Leformatfloatutilisequatreoctetspourreprsenterunnombrevirgule.Ledoublemultipliepardeuxcette quantit.Lintrtdudoubleestcertainementdamliorerlaprcisiondescalculs,plusquedlargirlazonede couverture. Enfin,leformatlongdoubleamlioreencorelaprcisionmaisleplussouventdonnelieudescalculsmuls parunlogicielpluttquedtreconfisaucoprocesseur arithmtique.

float double long double

32bits,7chiffressignificatifs 64bits,11chiffressignificatifs 80bits,15chiffressignificatifs

+/3,4x1038 +/1,7x10308 +/1,2x104932

Avantdemployercestypesdansvosprogrammes,ayezlespritlesindicationssuivantes : Maniezdesquantitsayantdesvaleursabsoluescomparables.Lescalculssefaisantavecunemantissede40 bitsenviron,ladditiondunnombretrsgrandetdunnombretrspetitprovoqueradeserreursdecalcul. Lesnombresentierssonttoujoursreprsentsexactementaveccesformats. Certainsnombresdcimauxtelsque1,45nadmettentpasdereprsentationexacteaveclesformatsfloatet

- 6-

double.Desheuristiquesintgresauxcoprocesseursarithmtiquestiennentcomptedecephnomne,mais
lescalculspeuventtrefaux. Laprcisiondiminuerapidementlorsquelavaleurabsolueaugmente.Utiliseruntype floatlalimitedesa reprsentationpeuttredramatiquepourlaqualitdescalculs.Passerundoubledonneradesrsultatsplus justes,avecunsurcrotdetempsdecalculngligeable. Enrsum,cesformatssontutileslorsquelacompatibilitavecunlogicielextrieurestenjeu,oubienlorsque lavitessedescalculsestunfacteurdterminant.Pourlesapplicationsscientifiquesoufinancires,ilpeuttre utile demployer une bibliothque spcialise pour reprsenter les nombres sous forme de chanes de caractres.

Leboolen
Le langage C ne connat pas les boolens en tant que type, mais a dtourn larithmtique entire pour valuersesprdicats. Unboolenestunevaleurquiestvaluecommevraieoufausse.Lesinstructionsconditionnelles, if,while, for...fonctionnentavecuntestquidoittredetypeboolen. Laconvention,lorsquelesentierssontutilisscommeboolens,estque0quivautfauxetnimportequelle autrevaleur,strictementnonnulle,quivautvrai. Comme laffectation produit une valeur gauche, parfois de type entier, les concepteurs ont d distinguer laffectationdelacomparaisonstrictededeuxquantits: if(x=3) Cettedernireclauseesttoujoursvrifie,car3estnonnul.Lavariablexreoitcettevaleur3,doncletestest positif(vrai).Lcriturecorrecteestsansaucundoute : if(x==3) Loprateur==testelgalitdelavariablexaveclavaleur3.Ilrenvoievraioufaux,selonlecas. Le compilateur C++ soulve un avertissement (warning) lorsquil identifie cette criture quivoque mais nanmoinslgaledunpointdevuesyntaxique.Cecidit,lelangageC++disposeaussidunvraitypeboolen pourvaluerdesprdicats :

bool

true false

Les valeurs true et false sont des mots cls du langage, pas des macroscommeonprocdaitparfoisavecC.

Lescaractresetleschanes
Une chane de caractres est une suite de caractres. Le compilateur range les caractres les uns aprs les autresetterminelasrieparuncaractredecode 0.OnappelleceformatchaneASCIIZ,avecunZpourzro terminal. Letypequidsigneleschanesdecaractresesthabituellement char*,maislestableaux char[ ]peuvent aussiconvenirsouscertainesconditions. Lorsque lon dsigne une chane littrale, dlimite par les caractres guillemets, le compilateur alloue une adressepourstockerlescaractresdelachane.Cetteadresseestenfaituneadressedecaractre(s),dole type char*. Il est naturellement possible de lire cette zone mmoire, mais la modification des caractres contenusdanscettezonenestpastoujourspossible,demmequelcritureaudelduzroterminal. Ilestpossibledecreruntableaudecaractres( char[

])assezvasteoudallouerunezonemmoirepour
- 7-

Openmirrors.com

recopierlachanelittraleetpourselivrertouteslesmodificationsncessaires. LecompilateurC++reconnatcommesonanleClessquencesdchappement.

\n \r

Nouvelleligne Retourchariot

\b \t

Sonnelacloche(bell) Tabulation

Lesvaleurslittralesdecaractresontdlimitesparlesignecommelindiquelextraitsuivant : // trois char a = char b = char tab caractres 65; B; // soit le code ASCII 66 = \t;

// deux chanes char* s = "bonjour"; char* u = "comment \n allez-vous ?"; Pour la chane u, la squence dchappement a t "dcolle" pour amliorer la lisibilit. Il est tout fait possibledesupprimerlesespacesdepartetdautre. Pourcopierdeschanes,concatnerdautreschanes,calculerlalongueur,effectuerdesrecherches,lalibrairie <string.h>fournittouteslesfonctionsncessaires. LelangageC++offreaussiunepriseenchargedeschanesplusvolueparlebiaisdesalibrairiestandard,la STL.

2.Instructionsdetests
Indispensableslnoncdunprogramme,lesoprateursetlesinstructionssontleslmentsquipermettent detraduireunalgorithmeenC++. Les oprateurs combinent diffrentes valeurs pour aboutir des expressions. Ces expressions sont de type numrique (entier, dcimal) ou boolen. Lorsque les oprateurs sont surchargs, dautres types peuvent dcoulerdelapplicationdoprateurs.

a.Instructionsdetests
C++ connat deux types de tests : les tests simples et les tests multiples. Le test simple est un basique de lalgorithme,ilsagitdelinstructionif. Le test multiple a t conu afin de tirer parti de linstruction switch du PDP11. Il sagit duneoptimisation, vitant de tester plusieurs fois une mme valeur entire. De nos jours, linstruction switch serait plutt un confortdeprogrammation,maisnombredelangagesdontC++faitpartiesentiennentlapprochedulangage C.

Letestsimple,if
Cetteinstructionexcuteconditionnellementuneautreinstruction,aprsvaluationdunprdicatboolen.Le stylederdactionestimportantpourlalisibilitduprogramme,maisninfluepassursonfonctionnement. if( prdicat ) instruction Ledcalagedunetabulationindiqueaulecteurquelinstructionnestexcutequconditionqueleprdicat

- 8-

soitvrifi(vrai).Uneinstructionpeutreprsenterplusieurschoses : instruction -> ; instruction_simple { instruction instruction_simple } Autrementdit,"instruction"peutdsignersoitlinstructionvide(;),soituneinstructionlmentairecommeune affectationouunappeldefonction,oubientreunblocdinstructionsdlimitparlesaccolades{et}. Ilestnoterlaprsencedeparenthsespourdlimiterleprdicat,cequiapermis dliminerlemotcl then prvuparleslangagesBasicetPascal. Silinstructionsersumeuneinstructionsimple,ilvautmieuxviterlesaccolades pournepassurchargerle programme. if(x==3) printf("x vaut 3") ;

Letestsimpleavecalternativeif...else
Ilestparfoisncessairedeprvoirlalternative,cestdirelinstructionexcutelorsqueleprdicatnestpas vrifi(faux). Cestprcismentlavocationdelaconstructionif ... else ....

if(prdicat) instruction_vrai else instruction_faux Bien sr, les rgles applicables la dfinition dune instruction simple ou compose demeurent vraies : nemployerlesaccoladesquesicelasavrencessaire. if(rendez_vous<mercredi) printf("rendez-vous en dbut de semaine"); else { printf("rendez-vous en fin de semaine\n"); printf("viter le week-end"); }

Letestmultipleswitch
Le test multiple prvoit les diffrentes valeurs que peut prendre une expression (ou une variable). Normalement,cesvaleurssontdetypeentieretsontconnuesaumomentdelacompilation. Linstruction fonctionne en numrant les diffrentes valeurs possibles, appeles cas. Une clause default sappliquelorsquelavaleurtestenasatisfaitaucundescasprvus. switch(valeur_entire) { case valeur1: instruction case valeur2: instruction ... default: instruction }

Openmirrors.com

- 9-

En principe, linstructiondoitseterminerparunbreak,autrement,lexcutioncontinuejusqu rencontrer cet arrt, quitte franchir de nouvelles lignes case. Cette construction particulire permet de grouper plusieurs cas : switch(x) { case 1: case 2: printf("x break; case 3: printf("x break; case 10: printf("x break; default: printf("x break; }

vaut 1 ou 2");

vaut 3");

vaut 10");

est diffrent de 1,2 et 10");

Laclausedefaultnapasbesoindefigurerendernireposition,maiscestsouventlquonlatrouve.Aussi, linstructionswitchnestpasobligedinclureuneclausedefault.

b.Oprateurs
Les oprateurs combinent diffrentes valeurs. Suivant le nombre de valeurs runies par loprateur, on a affairedesoprateursunairesoubinaires.Cesvaleursprennentalorslenomdoprandes. Cettesectiondresseunelistedesprincipauxoprateurs.Lesoprateursspcifiquesauxclasses,auxtableaux etauxfonctionsserontabordsultrieurement.

Oprateursdecomparaison
Cesoprateursvrifientunerelationdordreentredeuxoprandes.Lesoprandespeuventtredenimporte queltype,mais,parsoucidhomognit,ilvautmieuxquelestypessoientapparis. Lvaluation de lexpression faisant intervenir un oprateur de comparaison produit un rsultat de type boolen,vraioufaux.

== != < > <= >=

galitdesvaleurs(nepasemployerloprateurdaffectation=) diffrencedesvaleurs(quivautausignemathmatique infrieur suprieur infrieurougal suprieurougal )

Pour amliorer la lisibilit, il est recommand dutiliser des parenthses pour claircir un prdicat faisant intervenirplusieursoprateurs.

Oprateursarithmtiques
Il sagit bien entendu des quatre oprations de base addition, soustraction, multiplication, division
- 10 -

applicablestouttypenumrique,entierounon.Lesentiersconnaissentenpluslerestedeladivisionentire, quelonappelleparfoismodulo.

++ - * / %

addition soustraction multiplication division modulo

L encore, ne pas hsiter ajouter des parenthses pour amliorer la lisibilit, mme si certains oprateurs ontuneprsanceplusimportantequedautres(*sur+,parexemple).

Oprateurslogiques
Ilfautfaireattentionnepaslesconfondreaveclesoprateursboolens.Lesoprateurslogiquestravaillent auniveaudubit.

~ | & << >>

Complment1(nonlogique) oulogique etlogique ouexclusif dcalagegauche,nbits dcalagedroite,nbits

chaquefonctionlogiquecorrespondunetabledevrit,nouscommenonsavecle"nonlogique":

b
0 1

~b
1 0

Dcouvronsprsentle"oulogique":

b1
0 0 1 1

b2
0 1 0 1

b1 | b2
0 1 1 1

Voicimaintenantle"etlogique":

Openmirrors.com

- 11 -

Openmirrors.com

b1
0 0 1 1

b2
0 1 0 1

b1 & b2
0 0 0 1

Puisfinalementle"ouexclusif"

b1
0 0 1 1

b2
0 1 0 1

b1 b2
0 1 1 0

Pourcomprendrelesoprateursdedcalage,unschmaseraplusexplicatif :

Leprogrammecorrespondantressemblecequisuit: int main(int argc, _TCHAR* argv[]) { char c = 0x2D; printf("%x\n",c); // 2D char c1 = c << 1; printf("%x\n",c1); char c2 = c1 >> 2; printf("%x\n",c2); return 0; }
- 12 -

// 5A

// 16

Il est remarquer que le dcalage vers la gauche multiple la quantit par 2, alors que le dcalage droite diviselaquantitpar2.Siledcalagealieusurplusieursbits,laquantitestmodifiedansunfacteurde2 puissancelenombredebitsdcals. Vrifionscecidanslexempledonn:5A=5x16+10=90et2D=2x16+13=45.Etaussi,16=1x16+6= 22,cequiestbienlequartde90,enarithmtiqueentire. Les oprateurs logiques sont particulirement utiles lorsque lon structure un entier (ou un octet), ou bien lorsquelontraiteunfluxbinaire.Lesbibliothquesdeconnexionaurseauenontgalementbesoin. Ils sont beaucoup utiliss pour raliser des masques de bits, afin de lire telle ou telle partie dune valeur entire: int x = 0xCB723E21; c = (x & 0x00FF0000) >> 16; printf("%x\n", c); // affiche 72 Pourcomprendrelexcutiondeceprogramme,suivonslescalculssurundessin :

videmment,les24bitsdepoidsfortsontperduslorsdelaffectationdelavariable c,detypechar,donclarge de8bits.Maisonsaitaussiqueces24bitssontnuls.

Oprateursboolens
Lesoprateursboolensressemblentauxoprateurslogiquessicenestquilsfonctionnentsurdesvaleursde boolensetnonsurdesbits.Bienentendu,ilsreprennentlesmmestablesdevritmaisleconcepteurdu langageCadintroduiredesnotationsspcialespourdistinguerlesdeuxdomaines.Onsesouvienteneffet que le langage C assimile les entiers (forms de bits) et les boolens. Le langage C++ a conserv cette approchepourdesraisonsdecompatibilit.

Openmirrors.com

- 13 -

! && ||

ngationboolenne etboolen ouboolen

Ilfautremarquerladisparitionduouexclusif,quelonpeutconstruirepartirdesautresoprateurs: Xor(p,q) = (p || q) && ! ( p && q) Notezgalementlechangementdenotationpourlangation.Letilde,aulieudtredoubl,setransformeen pointdexclamation. Pour mettre en uvre ces oprateurs, on peut soit faire intervenir un boolen, soit employer le rsultat de lvaluationdunoprateurdecomparaison : bool p,q; int x, y; ... p = q && (x<y) ; Comme toujours, les parenthses sont apprciables pour amliorer la lisibilit. On peut aussi travailler sur la miseenpagepourfaciliterlamiseaupointdunprdicatunpeulong: if((x < 3 ) && f(34,2*p)<fgetc(fi) || ! (k %2==0)) ... devraitscrire: if((x < 3 ) && f(34,2*p)<fgetc(fi) || ! (k %2==0))

Lesoprateursdincrmentation
Ces oprateurs nous viennent directement de lassembleur du microprocesseur quipant le PDP11. En tant quetels,ilsagitjustedunenotationcommodepouraugmenteroudiminuerduneunitunevariable.Maisils peuvent devenir trs efficaces avec certains processeurs CISC, pour peu que le mode dadressage "relatif index"soitdisponible. Pourlheure,nousnousconcentronssurlasyntaxe:

var++ var-- --var


++var

valuelavaleurdelavariablepuisaugmentelavariableduneunit. valuelavaleurdelavariablepuisdiminuelavariableduneunit. Diminuelavaleurdelavariablepuisvaluesanouvellevaleur. Augmentelavaleurdelavariablepuisvaluesanouvellevaleur.

Dansuncertainnombredesituations,lesnotationsprfixesetpostfixesdonnerontlemmersultat: int x = 3; ++x; // ici x++ est tout fait quivalent printf("x=%d\n",x); // affiche x=3

- 14 -

Dansdautrescas,lordreesttrsimportant: int x=1,y=2; y=(++x); // x=2, y=2 y=(x++); // x=3, y=2 printf("x=%d, y=%d",x,y);

// x=3, y=2

3.Instructionsdeboucle
Lesinstructionsdeboucledfinissentdesinstructionsquisontexcutesplusieursfois.Lenombredexcutions quelonappelleitrationsestprvuparunecondition. Pourchoisirletypedeboucleappropri,ilfauttoujoursselaisserguiderparlancessitalgorithmique.Silon connatlavancelenombreditrations,cestuneboucle for.Danslesautrescas,cestunebouclewhile(ou do).

a.Labouclefor
Cette instruction est un peu une curiosit. Elle implmente la structure de contrle de flot dexcution pour dfinieparlalgorithmieimprative,maisilsagitenfaitduneinstructionwhiledguise.

for(initialisation ; prdicat ; incrment) instruction Linitialisationsertgnralementaffecterdesvariables.Leprdicatestleplussouventbassurunoprateur decomparaisontelque<.Lincrmentsertaugmenterunevariablediscrte. Prenonslexempleduneboucleforprvuepourexcuteruncertainnombreditrations:

for(int i=1; i<=10; i++) printf("i=%d\n",i); Nousaurionspucrirecelalaidedunebouclewhile:

int i=1; while(i<=10) { printf("i=%d\n",i); i++; } Toutefois,lcritureforestplusconcise. LesprogrammeursCetC++ontlhabitudedefairecommenceri0,cequichangelexpressiondutest: for(i=0; i<10; i++) ... Siloncomptebien,ilyatoujours10itrations.Nousverronsparlasuitequelestableauxsontindexspartir de0etquecettehabitudearrangebienlesprogrammes.Parcontre,sivoustraduisezdunprogrammePascal ouBasicenC++,ilfautbiensurveillercesvaleurslimites. Danslabouclefor,touteslespartiessontoptionnelles,maislespointsvirgulessonttoujoursl:

for( ; ; ) ...

Openmirrors.com

- 15 -

Dautresinstructions,tellesquebreak,aidentalorssortirdelaboucle.

b.Labouclewhile
Linstructionwhileestprivilgierlorsquelonneconnatpaslavancelenombreditrations.

while( prdicat ) instruction Bienque whilefonctionneparfaitementavecunevariablediscrte,ilnestpasraredetrouverdesexemples quiressemblentcela: while( L != null ) { printf("%s",L->element); L=L->suite; } Ilestremarquerquelinstruction associe au while peut trs bien ne pas tre excute, si le prdicat se rvlefauxdemble.

c.Laboucledo
Au contraire de la boucle while, le do provoque au moins lexcution dune itration, car lvaluation du prdicatsefaitautermedecetteexcution: do instruction while ( prdicat ) ; Unexempledutilisationdecetteboucleconsisteliredesentresutilisateur: char c; do { printf("Confirmer O/N"); scanf("%d",&c); } while(c!=o && c!=O && c!=n && c!=N);

d.Lesinstructionsdedbranchement
LelangageC++comptetroisinstructionsdedbranchementimpratif :goto, continueetbreak. Seules break et continue sont toujours usites. Linstruction goto a depuis longtemps fait la preuve des complicationsquelleentranepourlamaintenanceetlarelecturedesprogrammes. Linstruction break sert sortir dune structure de contrle, switch ou boucle. Linstruction continue ne sappliquequauxboucles,elledmarreunenouvelleitration. Il est toujours possible de construire ses programmes pour viter lemploi de continue et de goto. Linstructionbreakparcontreamliorelalisibilitduprogramme.

- 16 -

Ilnestpasraredecombinercesinstructionsavecdestests( if),maiscelanestpasunencessitimpose parlasyntaxe.

4.Tableaux
Les tableaux constituent, tout comme les variables, une structure trs importante pour les algorithmes. Un tableau est un ensemble de valeurs dun type dtermin. On accde chaque valeur en communiquant au tableauunnumro quelonappelleindex.EnC++,lesindexdbutent0etcroissentjusquN1,oNestla tailledutableau,cestdirelenombredlmentsquilcontient. Le type du tableau en fait le type des lments quil contient est quelconque : valeur scalaire, objet, pointeur...Voicideuxexemplesdetableaux: double coord[2]; // un tableau de deux double coord[0] et coord[1] char* dico[10000]; /* un tableau de 10000 pointeurs dico[0] dico[9999] */ Ilestgalementpossiblededfinirdestableauxmultidimensionnels: double matrice[3][3]; // une matrice, 2 dimensions

Pourinitialiseruntableau,onpeutaccderchacundeseslments: coord[0]=10; coord[1]=15; Ilestgalementpossibledinitialiserletableauenextension,cestdireenfournissantsesvaleursaumoment deladclaration: char separateurs[] = { ,*,- }; Pourlinitialisationdestableauxdechar,leslittralesdechanesrendentbiendesservices.Lcrituresuivante: char chaine[]="Bonjour";

Openmirrors.com

- 17 -

estpluscommodeemployerque: char chaine[]={B,o,n,j,o,u,r}; Voicimaintenantlexempledunerecherchedelapluspetitevaleurdansuntableaudetypedouble: double m; // valeur mini double tab[] = {-3, 8.2, 5, 57, -11, 1.4 }; int i; m=tab[0]; for(i=0; i<6; i++) if(tab[i]<m) m=tab[i]; // 6 valeurs dans le tableau

printf("La plus petite valeur est %f",m); Lasyntaxededclarationdun tableau repose sur lemploidescrochets [et ].Nousverronsunpeuplusloin quelespointeursetlestableauxdgagentunecertainesimilitude. Lestableauxdclarsaveclasyntaxe[]sontallousdanslaportecourante silsagitdunevariablelocale unefonctionoudunemthode,ilssontalloussurlapile.lextrieurilspeuventtreallousauniveaudutas (heap)globaloudusegmentdelobjetquilesporte.

5.Fonctionsetprototypes
Pour apprhender convenablement la programmation oriente objet, il est ncessaire de bien matriser la programmationfonctionnelle.Unefonctionestunensembledinstructionsetparfoisaussidevariableslocales auquelonadonnunnom.Cettefonctionadmetdesparamtresetgnralementvalueunevaleurenretour. Unefonctionestdoncmodliseparuneboteavecdesentresetunesortie:

Lasignaturedelafonctiondfinitsonnom,sesparamtresdentreetsasortie : int somme(int a,int b) Cette squence prcise que la fonction somme() reoit deux paramtres de type entier a et b (entre) et quelle retourne un entier. Peu importe pour linstant comment sexcute la fonction, cestdire comment la sommeestdtermine.

a.Dclarationdunefonction
En C++, il faut dclarer une fonction avant de lutiliser. Deux moyens sont prvus cet effet : la dfinition complte et le prototype. La dfinition complte dbute par la signature et se poursuit par la liste des instructionsentredeuxaccolades:

- 18 -

int somme(int a,int b) { return a+b; } Danslapprocheparprototype,ontermineladclarationparunpointvirgule. int somme(int a,int b) ; Lasignatureetlecorpsdelafonction(doncladfinitioncomplte)peuventainsifigurerdansunautrefichier sourcedextension.cpp.Lecompilateurnapasbesoindeuxpourcompiler,carleprototypelerenseignedj sur la liste des paramtres et le type de retour. Au moment de ldition des liens, "tous les morceaux sont recolls". Ilnestpasrarederegroupertouslesprototypesdansunfichierdentte .h,etdefournirlimplmentation dansunfichier.cpp,oubiensousformedunelibrairiestatique.lib.

b.Fonctionsetprocdures
La mme syntaxe est utilise pour dcrire ces deux lments, alors que des langages tels que Basic (Function/Sub) ou Pascal (Function/Procedure) les distinguent. En C++, comme en C, on emploie le typederetour void,termeempruntlalangueanglaiseetquisignifievide,vacant.Cetypeindique quune fonction ne renvoie rien (il sagit dune procdure) ou bien que lon na pas encore dinformation sur le type finalementemploy(casdespointeurs void*). Le corps dune fonction contient un certain nombre dinstructions return qui stoppent son excution et retournentunevaleurduntypeconformecelui delafonction: Typedefonction Typederetour Exemple

void f(...) int f(...) bool f(...) char* f(...)

- int bool char*

return; return 2; return (x==2); return "bonjour";

Pour une procdure, il est possible domettre linstruction return. Lexcution prend fin aprs la dernire instruction, celle qui prcde laccoladefermante.Pourunefonction,ilestncessairederenvoyerunevaleur conformeautypeannonc. Par ailleurs, une fonction (ou une procdure) peut contenir plusieurs instructions return,ainsiquelexplicite lexemplequisuit: enum age { enfant, adolescent, adulte }; age categoriser(int age) { if(age<13) return enfant; // on sen va // continue sur age>=13 if(age<18) return adolescent; // on sen va // continue sur age>=18 return adulte; // on sen va }

Openmirrors.com

- 19 -

int main(int argc, char* argv[]) { int old; scanf("%d",&old); // lire un entier depuis le clavier age a; a=categoriser(old); // dterminer la tranche dge printf("age=%d",a); return 0; } // afficher les rsultats

c.Appeldesfonctions
Pour appeler une fonction depuis une autre fonction, on crit son nom suivi de parenthses. Si la fonction admetdesparamtres,leursvaleurssontpassesdanslordre,entrelesparenthses. Si la fonction retourne une valeur, celleci peut tre affecte dans une variable, servir directement dans une expressionoubientreignore: int x; x=somme(3,b); // additionne 3 et b y=10+somme(5,7); // le rsultat de la fonction est additionn 10 somme(1,2); // rsultat perdu Il est important de ne pas se poser trop de questions quant au fonctionnement interne dune fonction laquelleonfaitappel:elleprenddesparamtresetretourneunevaleur.Cestsuffisantpourlappeler.Cette stratgierenddegrandsserviceslorsdelamiseaupointdefonctionsrcursives.Ilsagitdefonctionsquise rappellentellesmmesjusquobtentiondunrsultatdtermin.

d.Gestiondesvariableslocales
Lorsquunefonctionestappele,elleconstruitunenvironnementlocaldanslapile.Cetenvironnementcontient les valeurs des paramtres, puis les variables locales. Dans le corps de la fonction, paramtres et variables localesontlammeporte.Linstructionreturnprovoqueladestructiondecetenvironnement,etsilafonction retourneunevaleur,cettevaleurestlaissesurlapilelattentiondelafonctionappelante. Nous en dduisons que les variables locales perdent leur contenu lissue de lexcutiondune fonction. Le langage C a propos un mot cl spcial, static, qui rend persistantes les valeurs des variables locales. Sagissant dune curiosit qui nexiste pas dans dautres langages que le C++, cette forme est utiliser le moinspossible. Ilny a de toute faon pas de correspondance algorithmique. Donnons toutefois un exemple pour illustrer sa syntaxe: void dernier_appel() { static int heure; printf("time stamp du dernier appel : %d",heure); heure=time(); } Quoiquilensoit,lacrationpuisladestructiondunenvironnementlocalestunmcanismenormal.Sanslui, nous aurions du mal crire des fonctions rcursives, fonctions qui permettent de traiter avec beaucoup dlgancedesproblmesquipeuventtrecomplexesprogrammerdansdesversionsitratives.

e.Dfinirdesfonctionshomonymes(polymorphisme)
Desfonctionsquiportentlemmenom?Onpeutenconclurequellesremplissentlemmerle.Crerplusieurs versionsdunemmefonction,voilleurraisondtre.Cestleursignature,cestdirelaqualitetlaquantit
- 20 -

deleursarguments,quilesdistinguera. Onpeutainsicrerdeuxversionsdelafonctionsomme:unepremirequiprenddeuxnombres,unedeuxime quienprendtrois. int somme(int a,int b) { return a+b; } int somme(int a,int b,int c) { return a+b+c; } lappeldelafonctionsomme,lecompilateurchoisitlaformequiluiparatconvenirlemieux.Sil nentrouve pas,ilgnreuneerreur. int p=somme(3,2); // utilise la premire forme int q=somme(4,5,6); // utilise la seconde forme Attention toutefois, le compilateur ne peut pas toujours distinguer la version utiliser. Si nous ajoutons une troisimeversionutilisantdeuxnombresdetypeshort:

int somme(short a,short b) { return a+b; } Lappel dune version somme recevant 10 et 11 doit faire hsiter le compilateur. Ces littrales dentier sont aussi bien des int que des short. Certains compilateurs soulvent un avertissement (warning), dautres ignorentcefait.Onpeutalorsutiliserunoprateurdetranstypageparcoercitionpourindiqueraucompilateur quelleformeutiliser: somme((short) 3,(short) 4); // utilise la forme avec des short Ondsigneparfoislepolymorphismedefonction(lexistencesousplusieursformes)souslenomdesurcharge. Quoi quil en soit, cette notion est totalement indpendante de la programmation oriente objet qui na pas besoindellepourexister.

f.Fonctionsnombrevariabledarguments
Nousavonsdjrencontrunefonctionnombrevariabledarguments:printf.Cettefonctionadmetcomme premierparamtreunechanedeformatage,puisunesriedevaleursdestinestreprsentesparlebiais decesformateurs: printf("%s %d %x","bonjour",34,32); Danscetexemple,printfadmetquatrearguments.Lepremierrecensetroisformateurs(%s, %det%x),ilest suivipartroisvaleursconformesautypeindiqu parlesformateurs:

%s %d %x

chane(char*) entier(dcimal) entier(hexadcimal)

Vouspouvezdfinirvospropresfonctionsnombrevariabledarguments.Desmacrosspcialespermettentde

Openmirrors.com

- 21 -

traiterlalistedesargumentstransmisevotrefonction. #include <iostream.h> #include <stdarg.h> int somme(int n, ...) { va_list ap; // liste des paramtres va_start(ap,n); // se placer aprs le dernier argument formel int i,s=0; while(n--) { i=va_arg(ap,int); // rcuprer largument suivant de type int s+=i; } va_end(ap); // nettoyer la liste des arguments return s; } int main(int argc, char* argv[]) { cout << somme(4,1,2,3,4); // affiche le rsultat return 0; } Dans cet exemple, la fonction somme reoit au moins un paramtre, n, qui indique le nombre dlments additionner. Le symbole spcial ... dans la signature de la fonction indique quil sagissait du dernier paramtreformel,cestdiredudernierparamtrenomm.Lesautresparamtresquipeuvent treomis lappelsontaccessiblesvialamacrova_arg().

g.Donnerdesvaleurspardfautauxarguments
Cettesyntaxenapasdecorrespondancealgorithmique,maisellepeutguiderleprogrammeurlorsquilhsite fournircertainsparamtreslorsdelappeldunefonction. Parexemple,imaginonslafonctionsuivante: void printlog(char*message,FILE* f=NULL) { if(f==NULL) printf(message); // affiche lcran else fprintf(f,message); // affiche dans un fichier } Le programmeur qui utilise notre fonction printlog comprend que la fourniture du paramtre f nest pas obligatoire,puisquilareuunevaleurpardfaut. Onpeutalorsappelerprintlog()dedeuxfaons:

printlog("dmarrage de lapplication"); printlog("dmarrage de lapplication",f_erreur); Danslepremiercas,lemessagesafficheralcran,lafonctiondtectantunevaleurNULLpourleparamtre f. Dans le second cas, f_erreur tant rput non nul, le message sinscrira dans un fichier pralablement ouvert,reprsentparf_erreur. Attention de ne pas provoquer de conflit entre les versions polymorphes (surcharges) et les versions de fonctionsrecevantdesvaleurspardfaut.Laconstructionsuivanteestparexempleillicite:

- 22 -

int somme(int a,int b) { return a+b; } int somme(int a,int b,int c=0) { return a+b+c; } lappel de la fonction somme recevant deux arguments, le compilateur ne pourra dterminer sil sagit de lomissionduparamtrecoubiensileprogrammeuralintentiondutiliserlapremireforme.

h.Fonctionsenligne
Les fonctions en ligne offrent un temps dappel trs court puisque prcisment elles ne provoquent pas de dbranchement(gosub).Lecodequellesrenfermentestdveloppenlieuetplacedelappel. lutilisation,cettecaractristiquenapparatpasdanslasyntaxemaiscelapeutfairecrotrelatailleducode demanireinopportunesilafonctionestappele endiffrentspointsduprogramme. inline int somme(int a,int b) { return a+b; } ... x = somme(3,4); // Place le code ici plutt que deffectuer un dbranchement

i.FonctionsexternesdetypeC
Ladirectiveextern "C"indiqueaucompilateurquildoitutiliseruneconventiondappeldetypeCpourappeler une fonction. Les langages C++ et C ont des fonctionnements internes proches mais pas compltement identiques, notammentencequiconcernelappeldefonction.DanslecasdulangageC,lesparamtressont empils du dernier au premier, la fonction sexcute puis lappelant restaure la pile aprs avoir rcupr la valeurderetourdelafonction.DanslecasdulangageC++,lordredesparamtresestinverseetcestlappel quirestauresoncadredepile. En conclusion, vous devez prfixer vos dclarations de fonctions par extern compilateurC: extern "C" int yylex();

"C" si elles sont issues dun

j.Fonctionsrcursives
Il ne sagit pas dune spcificit du langage C++, aucune syntaxe particulire nest ncessaire, mais plutt dune caractristique supporte. Les fonctions C++ ont la possibilit de se rappeler ellesmmes. Pour le lecteurquidcouvrecestyledeprogrammation,lexempleducalculdelafactorielleestunbonpointdedpart. La factorielle (note en mathmatique !, mais cette notation na rien voir avec loprateur de ngation boolenneduC++)estune"fonction"quisedtermine commesuit: !1 = !2 = !3 = !4 = ... !n = 1 2 3 4 = x x x 1 1 = 2 2 x 1 = 6 3 x 2 x 1 = 24

n x (n-1) x (n-2) x ... x 1

Openmirrors.com

- 23 -

Ilestfacilededonneruneversionditeitrativedunefonctionquicalculecettefactorielle.Letype longat retenucarlafactoriellecroittrsrapidementetlalimitedesdeuxmilliardsdutypeintestviteatteinte.

long factorielle(long n) { long r=1; while(n-- >0) r=r*n; // on aurait pu noter aussi r*=n return r; } Cetteversionfonctionneparfaitement,sicenestquenlabsencedunnomexplicite pourlafonction,onaurait dumaldterminersiellecalculelafactorielle ouunautreproduit. Enreprenantlexpressiongnraledelafactorielle,onpeutprocderunercriture trssimple: !n = n x (n-1) x (n-2) x...x1 = n x !(n-1) Autrementdit,lafactorielledenestgalenmultipliparlafactoriellede(n1),aveccommepointdedpart! 1=1. Nousendduisonsunenouvelleversion: long factorielle_r(long n) { if(n==1) return 1; // !1=1 else return n*factorielle_r(n-1); // }

nx !(n-1)

Lcriture est beaucoup plus simple comprendre et reconnatre. Ceci dit, certains algorithmes se prtent biencestyledeprogrammation,commeleparcoursdedocumentsXML,alorsquedautresnentirerontaucun profit.Parailleurs,certainsalgorithmesdeviennentvitevoracesentermesdespaceutilis parlapile. Le calcul de la suite de Fibonnacci fib(n)=fib(n-1)+fib(n-2), avec fib(1)=fib(2)=1, plante souventauxenvironsdefib(50)tantlenombredecalculsensuspensestlev.

k.Lafonctionmain()
Tous les programmes C++ excutables contiennent une fonction main(), appartenant lespace de noms global. Cette fonction peut parfois porter un nom un peu diffrent, cela dpend des compilateurs et des diteursdeliens.Engnral,cestmain(principal). La fonction main() renvoie en principe un code entier, la convention voulant quun code nul signifie que le programmeafonctionnnormalementetquuncodenonnulindiqueuneerreurdontlinterprtationestlaisse auxsoinsduprogrammeur.Aveclapparitiondesprogrammesgraphiques(audtrimentdesutilitairesenligne de commande), cette convention a un peu tendance sestomper.Celadpenddessystmesdexploitation. Certainscompilateursacceptentmmeunedfinitionvoidpourmainetrenvoientuncode0pardfaut. Lafonctionmain()peutaussiadmettredesparamtresdestinsrecueillirlesargumentspassssurlaligne decommande: int main(int argc,char* argv[]) { } Le premier argument, de type entier, se nomme souvent argc pour argument count. Il dsigne le nombre dargumentspasssparlalignedecommande.Enprincipe,ilvautaumoinsun,letoutpremierargumenttant
- 24 -

le nom du programme excutable. Le second argument, argv pour argument value est un tableau de chanes.Ontrouvegalementcommesignaturechar**cequirevientaumme(voirlapartiesurlespointeurs). Ilestfaciledeprvoirunepetitebouclepourafficherlesparamtresdelalignedecommande: /* affiche.cpp */ int main(int argc,char* argv[]) { for(int i=1; i<argc; i++) printf("argument n%d = %s\n",i,argv[i]); } Nouscompilonsceprogrammeaveclalignesuivante: g++ affiche.cpp o affiche Puisnousexcutonsleprogramme : affiche valeur1 valeur2 "salut les amis" 34 Leprogrammeproduitlaffichagesuivant: argument argument argument argument n1 n2 n3 n4 = = = = valeur1 valeur2 "salut les amis" 34

Si certains arguments reprsentent des nombres, il faudra les convertir depuis le type chane dans le type voululaidedesfonctionscorrespondantes,tellesatoi()alphatointegerouatofalphatofloat. Enrsum,lessignaturessuivantessontpossiblespourmain():

void main() int main() int main(int argc) int main(char*argv[]) main(char**argv)
ou int

Enfaitintmain(),lecompilateurtransformantlui mmelafonctionetajoutantunreturn0lafin. Versionhabituelle. Licitemaissansintrt. Leprogrammeurdoitbiencontrlerle nombre dargumentspasss. Versionlapluslogiquelorsquelonsouhaite recueillir lesargumentsdelaligne decommande.

int main(int argc,char*argv []) ou int main(int argc,char** argv)

Quoiquilensoit,cettefonctionmain()estuniquedansunprogramme.Lesprogrammesquinencontiennent passontdecefaitdestinsconstruiredeslibrairiesstatiques.Ilestcourant,pourleslibrairiesdynamiques (DLL)depossder unefonctionlibmain()chargedeprocderdesinitialisations. Enfin,selonlessystmesdexploitation,onpourratrouverdessignaturesun peudiffrentes.Cestnotamment lecasdesapplicationsgraphiquesWindows.

6.Lespointeurs
Les pointeurs et lesrfrences sont des outils particulirement intressants. Le langage C ne connat pas les rfrences, mais il peut travailler avec les pointeurs en suivant les rgles applicables aux rfrences. Des langages plus rcents, comme Java, ont supprim les pointeurs de leur vocabulaire. Non pas parce quils
- 25 -

Openmirrors.com

pourraientavoirmauvaiserputationauprsdesprogrammeurs,maisparcequilsagissentunniveauplusbas que les rfrences, ce qui perturbe lusage doutils de haut niveau tels que le ramassemiettes (garbage collector). Pointeurs et rfrences sont des variables qui permettent datteindre dautres variables. Pour parvenir ce rsultat, le pointeur (ou la rfrence) utilise ladresse de la variable cible, cestdire le numro de la case mmoireoestrangelavaleurdecettevariable.Commelammoireestcompteenoctets,etquunevariable peut rpartir la reprsentation de sa valeur sur plusieurs octets, pointeurs et rfrences sont des variables dfiniespourtravailleravecuntypedonn,danslebutdelimiterleserreursdadressage.

a.Pointeurssurdesvariables
Commenonspartudierlareprsentationdunfragmentdelammoiredelordinateur.Cefragmentcontient unevariablexdetypechar,pralablementinitialiselavaleur3.Leplussouvent,lesadressesscriventen hexadcimalpourmieuxlesdistinguerdesvaleursstockesenmmoire,maisaussicarlesadresses16ou32 bitsscriventfacilementdanscettebase.

Pouraffecterlavaleur10lavariablex,parexemple,nouspouvonsutiliserlextraitdecodesuivant: char x=3; x=10; Si nous pouvions obtenir ladresse de la variable x, 0x1003 dans notre cas, nous pourrions modifier cette variablesansutiliserdirectementx.Pourcela,nousallonsdfinirunpointeurdetypechar,notchar*.Cette variable spciale, p, recevra ladresse de la variable x, dtermine laide dune syntaxe spciale.

- 26 -

Ensuite, nouspourronsmodifierlavaleursituecetemplacementmmoire,mmesilavariable xnestplus dansnotreporte. char* p; p=&x; // dclare un pointeur de type char // obtient ladresse de la variable x // affiche 1003 en hexa

printf("p=%x",p); *p=10;

// affecte indirectement la variable x

Il faut noter laspect quelque peu artificiel de cet exemple. Pour modifier la valeur dune variable, la syntaxe habituelleconvienttrsbienetilnyapasbesoindenchanger. Pour comprendre lutilit de cette approche, crons une procdure qui transforme un caractre minuscule en majuscule: void maj(char c) { printf("avant c=%c\n",c); if(c>=a && c<=z) c=c-(a-A); printf("aprs c=%c\n",c); } int main() { char x=a; maj(x); printf("finalement, x=%c\n",x); return 0; } Toutefois,lexcutiondeceprogrammenedonnepaslesrsultatsattendus:

Quesestilpass?lappeldelafonctionmaj(),nousavonstransmisparlintermdiairedelapileunecopie delavariable x.Danslaportedelafonction maj(),cettevaleursappellec.Ilsagitdunparamtrequiala duredeviedunevariablelocale.Toutsepassecommesinousavionscrit : Fonctionmaj Fonctionmain

char c=x

maj(x)

printf("avant c=%c\n",c); if(c>=a && c<=z)

Openmirrors.com

- 27 -

c=c-(a-A); printf("aprs c=%c\n",c);

printf("finalement, x=%c\n",x);

Oncomprendalorsquelavariablexestrestebientranquille!Cestsacopie,c,quiatmodifie. prsent,nousmodifionslafonctionmaj()pourquellereoivenonunevaleurdetypecharmaisunpointeur versunevariabledetypechar: void maj(char* c) { printf("avant c=%c\n",*c); if(*c>=a && *c<=z) *c=*c-(a-A); printf("aprs c=%c\n",*c); } int main() { char x=a; maj(&x); printf("finalement, x=%c\n",x); return 0; } Lexcutionestcettefoisciconformenosattentes,lavariablexabientmodifie:

Ilesttempsdersumerlesnotationsrelativesauxpointeurs:

char* p &x *p

dclarepcommepointeurdetypechar,cestdirecommepointeursurunevariable detypechar. dsigneladressedelavariablex. sipestunpointeur,dsignelavaleurpointeparp,donclavaleursituelacase mmoireindiqueparp.

Maintenantque maj()admetunpointeursurchar,ilnestpluspossibledelappelerenluitransmettantune littraledecaractre: Signature Appel Commentaire

- 28 -

Openmirrors.com

void maj(char c) void maj(char c) void maj(char* c) void maj(char* c)

maj(x) maj(t) maj(&x) maj(&t)

cinitialislavaleurdex cinitialislavaleurt cpointeursurchar,dsignelavariablex erreur,unelittraledecharnapasdadresse

b.Pointeursettableaux
Nous avons vu prcdemment la syntaxe de dclaration dun pointeur sur une variable. Ce pointeur sert atteindreunecasemmoireparlintermdiairedelanotation*: char* p; char c; p=&c; *p=K; Imaginonsquelaciblenesoitplusunevariablec,maisuneplagedecaractres(autrementdit,untableaude char).Nousobtiendronslareprsentationmmoiresuivantepouruneplagede5valeursdbutantladresse 0x1002:

Openmirrors.com

Pourdclareruntableaude5char,nouspouvonsutiliserlasyntaxesuivante: char tab[]={S,A,L,U,T }; Le compilateur va ranger ces cinq valeurs dans une partie de la mmoire. La variable tabcontiendraenfait ladressededbutdecetteplage.Autrementdit,tabsecomportecommeunpointeurdechar,endsignantle

Openmirrors.com

- 29 -

premierdeceschar. Poursuivonsnotreraisonnement: char* pt=tab; Le pointeur de char, pt,areuladressedutableau,cestdire quil pointe sur le premier char. Nous en dduisonsquelescrituressuivantessontquivalentes: *pt=S; tab[0]=S; Maintenant,nousvoudrionsatteindreladeuximecasedutableau: tab[1]=A; Enpassantparlepointeur,nousavonstroismoyensdobtenirlemmersultat: Dplacementdupointeur Notation tableau Notation pointeur

pt++; // dsigne la case daprs *pt=A;

pt[1]=A;

*(pt+1)=A;

Lapremirenotationconsistedplacerlepointeur.Rappelonsnousquilsagitdunevariable,dontlavaleur entireestuneadresse.Incrmentercettevaleurduneunitrevientdplacerlepointeurafinquildsignela casevoisine. Ensuite,nousutilisonslanotationhabituelle,*pt,pourcrirecettepositiondelammoire. Ladeuximenotation,pt[1],pousseencorelasimilitudeentrepointeurettableau.Siptettabconcidentsur lapremirecase,lanotationbasedecrochets [ ]doitgalementcorrespondrepourtouteslesvaleursdu tableau. Pourexpliquerlatroisimenotation,commenonspartudierlexpressionsuivante: *pt=A; Puisqueptreprsenteuneadresse(lavaleurdelavariable),ilsemblelogiquedcrire:

*(pt)=A; Maintenant, considrant que pt est un nombre entier, il est possible de lui additionner un autre entier, la sommedesdeuxreprsentantunenouvelleadresse: *(pt+1)=A; Sipointeursettableauxsontsiproches,pourquoiconserverlesdeux?Ilfautconsidrerquelepointeurest unevariabletotalementlibre,tantaffecteavecladressedunevariable &v,oubienrecevantladressedun bloc mmoire frachement allou par loprateur new ou par la fonction malloc(). Le tableau lui peut sinitialiseravecdesvaleursenextensionoubienavecloprateurnew,maispasaveclafonctionmalloc(). Ensuite,lespointeurssontemployslorsquelonarecourslarithmtiquedespointeursilssontprvuspour tre dplacs au gr des ncessits de lalgorithme. Le tableau lui est fixe, et la notation suivante est prohibe: char tab[]={ 2,3,44 };
- 30 -

tab++; // interdit Enfin, les pointeurs sont utiles certains algorithmes seulement. Privilgiez les tableaux chaque fois que ce sera possible, votre programme sera nettement plus portable. Dautant que les rfrences constituent une bonne alternative aux pointeurs. Mais lpoque de la cration du langage C, la programmation tait de beaucoup plus bas niveau que maintenant. Dautre part, bien manis, les pointeurs se rvlent plus rapides quelesrfrences,cequiestimportantdanscertainessituations.

c.Allocationdemmoire
Nouslavonsvu,lelangageC++aconservlesmcanismesdulangageC,toutencherchantenamliorer certains.Lammoirefaitpartiedeceuxl. Il existe deux faons de rserver de la mmoire : en demandant au systme, ou bien en utilisant des instructionsdulangage.Ladiffrenceestsubtile,maiscruciale. Danslapprochesystme,miseaupointparlelangageC,nousdisposonsdunefonction malloc()charge de rserver des octets. Cette fonction demande au systme dallouer une plage de n octets, que lon interprtera par la suite comme tant une plage de valeurs dun type donn, au moyen dun transtypage. Commecestlesystmequiaalloucettemmoire,ilestgalementncessairedelaluirendreaumoyendela fonctionfree()quiadmetunpointeursurvoid,autrementdit,unpointeurdenimportequeltype. Lorsquelonutiliselesinstructions newetdelete,lelangageadaptesagestion delammoireenfonctiondu typerserv.Aucuntranstypagenestncessaire,cequisimplifielasyntaxe. Dans les faits, avec les compilateurs modernes, la fonction malloc() et linstruction new partagent le mme espace mmoire, appel le tas, pour effectuer leurs rservations. Toutefois, les gestionnaires dallocations ntant pas les mmes, il faut veiller dsallouer la mmoire avec le moyen qui correspond : free() pour rendrelammoirealloueparmalloc(),delete()pourrendrelammoireobtenueparnew. SivousdveloppezunnouveauprogrammeC++,privilgiezlinstruction new.Lafonction malloc()doittre rservelaportabilitdesanciensprogrammes C.Cecidit,ilexistedescasoilfautemployerunefonction pourallouerdelammoireayantunaccspartag:lePressepapierssousWindows, unezonedemmoire partagesousUnix...

Allocationparmalloc()
Lafonction malloc()estdclaredanslentte <memory.h>.Dautresenttespeuventconvenir,comme <stdlib.h>. Cettefonctionestdclareselonleprototypesuivant: void* malloc(int n); La fonction est charge dallouer n octets dans la mmoire du systme (en fait, celle du processus). Elle retourneladressedecetteplagesouslaformedunpointeursurvoid. Si la rservation ne peut tre satisfaite, la fonction renvoie 0, ladresse 0, qui est considre comme inaccessible. Pour viter demployer une valeur littrale aussi vocatrice, les concepteurs du langage C ont imaginlamacroNULL:NULL #define NULL ((void*)0) Silarservationestsatisfaite,leprogrammeurdoitconvertirlepointeurvoid*dansuntypeappropri: char*p; p=(char*) malloc(15); // alloue 15 char if(p==NULL)

Openmirrors.com

- 31 -

printf("erreur dallocation"); Nombredecompilateursaccepteraientlcriture p=malloc(15),maisparrigueur dcriture,ondoitsefforcer deffectueruntranstypageparcoercition,enindiquantquelonprendlaresponsabilitdinterprtercettezone commetantunezonede15char. Par ailleurs, si le type est plus large que le char, il faut employer loprateur suffisammentdoctets: double*d=(double*) malloc(30*sizeof(double)); // alloue 30 double Lorsqueleblocestallou,onsensertcommenimportequeltableau,enutilisantlanotationdesonchoix:*d, d[]ou*(d+i). Lorsquelammoirenestplusutile,leblocdoittrerenduausystmeparlintermdiaire delafonctionfree(): free(d);

sizeof() pour rserver

Allocationparnew
Lallocation par new sera pour linstant rserve aux tableaux. Lorsque nous traiterons linstanciation, loprateurnewaccompliraunautrerle,essentiel. Lasyntaxegnraledelinstructionnewest:

type* new type < [taille] > Autrement dit, loprateur new renvoie le pointeur vers le type allou. La taille est optionnelle, lunit est la valeurpardfaut. Voiciquelquesexemplesdallocationparloprateurnew: Allouer15char char*p; p=new char[15];

Allouer30double double*d; d=new double[30];

string * s; s=new string;

Allouerunechanelaidedelaclassestring(cf. Instanciation de classes)

Unefoisleblocallouparnew,onlutiliseaveclesmmesnotationsquelorsquilatrservparmalloc(). Enrevanche,leblocdoittreimprativementlibrlaidedelinstructiondelete:

delete(p);

d.Arithmtiquedespointeurs
Lorsque nous dplaons un pointeur, par incrment ou par addition, combien doctets sont balays ? Si le pointeurestchar*,larponseestsimple:

- 32 -

char*p; char c; p=&x; p++; // passe ladresse suivante (en octets) Demme,lorsquelonaccdelavaleurdsigneparunpointeur,combiendoctetssontlusoucrits?Encore unefoisceladpenddutypedupointeur. Prenonslexemplesuivant: float*p; char*t; t=new char[12]; for(int i=0; i<10; i++) t[i]=1; p=(float*)t; // peu rigoureux mais lgal *(p+1)=0; Ensortiedeceprogramme,quelestltatdutableaut?Lcriture*(p+1)=0aimpact4octets,compterde lacinquimeposition,commelemontrelillustrationciaprs:

Openmirrors.com

- 33 -

Lorsqueloncompteen float,ilfautmultiplierpar4touslesdplacements.Lcritureimpacteaussi4octets aulieudunseul,etce,mmesilazoneavaittrservecommeunezonedechar. Cettergledecalculsappellelarithmtiquedespointeurs,etdoittreappliqueaveclaplusgranderigueur quisoit.

e.Pointeursdepointeurs
Maintenant que nous connaissons bien les pointeurs, pourquoi ne pas dfinir un pointeur qui dsigne une variabledetypepointeur?Cetteoprationestfinalementassezcourante,silonconsidrequeleslittralesde chanessontdestableauxde char,autrementditdespointeursde char (char*). La fonction main(), admettantcommeparamtreuntableaudechanes,reoitenralitunpointeurdepointeurs. Lespointeursdepointeursnesontfinalementpassicomplexes,ilsconstituentsimplementuneindirectionde plus.Ilnefautpasenabuseret,afindesimplifieraumaximumlesnotations,onprivilgieralesnotationsde typetableau. char**argv; // un pointeur de pointeurs de type char char* argv[]; // un tableau de pointeurs de type char Bienentendu,lesnotationsdestinesauxpointeursusuelsrestentapplicablesauxpointeursdepointeurs: char*p; char* *pp; pp=&p; *pp="salut"; **pp=S; printf("%s",p); // un pointeur de type char // un pointeur de pointeur de type char // &p=adresse de p. pp dsigne p // autrement dit, p="salut" // autrement dit, *p=S // affiche Salut

f.Pointeursdefonctions
Puisquelesinstructionsdfinissantunefonctionsont,linstardesvariables,rangesdanslammoire,nous pouvonsadmettrequelesfonctionssontenfaitdesadresses:cellesdeleurpremireinstruction.Partantde l, il devient possible de dfinir des pointeurs de fonctions pour appeler indirectement certaines dentre elles. Ilapparatquelespointeursdefonctionssontemploysdansquelquessituations particulires.Toutdabord, unpointeurdefonctionrendcertainsalgorithmesgnriques.Prenonslexempledelalgorithmedetrirapide. Celuicirestelemme,queloncherchetrierunensembledentiers,unensembledeboolens,oudesobjets denaturevarie.Pourrendreleprogrammeindpendant dutypededonnestrier,ilestpossibledutiliserun pointeurversunefonctionquiadmettedeuxvaleursetquiindiquelaquelleestlaplusgrande. On rencontre galement des pointeurs de fonctions lorsque lon applique des mthodes des objets. Le chapitresurladressagerelatifnousdonneraplusdinformationscesujet. Enfin,ilnestpasraredefournirunmodule"systme"unpointeurversunefonctionquiseraappelelorsque surviendraunvnementparticulier.Ondsigne cemcanismeparletermedefonctionscallback(rappelables).

Utilisationdepointeursdefonctionspourrendrelesalgorithmesgnriques
Danslebutdillustrercetteapproche,nousproposonsdtudierlalgorithmedutrirapide. Pour comprendre cet algorithme, nous commenons par partitionner un tableau. Un tableau de valeurs, par exemple des entiers, est partitionn autour dune valeur pivot en plaant gauche de ce pivot toutes les valeursinfrieuresetdroite,touteslesvaleurssuprieures.

- 34 -

Openmirrors.com
Toutdabordletableaudorigine,avecunpivot(engras),choisiarbitrairementaumilieu: 2 8 3 7 5 9 3 10 3

Voilmaintenantletableaupartitionn.Lepivotpeutavoirchangdeplace. 2 3 3 5 7 9 8 10 3

Lalgorithmedutrirapideritrecettepartition,gaucheetdroitedupivot,rcursivement.Finalement,nous rcupronsuntableaucompltementtri. Voicipourcommenceruneimplmentationfonctionnantpouruntableaudentiers.Nousvousencourageonsla testertellequellesivousntespasfamiliaris aveccetalgorithme: // chapitre 1 partition.cpp : dfinit le point // dentre pour lapplication console. // int partition(int* T,int m,int d) { // valeur pivot, variable dchange int v,aux; int m1=m,d1=d; // initialisation v=T[m+(d-m)/2]; // tant que les index ne se croisent pas while(m<d) { // rechercher une valeur infrieure droite while(m<d && T[d]>v) d--; // rechercher une valeur suprieure gauche while(m<d && T[m]<v) m++; if(m>=d) break; if(T[m] != T[d]) { // change aux=T[d]; T[d]=T[m]; T[m]=aux; } else d--; } return m; } void tri_aux(int* T,int m,int d) { if(m>=d) return; // rien trier int k=partition(T,m,d); // partitionne entre m et d tri_aux(T,m,k-1); // tri gauche tri_aux(T,k+1,d); // tri droite } void tri(int* T,int length) {
- 35 -

Openmirrors.com

tri_aux(T,0,length-1); } void afficher(int T[],int m,int d) { // affichage du tableau chaque tape for(int i=m; i<=d; i++) printf("%d,",T[i]); printf("\n"); } int main() { int tab[]={ 5,1,7,2,8,4,9,13}; tri(tab,8); afficher(tab,0,7); return 0; } NousproposonsmaintenantuneimplmentationC++dutrirapide,prenantenparamtresuntableautrier, desindicesncessairesaufonctionnementdelalgorithme,plusdeuxpointeursdefonction.Lundsigneune fonctiondecomparaison,lautreunefonctiondchange. Voici tout dabord, la dfinition des pointeurs de fonction. Comme lcriture est un peu lourde, on a souvent recoursladfinitiondaliasdetype(typedef) : typedef void (*pf_echange)(void*,int,int); typedef int (*pf_comp)(void*,int,int); Le premier modle de fonction, pf_echange, caractrise la signature dune fonction ne renvoyant rien et admettantuntableaudevoid(touttypedevaleur, enfait),ainsiquedeuxentiers. Lesecondmodle,pf_comp,admetlesmmesparamtresmaisrenvoieunentier,rsultatdelacomparaison entre deux valeurs. Dans les deux cas, les entiers admis comme paramtres sont les index des valeurs du tableau,comparer ouchanger,selonlecas. Ilfautmaintenantimplmenterdeuxfonctionsrespectantcessignatures: void int_echange(void*t,int p1,int p2) { int*T=(int*)t; // transtypage (cast) int aux=T[p1]; T[p1]=T[p2]; T[p2]=aux; } int int_compare(void*t,int p1,int p2) { int v1,v2; int*T=(int*)t; // transtypage (cast) v1=(int) T[p1]; v2=(int) T[p2]; return v1-v2; } Jusqu prsent, nous navons pas dautres moyens dtre indpendant visvis des types que dutiliser un pointeursurvoid.Cequiexpliqueletranstypageunpeubrutal,levoid*tantpromuenint*. Ilnenousresteplusquamnagerleprogrammeexistantpourtravailleraveccesfonctions,parlentremise depointeurs: // lalgorithme devenu gnral

- 36 -

int partition(void* T,int m,int d,pf_echange fswap,pf_comp fcomp) { // valeur pivot, variable dchange int pv; int m1=m,d1=d; // initialisation pv=m+(d-m)/2; // position du pivot // tant que les index ne se croisent pas while(m<d) { // rechercher une valeur infrieure droite while(m<d && (*fcomp)(T,d,pv)>0) d--; // rechercher une valeur suprieure gauche while(m<d && (*fcomp)(T,m,pv)<0) m++; if(m>=d) break; if((*fcomp)(T,m,d)!=0) { // change (*fswap)(T,m,d); } else d--; } return m; } void tri_aux(int* T,int m,int d,pf_echange fswap,pf_comp fcomp) { if(m>=d) return; // rien trier int k=partition(T,m,d,fswap,fcomp); // partitionne entre m et d tri_aux(T,m,k-1,fswap,fcomp); // tri gauche tri_aux(T,k+1,d,fswap,fcomp); // tri droite } void tri(int* T,int length,pf_echange fswap,pf_comp fcomp) { tri_aux(T,0,length-1,fswap,fcomp); } void afficher(int T[],int m,int d) { // affichage du tableau chaque tape for(int i=m; i<=d; i++) printf("%d,",T[i]); printf("\n"); } int main() { int tab[]={ 5,1,7,2,8,4,9,13}; tri(tab,8,&int_echange,&int_compare); afficher(tab,0,7); return 0; } Au passage, nous relevons que les notations associes aux pointeurs de variables restent applicables aux pointeursdefonctions:

&int_echange

adressedelafonctionint_echange()

Openmirrors.com

- 37 -

(*fcomp)(T,m,d)

Contenu l adresse fcomp, autrement dit la fonction dsigne par fcompestappeleaveclesparamtres(T,m,d).

Fonctionscallback
LAPI(ApplicationProgrammingInterface)standardduC++proposepeudefonctionscallback.Pourlessystmes graphiques,commeWindows,cestaucontrairemonnaiecourante.Lesgestionnairesdvnementsassocis unclicsurunboutonsontsouventdesfonctionscallback. Danscetypedesituation,nousavonsgnralementunsystmedenregistrement.Pourunvnementdonn unclicsurunboutonparexemple ,uneouplusieursfonctionssontenregistres. Lorsque lvnement survient, chacune de ces fonctions est appele dans le but daccomplir un travail spcifiqueouvrirunefentre,raliseruncalcul... Nous pouvons simuler cette approche laide dun petit programme. Il est constitu dunepartiesystmeet dune partie application. La partie systme possde un dispositif pour enregistrer des gestionnaires dvnements,ainsiquunefonctionquiscruteleclavier.Lorsqueceluiciestsollicit,touslesgestionnaires(ce sontdesfonctions)sontappelsavecunargumentreprsentantlatouchepresse. // partie systme #include <conio.h> typedef void (*pf_key_press)(int key);

pf_key_press*gestionnaires; int nb_gestionnaires; void { init() gestionnaires=new pf_key_press[10]; nb_gestionnaires=0; } void { } void propager_evenement(int touche) { for(int i=0;i<nb_gestionnaires; i++) (*gestionnaires[i])(touche); // appelle } enregistrer(pf_key_press gestionnaire) // 10 gestionnaires max

gestionnaires[nb_gestionnaires++]=gestionnaire;

le

gestionnaire

void surveiller_clavier() { int touche; do { while(!_kbhit()); // attend la frappe dune touche=_getch(); // rcupre la touche propager_evenement(touche); } while(touche!=q); }

touche

Voilmaintenantlapartieapplication,quiconsisteendeuxgestionnairesplusunefonctionmain(): // partie application void touche_pressee1(int key) { printf("1. Vous avez press la touche %c\n",key);
- 38 -

} void touche_pressee2(int key) { printf("2. Vous avez press la touche %c\n",key); } // main() int main(int argc, char* argv[]) { // initialisation du systme init(); // ces deux fonctions seront appeles lorsque le clavier // sera sollicit enregistrer(&touche_pressee1); enregistrer(&touche_pressee2); // dmarrage surveiller_clavier(); return 0; } Lexcutionduprogrammepeutdonnerquelquechosequiressemblecela :

Bien que sommaire, ce programme illustre bien le fonctionnement des systmes dexploitation graphiques commeWindowsouMacOS.

7.Rfrences
Bienquaccomplissantlemmerlequelespointeurs,lesrfrencesoffrentunesyntaxeplussimpleetenmme tempslimitentlesrisquesdaccserronlammoire. Unerfrenceesttoujoursassocieunevariable,alorsquunpointeurpeuttremodifivialarithmtiquedes pointeurs. Lasyntaxededfinitionduntyperfrenceutiliseleprfixe&,enremplacementdeltoile.Parcontre,ilnefaut pasconfondreceprfixeavecloprateur&quiextraitladressedunevariableoudunefonction. char c; // un caractre char*p; // un pointeur de char p=&c; // p dsigne c, et &c reprsente ladresse de c char & refc=c; // refc est une rfrence de char, refc dsigne c Danscetextraitdecode,nousavonsdfiniunerfrencedechar,refc,dsignantlavariablec.Cetterfrence estdevenueunaliasdelavariablec,aussi,toutemodificationameneparrefcimpacteraenralitc,mmeen
- 39 -

Openmirrors.com

dehorsdesaporte: refc++; // incrmente en fait c

Commedanslecasdespointeurs,lesrfrencesnontderelintrtquepourraliserdeseffetsdebord.Pour illustrercetusage,reprenonslafonctionmaj()tudieprcdemment. Versionpointeur Versionrfrence

void maj(char* c) { printf("avant c=%c\n",*c); if(*c>=a && *c<=z) *c=*c-(a-A); printf("aprs c=%c\n",*c); }

void maj(char& c) { printf("avant c=%c\n",c); if(c>=a && c<=z) c=c-(a-A); printf("aprs c=%c\n",&c); }

int main() { char x=a; maj(&x); printf("finalement, x=%c\n",x); return 0; }

int main() { char x=a; maj(x); printf("finalement, x=%c\n",x); return 0; }

Nousnousrendonscomptequelasyntaxeparrfrenceestplussimplequecelleproposeparlestylepointeur. Lcriture se rapproche davantage du passage par valeur et pourtant leffet de bord la modification par une procdure estpossible.

Uneplusgrandescurit
Les rfrences offrent une scurit bien plus grande que les pointeurs, puisque ladresse manie par la rfrencenestpasvaluable.Lepointeurestlibredtredplac,parincrment,addition...Larfrencetant toujours,dssadclaration,associeunevariable,lesrisquesderreurssontlimits.

Lesrfrencesconstantes
Les rfrences constantes, gnralement utilises pour les fonctions, garantissent que la variable nest pas modifieparlafonction. void fonction_sans_risque(const int & x) { printf("x=%d",x); // ok x=4; // erreur } Onpeutobjecterquelintrtdunetellemthodeestquasinul.quoibonpasserunentierparrfrencesilon svertue le rendre invariable ? Pour un type primitif comme int, la cause est entendue, mais pour un type objet (une classe), les cas dapplication sont nombreux. Les mthodes sont applicables, les champs sont modifiables, mme si la rfrence est constante. Le passage par rfrence autorise les effets de bord, lobjet ntantpascopidanslapile.Dautrepartlappelestplusrapidepuisquelarfrencesassimileuneadresse, cequiestsouventplusconomiquequederecopiertousleschampsdelobjetdanslapile.

Renvoyerunerfrencedepuisunefonction

- 40 -

Ilesttoutfaitpossiblededfinirunefonctionquirenvoieunerfrence.Envoiciunexemple: double euro,fs,livre; double & cours(int pays) { switch(pays) { case 1: return livre; case 2 : return fs; default: return euro; } } Lintrtdunetelleapprocheestquetoutlemondetravailleaveclammevaleur. Silesvariables euro,fsou livre voient leur valeur modifie, toutes les fonctions les utilisant utiliseront les valeurs jour, la fonction cours()ayanttransmisunerfrencepluttquunevaleur. Cet emploi des rfrences voque galement lusage de champs statiques dans une classe, avec certes une syntaxeunpeudiffrente.

8.Constantes
a.Constantessymboliques
Leprprocesseur,utilitairedeprtraitementtextuel,remplittroismissionsimportantes :
q

ilinclutlesfichiersdsignsparladirective#include ilvaluelaprsencedemacrosparladirective#ifdef ilvaluelesmacrosdfiniesparladirective#define.

Ceprprocesseurtravailleenamontducompilateur.Depuisquelesnouveauxlangagesdeprogrammationont abandonnsonusage(Javaparexemple),ilvautmieuxlimiterlenombredemacrosdfiniesavec#define. Onpeuttoutefoisutilisercettederniredirectivepourdfinirdesconstantessymboliques : #define PI 3.14159265358 Danslesfichiersquiontreucettedfinition,leprprocesseurremplaceralachanePIparsavaleurtextuelle danstoutessesoccurrencesquinapparaissentpasdansunechanedecaractres. #define PI 3.14 double x=PI; char*a="Le savant grec Pythagore a dcouvert PI sans calculette"; Lecompilateurreoituneversionmodifiedudernierfragment : double x=3.14; char*a="Le savant grec Pythagore a dcouvert PI sans calculette";

Openmirrors.com

- 41 -

b.Letypevoid
Aucunevariablenepeuttretypevoid,pourtantcetypeentredanslaclassificationdestypeslmentaires, commeint,short... Cemotclestutilispourindiquerquunefonctionnerenvoierien,cestuneprocdure : void afficher(char*s) { } On utilisera aussi void

*, pointeur sur un type indtermin. Un transtypage sert ensuite convertir ce

pointeurdansletypequiconvient : void* malloc(int nb_octets) // fonction qui renvoie void* { ..} char*s=(char*) malloc(15) ; // alloue 15 octets CesystmetaitpourleCunmoindremaldanssatentativedesupporterlagnricit.Pluttquedassimiler ladresseunentiercequisepassedetoutefaonlorsquelonparledarithmtiquedespointeursondira que malloc()allouedesoctetsetretourneladressedelazonealloue.Letypedelazone(int,double...) dpenddelinterprtationquenfaitleprogrammeur.Cetteinterprtationestprciseparlaconversiondune valeurvoid*enchar*dansnotreexemple.

c.Lesaliasdetype,typedef
Cet oprateur sert crer des alias vers des types exigeant des notations alambiques. Plutt que dcrire trssouventunsigned char,onprfreradclarerletypeuchar :

typedef unsigned char uchar ; Parlasuite,desvariablesprendrontindiffremmentletypeunsigned

charouuchar :

uchar c ; // quivalent unsigned char c ;

d.Constantesetnumrations
LelangageC++aintroduitlemotclconstquiempchelamodificationdunevariableaprssoninitialisation :

const double pi=3.14; Cettenotationestcommunedautreslangages,cequilarendbienplusportable queladirective#define. Lorsquelavaleurnumriqueduneconstanteimportemoinsquesonlibell,lesnumrationsconstituentune trsbonnesolution : enum Jour { lundi, mardi, mercredi, jeudi, vendredi, samedi, dimanche } ; Jour rendez_vous; int main(int argc, char* argv[]) { rendez_vous=mercredi; return 0; }

- 42 -

Dans cette configuration, la valeur relle de mercredi importe peu. Cest le nom de la constante qui est vocateur. Enfait,lesnumrationssontassimilablesuntypeentier,aussiestilpossibledefixerlavaleurdedpartde lnumration : enum Couleur { Rouge=38, Jaune, Bleu };

Openmirrors.com

- 43 -

Exceptions
1.Lesapprochesdebasniveau
Tout programme est soumis aux alas de lenvironnement qui le fait fonctionner. Il peut intervenir des dfaillancesmatrielles,certainsprocessusbloquentdesressourcescritiques,lammoirenestpasinpuisable... Pourquunprogrammeentredanslacatgoriedeslogiciels,ildoittretolrantvisvisdecesvnementset nepassebloquer,oupire,sinterromprebrutalement. Les anciens langages de programmation sont en gnral assez mal pourvus pour traiter les situations problmatiques,etlesdveloppeurssesontsouventappuyssurlesdispositifsprvusparleurenvironnement.

a.Drapeauxetinterruptions
Certains microprocesseurs disposent dinstructions destines dclencher des sousprogrammes des fonctionsenlangageClorsquecertainesconditionssontrunies:unedivisionparzro,undpassementde capacit, une faute dans la gestion de la mmoire pagine... Il nest pas rare de voir une partie de ces interruptionslaisseslentendementduprogrammeursystme.Ainsi,lesystmedexploitationMSDOSatil programm sur linterruption n19 le redmarrage du systme. Il existe des pilotes de priphriques qui emploient ces interruptions pour synchroniser des changes de donnes. Citons les pilotes de disques durs, daffichageetceuxprenantenchargelescartesdacquisition numriqueoulescartesson. laide des instructions adquates, qui peuvent tre symbolises par lappel dune fonction de lAPI mise dispositionparlesystmedexploitation,leprogramme enregistreladressedunefonctionditecallbackpourun numrodinterruptiondonn. Cette interruption peut ensuite tre dclenche directement par le microprocesseur en cas de division par zro par exemple ou par un programme. Lorsque linterruption est dclenche, le microprocesseur appelle automatiquement la fonction callback. Lorsque cest un programme qui est lorigine du dclenchement de linterruption,onparledinterruptionlogicielle.

- 1-

Pourautant,lesinterruptionsneconstituentpasunmoyenassezgnralpourletraitementdeserreurs.Pour commencer, tous les microprocesseurs ne sont pas quips de ce dispositif. Dautre part, lunique table des interruptions est gre par le systme. Enfin, certaines interruptions doivent tre dclenches avec des prioritsplusfortesquedautres. En rsum, les interruptions constituent une aide intressante lorsque le programme dcrit une couche du systme dexploitation, mais se rvlent trop limites pour grer des erreurs applicatives. Il est par contre frquent que le gestionnaire dinterruptions prvu par le systme dexploitation avise le processus qui fonctionneaumomentosurvientlincidentaumoyenduneAPIdeplushautniveau. DenombreusesfonctionsdelabibliothquestandardduCadoptentuncomportementparticulierpoursignaler quuneoprationnapaspuaboutir.Leplussouvent,lersultatenvoyapourvaleuruncodeindiquantltat de lopration. Ainsi la fonction getc(FILE*) renvoie un char sur 4 octets. Si lentier vaut1 (0xFFFFFF en hexadcimal),lefluxestarrivpuisement.Lafonctionfopen()renvoieunpointeurNULLsilouverturedu fichiernapaspuaboutir. Le programmeur peut donc dtecter ces situations finalement assez simples traiter. Le plus souvent, un message sorti sur la console avertit lutilisateur des circonstances supposes entourant la dtection du problme. Cemcanismetrouvecependantseslimitesdanslecasdunalgorithmesophistiqu, lafonctionquidtecte un problme na peuttre pas les moyens de dcider des actions mener pour le rsoudre. Ainsi, le dveloppeur seratil tent dutiliser une variable globale signalant la prsence dune erreur. Il nest en effet pasopportundemodifierlasignaturedesfonctionscarcelareviendraitremettreencauselalgorithme. La gestion des erreurs base de drapeaux pose souvent des problmes dorganisation, laccsconcurrentiel ntantengnralpasassur,nimmeledroutementasynchronelorsqueleproblmesurvient.

b.TraitementdeserreursenlangageC
LabibliothquedulangageCmetdispositionduprogrammeurdesinformationsimportantessurlanaturede lerreur qui vient de se produire. Ainsi lorsquun appel fopen() choue, plusieurs raisons doivent tre dpartages :absencedufichier,droitsdaccsinsuffisants,avariematrielle... Le programme suivant utilise des fonctions du langage C tout fait applicables en C++ pour expliquer, de diffrentesmanires,lescirconstancesdunfopen()nefonctionnantpas:

#include <stdio.h> #include <stdlib.h> #include <errno.h> int main(int argc, char* argv[]) { FILE* f; f = fopen("introuvable.txt", "rb"); if(f==NULL) { // dtection de la nature de lerreur int e = errno; // dernire erreurFonction;errno printf("Erreur #%d\n",e); // affichage dun message circonstanci perror("Erreur: ");Fonction;perror // sortie dun message sur un char* char* msg = strerror(errno); printf("msg=%s\n",msg); } else fclose(f); return 0; }

- 2-

Openmirrors.com

Le fichier suivants:

introuvable.txt tant videmment absent de notre exemple, nous obtenons les rsultats

Les fonctions offertes par la bibliothque du langage C sont un premier pas mais ne rsolvent pas tous les problmes. Pour commencer, la variable errno est une variable globale, donc unique, et sa valeur risque dtre crase lorsquuneautreerreurintervientsilaconsultationdelapremiretardetrop.Dautre part, ce dispositif nvolue pas facilement pour le dveloppeur qui souhaiterait mettre en place des exceptions applicatives.

2.Lesexceptionsplussresqueleserreurs
Lnorme avantage des exceptions vient du fait quelles sont intrinsques au langage, et mme lenvironnement dexcution (runtime voire framework). Elles sont donc beaucoup plus sres pour contrler lexcution dun programme multithread (ce qui est le cas de nombreuses librairies). De plus les exceptions guident le programmeur et lui facilitent la tche de structuration du code. En dautres termes il est difficiledesenpasserlorsquelonmatriseleurutilisation.

Certainslangagesvontmmeplusloinetexigentleurpriseencomptelardaction duprogramme. CestnotammentlecasdeJava.

LelangageC++proposedesexceptionsstructures,bienentendubasedeclassesetdunegestionavance delapile.Lideestdemettreunesquencedinstructionspouvantcontenirdesappelsdesfonctionssous surveillance.Lorsquunproblmesurvient,leprogrammeurpeutintercepterlexception dcrivantleproblmeau fildesapropagationdanslapile. Le principe des exceptions C++ est de sparer la dtection dun problme de son traitement. En effet, une fonctiondecalculnasansdoutepaslesmoyensdedciderdelastratgieadopterencasdedfaillance.Les diffrentes alternatives sont de continuer avec un rsultat faux, dintgrer de nouvelles valeurs saisies par lutilisateur,desuspendrelecalcul... Lorsquunefonctiondclencheuneexception,celleciestpropagetraverslapiledesappelsjusqucequelle soitintercepte.

CetteorganisationrduitnantlescontraintesimposesparlabibliothquedulangageCilnyaplusdaccs

- 3-

concurrentielunevariableglobalechargededcrireltatderreur,puisquelecontextedelerreurestdfini paruneinstancepartentire.Ledclenchementduneexceptionprovoqueunretourimmdiatdelaprocdure oudelafonctionetdbutelarecherchedunblocdetraitement.

3.Propagationexplicite
Lemotclthrowsertdclencheruneexception.Ilestsuividuneinstancedontlasmantiqueestlaissela chargeduprogrammeur.Parfoisletypemmeutilispourinstancierlexceptionsuffitprciserlescirconstances delerreur. Lorsquune exception est leve, la pile dappels est parcourue la recherche dun bloc dinterception correspondantcetypedexception. Reprenonsnotreexempledouverturedefichieretamlioronsleennousaidant desexceptions: FILE*ouvrir(char*nom) { FILE*f; f = fopen(nom,"rb"); if(f==NULL) throw 1 ; return f; } int main(int argc, char* argv[]) { FILE*fichier; try { fichier = ouvrir(argv[1]); char temp[50]; fread(temp, 50, sizeof(char),fichier); fclose(fichier); } catch(int code) { printf("Une erreur est survenue, code=%d\n", code); } return 0; } Cest la fonction ouvrir() qui dcide de dclencher une exception de "valeur" 1 lorsque lappel fopen choue. Si tel est le cas, linstruction return nest pas excute, le throw interrompant la squence dexcution. Dans la fonction main(), les instructions ouvrir()...fclose() sont mises sous surveillance au moyen dun bloc try (essai). Si lune de ces instructions dclenche, mme indirectement, une exception, le bloc try sinterrompt et lenvironnement dexcution cherche un bloc catch (attraper) qui intercepte une exception du typecorrespondantceluilev.

4.Typesdexceptionspersonnaliss
a.Dfinitiondeclassesdexception
Notre programme prcdent peut facilement tre amlior en crant une classe dexception. Il est vrai quun codeentiernestpastrssignificatif,alorsquuntypepartentireatoutpourltre.

- 4-

Openmirrors.com

class OuvertureFichierException {} ; Nousmodifionslasquencedelevedexception: if(f==NULL) throw OuvertureFichierException(); Enconsquence,lebloccatchdoitluimmetreamnag:

catch(OuvertureFichierException) { printf("Le fichier na pas pu tre ouvert\n"); } Maintenant que nous savons crer des types dexception personnaliss, nous pouvons sans peine prvoir plusieursblocscatchpourunseulbloctry.

try { fichier=ouvrir("test.txt"); char temp[50]; fread(temp,50,sizeof(char),fichier); fclose(fichier); } catch(OuvertureFichierException) { printf("Le fichier na pas pu tre ouvert\n"); } catch(FermetureFichierException) { printf("Le fichier na pas pu tre ouvert\n"); }

b.Instanciationdeclasses
Lesclassesdexceptionpeuventtreinstanciesavecdesparamtresquidcriventleplusfinementpossible lerreur.Ainsi,nouspouvonsmodifiernotreclassederreurenladotantdunconstructeur: class OuvertureFichierException { public: char*message; OuvertureFichierException() { message=""; } OuvertureFichierException(char*msg) : message(msg) {} OuvertureFichierException(std::string m) { using namespace std; message=new char[m.length()+1]; m.copy(message,m.length()); message[m.length()]=0; } } ;

- 5-

La syntaxe std::string prcise que le type string est dfini dans lespace de noms std (espace de nomsdelabibliothquestandardSTL).

Lecodelevantlexceptionpeutmaintenantprciserlescirconstancesdelerreur : if(f==NULL) throw OuvertureFichierException( std::string("Impossible douvrir ") + nom); En consquence, nous donnons un nom linstance de afficher(outraiter)lemaximumdinformations: catch(OuvertureFichierException e) { printf("Le fichier na pas pu tre ouvert\n"); printf(e.message); }

OuvertureFichier Exception pour pouvoir

c.Classesdexceptiondrives
Il est frquent de regrouper les classes dexception ayant trait la mme smantique derreur par le biais dune drivation. Dans lexemple cidessous, loprateur : indique que les classes OuvertureFichierException, ES_FichierExceptionetFermeturefichierExceptionhritent delaclasseFichierException(cf.chapitreProgrammationorienteobjet).

class FichierException {} ; class OuvertureFichierException : public FichierException {} ; class ES_FichierException : public FichierException {} ; class FermetureFichierException : public FichierException {} ; Lcriture des blocs catch peut pleinement profiter de cette organisation pour filtrer dabord finement puis grossirementlescausesdchec: catch(OuvertureFichierException e) { printf("Le fichier na pas pu tre ouvert\n"); printf(e.message); } catch(FichierException) { printf("Une erreur de type fichier est survenue"); } On doit alors veiller placer en dernier la classe de base : elle intercepte toutes les exceptions de type FichierException qui ne correspondent pas des cas plus prcis, comme OuvertureFichierException. La bibliothque standard possde un certain nombre dexceptions drives en plusieurs smantiques : exceptiongnrale,exceptiondentresortie,exceptionmathmatique...

5.Priseenchargeduneexceptionetrelance

- 6-

Openmirrors.com

Puisquenotrefonction ouvrir()estsusceptiblededclencherdesexceptions,ilestimportantdeprvenirle programmequunbloctry/catchseraitbienvenu.Lemotclthrowsertgalementdanscettesituation:

FILE*ouvrir(char*nom) throw (OuvertureFichierException) Suivant les compilateurs, lappel de cette fonction sans bloc try/catch lvera un avertissement, voire une erreurdecompilation. Une fonction qui est marque throw (TypeException) peut se dispenser de la mise en place dun bloc try/catch pour appeler des fonctions marques avec le mme niveau dexception. Ce principe est dailleurs particulirement utile en Java, langage qui reprend le mcanisme des exceptions de C++ en renforant le contrledesmarquages. Si un bloc catch veut relancer une exception qui aurait t dj intercepte, il peut utiliser le mot cl throw seul: catch(TypException) { throw; // relance }

6.Exceptionsnoninterceptes
La syntaxe

catch(...) est utile pour intercepter tout type dexception. Toutefois, lemploi dun bloc try/catchauniveaudelafonctionmain()peutservlercontraignant.Certainesexceptionspeuventdonc
tredclenchesmaisnoninterceptes. Enprincipe,unetellesituationconduitlarrtduprogramme.Ilestgalementpossibledutiliserlafonctionde labibliothquestandard std:terminate()oubiendenregistrerunefonctioncallbacklaidedelafonction std:set_terminate()pourprvenircetteinterruptioninopportune.

7.Acquisitionderessources
RAII est lacronyme de Resource Acquisition Is Initialisation. Cela signifie que les ressources sont acquises et initialisesaucoursduneoprationatomique(inscable). Quelestlerapportaveclagestiondesexceptions? Dans le cas o des objets initialiss depuis une fonction o survient une exception auraient acquis des ressources, il conviendrait de les relcher avant que le flot dexcution ne soit drout sur le gestionnaire adquat. Cela tombe bien car C++ libre toujours les objets locaux avant de quitter la porte dune fonction, mmesiuneexceptionestleveoupropage. Lexemplesuit: #include <iostream> using namespace std; // affiche un message tabul void display_log(char*fname,char*message,int tab) { while(tab-->0) // dcompte le nombre de tabulations cout << "\t"; cout << fname << ":\t" << message << endl << endl; } // classe dexception personnalise class Cexception { public: Cexception(){};

- 7-

~Cexception(){}; // circonstance de lexception const char *Message() const { return "Une erreur sest produite."; } }; const int BUFFER_SIZE = 500; // type dobjet utilisant des ressources class Cresource { private: char*buffer; public: Cresource(); ~Cresource(); }; Cresource::CResource() { display_log("CResource::Constructeur","Allocation du buffer.",1); buffer = new char[BUFFER_SIZE]; } Cresource::~CResource() { display_log("CResource::Destructeur","Liberation du buffer.",1); delete buffer; } // fonction instanciant une classe Cresource. // les objets correspondant allouent 500 octet de mmoire. void fonction() { CResource resource; display_log("fonction","declenchement de lexception Cexception.",1); throw Cexception(); } // fonction principale int main() { try { display_log("main","Appel de fonction().",0); fonction(); } catch( CException ex ) { display_log("catch(CException)",(char*)ex.Message(),0); } display_log("main","Apres le try / catch.",0); return 0; } Latracecranindiquebienquelobjetresourceestdtruit(doncsesressourcesinternesrelches)avantque lecatchnecapteleflotdexcution:

- 8-

Openmirrors.com

LemcanismeRAIIestdoncassezprochedelaconstructiontry/finallydeC#etdeJava.

- 9-

Programmationstructure
Les langages de programmation ont commenc trs tt assembler les instructions sous la forme de groupes rutilisables,lesfonctions.Lesvariablesontnaturellementprislemmechemin,bienquunpeuplustardivement. Letableaupermetdetraitercertainsalgorithmes,conditionqueladonnetraitersoitduntypeuniforme(char, int...). Lorsque la donne traiter contient des informations de natures diffrentes, il faut recourir plusieurs tableaux, ou bien un seul tableau en utilisant un type fourretout void*. Il faut bien le reconnatre, cette solutionestproscrire. la place, nous dfinissons des structures regroupant plusieurs variables appeles champs. Ces variables existentenautantdexemplairesquesouhait,chaqueexemplaireprenantlenomdinstance. LelangageC++connatplusieursformescomposites: lesstructuresetlesunions,amnagespartirduC lesclasses,quiseronttraitesauchapitresuivant.

1.Structures
Les structures du C++ comme celles du C dfinissent de nouveaux types de donnes. Le nom donn la structureengendreuntypededonnes: struct Personne { char nom[50]; int age; } ; partir de cette structure Personne, nous allons maintenant crer des variables, en suivant la syntaxe habituellededclarationquiassocieuntypeetunnom: Personne jean, albertine; JeanetalbertinesontdeuxvariablesdutypePersonne.Commeilsagitduntypenonprimitifchar, int...,on dit quil sagit dinstances de la structure Personne. Le terme instance rappelle que le nom et lge sont des caractristiquespropreschaquepersonne.

Openmirrors.com

- 1-

Onutiliseunenotationparticulirepouratteindreleschampsduneinstance : jean.age = 50; // lge de jean printf("%s",albertine.nom); // le nom dalbertine Cettenotationrelielechampsoninstance.

a.Constitutiondunestructure
Une structure peut contenir un nombre illimit de champ. Pour le lecteur qui dcouvre ce type de programmationetquiesthabituauxbasesdedonnes,ilestutiledecomparerunestructureavecunetable dansunebase. C++ structure champ instance table champ/colonne enregistrement SQL

Chaque champ de la structure est bien entendu dun type particulier. Il peut tre dun type primitif (char, int...)oubienduntypestructure.Onpeutaussiobtenirdesconstructionsintressantes,parcomposition:

struct Adresse { char *adresse1, *adresse2; int code_postal; char* ville; } ; struct Client { char*nom; Adresse adresse; } ; Onutiliseloprateurpointcommeaiguilleurpouratteindrelechampdsir : Client cli; cli.nom = "Les minoteries runies"; cli.adresse.ville = "Pau"; Enfin,ilestpossiblededfinirdesstructuresautorfrentes,cestdiredesstructuresdontundeschamps estunpointeurversuneinstancedelammestructure.Celapermetdeconstruiredesstructuresdynamiques, tellesqueleslistes,lesarbresetlesgraphes: struct Liste { char* element; Liste* suite; } ; Le compilateur na aucun mal envisager cette construction : il connat fort bien la taille dun pointeur, gnralement4octets,doncpourluilatailledelastructureestparfaitementcalculable.

- 2-

b.Instanciationdestructures
Ilexisteplusieursmoyensdinstancierunestructure.Nouslavonsvu,unestructureengendreunnouveautype dedonnes,donclasyntaxeclassiquepourdclarerdesvariablesfonctionnetrsbien: Personne mireille;

Instanciationladfinition
Lasyntaxededclarationdunestructurenousrserveunesurprise:ilestpossiblededfinirdesinstances aussittaprsladclarationdutype: struct Personne { char*nom; int age; } jean,albertine ; Dansnotrecas,jeanetalbertinesontdeuxinstancesdelastructurePersonne.Biensr,ilestpossibleparla suitedutiliserdautresmodesdinstanciation.

Instanciationparrservationdemmoire
Finalement, linstanciation agit comme lallocation dun espace segment pour ranger les champs du nouvel exemplaire.Lafonctionmalloc()quiallouedesoctetsaccomplitjustementcettetche:

Personne* serge = (Personne*) malloc(sizeof(Personne)) La fonction malloc() retournant un pointeur sur void, on opre un transtypage ( cast) vers le type Personne*danslebutdaccorderchaquectdelgalit.Loprateur sizeof()dterminelatailledela structure,enoctets. Onaccdealorsauxchampsparloprateur->quiremplacelepoint:

serge->age = 37; Pour rserver plusieurs instances conscutives un tableau il faut multiplier la taille de la structure par le nombredlmentsrserver: Personne* personnel = (Personne*) malloc(sizeof(Personne)*5) Pour accder un champ dune instance, on combine la notation prcdente avec celle employe pour les tableaux: personnel[0]->nom personnel[0]->age personnel[1]->nom personnel[1]->age = = = = "Georgette"; 41; "Amandine"; 27;

La fonction malloc() est dclare dans lentte <memory.h> quil faut inclure si besoin. Ce type dinstanciationfonctionnaitdjenlangageC,maisloprateurnew,introduitenC++,vaplusloin.

c.Instanciationavecloprateurnew
Eneffet,loprateurnewsimplifielasyntaxe,carilrenvoieunpointeurcorrespondantautypeallou:

Openmirrors.com

- 3-

Personne*josette = new Personne; Aucun transtypage nest ncessaire, loprateurnewappliqulastructurePersonnerenvoyantunpointeur (uneadresse)detypePersonne*. Pourrserveruntableau,ilsuffitdajouterlenombredlmentsentrecrochets : Personne*employes = new Personne[10]; Lencorelasyntaxeestsimplifie,puisquilestinutiledeprciserlatailledechaqueinstance.Loprateurnew laprenddirectementencompte,sachantquilestappliquuntypeparticulier,enloccurrencePersonne. La simplification de lcriture nest pas la seule avance de loprateur new. Si la structure dispose dun constructeur(voircesujetlechapitresurlesclasses),celuiciestappellorsquelastructureestinstancie parlebiaisdeloprateur new,alorsquelafonctionmalloc()secontentederserverdelammoire.Lerle dun constructeur, fonction "interne" la structure, est dinitialiser les champs de la nouvelle instance. Nous reviendronsendtailsursonfonctionnement.

d.Pointeursetstructures
Quellequesoitlafaondontatinstancielastructure,parmalloc()ouparnew,laccsauchampsefait laide de la notation flche plutt que point, cette dernire tant rserve pour laccs une instance par valeur. Loprateur & appliqu une instance a le mme sens que pour nimporte quelle variable, savoir son adresse. Sicetoprateurestcombinlaccsunchamp,onpeutobtenirladressedecechamppouruneinstanceen particulier. Le tableau ciaprs rsume ces modalits daccs. Pour le lire, nous considrons les lignes suivantes: Personne jean; Personne* daniel = new Personne; Personne* personnel = new Personne[10];

jean.age daniel->age &jean &jean.age &daniel &daniel->age personnel[2]->age personne[2] &personne[2]->age

Lechampageserapportantjean. Lechampageserapportantdaniel,cederniertantunpointeur. Ladressedejean.PermetdcrirePersonne*jean_prime=&jean. Ladresseduchampagepourlinstancejean. Ladressedupointeurdaniel,quinestpascelledelinstance. Ladresseduchampagepourlinstancedaniel. Lgedelapersonneportantlenumro2. Ladressedelapersonneportantlenumro2. Ladresseduchampagepourlapersonneportantlenumro2.

Nousconstatonsquaucunenouveautnafaitsonapparition.Lesnotationsdemeurentcohrentes.

e.Organisationdelaprogrammation

- 4-

Lorsquunestructureestcre,ilnestpasraredelavoirdfiniedansunfichierdentte.hportantsonnom. Parlasuite,touslesmodules cppcontenantdesfonctionsquivontutilisercettestructuredevrontinclurele fichierparlintermdiairedeladirective#include.

Inclusionlaidede#include
PrenonslecasdenotrestructurePersonne,elleseradfiniedanslefichierpersonne.h:

// Fichier : personne.h struct Personne { char nom[50]; int age; } ; Chaque module dextension sparmentdesautres: #include "personne.h" int main() { Personne jean; Jean.age=30; }

.cpp

lutilisant doit luimme inclure ce fichier, sachant quil est compil

Protectioncontrelesinclusionsmultiples
Lesystmedesenttesdonnedebonsrsultatsmaisparfoiscertainsfichiers.hsontinclusplusieursfois,ce quiconduitdesdclarationsmultiplesdutypePersonne,faittrspeuapprciparlecompilateur. Nous disposons de deux moyens pour rgler cette difficult. Tout dabord, il est possible demployer une directivedecompilation#ifndefsuiviedun#define:

#ifndef _Personne #define _Personne // Fichier : personne.h struct Personne { char nom[50]; int age; } ; #endif Lasecondetechnique,plussimple,consisteutiliserunedirectivepropreuncompilateur, #pragma once. Cettedirective,placeendbutdefichierdentte,assurequelecontenuneserapasaccidentellementinclus deux fois. Si le cas se produit, le compilateur recevra une version ne contenant pas deux fois la mme dfinition,donc,nousnauronspasderreur. La premire technique semble peuttre moins directe, pourtant elle est davantage portable, puisque les directives #pragma (pour pragmatique) dpendent de chaque compilateur. Vous tes bien entendu susceptiblederencontrer lesdeuxdansunprogrammeC++tiers.

2.Unions
Une union est une structure spciale lintrieur de laquelle les champs se recouvrent. Cette construction particulireautoriseuntassementdesdonnes,uneconomiesubstantielle.Lorsquunchampestcritpourune instance,ilcraselesautrespuisquetousleschampsontlammeadresse.Latailledelunioncorresponddonc
- 5-

Openmirrors.com

latailleduchamplepluslarge. Lesunionsontdeuxtypesdapplication.Pourcommencer,celapermetdesegmenter unestructuredediffrentes manires. Nous pouvons citer comme exemple la structure address, qui accueille une union destine reprsenterdiffrentsformatsdadressesrseau.EnfonctiondelanaturedurseauIP,AppleTalk,SPXles adressessontreprsentesdemaniresdiffrentes. Commedeuximetypedapplication,nouspouvonspenserlareprsentationdesnombres.Suivantquenous souhaitonsprivilgierlavitesseoulaprcision descalculs,nouspouvonsutiliseruneunionpourcalculerenint, enfloatouendouble.Utiliserunestructureneseraitpasunebonneidecarchaque"nombre"occuperait4+4+8 soit16octets.Luniondonnedemeilleursrsultats: union Valeur { int nb_i; float nb_f; double nb_d; } ; Latailledecetteuniongale8octets,soitlespaceoccupparundouble. Ilnestpasraredinclureuneuniondansunestructure,unchampsupplmentaireindiquantlequeldeschamps delunionest"actif": enum TNB { t_aucun,t_int, t_float, t_double }; struct Nombre { char t_nb; union Valeur { int nb_i; float nb_f; double nb_d; } val ; } ; Nouspouvonsprsentimaginerquelquesfonctionspourtravailleraveccetteconstruction: void afficher(Nombre& n) { switch(n.t_nb) { case t_int: printf("%d\t",n.val.nb_i); break; case t_float: printf("%f\t",n.val.nb_f); break; case t_double: printf("%f\t",n.val.nb_d); break; } } Nombre* lire_int() { Nombre* c=new Nombre; c->t_nb=t_int; printf("entier: "); scanf("%d",&c->val.nb_i); return c; }

- 6-

Nombre* lire_float() { Nombre* c=new Nombre; c->t_nb=t_float; printf("dcimal: "); scanf("%f",&c->val.nb_f); //cin >> c->val.nb_f; // cin >> float& return c; } Nombre lire_double() { Nombre c; c.t_nb=t_double; printf("double: "); scanf("%lg",&c.val.nb_d); return c; } Dans cet exemple, nous avons mlang diffrents modes de passage ainsi que diffrents modes de retour (valeur,adresse...). Pourcequiestdelinstanciationetdelaccsauxchamps,lunionadoptelesmmesusages(notations)quela structure.

3.Copiedestructures
Que se passetil lorsque nous copions une structure par valeur dans une autre structure ? Par exemple, que donnelexcutionduprogrammesuivant ? struct Rib { char*banque; int guichet; int compte; } ; int main(int argc, char* argv[]) { Rib compte1,compte2; compte1.banque = "Banca"; compte1.guichet = 1234; compte1.compte = 555666777; compte2 = compte1; printf("compte1, %s %d %d\n",compte1.banque,compte1.guichet, compte1.compte); printf("compte2, %s %d %d\n",compte2.banque,compte2.guichet, compte2.compte); return 0; } Lexcution de ce programme indique que les valeurs de chaque champ sont effectivement copies de la structurecompte1lastructurecompte2.Ilfautcependantfaireattentionauchampbanque.Sagissantdun pointeur,lamodification delazonepointeaffecteralesdeuxinstances: Rib compte1,compte2; // alloue une seule chane de caractres char* banque=new char[50]; strcpy(banque,"Banque A");

Openmirrors.com

- 7-

// renseigne la premire instance compte1.banque = banque; compte1.guichet = 1234; compte1.compte = 555666777; // copie les valeurs dans la seconde compte2 = compte1; // affichage printf("compte1, %s %d %d\n",compte1.banque,compte1.guichet, compte1.compte); printf("compte2, %s %d %d\n",compte2.banque,compte2.guichet, compte2.compte); // apparemment, on modifie uniquement compte1 printf("\nModification de compte1\n"); strcpy(compte1.banque,"Banque B"); // vrification faite, les deux instances sont sur "Banque B" printf("compte1, %s %d %d\n",compte1.banque,compte1.guichet, compte1.compte); printf("compte2, %s %d %d\n",compte2.banque,compte2.guichet, compte2.compte); Vrificationfaite,lexcutionindiquebienquelesdeuxpointeursdsignentlammeadresse:

La copie des structures contenant des champs de type pointeur nest pas la seule situation poser des problmes.Considronsprsentlextraitsuivant: Rib *c1,*c2; c1 = &compte1; c2 = c1; c1->compte = 333; printf("c2, %s %d %d\n",c2->banque,c2->guichet,c2->compte); Lexcutionindique333commenumrodecomptepourc2.Autrementdit,c1et c2dsignentlemmeobjet,et laffectationc2=c1nariencopidutout,saufladressedelinstance. Ilauraittplusjudicieuxdutiliserlafonctionmemcpy():

c2 = new Rib; memcpy(c2,c1,sizeof(Rib)); c1->compte = 222; printf("c2, %s %d %d\n",c2->banque,c2->guichet,c2->compte); Cettefois,lexcutionindiquebienque c2 et c1sontindpendants.Cestlallocation par new(ou malloc()) quiaurafaitladiffrence.

4.Crationdaliasdetypesdestructure
Linstructiontypedeftudieauchapitreprcdentsertgalementdfinirdesalias(detypes)destructure:

- 8-

Openmirrors.com
// une structure dcrivant un nombre complexe struct NombreComplexe { double reel,imaginaire; }; typedef NombreComplexe Complexe; typedef NombreComplexe* Pcomplexe; typedef NombreComplexe& Rcomplexe; int main() { Complexe c1; PComplexe pc1 = new Complexe; RComplexe rc1 = c1; return 0; } Cet usage est gnralement dvolu aux API systmes qui dfinissent des types puis des appellations pour diffrentsenvironnements.Parexemplelechar*devient LPCTSTR(longpointertoconstantstring),lepointeur destructureRectdevientLPRECT // alias de type // alias de type pointeur // alais de type rfrence

5.Structureetfonction
Cesontlesfonctionsquioprentsurlesstructures.Ilestfrquentdedclarerlesstructurescommevariables localesdunefonctiondanslebutdelesutilisercommeparamtresdautresfonctions.Quelestalorslemeilleur moyenpourlestransmettre?Nousavonsnotredispositionlestroismodeshabituels,parvaleur,paradresse (pointeur)ouparrfrence.

a.Passerunestructureparvaleurcommeparamtre
Lemodeparvaleurestindiqusilastructureestdepetitetailleetsisesvaleursdoiventtreprotgescontre toute modification intempestive de la part de la fonction appele. Ce mode implique la recopie de tous les champsdelinstancedanslapile,cequipeutprendreuncertaintempsetconsommerdesressourcesmmoire forcment limites. Dans le cas des fonctions rcursives, la taille de la pile a dj tendance grandir rapidement,ilnestdoncpasjudicieuxdelasurchargerinutilement. Toutefois,cettecopieempchedeseffetsdebordpuisquecestunecopiedelastructurequiestpasse. void afficher(Nombre n) { switch(n.t_nb) { case t_int: printf("%d\t",n.val.nb_i); break; case t_float: printf("%f\t",n.val.nb_f); break; case t_double: printf("%f\t",n.val.nb_d); break; } }

b.Passerunestructureparrfrencecommeparamtre
Cemodeconstitueuneavanceconsidrable,puisquecestlarfrence(adresseinaltrable)delastructure quiesttransmise.Cetteinformationoccupe 4octets(encompilation32bits)etautoriseleseffetsdebordsous

Openmirrors.com

- 9-

certaines conditions. De plus, le passage par rfrence est transparent pour le programmeur, ce dernier nayantaucunechancedepasserunevaleurlittralecommeinstancedestructure. void afficher(Nombre& n) Nousverronscommentlastructure(classe)peuttreamnagedemanirecequelaccs en modification deschampspuissetrecontrldemanirefine. Ilestpossibledeprotgerlastructureendclarantleparamtrelaidedumotclconst:

void afficher(const Nombre& n) { n.val.nb_i=10; // erreur, n invariable }

c.Passerunestructureparadressecommeparamtre
Ce mode continue tre le plus utilis, sans doute car il est la seule alternative au passage par valeur autoriseenlangageC.Ilncessiteparfoisdemployerloprateur &lappeldelafonction,etlepointeurreu parlafonctionseconformelarithmtiquedespointeurs.Enfin,cemodeautoriseleseffetsdebord. void afficher(Nombre* n)

d.Delaprogrammationfonctionnellelaprogrammationobjet
En admettant que certaines fonctions puissent migrer lintrieur de la structure, dans le but vident de sappliquer aux champs dune instance en particulier, nous dcouvrons cette notion de la programmation oriente objet que lon appelle lencapsulation, cestdire la runion dune structure et de fonctions. Cette constructionesttoutfaitlgaleenC++: struct Nombre { char t_nb; union Valeur { int nb_i; float nb_f; double nb_d; } val ; void afficher() { switch(t_nb) { case t_int: printf("%d\t",val.nb_i); break; case t_float: printf("%f\t",val.nb_f); break; case t_double: printf("%f\t",val.nb_d); break; } } } ; Connaissantun nombre,ilesttrsfaciledemodifierlesnotationspourutiliser cettemthode afficher() enremplacementdelafonctionafficher(Nombre&) :

- 10 -

Nombre a; a =lire_float(); // utilise la fonction lire_float() a.afficher(); // utilise la mthode afficher() afficher(a); // utilise la fonction afficher() Nousverronsauchapitresuivantcequidistinguelaprogrammationfonctionnelleetlaprogrammationoriente objet.Pourlelecteurquiconnatlanotiondevisibilitdansuneclasse,ilestutiledeprciserquelesmembres dunestructure(champsoumthodes)sontpublicspardfaut.

Openmirrors.com

- 11 -

Gestiondelammoire
Lammoireestvitalepourlesystmedexploitationdontlatcheprincipaleconsistesparerchaqueprocessus desautres,enluiallouantunequantitinitialedemmoire.Nousdcouvronsdiffrentesstratgiesdallocationen fonction des systmes. Les plus anciens (MSDOS) et les plus basiques (sur microcontrleur) adoptent une allocationparblocsdunetailledtermine(64 koparexemple),cettetaillentantpasamenevolueraucours dutemps.Dautressystmesontrecourslapaginationunesegmentationfinedelespacemmoireenblocsde 4kocequiautoriseunegestiondynamiqueetefficacedelammoire.Conscutivementlemploidelammoire pagine,ladresseexprimeparunpointeurnestjamaisuneadressephysique,maislogique, lemicroprocesseur sechargeantdelatraduction. Pourcequiestdumodleapplicatif,cestdiredelasegmentationdelammoire duprocessus,nousavonsen gnralleszonessuivantes:

Le langage C++ tant comme son prdcesseur trs proche du systme dexploitation, il convient de bien connatrelesdiffrentstypesdegestiondelammoire.

1.Alignementdesdonnes
Ledveloppeurdoitbienprendreencomptecetaspectdelagestiondelammoire silaportabilitestuncritre important.Touslescompilateursettouslesmicroprocesseursnerangentpaslesdonnesdelammefaon. PrenonslecasdelastructurePersonne:

struct Personne { char age; char*nom; } ; SachantquelemicroprocesseurPentiumlitlammoireparblocsde4 octets,nombredecompilateursinsreront 3octetsderemplissagepouralignerlechampnomsurleprochainbloc.Ilenrsulteunetailledestructuregale 8alorsquelecomptagemanueldonne5.

- 1-

Aussi, beaucoup de microprocesseurs utilisent la reprsentation bigendian, ce qui fait que les donnes sont ranges en mmoire en commenant par le poids fort. Le Pentium utilise la convention inverse. Ces caractristiquessontdterminanteslorsquunfichierestchangentredeuxplatesformes, mme silsagitdu mmeprogrammeC++,compilpourchaqueplateforme,soitavecdeuxcompilateursdiffrents.

2.Allocationdemmoireinterprocessus
Nous connaissons dj deux moyens dallouer de la mmoire dynamiquement. La fonction malloc() et loprateurnewontunevocation"langage".Danscertainessituations,ilestncessairedefaireappeldautres fonctionsoffertesparlesystme.Lammoirepartage,utiliseparlePressepapiersdeWindowsestunbon exemple. Lacommunicationinterprocessusestuneprrogativedusystmedexploitation.Commebeaucoupdentreeux sontprogrammsenlangageCouC++,ilestassezfaciledutilisercesfonctions.Toutefois,lanotiondepointeur nexistepasdanstousleslangages,etlesfonctionsdallocationretournentsouventunentier,appel handle, identifiantleblocallou.Ladsignationdecetentierprendsouventcommenom Hresult(HandleResult),qui estenfaitunentierdguis. NousretrouveronsdesexemplesdecetypeauchapitreconsacrlaprogrammationC++sousWindows.

- 2-

Openmirrors.com

Openmirrors.com

LabibliothquestandardduC
Aveclesstructuresetlallocationdelammoire,nousnousrendonscomptequunlangagedoitsappuyersurdes librairies systme pour construire des applications compltes. Le langage C++ possde sa propre librairie, mais nombredeprogrammeursutilisenttoujourslesfonctionsstandardsdulangageC.

1.LesfonctionscommunesdulangageC<stdlib.h>
La librairie standard

stdlib.h contient des fonctions dordre gnral. Certaines fonctions peuvent tre

dailleursdclaresdansdautresenttes. Voici une liste rsumant quelques fonctions intressantes pour le dveloppement courant. Il est judicieux de consulterlouvragedeKernighanetRitchieoubienunedocumentationfournieaveclecompilateurpourconnatre lafoislalistecompltedesfonctionsetenmmetempsleursignature. LeKernighanetRitchieestlouvragederfrencecritparlescrateursdulangageC.Ilesttoujoursditet misjourpartirdesvolutionsdulangageC. Fonctions Utilit Fonctions de conversion entre un type chane et un type numrique. Accsauxvariablesdenvironnementsystme. Allocationdemmoire. Fonctionsmathmatiques.

atoi,atof,strtod... getenv,setenv malloc,calloc rand,abs

Labibliothquestandardstdlibcontientaussidesmacrosinstructionsbasessurlasyntaxe#define:

Macro

Utilit Donnelesvaleursminetmaxdedeuxarguments Littralement(void*)0

__min,__max NULL

Unpetitexemplemontrecommentutiliserlabibliothque: char* lecture = new char[500]; printf("Nombre ? "); scanf("%s",lecture); double nombre = atof(lecture); double pi = 3.14159265358; printf("Le nombre le plus grand est %2f\n",__max(nombre,pi));

- 1-

2.Chanes<string.h>
Le langage C++ gre toujours ses littrales de chane au format char* dfini par le langage C. Aussi estil importantdeconnatrelesprincipalesfonctionsdelabibliothquestring.h.

Fonctions

Utilit Vision mmoire des chanes de caractres : copie, comparaison,initialisation Copie,comparaison,concatnation,longueur Recherchedecaractre,desouschane Conversionminuscule/majuscule

memcpy,memcmp,memset strcpy,strcmp,strcat,strlen strchr,strstr strlwr,strupr

Prenonslencoreunexemplepourillustrerlemploidecettelibrairie: /* * Recherche */ printf("** Recherche\n"); // chane analyser char* chaine1 = "Amateurs de C++ et de programmation"; // motif rechercher char* motif = "C++"; // recherche : strstr fournit un pointeur sur la sous-chane char* souschaine = strstr(chaine1,motif); // rsultat if(souschaine != NULL) printf("Trouve le motif [%s] dans [%s] :\n[%s] a la position [%d]\n\n", motif,chaine1,souschaine,souschaine-chaine1); /* * Comparaison, concatnation */ printf("** Comparaisons, concatenations\n");

- 2-

Openmirrors.com

// rserver de la mmoire char* chaine2 = new char[strlen(chaine1) + 50]; // construire la chane strcpy(chaine2,chaine1); strcat(chaine2,", un nouvel ouvrage est disponible chez ENI"); // rsultat printf("chaine2 = %s\n",chaine2); // initialiser une chane char chaine3[50]; memset(chaine3,O,3); // chaque octet est initialis par le caractre O chaine3[3]=0; // 0 terminal (identique \0 la 4me position // rsultat if(strcmp(chaine3,"OOO")) printf("chaine3 = %s\n",chaine3); // ne pas se tromper avec les longueurs char* chaine4 = "ABC"; char* chaine5 = new char[ strlen(chaine4) + 1]; // +1 pour le 0 terminal strcpy(chaine5, chaine4); // passer en minuscule char* chaine6 = strlwr(chaine5); // attention chaine5 est modifie printf("chaine5=%s chaine6=%s\n\n", chaine5, chaine6); /* * E/S */ printf("** E/S\n"); char message[500]; // crire un message format dans une chane sprintf(message,"2 x 2 = %d\n", 2*2); printf("%s\n",message); Lexcutiondeceprogrammenousdonnelersultatsuivant:

3.Fichiers<stdio.h>
- 3-

Les fonctions dclares dans stdio.h sont consacres aux oprations dentresortie, au sens large. On trouve deux types de prise en charge des fichiers : par handle systme et par structure FILE*. Le premier typeestplusancienetfortementliUnix.Toutefois,latechniquedu handleatgnralisedansdautres partiesdusystmedexploitation:lessocketsetlerseau,legraphisme... Un handle est gnralement assimilable un entier. Il sagit dun identificateur gr par le systme pour dsignerdiffrentstypesderessources. Laccsparlastructure FILE*estquantluideplushautniveau,doncplusspcialis.Certains"fichiers"sont djdclars,telsquestdin,stdout,stderr etstdprn. Enfin, stdio.h propose des fonctions de formatage vers des chanes de caractres, telles que sprintf (galementprsentedansstring.h).

Fonctions

Utilit Impressionsurstdout,lecturedepuisstdin ImpressionetlecturedepuisunFILE* Impressionsurunechane Ouverture, fermeture, lecture, criture sur un (entier)

printf,scanf fprintf,fscanf sprintf open,close,read,write fopen,fclose,fread,fwrite fflush putc,getc fputc,fgetc feof ftell,fseek remove, tempnam rename, unlink,

handle

Ouverture,fermeture,lectureetcrituresurunFILE* Transfert du tampon (buffer) vers son priphrique (FILE*) criture,lecturedunseulcaractre(handle) criture,lecturedunseulcaractredepuisunFILE* Testdelafindunflux(FILE*) Lecture et dplacement du pointeur de lecture /criture dansunfichierFILE* Gestiondunomdunfichier

Lexemple est ici un peu plus dvelopp que prcdemment. Il sagit dafficher le contenu dun fichier en hexadcimaletenASCII(onditdumper).Leprogrammecontientdesmthodesutilitairesdedplacementdans lefichierainsiquunefonctiondelecturecompltedufichier.Lelecteurpourratitredexerciceremplacercette fonctionparunmenuproposantlutilisateurdelirelignelignelefichier,dereveniraudbut // dfinit le point dentre pour lapplication console. // #include #include #include #include "stdafx.h" <stdio.h> <stdlib.h> <string.h>

const int T_BLOC = 16; // revient au dbut du fichier void debut(FILE* f) { fseek(f,0,SEEK_SET);

- 4-

Openmirrors.com

} // va la fin du fichier void fin(FILE* f) { // longueur du fichier fseek(f,0,SEEK_END); long taille = ftell(f); fseek(f,taille - (taille % T_BLOC), SEEK_SET); } // remonte dans le fichier void haut(FILE* f) { fseek(f, -T_BLOC, SEEK_CUR); } // descend dans le fichier void bas(FILE* f) { fseek(f, T_BLOC, SEEK_CUR); } // dtermine la taille du fichier long taille(FILE* f) { // position actuelle long pos = ftell(f); // va la fin et dtermine la longeur partir de la position fseek(f, 0, SEEK_END); long taille = ftell(f); // revient au point de dbut fseek(f, pos, SEEK_SET); return taille; } void afficher_bloc(FILE* f) { int offset = ftell(f); char chaine_hexa[T_BLOC*3 + 2 + 1]; char chaine_ascii[T_BLOC*2 + 2 + 1]; strcpy(chaine_hexa,""); strcpy(chaine_ascii,""); int data; for(int i=0;i<16;i++) { int car = fgetc(f); if(car == -1) break; char concat[50]; sprintf(concat,"%2x ",car); strcat(chaine_hexa,concat); sprintf(concat,"%c",car>=32?car: ); strcat(chaine_ascii,concat); if(i == T_BLOC/2) { strcat(chaine_hexa," "); strcat(chaine_ascii," "); } }

Openmirrors.com

- 5-

// fprintf(stdout) = printf ! fprintf(stdout,"%4d | %s | %s\n",offset,chaine_hexa,chaine_ascii); } int main(int argc, char* argv[]) { char* nom_fichier="c:file://temp//briceip.txt"; FILE*f = NULL; try { f = fopen(nom_fichier,"rb"); /* * lecture continue du fichier */ long p = 0; long t = taille(f); debut(f); while(p<t) { afficher_bloc(f); p+=T_BLOC; } fclose(f); } catch(...) { printf("erreur\n"); }

return 0; } Voicilersultatpourunfichierdexemple:

- 6-

Openmirrors.com

S-ar putea să vă placă și