398

Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

  • Upload
    others

  • View
    17

  • Download
    5

Embed Size (px)

Citation preview

Page 1: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,
Page 2: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Résumé

150exercicescorrigéspourmaîtriserlalangageC++ComplémentidéaldeProgrammerenlangageC++,dumêmeauteur,cetouvragevouspropose150exercicescorrigésetcommentéspourmieuxassimilerlasyntaxedebaseduC++(typesetopérateurs,instructionsdecontrôle,fonctions,tableaux,pointeurs…)etlesconceptsobjetdulangage.

LesexercicesproposésvouspermettrontdevousforgerunevéritableméthodologiedeconceptiondevospropresclassesC++.Voussaureznotammentdéciderdubien-fondéde la surdéfinition de l’opérateur d’affectation ou du constructeur par recopie, tirerparti de l’héritage (simple oumultiple), créer vos propres bibliothèques de classes,exploiterlespossibilitésoffertesparlespatronsdefonctionsetdeclasses,etc.

Chaque chapitre débute par un rappel de cours suivi de plusieurs exercices dedifficulté croissante. Les corrigés sont tous présentés suivant le même canevas :analyse détaillée du problème, solution sous forme de programme avec exemple derésultat d’exécution, justification des choix opérés – car il n’y a jamais de solutionuniqueàunproblèmedonné!–et, sibesoin,commentairessur lespointsdélicatsetsuggestionssurlesextensionspossiblesduprogramme.

Lecodesourcedescorrigésestfournisurlesitewww.editions-eyrolles.com.

Àquis’adressecelivre?•Auxétudiantsdescursusuniversitaires (DUT, licence,master), ainsiqu’auxélèvesdesécolesd’ingénieur.

•Àtoutprogrammeurayantdéjàuneexpériencedelaprogrammation(C,Python,Java,PHP…)etsouhaitants’initieraulangageC++.

Ausommaire

Généralités sur le C++, types de base, opérateurs et expressions (7 exercices) •Instructionsdecontrôle(16exercices)•Fonctions(10exercices)•Tableaux,pointeurset chaînes de type C (13 exercices) • Structures (6 exercices) • Du C au C++ (9exercices) • Classes, constructeurs et destructeurs (7exercices) • Propriétés desfonctionsmembres(5exercices)Construction,destructionetinitialisationdesobjets(7exercices)•Fonctionsamies(3exercices)•Surdéfinitiond’opérateurs(11exercices)•Conversionsdetypedéfiniesparl’utilisateur(7exercices)•Techniquedel’héritage(7exercices)•Héritagemultiple(6exercices)•Fonctionsvirtuellesetpolymorphisme(3

1

Page 3: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

exercices) • Flots d’entrée et de sortie (5 exercices) • Patrons de fonctions (4exercices)•Patronsdeclasses(8exercices)•Gestiondesexceptions(7exercices)•Exercicesdesynthèse(6exercices)•Composantsstandard(11exercices).

BiographieauteurIngénieurinformaticienauCNRS,ClaudeDelannoypossèdeunegrandepratiquedelaformation continue et de l’enseignement supérieur. Réputés pour la qualité de leurdémarche pédagogique, ses ouvrages sur les langages et la programmation totalisentplusde500000exemplairesvendus.

www.editions-eyrolles.com

2

Page 4: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

AUXÉDITIONSEYROLLES

Dumêmeauteur

C.DELANNOY.–ProgrammerenlangageC++.N°14008,8eédition,2011,820pages.C.DELANNOY.–ProgrammerenJava.Java5à8.N°11889,9eédition,2014,948pages(rééditionavecnouvelleprésentation,2016).

C.DELANNOY.–ExercicesenJava.N°67385,4eédition,2014,360pages(rééditionavecnouvelleprésentation,2016).C.DELANNOY.–S’initieràlaprogrammationetàl’orientéobjet.AvecdesexemplesenC,C++,C#,Python,JavaetPHP.N°11826,2eédition,2014,360pages.

C.DELANNOY.–LeguidecompletdulangageC.N°14012,2014,844pages.

AutresouvragesH.BERSINI,I.WELLESZ.–Laprogrammationorientéeobjet.CoursetexercicesenUML2avecPython,PHP,Java,C#,C++.N°67399,7eédition,2017,672pages.

P.ROQUES.–UML2parlapratiqueN°12565,7eédition,2009,396pages.J.ENGELS.–PHP7.Coursetexercices.N°67360,2017,608pages.

P.MARTIN,J.PAULI,C.PIERREdeGEYERetE.DASPET.–PHP7avancé.N°14357,2016,728pages.G.SWINNEN.–ApprendreàprogrammeravecPython3.N°13434,3eédition,2012,435pages.

E.BIERNAT,M.LUTZ.–Datascience:fondamentauxetétudesdecas.N°14243,2015,312pages.

3

Page 5: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

ClaudeDelannoy

ExercicesenlangageC++

3eédition

Troisièmetirage2017,avecnouvelleprésentation

4

Page 6: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

ÉDITIONSEYROLLES61,bdSaint-Germain75240ParisCedex05

www.editions-eyrolles.com

Attention:pourlirelesexemplesdelignesdecode,réduisezlapolicedevotresupportaumaximum.

Enapplicationdelaloidu11mars1957,ilestinterditdereproduireintégralementoupartiellementleprésentouvrage,surquelquesupportquecesoit,sansl’autorisationdel’ÉditeurouduCentrefrançaisd’exploitationdudroitdecopie,20,ruedesGrands-Augustins,75006Paris.

La troisième édition du présent ouvrage est parue en 2007 sous l’ISBN 978-2-212-12201-5. À l’occasion de cetroisièmetirage,ellebénéficied’unenouvellecouverture.Letexteresteinchangé.

©GroupeEyrolles,1997-2007,pourletextedelaprésenteédition.©GroupeEyrolles,2017,pourlanouvelleprésentation.ISBN:978-2-212-67387-6.

5

Page 7: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Avant-propos

La maîtrise d’un langage de programmation passe obligatoirement par la pratique,c’est-à-dire la recherche personnelle d’une solution à un problème donné, et cetteaffirmationrestevraiepourleprogrammeurchevronnéquiétudieunnouveaulangage.C’estdanscettesituationquesetrouvegénéralementunepersonnequiabordeleC++:

soitelleconnaîtdéjàun langageprocéduralclassiqueautreque leC(Java,VisualBasic,Pascal,...),

soitelleconnaîtdéjàlalangageCsurlequels’appuieeffectivementC++;toutefois,cedernierlangageintroduitsuffisammentdepossibilitéssupplémentairesetsurtoutde nouveaux concepts (en particulier ceux de la Programmation Orientée Objet)pourquesonapprentissages’apparenteàceluid’unnouveaulangage.

Cet ouvrage vous propose d’accompagner votre étude du C++ et de la prolonger àl’aide d’exercices appropriés, variés et de difficulté croissante, et ceci quelles quesoientvosconnaissancespréalables.Ilcomporte:

4chapitresdestinésàceuxd’entrevousquineconnaissentpasleC:typesdebase,opérateursetexpressions;instructionsdecontrôle;fonctions;tableaux,pointeursetchaînesdestyleC;

unchapitredestinéàassurerlatransitiondeCàC++destinésàceuxquiconnaissentdéjàlelangageC;

seizechapitresdestinésàtous:lesnotionsdeclasse,constructeuretdestructeur;lespropriétés des fonctionsmembre ; la construction, la destruction et l’initialisationdesobjets;lesfonctionsamies;lasurdéfinitiond’opérateurs;lesconversionsdetypedéfiniesparl’utilisateur;latechniquedel’héritage;lesfonctionsvirtuelles;

6

Page 8: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

lesflotsd’entréeetdesortie,lespatronsdefonctionsetlespatronsdeclasses;lagestiondesexceptions.Lechapitre20proposedesexercicesdesynthèse.

Chaque chapitre débute par un rappel détaillé des connaissances nécessaires pouraborder lesexercicescorrespondants (naturellement,unexerciced’unchapitredonnépeutfaireintervenirdespointsrésumésdansleschapitresprécédents).

Lecourscompletcorrespondantàcesrésuméssetrouvedansl’ouvrageApprendre leC++,dumêmeauteur.

Auseindechaquechapitre, lesexercicesproposésvontd’uneapplicationimmédiatedu cours à des réalisations de classes relativement complètes. Au fil de votreprogression dans l’ouvrage, vous réaliserez des classes de plus en plus réalistes etopérationnelles,etayantunintérêtgénéral;citons,parexemple:

lesensembles;

lesvecteursdynamiques;

lestableauxdynamiquesàplusieursdimensions;

leslisteschaînées;

lestableauxdebits;

les(vraies)chaînesdecaractères;

lespiles;

lescomplexes.

Naturellement,touslesexercicessontcorrigés.Pourlaplupart,lasolutionproposéenese limite pas à une simple liste d’un programme (laquelle ne représente finalementqu’une rédactionpossibleparmid’autres).Vousy trouverezune analysedétailléeduproblèmeet,sibesoin,lesjustificationsdecertainschoix.Descommentairesviennent,lecaséchéant,éclairerlespartiesquelquepeudélicates.Fréquemment,voustrouverezdessuggestionsdeprolongementoudegénéralisationduproblèmeabordé.

Outre la maîtrise du langage C++ proprement dit, les exercices proposés vouspermettront de vous forger uneméthodologie de conception de vos propres classes.Notamment,voussaurez:

décider du bien-fondé de la surdéfinition de l’opérateur d’affectation ou duconstructeurparrecopie;

exploiter, lorsque vous jugerez que cela est opportun, les possibilités de«conversionsimplicites»quelecompilateurpeutmettreenplace;

7

Page 9: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

tirerpartidel’héritage(simpleoumultiple)etdéterminerquelsavantagesprésentela création d’une bibliothèque de classes, notamment par le biais du typagedynamiquedesobjetsquidécouledel’emploidesfonctionsvirtuelles;

mettreenœuvrelespossibilitésdefonctionsgénériques(patronsdefonctions)etdeclassesgénériques(patronsdeclasses).

Quelques exercices proposés dans les précédentes éditions de l’ouvrage trouventmaintenant une solution évidente en faisant appel aux composants standard introduitspar la norme. Nous les avons cependant conservés, dans lamesure où la recherched’une solution ne faisant pas appel aux composants standard conserve un intérêtdidactiquemanifeste.De surcroît,nousavons introduitunnouveauchapitre (21),quimontrecommentrésoudrelesexerciceslorsqu’onaccepte,cettefois,derecouriràcescomposantsstandard.

8

Page 10: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Tabledesmatières

1Généralités,typesdebase,opérateursetexpressionsExercice1Exercice2Exercice3Exercice4Exercice5Exercice6Exercice7

2LesinstructionsdecontrôleExercice8Exercice9Exercice10Exercice11Exercice12Exercice13Exercice14Exercice15Exercice16Exercice17Exercice18Exercice19Exercice20Exercice21Exercice22Exercice23

3LesfonctionsExercice24Exercice25Exercice26Exercice27Exercice28

9

Page 11: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice29Exercice30Exercice31Exercice32Exercice33

4Lestableaux,lespointeursetleschaînesdestyleCExercice34Exercice35Exercice36Exercice37Exercice38Exercice39Exercice40Exercice41Exercice42Exercice43Exercice44Exercice45Exercice46

5LesstructuresExercice47Exercice48Exercice49Exercice50Exercice51Exercice52

6DeCàC++Exercice53Exercice54Exercice55Exercice56Exercice57Exercice58Exercice59Exercice60Exercice61

7Notionsdeclasse,constructeuretdestructeur

10

Page 12: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice62Exercice63Exercice64Exercice65Exercice66Exercice67Exercice68

8PropriétésdesfonctionsmembreExercice69Exercice70Exercice71Exercice72Exercice73

9Construction,destructionetinitialisationdesobjetsExercice74Exercice75Exercice76Exercice77Exercice78Exercice79Exercice80

10LesfonctionsamiesExercice81Exercice82Exercice83

11Surdéfinitiond’opérateursExercice84Exercice85Exercice86Exercice87Exercice88Exercice89Exercice90Exercice91Exercice92Exercice93Exercice94

11

Page 13: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

12Lesconversionsdetypedéfiniesparl’utilisateurExercice95Exercice96Exercice97Exercice98Exercice99Exercice100Exercice101

13Latechniquedel’héritageExercice102Exercice103Exercice104Exercice105Exercice106Exercice107Exercice108

14L’héritagemultipleExercice109Exercice110Exercice111Exercice112Exercice113Exercice114

15LesfonctionsvirtuellesExercice115Exercice116Exercice117

16Lesflotsd’entréeetdesortieExercice118Exercice119Exercice120Exercice121Exercice122

17LespatronsdefonctionsExercice123Exercice124

12

Page 14: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice125Exercice126

18LespatronsdeclassesExercice127Exercice128Exercice129Exercice130Exercice131Exercice132Exercice133Exercice134

19GestiondesexceptionsExercice135Exercice136Exercice137Exercice138Exercice139Exercice140Exercice141

20ExercicesdesynthèseExercice142Exercice143Exercice144Exercice145Exercice146Exercice147

21LescomposantsstandardExercice148(67revisité)Exercice149(68revisité)Exercice150(77revisité)Exercice151(78revisité)Exercice152(79revisité)Exercice153(90revisité)Exercice154(91revisité)Exercice155(93revisité)Exercice156(142revisité)Exercice157(143revisité)

13

Page 15: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice158(94revisité)

14

Page 16: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre1Généralités,typesdebase,opérateurset

expressions

15

Page 17: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

GénéralitésLecanevasminimalàutiliserpourréaliserunprogrammeC++seprésenteainsi:

#include<iostream>

usingnamespacestd;

main()//en-tête

{.....//corpsduprogramme

}

Toutevariableutiliséedansunprogrammedoitavoirfaitl’objetd’unedéclarationenprécisant le type et, éventuellement, la valeur initiale. Voici des exemples dedéclarations:

inti;//iestunevariabledetypeintnomméei

floatx=5.25;//xestunevariabledetypefloatnomméex

//initialiséeaveclavaleur5.25

constintNFOIS=5;//NFOISestunevariabledetypeintdontla

//valeur,fixéeà5,nepeutplusêtremodifiée

L’affichaged’informations à l’écran est réalisé en envoyant des valeurs sur le « flotcout»,commedans:

cout<<n<<2*p;//affichelesvaleursdenetde2*psurl’écran

La lecture d’informations au clavier est réalisée en extrayant des valeurs du « flotcin»,commedans:

cin>>x>>y;//litdeuxvaleursauclavieretlesaffecteàxetày

TypesdebaseLes types de base sont ceux à partir desquels seront construits tous les autres, ditsdérivés(ils’agiradestypesstructuréscommelestableaux,lesstructures,lesunionsetlesclasses,oud’autrestypessimplescommelespointeursoulesénumérations).

Ilexistetroistypesentiers:shortint(oushort),intetlongint(oulong).Leslimitationscorrespondantes dépendent de l’implémentation.On peut également définir des typesentiersnonsignés:unsignedshortint(ouunsignedshort),unsignedintetunsignedlongint(ouunsigned long). Ces derniers sont essentiellement destinés à lamanipulation demotifsbinaires.

Lesconstantesentièrespeuventêtreécritesennotationhexadécimale(comme0xF54B)ouoctale(comme014).Onpeutajouterle«suffixe»upourunentiernonsignéetle

16

Page 18: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

suffixelpourunentierdetypelong.

Il existe trois types flottants : float,doubleetlongdouble.Laprécisionet le«domainereprésentable»dépendentdel’implémentation.

Letype«caractère»permetdemanipulerdescaractèrescodéssurunoctet.Lecodeutilisédépenddel’implémentation.Ilexistetroistypescaractère:signedchar,unsignedcharetchar(lanormeneprécisepass’ilcorrespondàsignedcharouunsignedchar).

Les constantes de type caractère, lorsqu’elles correspondent à des « caractèresimprimables»,senotentenplaçantlecaractèrecorrespondantentreapostrophes.

Certains caractères disposent d’une représentation conventionnelle utilisant lecaractère«\»notamment’\n’quidésigneunsautdeligne.Demême,’\’’représentelecaractère ’ et ’\"’ désigne le caractère ". On peut également utiliser la notationhexadécimale(commedans’\x41’)ouoctale(commedans’\07’).

Letypeboolpermetdemanipulerdes«booléens».Ildisposededeuxconstantesnotéestrueetfalse.

LesopérateursdeC++Voici un tableau présentant l’ensemble des opérateurs de C++ (certains ne serontexploitésquedansdeschapitresultérieurs):

Catégorie Opérateurs Associativité

Résolutiondeportée ::(portéeglobale-unaire)::(portéedeclasse-binaire)

<---->

Référence ()[]->. -->

Unaire

+-++--!~*&sizeofcastdynamic_caststatic_castreinterpret_castconst_castnewnew[]deletedelete[]

<---

Sélection ->*.* <--Arithmétique */% --->Arithmétique +- --->Décalage <<>> --->Relationnels <<=>>= --->Relationnels ==!= --->Manipulationdebits & --->

17

Page 19: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Manipulationdebits ^ --->Manipulationdebits | --->Logique && --->Logique || --->Conditionnel(ternaire) ?: --->

Affectation =+=-=*=/=%=&=^=|=<<=>>= <---

Séquentiel , --->

LesopérateursarithmétiquesetlesopérateursrelationnelsLesopérateursarithmétiquesbinaires(+,-,*et/)etlesopérateursrelationnelsnesontdéfinisquepourdesopérandesd’unmêmetypeparmi:int,longint(etleursvariantesnonsignées),float,doubleetlongdouble.Maisonpeutconstituerdesexpressionsmixtes(opérandesdetypesdifférents)oucontenantdesopérandesd’autrestypes(bool,charetshort),grâceàl’existencededeuxsortesdeconversionsimplicites:

lesconversionsd’ajustementdetype,selonl’unedeshiérarchies:int->long->float->double->longdouble

unsingedint->unsignedlong->float->double->longdouble

lespromotionsnumériques,àsavoirdesconversionssystématiquesdechar(avecousansattributdesigne),booletshortenint.

LesopérateurslogiquesLes opérateurs logiques && (et), || (ou) et (non) acceptent n’importe quel opérandenumérique(entierouflottant)oupointeur,enconsidérantquetoutopérandedevaleurnonnullecorrespondà«faux»:

18

Page 20: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Lesdeuxopérateurs&&et||sont«àcourt-circuit»:lesecondopéranden’estévaluéquesilaconnaissancedesavaleurestindispensable.

Opérateursd’affectationL’opérandedegauched’unopérateurd’affectationdoitêtreunelvalue,c’est-à-direlaréférenceàquelquechosedemodifiable.

Lesopérateursd’affectation(=,-=,+=...),appliquésàdesvaleursdetypenumérique,provoquentlaconversiondeleuropérandededroitedansletypedeleuropérandedegauche.Cetteconversion«forcée»peutêtre«dégradante».

Opérateursd’incrémentationetdedécrémentationLesopérateursunairesd’incrémentation (++) etdedécrémentation (--) agissent sur lavaleurdeleuruniqueopérande(quidoitêtreunelvalue)etfournissentlavaleuraprèsmodification lorsqu’ils sontplacésàgauche (commedans ++n) ou avantmodificationlorsqu’ilssontplacésàdroite(commedansn--).

OpérateurdecastIlestpossibledeforcerlaconversiond’uneexpressionquelconquedansuntypedesonchoix, grâce à l’opérateur dit de « cast ». Par exemple, si n et p sont des variablesentières,l’expression:

19

Page 21: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

(double)n/p//ou:static_cast<double>(n/p)

auracommevaleurcelledel’expressionentièren/pconvertieendouble.

OpérateurconditionnelCetopérateurternairefournitcommerésultatlavaleurdesondeuxièmeopérandesilaconditionmentionnée en premier opérande est non nulle (vraie pour une expressionbooléenne),etlavaleurdesontroisièmeopérandedanslecascontraire.Parexemple,aveccetteaffectation:

max=a>b?a:b;

onobtiendradanslavariablemaxlavaleurdeasilaconditiona>bestvraie,lavaleurdebdanslecascontraire.Avec:

valeur=2*n-1?a:b;

onobtiendradanslavariablevaleurlavaleurdeasil’expression2*n-1estnonnulle,lavaleurdebdanslecascontraire.

20

Page 22: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice1

ÉnoncéÉliminerlesparenthèsessuperfluesdanslesexpressionssuivantes:

a=(x+5)/*expression1*/

a=(x=y)+2/*expression2*/

a=(x==y)/*expression3*/

(a<b)&&(c<d)/*expression4*/

(i++)*(n+p)/*expression5*/

a=x+5/*expression1*/

L’opérateur+estprioritairesurl’opérateurd’affectation=.a=(x=y)+2/*expression2*/

Ici,l’opérateur+étantprioritairesur=,lesparenthèsessontindispensables.a=x==y/*expression3*/

L’opérateur==estprioritairesur=.a<b&&c<d/*expression4*/

L’opérateur&&estprioritairesurl’opérateur<.i++*(n+p)/*expression5*/

L’opérateur++estprioritairesur*;enrevanche,*estprioritairesur+,desortequ’onnepeutéliminerlesdernièresparenthèses.

21

Page 23: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice2

ÉnoncéSoientlesdéclarations:

charc='\x01';

shortintp=10;

Quelssontletypeetlavaleurdechacunedesexpressionssuivantes:p+3/*1*/

c+1/*2*/

p+c/*3*/

3*p+5*c/*4*/

1.pestd’abordsoumisàlaconversion«systématique»short->int,avantd’êtreajoutéàlavaleur3(int).Lerésultat13estdetypeint.

2.cestd’abordsoumisàlaconversion«systématique»char->int(cequiaboutitàlavaleur1),avantd’êtreajoutéàlavaleur1(int).Lerésultat2estdetypeint.

3.pestd’abordsoumisàlaconversionsystématiqueshort->int,tandisquecestsoumisà laconversion systématique char->int ; les résultats sont alors additionnés pouraboutiràlavaleur11detypeint.

4.petcsontd’abordsoumisauxmêmesconversionssystématiquesqueci-dessus;lerésultat35estdetypeint.

22

Page 24: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice3

ÉnoncéSoientlesdéclarations:

charc='\x05';

intn=5;

longp=1000;

floatx=1.25;

doublez=5.5;

Quelssontletypeetlavaleurdechacunedesexpressionssuivantes:n+c+p/*1*/

2*x+c/*2*/

(char)n+c/*3*/

(float)z+n/2/*4*/

1.cesttoutd’abordconvertienint,avantd’êtreajoutéàn.Lerésultat(10),detypeint,estalorsconvertienlong,avantd’êtreajoutéàp.Onobtientfinalementlavaleur1010,detypelong.

2.Onévalued’abordlavaleurde2*x,enconvertissant2(int)enfloat,cequifournitlavaleur 2.5 (de type float). Par ailleurs, c est converti en int (conversionsystématique).Onévalueensuitelavaleurde2*x,enconvertissant2(int)enfloat,cequifournitlavaleur2.5(detypefloat).Poureffectuerl’addition,onconvertitalorslavaleurentière5 (c)enfloat, avantde l’ajouterau résultatprécédent.Onobtientfinalementlavaleur7.75,detypefloat.

3.nesttoutd’abordconvertienchar(àcausedel’opérateurde«cast»),tandisquecestconverti(conversionsystématique)enint.Puis,pourprocéderàl’addition,ilestnécessaire de reconvertir la valeur de (char) n en int. Finalement, on obtient lavaleur10,detypeint.

4.z estd’abordconverti en float, cequi fournit lavaleur 5.5 (approximative, car, enfait, on obtient une valeur un peu moins précise que ne le serait 5.5 exprimé endouble). Par ailleurs, on procède à la division entière de n par 2, ce qui fournit lavaleurentière2.Cettedernièreestensuiteconvertieenfloat,avantd’êtreajoutéeà5.5,cequifournitlerésultat7.5,detypefloat.

23

Page 25: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice4

ÉnoncéSoientlesdéclarationssuivantes:

intn=5,p=9;

intq;

floatx;

Quelle est lavaleur affectée auxdifférentesvariables concernéespar chacunedesinstructionssuivantes?

q=n<p;/*1*/

q=n==p;/*2*/

q=p%n+p>n;/*3*/

x=p/n;/*4*/

x=(float)p/n;/*5*/

x=(p+0.5)/n;/*6*/

x=(int)(p+0.5)/n;/*7*/

q=n*(p>n?n:p);/*8*/

q=n*(p<n?n:p);/*9*/

1.1

2.0

3.5(p%nvaut4,tandisquep>nvaut1).

4.1(p/nestd’abordévaluéenint,cequifournit1;puislerésultatestconvertienfloat,avantd’êtreaffectéàx).

5.1.8(pestconvertienfloat,avantd’êtrediviséparlerésultatdelaconversiondenenfloat).

6.1.9 (p est converti en float, avant d’être ajouté à 0.5 ; le résultat est divisé par lerésultatdelaconversiondenenfloat).

7.1(pestconvertienfloat,avantd’êtreajoutéà0.5;lerésultat(5.5)estalorsconvertienintavantd’êtrediviséparn).

8.25

9.45

24

Page 26: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice5

ÉnoncéQuelsrésultatsfournitleprogrammesuivant:

#include<iostream>

usingnamespacestd;

main()

{

inti,j,n;

i=0;n=i++;

cout<<"A:i="<<i<<"n="<<n<<"\n";

i=10;n=++i;

cout<<"B:i="<<i<<"n="<<n<<"\n";

i=20;j=5;n=i++*++j;

cout<<"C:i="<<i<<"j="<<j<<"n="<<n<<"\n";

i=15;n=i+=3;

cout<<"D:i="<<i<<"n="<<n<<"\n";

i=3;j=5;n=i*=--j;

cout<<"E:i="<<i<<"j="<<j<<"n="<<n<<"\n";

}

A:i=1n=0

B:i=11n=11

C:i=21j=6n=120

D:i=18n=18

E:i=12j=4n=12

25

Page 27: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice6

ÉnoncéQuelsrésultatsfourniraceprogramme:

#include<iostream>

usingnamespacestd;

main()

{

intn=10,p=5,q=10,r;

r=n==(p=q);

cout<<"A:n="<<n<<"p="<<p<<"q="<<q

<<"r="<<r<<"\n";

n=p=q=5;

n+=p+=q;

cout<<"B:n="<<n<<"p="<<p<<"q="<<q<<"\n";

q=n<p?n++:p++;

cout<<"C:n="<<n<<"p="<<p<<"q="<<q<<"\n";

q=n>p?n++:p++;

cout<<"D:n="<<n<<"p="<<p<<"q="<<q<<"\n";

}

A:n=10p=10q=10r=1

B:n=15p=10q=5

C:n=15p=11q=10

D:n=16p=11q=15

26

Page 28: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice7

ÉnoncéQuelsrésultatsfourniraceprogramme:

#include<iostream>

usingnamespacestd;

main()

{intn,p,q;

n=5;p=2;/*cas1*/

q=n++>p||p++!=3;

cout<<"A:n="<<n<<"p="<<p<<"q="<<q<<"\n";

n=5;p=2;/*cas2*/

q=n++<p||p++!=3;

cout<<"B:n="<<n<<"p="<<p<<"q="<<q<<"\n";

n=5;p=2;/*cas3*/

q=++n==3&&++p==3;

cout<<"C:n="<<n<<"p="<<p<<"q="<<q<<"\n";

n=5;p=2;/*cas4*/

q=++n==6&&++p==3;

cout<<"D:n="<<n<<"p="<<p<<"q="<<q<<"\n";

}

Ilnefautpasoublierquelesopérateurs&&et||n’évaluent leursecondopérandequelorsquecelaestnécessaire.Ainsi,ici,iln’estpasévaluédanslescas1et3.Voicilesrésultatsfournisparceprogramme:

A:n=6p=2q=1

B:n=6p=3q=1

C:n=6p=2q=0

D:n=6p=3q=1

27

Page 29: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre2Lesinstructionsdecontrôle

28

Page 30: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

Letermeinstructiondésigneraindifféremment:uneinstructionsimple(terminéeparunpoint-virgule),uneinstructionstructurée(choix,boucle)ouunbloc(instructionsentre{et}).

InstructionifEllepossèdedeuxformes:

if(expression)instruction_1

elseinstruction_2

if(expression)instruction_1

Lorsquedesinstructionsifsontimbriquées,unelseserapporte toujoursaudernierifauquelunelsen’apasencoreétéattribué.

Instructionswitchswitch(expression){bloc_d_instructions]

Cette instruction évalue la valeur de l’expression entièrementionnée, puis recherchedansleblocquisuits’ilexisteuneétiquettedelaformecasex(xétantuneexpressionconstante,c’est-à-direcalculablepar lecompilateur)correspondantàcettevaleur.Sic’estlecas,ilyabranchementàl’instructionfigurantàlasuitedecetteétiquette.Danslecascontraire,onpasseàl’instructionsuivantlebloc.L’expressionpeutêtrede typechar,auquelcaselleseraconvertieenentier.

Uneinstructionswitchpeutconteniruneouplusieursinstructionsbreakquiprovoquentlasortie du bloc. Il est possible d’utiliser lemot default comme étiquette à laquelle leprogramme se branche lorsque aucune valeur satisfaisante n’a été rencontréeauparavant.

Instructionsdo...whileetwhiledoinstructionwhile(expression);

while(expression)instruction

L’expressiongouvernantlabouclepeutêtred’untypequelconque;elleseraconvertieenboolselonlarègle:nonnuldevientvrai,nuldevientfaux.

29

Page 31: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Instructionforfor([expression_déclaration_1];[expression_2];[expression_3])

instruction

Lescrochetssignifientqueleurcontenuestfacultatif.

expression_déclaration_1estsoituneexpression,soitunedéclarationd’uneouplusieursvariablesd’unmêmetype,initialiséesounon;

expression_2 est une expression quelconque (qui sera éventuellement convertie enbool);

expression_3estuneexpressionquelconque.

Cetteinstructionestéquivalenteà:{expression_déclaration_1;

while(expression_2){instruction;

expression_3;

}

}

Instructionsbreak,continueetgotoUneboucle (do...while,while ou for) peut contenir une ou plusieurs instructions breakdont le rôleestd’interrompre ledéroulementde laboucle, enpassantà l’instructionquisuitcetteboucle.Encasdebouclesimbriquées,breakfaitsortirdelabouclelaplusinterne.Sibreakapparaîtdansunswitchimbriquédansuneboucle,ellenefaitsortirqueduswitch.L’instruction continue s’emploie uniquement dans une boucle. Elle permet de passerprématurémentautourdebouclesuivant.L’instructiongotopermetlebranchementenunemplacementquelconqueduprogramme,repéréparuneétiquette,commedanscetexempleoù,lorsqu’unecertaineconditionestvraie,onsebrancheàl’étiquetteerreur:

for(...){.....

if(...)gotoerreur;

.....

}

erreur:.....

30

Page 32: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice8

ÉnoncéQuelleserreursontétécommisesdanschacundesgroupesd’instructionssuivants:

1.if(a<b)cout<<"ascendant"

elsecout<<"nonascendant";

2.intn;

...

switch(2*n+1)

{case1:cout<<"petit";

casen:cout<<"moyen";

}

3.constintLIMITE=100

intn;

...

switch(n)

{caseLIMITE-1:cout<<"unpeumoins";

caseLIMITE:cout<<"juste";

caseLIMITE+1:cout<<"unpeuplus";

}

1.Ilmanqueunpoint-virguleàlafindelapremièreligne:if(a<b)cout<<"ascendant";

elsecout<<"nonascendant";

2. Les valeurs suivant le mot case doivent obligatoirement être des « expressionsconstantes»,c’est-à-diredesexpressionscalculablesparlecompilateurlui-même.Cen’estpaslecasden.

3. Aucune erreur, les expressions telles que LIMITE-1 étant bien des expressionsconstantes(cequin’étaitpaslecasenlangageC).

31

Page 33: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice9

ÉnoncéSoitleprogrammesuivant:

#include<iostream>

main()

{intn;

cin>>n;

switch(n)

{case0:cout<<"Nul\n";

case1:

case2:cout<<"Petit\n";

break;

case3:

case4:

case5:cout<<"Moyen\n";

default:cout<<"Grand\n";

}

}

Quelsrésultatsaffiche-t-illorsqu’onluifournitendonnée:a.0b.1c.4d.10e.-5

a.Nul

Petit

b.Petit

c.Moyen

Grand

d.Grand

e.Grand

32

Page 34: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice10

ÉnoncéQuelleserreursontétécommisesdanschacunedesinstructionssuivantes:

a.docin>>cwhile(c!='\n');

b.dowhile(cin>>c,c!='\n');

c.do{}while(1);

a.Ilmanqueunpoint-virgule:docin>>c;while(c!='\n');

b. Il manque une instruction (éventuellement « vide ») après lemot do. On pourraitécrire,parexemple:do{}while((cin>>c,c!='\n');

ou:do;while(cin>>c,c!='\n');

c.Iln’yaurapasd’erreurdecompilation(lavaleurentière1estconvertieenbooléen,cequifournitlavaleurvrai);toutefois,ils’agitd’une«boucleinfinie».

33

Page 35: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice11

ÉnoncéÉcrirepluslisiblement:

do{}while(cout<<"donnezunnombre>0",cin>>n,n<=0);

Plusieurs possibilités existent, puisqu’il « suffit » de reporter, dans le corps de laboucle,desinstructionsfigurant«artificiellement»sousformed’expressionsdanslaconditiondepoursuite:

do

cout<<donnezunnombre>0";

while(cin>>n,n<=0);

ou,mieux:do

{cout<<"donnezunnombre>0";

cin>>n;

}

while(n<=0);

34

Page 36: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice12

ÉnoncéSoitlepetitprogrammesuivant:

#include<iostream>

usingnamespacestd;

main()

{inti,n,som;

som=0;

for(i=0;i<4;i++)

{cout<<"donnezunentier";

cin>>n;

som+=n;

}

cout<<"Somme:"<<som;

}

Écrireunprogrammeréalisantexactementlamêmechose,enemployant,àlaplacedel’instructionfor:a.uneinstructionwhile,b.uneinstructiondo...while.

a.#include<iostream>

usingnamespacestd;

main()

{inti,n,som;

som=0;

i=0;/*nepasoubliercette"initialisation"*/

while(i<4)

{cout<<"donnezunentier";

cin>>n;

som+=n;

i++;/*nicette"incrémentation"*/

}

cout<<"Somme:"<<som;

}

b.#include<iostream>

usingnamespacestd;

main()

{inti,n,som;

som=0;

i=0;/*nepasoubliercette"initialisation"*/

do

{cout<<"donnezunentier";

cin>>n;

som+=n;

i++;/*nicette"incrémentation"*/

}

35

Page 37: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

while(i<4);/*attention,ici,toujours<4*/

cout<<"Somme:"<<som;

}

36

Page 38: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice13

ÉnoncéQuelsrésultatsfournitleprogrammesuivant:

#include<iostream>

usingnamespacestd;

main()

{intn=0;

do

{if(n%2==0){cout<<n<<"estpair\n";

n+=3;

continue;

}

if(n%3==0){cout<<n<<"estmultiplede3\n";

n+=5;

}

if(n%5==0){cout<<n<<"estmultiplede5\n";

break;

}

n+=1;

}

while(1);

}

0estpair

3estmultiplede3

9estmultiplede3

15estmultiplede3

20estmultiplede5

37

Page 39: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice14

ÉnoncéQuelsrésultatsfournitleprogrammesuivant:

#include<iostream>

usingnamespacestd;

main()

{intn,p;

n=0;

while(n<=5)n++;

cout<<"A:n="<<n<<"\n";

n=p=0;

while(n<=8)n+=p++;

cout<<"B:n="<<n<<"\n";

n=p=0;

while(n<=8)n+=++p;

cout<<"C:n="<<n<<"\n";

n=p=0;

while(p<=5)n+=p++;

cout<<"D:n="<<n<<"\n";

n=p=0;

while(p<=5)n+=++p;

cout<<"E:n="<<n<<"\n";

}

A:n=6

B:n=10

C:n=10

D:n=15

E:n=21

38

Page 40: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice15

ÉnoncéQuelsrésultatsfournitleprogrammesuivant:

#include<iostream>

usingnamespacestd;

main()

{intn,p;

n=p=0;

while(n<5)n+=2;p++;

cout<<"A:n="<<n<<"p="<<p<<"\n";

n=p=0;

while(n<5){n+=2;p++;}

cout<<"B:n="<<n<<"p="<<p<<"\n";

}

A:n=6,p=1

B:n=6,p=3

39

Page 41: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice16

ÉnoncéQuelsrésultatsfournitleprogrammesuivant:

#include<iostream>

usingnamespacestd;

main()

{inti,n;

for(i=0,n=0;i<5;i++)n++;

cout<<"A:i="<<i<<"n="<<n<<"\n";

for(i=0,n=0;i<5;i++,n++){}

cout<<"B:i="<<i<<"n="<<n<<"\n";

for(i=0,n=50;n>10;i++,n-=i){}

cout<<"C:i="<<i<<"n="<<n<<"\n";

for(i=0,n=0;i<3;i++,n+=i,

cout<<"D:i="<<i<<"n="<<n<<"\n");

cout<<"E:i="<<i<<"n="<<n<<"\n";

}

A:i=5,n=5

B:i=5,n=5

C:i=9,n=5

D:i=1,n=1

D:i=2,n=3

D:i=3,n=6

E:i=3,n=6

40

Page 42: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice17

ÉnoncéÉcrireunprogrammequicalculelesracinescarréesdenombresfournisendonnée.Ils’arrêteralorsqu’onluifourniralavaleur0.Ilrefuseralesvaleursnégatives.Sonexécutionseprésenteraainsi:

donnezunnombrepositif:2

saracinecarréeest:1.414214e+00

donnezunnombrepositif:-1

svppositif

donnezunnombrepositif:5

saracinecarréeest:2.236068e+00

donnezunnombrepositif:0

Rappelonsque la fonctionsqrt fournit la racinecarrée(double)de lavaleur (double)qu’onluidonneenargument.

Ilexistebeaucoupderédactionspossibles;envoici3:

1.#include<iostream>

#include<cmath>//pourladéclarationdesqrt

usingnamespacestd;

main()

{doublex;

do

{cout<<"donnezunnombrepositif:";

cin>>x;

if(x<0)cout<<"svppositif\n";

if(x<=0)continue;

cout<<"saracinecarréeest:"<<sqrt(x)<<"\n";

}

while(x);

}

2.#include<iostream>

#include<cmath>

usingnamespacestd;

main()

{doublex;

do

{cout<<"donnezunnombrepositif:";

cin>>x;

if(x<0){cout<<"svppositif\n";

continue;

}

41

Page 43: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

if(x>0)cout<<"saracinecarréeest:"<<sqrt(x)<<"\n";

}

while(x);

}

3.#include<iostream>

#include<cmath>

usingnamespacestd;

main()

{doublex;

do

{cout<<"donnezunnombrepositif:";

cin>>x;

if(x<0){cout<<"svppositif\n";

continue;

}

if(x>0)cout<<"saracinecarréeest:"<<sqrt(x)<<"\n";

if(x==0)break;

}

while(1);

}

42

Page 44: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice18

ÉnoncéCalculerlasommedesnpremierstermesdela«sérieharmonique»,c’est-à-direlasomme:

1+1/2+1/3+1/4+.....+1/n

Lavaleurdenseralueendonnée.

#include<iostream>

usingnamespacestd;

main()

{

intnt;/*nombredetermesdelasérieharmonique*/

floatsom;/*pourlasommedelasérie*/

inti;

do

{cout<<"combiendetermes:";

cin>>nt;

}

while(nt<1);

for(i=1,som=0;i<=nt;i++)som+=(float)1/i;

cout<<"Sommedes"<<nt<<"premierstermes="<<som;

}

1.Rappelonsquedans:som+=(float)1/i

l’expressiondedroiteestévaluéeenconvertissantd’abord1etienfloat.Ilfautéviterd’écrire:som+=1/i

auquel cas, les valeurs de 1/i seraient toujours nulles (sauf pour i=1) puisquel’opérateur/,lorsqu’ilportesurdesentiers,correspondàladivisionentière.Demême,enécrivant:som+=(float)(1/i)

le résultatne seraitpasplus satisfaisantpuisque la conversionen flottantn’auraitlieuqu’aprèsladivision(enentier).Enrevanche,onpourraitécrire:som+=1.0/i;

2. Si l’on cherchait à exécuter ce programme pour des valeurs élevées de n (enprévoyantalorsunevariabledetypefloatoudouble),onconstateraitquelavaleurdela somme semble « converger » vers une limite (bien qu’en théorie la série

43

Page 45: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

harmonique«diverge»).Celaprovienttoutsimplementdeceque,dèsquelavaleurde 1/i est « petite » devant som, le résultat de l’addition de 1/i et de som estexactementsom.Onpourrait toutefoisaméliorer le résultateneffectuant lasomme«àl’envers»(eneffet,danscecas,lerapportentrelavaleuràajouteretlasommecouranteseraitplusfaiblequeprécédemment).

44

Page 46: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice19

ÉnoncéAfficherun triangle isocèle forméd’étoiles.Lahauteurdu triangle (c’est-à-dire lenombre de lignes) sera fourni en donnée, comme dans l’exemple ci-dessous. Ons’arrangera pour que la dernière ligne du triangle s’affiche sur le bord gauche del’écran.

combiendelignes?10

*

***

*****

*******

*********

***********

*************

***************

*****************

*******************

#include<iostream>

usingnamespacestd;

main()

{constcharcar='*';/*caractèrederemplissage*/

intnlignes;/*nombretotaldelignes*/

intnl;/*compteurdeligne*/

intnesp;/*nombred'espacesprécédantuneétoile*/

intj;

cout<<"combiendelignes?";

cin>>nlignes;

for(nl=0;nl<nlignes;nl++)

{nesp=nlignes-nl-1;

for(j=0;j<nesp;j++)cout<<'';

for(j=0;j<2*nl+1;j++)cout<<car;

cout<<'\n';

}

}

45

Page 47: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice20

ÉnoncéAffichertouteslesmanièrespossiblesd’obteniruneuroavecdespiècesde2cents,5centset10cents.Direcombiendepossibilitésontétéainsitrouvées.Lesrésultatsserontaffichéscommesuit:

1euro=50X2c

1euro=45X2c2X5c

1euro=40X2c4X5c

1euro=35X2c6X5c

1euro=30X2c8X5c

1euro=25X2c10X5c

1euro=20X2c12X5c

1euro=15X2c14X5c

1euro=10X2c16X5c

1euro=5X2c18X5c

1euro=20X5c

1euro=45X2c1X10c

1euro=40X2c2X5c1X10c

1euro=35X2c4X5c1X10c

1euro=10X2c2X5c7X10c

1euro=5X2c4X5c7X10c

1euro=6X5c7X10c

1euro=10X2c8X10c

1euro=5X2c2X5c8X10c

1euro=4X5c8X10c

1euro=5X2c9X10c

1euro=2X5c9X10c

1euro=10X10c

Entout,ilya66façonsdefaire1euro

Rappelonsquel’insertiondansleflotcoutd’uneexpressiondelaformesetw(n,oùnestuneexpressionentière,demandederéaliserl’affichagesuivant(etuniquementcedernier) sur n caractères au minimum. L’emploi de setw nécessite l’inclusion dufichieriomanip.

#include<iostream>

#include<iomanip>//poursetw

usingnamespacestd;

main()

{

intnbf;//compteurdunombredefaçonsdefaire1euro

intn10;//nombredepiècesde10centimes

intn5;//nombredepiècesde5centimes

intn2;//nombredepiècesde2centimes

nbf=0;

for(n10=0;n10<=10;n10++)

for(n5=0;n5<=20;n5++)

for(n2=0;n2<=50;n2++)

if(2*n2+5*n5+10*n10==100)

46

Page 48: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

{nbf++;

cout<<"1euro=";

if(n2)cout<<setw(2)<<n2<<"X2c";

if(n5)cout<<setw(2)<<n5<<"X5c";

if(n10)cout<<setw(2)<<n10<<"X10c";

cout<<"\n";

}

cout<<"\nEntout,ilya"<<nbf<<"façonsdefaire1euro\n";

}

47

Page 49: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice21

ÉnoncéÉcrireunprogrammequidéterminelanièmevaleurun(nétantfourniendonnée)dela«suitedeFibonacci»définiecommesuit:

u1=1

u2=1

un=un-1+un-2pourn>2

#include<iostream>

usingnamespacestd;

main()

{

intu1,u2,u3;/*pour"parcourir"lasuite*/

intn;/*rangdutermedemandé*/

inti;/*compteur*/

do

{cout<<"rangdutermedemandé(aumoins3)?";

cin>>n;

}

while(n<3);

u2=u1=1;/*lesdeuxpremierstermes*/

i=2;

while(i++<=n)/*attention,l'algorithmenefonctionne*/

{u3=u1+u2;/*quepourn>2*/

u1=u2;

u2=u3;

}

//autreformulationpossible:

//for(i=3;i<=n;i++,u1=u2,u2=u3)u3=u1+u2;

cout<<"Valeurdutermederang"<<n<<":"<<u3;

}

Notezque, commeà l’accoutuméeenC++,beaucoupde formulations sontpossibles.Nousenavonsd’ailleursplacéunesecondeencommentairedenotreprogramme.

48

Page 50: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice22

ÉnoncéÉcrire un programme qui trouve la plus grande et la plus petite valeur d’unesuccessiondenotes(nombresentiersentre0et20)fourniesendonnées,ainsiquelenombredefoisoùcemaximumetceminimumontétéattribués.Onsupposeraquelesnotes,ennombrenonconnuàl’avance,serontterminéesparunevaleurnégative.Ons’astreindraànepasutiliserde«tableau».L’exécutionduprogrammepourraseprésenterainsi:

donnezunenote(-1pourfinir):12

donnezunenote(-1pourfinir):8

donnezunenote(-1pourfinir):13

donnezunenote(-1pourfinir):7

donnezunenote(-1pourfinir):11

donnezunenote(-1pourfinir):12

donnezunenote(-1pourfinir):7

donnezunenote(-1pourfinir):9

donnezunenote(-1pourfinir):-1

notemaximale:13attribuée1fois

noteminimale:7attribuée2fois

#include<iostream>

usingnamespacestd;

main()

{intnote;//note"courante"

intmax;//notemaxi

intmin;//notemini

intnmax;//nombredefoisoùlanotemaxiaététrouvée

intnmin;//nombredefoisoùlanoteminiaététrouvée

max=-1;//initialisationmax(possiblecartoutesnotes>=0

min=21;//initialisationmin(possiblecartoutesnotes<21

while(cout<<"donnezunenote(-1pourfinir):",

cin>>note,note>=0)

{if(note==max)nmax++;

if(note>max){max=note;

nmax=1;

}

if(note==min)nmin++;

if(note<min){min=note;

nmin=1;

}

}

if(max>=0)

{cout<<"\nnotemaximale:"<<max<<"attribuée"

<<nmax<<"fois\n";

cout<<"noteminimale:"<<min<<"attribuée"

<<nmin<<"fois\n";

}

elsecout<<"vousn'avezfourniaucunenote";

49

Page 51: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

50

Page 52: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice23

ÉnoncéÉcrireunprogrammequiaffichela«tabledemultiplication»desnombresde1à10,souslaformesuivante:

I12345678910

-----------------------------------------------

1I12345678910

2I2468101214161820

3I36912151821242730

4I481216202428323640

5I5101520253035404550

6I6121824303642485460

7I7142128354249566370

8I8162432404856647280

9I9182736455463728190

10I102030405060708090100

Rappelonsquel’insertiondansleflotcoutd’uneexpressiondelaformesetw(n),oùnestuneexpressionentière,demandederéaliser l’affichagesuivantsurncaractèresauminimum.L’emploidesetwnécessitel’inclusiondufichieriomanip.

#include<iostream>

#include<iomanip>//poursetw

usingnamespacestd;

main()

{constintNMAX=10;//nombredevaleurs

inti,j;

/*affichageligneen-tête*/

cout<<"I";

for(j=1;j<=NMAX;j++)cout<<setw(4)<<j;

cout<<"\n";

printf("-------");

for(j=1;j<=NMAX;j++)cout<<"----";

cout<<"\n";

/*affichagedesdifférenteslignes*/

for(i=1;i<=NMAX;i++)

{cout<<setw(4)<<i<<"I";

for(j=1;j<=NMAX;j++)

cout<<setw(4)<<i*j;

cout<<"\n";

}

}

51

Page 53: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre3Lesfonctions

52

Page 54: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

GénéralitésUne fonction est unblocd’instructions éventuellementparamétrépar unouplusieursarguments et pouvant fournir un résultat nommé souvent « valeur de retour ». Ondistingue ladéfinitiond’unefonctiondesonutilisation,cettedernièrenécessitantunedéclaration.

Ladéfinitiond’unefonctionseprésentecommedanscetexemple:floatfexple(floatx,intb,intc)//en-têtedelafonction

{//corpsdelafonction

}

L’en-têtepréciselenomdelafonction(fexple)ainsiqueletypeetlenom(muet)desesdifférentsarguments(x,betc).Lecorpsestunblocd’instructionsquidéfinitlerôledelafonction.

Auseind’uneautrefonction(ycomprismain),onutilisecettefonctiondecettefaçon:floatfepxle(float,int,int);//déclarationdefexple("prototype")

.....

fexple(z,n,p);//appelfexpleaveclesargumentseffectifsz,netp

Ladéclaration d’une fonction peut être omise lorsqu’elle est connue du compilateur,c’est-à-direquesadéfinitionadéjàétérencontréedanslemêmefichiersource.

ModedetransmissiondesargumentsPardéfaut,lesargumentssonttransmisparvaleur.Danscecas,lesargumentseffectifspeuventseprésentersouslaformed’uneexpressionquelconque.

Enfaisantsuivredusymbole& letyped’unargumentdansl’en-têted’unefonction(etdans sadéclaration),on réaliseune transmissionpar référence.Cela signifieque leséventuelles modifications effectuées au sein de la fonction porteront sur l’argumenteffectifdel’appeletnonplussurunecopie.Onnoteraqu’alorsl’argumenteffectifdoitobligatoirement être une lvalue du même type que l’argument muet correspondant.Toutefois,si l’argumentmuetest,desurcroît,déclaréavecl’attributconst, la fonctionreçoit quandmêmeune copie de l’argument effectif correspondant, lequel peut alorsêtreuneconstanteouuneexpressiond’untypesusceptibled’êtreconvertidansletypeattendu.

Cespossibilitésdetransmissionparréférences’appliquentégalementàunevaleurde

53

Page 55: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

retour(danscecas,lanotiondeconstancen’aplusdesignification).

La notion de référence est théoriquement indépendante de celle de transmissiond’argument;enpratique,elleestrarementutiliséeendehorsdececontexte.

L’instructionreturnL’instruction return sert à la fois à fournir une valeur de retour et à mettre fin àl’exécutiondelafonction.Ellepeutmentionneruneexpression;ellepeutapparaîtreàplusieurs reprises dans une même fonction ; si aucune instruction return n’estmentionnée,leretourestmisenplaceàlafindelafonction.

Lorsqu’unefonctionnefournitaucunrésultat,sonen-têteetsadéclarationcomportentlemotvoidàlaplacedutypedelavaleurderetour,commedans:

voidfSansValRetour(...)

Lorsqu’unefonctionnereçoitaucunargument,l’en-têteetladéclarationcomportentunelistevide,commedans:

intfSansArguments()

LesargumentspardéfautDans la déclaration d’une fonction, il est possible de prévoir pour un ou plusieursarguments(obligatoirementlesderniersdelaliste)desvaleurspardéfaut;ellessontindiquéesparlesigne=,àlasuitedutypedel’argument,commedanscetexemple:

floatfct(char,int=10,float=0.0);

Cesvaleurspardéfautserontalorsutiliséeslorsqu’onappelleraladitefonctionavecunnombre d’arguments inférieur à celui prévu. Par exemple, avec la précédentedéclaration,l’appelfct('a')seraéquivalentàfct('a',10,0.0) ;demême,l’appelfct('x',12)seraéquivalentàfct('x',12,0.0).Enrevanche,l’appelfct()seraillégal.

ConversiondesargumentsLorsqu’unargumentesttransmisparvaleur,ilestéventuellementconvertidansletypementionnédansladéclaration(laconversionpeutêtredégradante).Cespossibilitésdeconversiondisparaissentencasdetransmissionparréférence:l’argumenteffectifdoitalorsêtreune lvalue du typeprévu ; cttedernièrenepeutposséder l’attribut const si

54

Page 56: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

celui-ci n’est pas prévu dans l’argument muet ; en revanche, si l’agument muetmentionne l’attributconst, l’argumenteffectifpourraêtrenonseulementuneconstante,maiségalementuneexpressiond’untypequelconquedontlavaleurseraalorsconvertiedansunevariabletemporairedontl’adresseserafournieàlafonction.

VariablesglobalesetlocalesUnevariabledéclaréeendehorsdetoutefonction(ycomprisdumain)estditeglobale.Laportéed’unevariableglobaleestlimitéeàlapartieduprogrammesourcesuivantsadéclaration.Lesvariableglobalesontuneclassed’allocationstatique,cequisignifiequeleursemplacementsenmémoirerestentfixespendantl’exécutionduprogramme.

Unevariabledéclaréedansunefonctionestditelocale.Laportéed’unevariablelocaleest limitée à la fonctiondans laquelle elle est définie.Lesvariables locales ont uneclassed’allocationautomatique,cequisignifiequeleursemplacementssontallouésàl’entréedanslafonction,etlibérésàlasortie.Ilestpossiblededéclarerdesvariableslocalesàunbloc.

On peut demander qu’une variable locale soit de classe d’allocation statique, en ladéclarantàl’aidedumot-cléstatic,commedans:

staticinti;

Les variables de classe statique sont, par défaut, initialisées à zéro. On peut lesinitialiser explicitement à l’aide d’expressions constantes d’un type compatible paraffectation avec celui de la variable. Celles de classe automatique ne sont pasinitialisées par défaut. Elles doivent être initialisées à l’aide d’une expressionquelconque,d’untypecompatibleparaffectationavecceluidelavariable.

SurdéfinitiondefonctionsEn C++, il est possible, au sein d’un même programme, que plusieurs fonctionspossèdent lemêmenom.Danscecas, lorsque lecompilateurrencontre l’appeld’unetellefonction,ileffectuelechoixdela«bonne»fonctionentenantcomptedelanaturedesargumentseffectifs.

D’unemanière générale, si les règles utilisées par le compilateur pour sa recherchesontassezintuitives,leurénoncéprécisestassezcomplexe,etnousnelerappelleronspasici.Ontrouveradenombreuxexemplesdesurdéfinitionetunrécapitulatifcompletdes règles dans nos ouvrages consacrés au C++ (publiés également aux éditionsEyrolles).Signalonssimplementque larecherched’unefonctionsurdéfiniepeutfaireintervenir toutes les conversions usuelles (promotions numériques et conversions

55

Page 57: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

standards,cesdernièrespouvantêtredégradantes),ainsiquelesconversionsdéfiniespar l’utilisateur en cas d’argument de type classe, à condition qu’aucune ambiguïtén’apparaisse.

LesfonctionsenligneUne fonction en ligne (on dit aussi « développée ») est une fonction dont lesinstructionssontincorporéesparlecompilateur(danslemoduleobjetcorrespondant)àchaqueappel.Celaévitelapertedetempsnécessaireàunappelusuel(changementdecontexte, copie des valeurs des arguments sur la « pile »...) ; en revanche, lesinstructionsenquestionsontgénéréesplusieursfois.

Une fonctionen ligneestnécessairementdéfinieenmême tempsqu’elleestdéclarée(elle ne peut plus être compilée séparément) et son en-tête est précédée dumot-cléinline,commedans:

inlinefct(...){.....}

56

Page 58: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice24

ÉnoncéQuelle modification faut-il apporter au programme suivant pour qu’il deviennecorrect:

#include<iostream>

usingnamespacestd;

main()

{intn,p=5;

n=fct(p);

cout<<"p="<<p<<"n="<<n;

}

intfct(intr)

{return2*r;

}

La fonction fct est utilisée dans la fonction main, sans être encore « connue » ducompilateur,cequiprovoqueuneerreurdecompilation.Pouryrémédier,ondisposededeuxpossibilités:

déclarerlafonctionavantsonutilisationdansmain,depréférenceaudébut:#include<iostream>

usingnamespacestd;

main()

{intfct(int);//déclarationdefct;onpourraitécrireintfct

(intx)

intn,p=5;

n=fct(p);

cout<<"p="<<p<<"n="<<n;

}

intfct(intr)

{return2*r;

}

placerladéfinitiondelafonctionavantcelledelafonctionmain:#include<iostream>

usingnamespacestd;

intfct(intr)

{return2*r;

}

main()

{intn,p=5;

n=fct(p);

cout<<"p="<<p<<"n="<<n;

}

Ilestconseilléd’utiliserlapremièredémarchequialeméritedepermettredemodifierlesemplacementsdesdéfinitionsdefonctionsdansunfichiersourceou,même,de le

57

Page 59: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

scinderenplusieursparties(compilationséparée).

58

Page 60: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice25

ÉnoncéÉcrire:

•unefonction,nomméef1,secontentantd’afficher«bonjour»(elleneposséderaaucunargument,nivaleurderetour);

• une fonction, nommée f2, qui affiche « bonjour » un nombre de fois égal à lavaleurreçueenargument(int)etquinerenvoieaucunevaleur;

• une fonction, nommée f3, qui fait la même chose que f2, mais qui, de plus,renvoielavaleur(int)0.

Écrire un petit programme appelant successivement chacune de ces 3 fonctions,après les avoir convenablement déclarées (on ne fera aucune hypothèse sur lesemplacementsrelatifsdesdifférentesfonctionscomposantlefichiersource).

L’énoncéneprécisant rien, nousutiliseronsune transmissiond’argumentsparvaleur.Comme on n’impose pas d’ordre aux définitions des différentes fonctions dans lefichiersource,ondéclarerasystématiquementtouteslesfonctionsutilisées.

#include<iostream>

usingnamespacestd;

voidf1(void)

{cout<<"bonjour\n";

}

voidf2(intn)

{inti;

for(i=0;i<n;i++)

cout<<"bonjour\n";

}

intf3(intn)

{inti;

for(i=0;i<n;i++)

cout<<"bonjour\n";

return0;

}

main()

{voidf1(void);

voidf2(int);

intf3(int);

f1();

f2(3);

f3(3);

}

59

Page 61: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice26

ÉnoncéQuelsrésultatsfourniraceprogramme:

#include<iostream>

usingnamespacestd;

intn=10,q=2;

main()

{

intfct(int);

voidf(void);

intn=0,p=5;

n=fct(p);

cout<<"A:dansmain,n="<<n<<"p="<<p

<<"q="<<q<<"\n";

f();

}

intfct(intp)

{

intq;

q=2*p+n;

cout<<"B:dansfct,n="<<n<<"p="<<p

<<"q="<<q<<"\n";

returnq;

}

voidf(void)

{

intp=q*n;

cout<<"C:dansf,n="<<n<<"p="<<p

<<"q="<<q<<"\n";

}

B:dansfct,n=10,p=5,q=20

A:dansmain,n=20,p=5,q=2

C:dansf,n=10,p=20,q=2

60

Page 62: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice27

ÉnoncéÉcrireunefonctionquireçoitenarguments2nombresflottantsetuncaractère,etquifournit un résultat correspondant à l’une des 4 opérations appliquées à ses deuxpremiersarguments,enfonctiondelavaleurdudernier,àsavoir:additionpourlecaractère+, soustractionpour-,multiplicationpour * etdivisionpour / (tout autrecaractèrequel’undes4citésserainterprétécommeuneaddition).Onnetiendrapascomptedesrisquesdedivisionparzéro.

Écrire un petit programme (main) utilisant cette fonction pour effectuer les 4opérationssurles2nombresfournisendonnée.

#include<iostream>

usingnamespacestd;

floatoper(floatv1,floatv2,charop)

{floatres;

switch(op)

{case'+':res=v1+v2;

break;

case'-':res=v1-v2;

break;

case'*':res=v1*v2;

break;

case'/':res=v1/v2;

break;

default:res=v1+v2;

}

returnres;

}

main()

{floatoper(float,float,char);//déclarationdeoper

floatx,y;

cout<<"donnezdeuxnombresréels:";

cin>>x>>y;

cout<<"leursommeest:"<<oper(x,y,'+')<<"\n";

cout<<"leurdifférenceest:"<<oper(x,y,'-')<<"\n";

cout<<"leurproduitest:"<<oper(x,y,'*')<<"\n";

cout<<"leurquotientest:"<<oper(x,y,'/')<<"\n";

}

61

Page 63: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice28

ÉnoncéTransformer le programme (fonction + main) écrit dans l’exercice précédent demanièrequelafonctionnedisposeplusquede2arguments,lecaractèreindiquantlanature de l’opération à effectuer étant précisé, cette fois, à l’aide d’une variableglobale.

#include<iostream>

usingnamespacestd;

charop;//variableglobalepourlanaturedel'opération

//attention:doitêtredéclaréeavantd'êtreutilisée

floatoper(floatv1,floatv2)

{floatres;

switch(op)

{case'+':res=v1+v2;

break;

case'-':res=v1-v2;

break;

case'*':res=v1*v2;

break;

case'/':res=v1/v2;

break;

default:res=v1+v2;

}

returnres;

}

main()

{floatoper(float,float);/*prototypedeoper*/

floatx,y;

cout<<"donnezdeuxnombresréels:";

cin>>x>>y;

op='+';

cout<<"leursommeest:"<<oper(x,y)<<"\n";

op='-';

cout<<"leurdifférenceest:"<<oper(x,y)<<"\n";

op='*';

cout<<"leurproduitest:"<<oper(x,y)<<"\n";

op='/';

cout<<"leurquotientest:"<<oper(x,y)<<"\n";

}

Ils’agissait icid’unexerciced’«école»destinéàforcerl’utilisationd’unevariableglobale.Dans lapratique, on évitera autantquepossible cegenredeprogrammation

62

Page 64: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

quifavorisetroplesrisquesd’«effetsdebord».

63

Page 65: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice29

ÉnoncéÉcrireunefonction,sansargumentnivaleurderetour,quisecontented’afficher,àchaqueappel,lenombretotaldefoisoùelleaétéappeléesouslaforme:appelnuméro3

La meilleure solution consiste à prévoir, au sein de la fonction en question, unevariabledeclassestatique.Elleserainitialiséeuneseulefoisàzéro(ouàtouteautrevaleur éventuellement explicitée) au début de l’exécution du programme. Ici, nousavons,deplus,prévuunpetitprogrammed’essai.

#include<iostream>

usingnamespacestd;

voidfcompte(void)

{

staticinti;//ilestinutile,maispasinterdit,d'écrirei=0

i++;

cout<<"appelnuméro"<<i<<"\n";

}

/*petitprogrammed'essaidefcompte*/

main()

{voidfcompte(void);

inti;

for(i=0;i<3;i++)fcompte();

}

Là encore, la démarche consistant à utiliser comme compteur d’appels une variableglobale(quidevraitalorsêtreconnueduprogrammeutilisateur)estàproscrire.

64

Page 66: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice30

ÉnoncéÉcrire2fonctionsàunargumententieretunevaleurderetourentièrepermettantdeprécisersil’argumentreçuestmultiplede2(pourlapremièrefonction)oumultiplede3(pourlasecondefonction).

Utilisercesdeuxfonctionsdansunpetitprogrammequi litunnombreentieretquiprécises’ilestpair,multiplede3et/oumultiplede6,commedanscetexemple(ilyadeuxexécutions):

donnezunentier:9

ilestmultiplede3

---------------

donnezunentier:12

ilestpair

ilestmultiplede3

ilestdivisiblepar6

#include<iostream>

usingnamespacestd;

intmul2(intn)

{if(n%2)return0;

elsereturn1;

}

intmul3(intn)

{if(n%3)return0;

elsereturn1;

}

main()

{intmul2(int);

intmul3(int);

intn;

cout<<"donnezunentier:";

cin>>n;

if(mul2(n))cout<<"ilestpair\n";

if(mul3(n))cout<<"ilestmultiplede3\n";

if(mul2(n)&&mul3(n))cout<<"ilestdivisiblepar6\n";

}

65

Page 67: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice31

ÉnoncéÉcrire une fonction permettant d’ajouter une valeur fournie en argument à unevariablefournieégalementenargument.Parexemple,l’appel(netpétantentiers):

ajouter(2*p+1,n);

ajouteralavaleurdel’expression2*p+1àlavariablen.Écrireunpetitprogrammedetestdelafonction.

Étantdonnéque la fonctiondoit être enmesuredemodifier lavaleurde son secondargument,ilestnécessairequecederniersoittransmisparréférence.

#include<iostream>

usingnamespacestd;

voidajoute(intexp,int&var)

{var+=exp;

return;

}

main()

{voidajoute(int,int&);

intn=12;

intp=3;

cout<<"Avant,n="<<n<<"\n";

ajoute(2*p+1,n);

cout<<"Après,n="<<n<<"\n";

}

66

Page 68: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice32

ÉnoncéSoientlesdéclarationssuivantes:

intfct(int);//fonctionI

intfct(float);//fonctionII

voidfct(int,float);//fonctionIII

voidfct(float,int);//fonctionIV

intn,p;

floatx,y;

charc;

doublez;

Les appels suivants sont-ils corrects et, si oui, quelles seront les fonctionseffectivementappeléesetlesconversionséventuellementmisesenplace?

a.fct(n);

b.fct(x);

c.fct(n,x);

d.fct(x,n);

e.fct(c);

f.fct(n,p);

g.fct(n,c);

h.fct(n,z);

i.fct(z,z);

Lescasa,b,cetdneposentaucunproblème.IlyarespectivementappeldesfonctionsI,II,IIIetIV,sansqu’aucuneconversiond’argumentnesoitnécessaire.

e.AppeldelafonctionI,aprèsconversiondelavaleurdecenint.

f.Appelincorrect,comptetenudesonambiguïté;deuxpossibilitésexistenteneffet:conservern,convertirpenfloatetappelerlafonctionIIIou,aucontraire,convertirnenfloat,conserverpetappelerlafonctionIV.

g.AppeldelafonctionIII,aprèsconversiondecenfloat.

67

Page 69: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

h.AppeldelafonctionIII,aprèsconversion(dégradante)dezenfloat.

i.Appelincorrect,comptetenudesonambiguïté;deuxpossibilitésexistenteneffet:convertirlepremierargumentenfloatet lesecondenintetappeler lafonctionIIIou,aucontraire,convertirlepremierargumentenintetlesecondenfloatetappelerlafonctionIV.

68

Page 70: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice33

Énoncéa.Transformerleprogrammesuivantpourquelafonctionfctdevienneunefonctionenligne.

#include<iostream>

usingnamespacestd;

main()

{intfct(char,int);//déclaration(prototype)defct

intn=150,p;

charc='s';

p=fct(c,n);

cout<<"fct(\'"<<c<<"\',"<<n<<")vaut:"<<p;

}

intfct(charc,intn)//définitiondefct

{intres;

if(c=='a')res=n+c;

elseif(c=='s')res=n-c;

elseres=n*c;

returnres;

}

b.Commentfaudrait-ilprocédersil’onsouhaitaitquelafonctionfct soitcompiléeséparément?

a. Nous devons donc d’abord déclarer (et définir en même temps) la fonction fctcommeunefonctionenligne.Leprogrammemains’écritdelamêmemanière,sicen’est que la déclaration de fct n’y est plus nécessaire puisqu’elle apparaîtauparavant (il reste permis de la déclarer, à condition de ne pas utiliser lequalificatifinline).#include<iostream>

usingnamespacestd;

inlineintfct(charc,intn)

{intres;

if(c=='a')res=n+c;

elseif(c=='s')res=n-c;

elseres=n*c;

returnres;

}

main()

{intn=150,p;

charc='s';

p=fct(c,n);

cout<<"fct(\'"<<c<<"\',"<<n<<")vaut:"<<p;

}

b.Ils’agitenfaitd’unequestionpiège.Eneffet,lafonctionfctétantenligne,ellenepeutplusêtrecompiléeséparément.Ilestcependantpossibledelaconserverdans

69

Page 71: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

un fichier d’extension h et d’incorporer simplement ce fichier par #include pourcompilerlemain.Cettedémarcheserencontrerad’ailleursfréquemmentdanslecasdeclassescomportantdesfonctionsenligne.Alors,dansunfichierd’extensionh,ontrouvera la déclaration de la classe en question, à l’intérieur de laquelleapparaîtrontles«déclarations-définitions»desfonctionsenligne.

70

Page 72: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre4Lestableaux,lespointeursetleschaînes

destyleC

71

Page 73: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

TableauàunindiceUn tableau à un indice est un ensemble d’éléments de même type désignés par unidentificateur unique. Chaque élément est repéré par un indice précisant sa positiondansl’ensemble(lepremierélémentestrepéréparl’indice0).

L’instructionsuivante:floatt[10];

réservel’emplacementpouruntableaude10élémentsdetypefloat,nommét.

Un élément de tableau est une lvalue.Un indice peut prendre la forme de n’importequelleexpressionarithmétiqued’untypeentierquelconque.Lenombred’élémentsd’untableau(dimension)doitêtreindiquésousformed’uneexpressionconstante.

TableauxàplusieursindicesLadéclaration:

intt[5][3];

réserve un tableau de 15 (5 × 3) éléments.Un élément quelconque de ce tableau setrouvealorsrepérépardeuxindicescommedanscesnotations:

t[3][2]t[i][j]

D’unemanièregénérale,onpeutdéfinirdestableauxcomportantunnombrequelconqued’indices.

Lesélémentsd’untableausontrangésenmémoireselonl’ordreobtenuenfaisantvarierledernierindiceenpremier.

InitialisationdestableauxLes tableaux de classe statique sont, par défaut, initialisés à zéro. Les tableaux declasseautomatiquenesontpasinitialiséspardéfaut.

On peut initialiser (partiellement ou totalement) un tableau lors de sa déclaration,commedanscesexemples:

72

Page 74: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

intt1[5]={10,20,5,0,3};

//placelesvaleurs10,20,5,0et3danslescinqélémentsdet1

intt2[5]={10,20};

//placelesvaleurs10et20danslesdeuxpremiersélémentsdet2

Lesdeuxdéclarationssuivantessontéquivalentes:inttab[3][4]={{1,2,3,4},

{5,6,7,8},

{9,10,11,12}}

inttab[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

Lesinitialiseursfournispourl’initialisationdesélémentsd’untableaudoiventêtredesexpressions constantes pour les tableaux de classe statique, et des expressionsquelconques (d’un type compatible par affectation) pour les tableaux de classeautomatique.

Lespointeurs,lesopérateurs&et*Unevariablepointeurestdestinéeàmanipulerdesadressesd’informationsd’un typedonné.Onladéclarecommedanscesexemples:

int*adi;//adicontiendradesadressesd’entiersdetypeint

float*adf;//adfcontiendradesadressesdeflottantsdetypefloat

L’opérateur&permetd’obtenirl’adressed’unevariable:intn;

.....

adi=&n;//adicontientl’adresseden

L’opérateur* permet d’obtenir l’information d’adresse donnée.Ainsi *adi désigne lalvalued’adresseadi,c’est-à-direicin:

intp=*adi;//placedansplavaleurden

*adi=40;//placelavaleur40àl’adressecontenuedansadi,

//doncicidansn

Ilexisteuntype«pointeurgénérique»,c’est-à-direpourlequelonneprécisepaslanaturedesinformationspointées,àsavoirletypevoid*.

OpérationssurlespointeursOnpeutincrémenteroudécrémenterdespointeursd’unevaleurdonnée.Parexemple:

adi++;//modifieadidemanièrequ’ellepointe

//surl’entiersuivantn

adi-=10;//modifieadidemanièrequ’ellepointe

//10entiersavant

L’unitéd’incrémentationoudedécrémentationdespointeursgénériquesestl’octet.

Onpeutcomparerousoustrairedespointeursdemêmetype(pointantsurdeséléments

73

Page 75: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

demêmetype).

AffectationsdepointeursetconversionsIln’existeaucuneconversionimplicited’untypepointeurenunautre,àl’exceptiondelaconversionenpointeurgénérique.

Iln’existeaucuneconversionimplicited’unentierenunpointeur,à l’exceptiondelavaleur0quiestalorsconvertieenunpointeurne«pointantsurrien».

TableauxetpointeursUnnomdetableauestuneconstantepointeur.Avec:

intt[10];

t+1estéquivalentà&t[1];

t+iestéquivalentà&t[i];

ti]estéquivalentà*(t+i).

Avec:intt[3][4];

testéquivalentà&t[0][0]ouàt[0];

t+2estéquivalentà&t[2][0]ouàt[2]

Lorsqu’unnomdetableauestutiliséenargumenteffectif,c’est(lavaleurde)l’adressequiesttransmiseàlafonction.Lanotiondetransmissionparréférencen’apasdesensdans ce cas. Dans la déclaration d’une fonction disposant d’un argument de typetableau, on peut utiliser indifféremment le formalisme « tableau » (avec ou sans ladimensioneffectivedutableau)ouleformalismepointeur;cestroisdéclarationssontéquivalentes:

voidfct(intt[10])

voidfct(intt[])

voidfct(int*t)

GestiondynamiquedelamémoireSitypereprésenteladescriptiond’untypeabsolumentquelconqueetsinreprésenteuneexpressiond’untypeentier(généralementlongouunsignedlong),l’expression:

newtype[n]

74

Page 76: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

allouel’emplacementnécessairepournélémentsdutypeindiquéetfournitenrésultatunpointeur(detypetype*)surlepremierélément.Encasd’échecd’allocation,ilyadéclenchement d’une exception bad_alloc (les exceptions font l’objet du chapitre 19).L’indication n est facultative : avec new type, on obtient un emplacement pour unélémentdutypeindiqué,commesil’onavaitécritnewtype[1].

L’expression:deleteadresse//ilexisteuneautresyntaxepourles

//tableauxd’objets-voirchap.9

libèreunemplacementpréalablementallouéparnewà l’adresse indiquée. Iln’estpasnécessairederépéterlenombred’éléments,dumoinslorsqu’ilnes’agitpasd’objets,mêmesicelui-ciestdifférentde1.Lecasdestableauxd’objetsestexaminéauchapitre9.

PointeurssurdesfonctionsUn pointeur sur une fonction est défini par le type des arguments et de la valeur deretourdelafonction,commedans:

int(*adf)(double,int);//adfestunpointeursurunefonction

//recevantundoubleetunint

//etrenvoyantunint

Unnomdefonction,employéseul,esttraduitparlecompilateurenl’adressedecettefonction.

ChaînesdestyleCUne chaîne de caractère de style C est une suite d’octets (représentant chacun uncaractère),terminéeparunoctetdecodenul.Leschaînesconstantessontreprésentéesseloncetteconvention.Leslecturesopéréessurleflotcin,ainsiquelesécrituressurleflotcout,utilisentcetteconventionlorsqu’ellesportentsurdeslvaluedetypetableaudecaractèresoupointeursurdescaractères(typechar*).

Un tableau de caractères peut être initialisé par une chaîne constante. Ces deuxinstructionssontéquivalentes:

charch[20]="bonjour";

charch[20]={’b’,’o’,’n’,’j’,’o’,’u’,’r’,’\0’);

ArgumentsdelalignedecommandeLors du lancement d’un programme, la plupart des environnements permettent de lui

75

Page 77: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

fournirdes«paramètres».Ceux-cisontalorssimplementtransmisàlafonctionmain,sousformedechaînedestyleC,commedesargumentseffectifsd’appeld’unefonction.Ilssont,enoutre,précédésdunombretotald’arguments(int)etdunomduprogramme(chaîne de styleC). Pour pouvoir les exploiter, il est alors nécessaire d’utiliser unedéclarationdemaintelleque:

main(intnbarg,char*argv[])

76

Page 78: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice34

ÉnoncéQuelsrésultatsfourniraceprogramme:

#include<stdio.h>

#include<iostream>

usingnamespacestd;

main()

{

intt[3];

inti,j;

int*adt;

for(i=0,j=0;i<3;i++)t[i]=j+++i;/*1*/

for(i=0;i<3;i++)cout<<t[i]<<"";/*2*/

cout<<"\n";

for(i=0;i<3;i++)cout<<*(t+i)<<"";/*3*/

printf("\n");

for(adt=t;adt<t+3;adt++)cout<<*adt<<"";/*4*/

cout<<"\n";

for(adt=t+2;adt>=t;adt--)cout<<*adt<<"";/*5*/

cout<<"\n";

}

/*1*/remplitletableauaveclesvaleurs0(0+0),2(1+1)et4(2+2);onobtiendraitplussimplementlemêmerésultatavecl’expression2*i.

/*2*/afficheclassiquementlesvaleursdutableaut,dansl’ordrenaturel.

/*3*/ fait lamêmechose, enutilisant le formalismepointeur au lieudu formalismetableau.Ainsi,*(t+i)estparfaitementéquivalentàt[i].

/*4*/faitlamêmechose,enutilisantlalvalueadt(àlaquelleonaaffectéinitialementl’adressetdutableau)etenl’incrémentantpourparcourirlesdifférentesadressesdes4élémentsdutableau.

/*5*/affichelesvaleursdet,àl’envers,enutilisantlemêmeformalismepointeurquedans4.Onauraitpuécrire,defaçonéquivalente:

for(i=2;i>=0;i--)cout<<t[i]<<"";

Voicilesrésultatsfournisparceprogramme:024

77

Page 79: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

024

024

420

78

Page 80: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice35

ÉnoncéÉcrire,dedeuxfaçonsdifférentes,unprogrammequilit10nombresentiersdansuntableauavantd’enrechercherleplusgrandetlepluspetit:a.enutilisantuniquementle«formalismetableau»;b.enutilisantle«formalismepointeur»,àchaquefoisquecelaestpossible.

a.La programmation est, ici, classique.Nous avons simplement défini une constanteNVAL destinée à contenir le nombre de valeurs du tableau. Notez bien que ladéclarationintt[NVAL]estacceptéepuisqueNVALestune«expressionconstante».#include<iostream>

usingnamespacestd;

main()

{constintNVAL=10;/*nombredevaleursdutableau*/

inti,min,max;

intt[NVAL];

cout<<"donnez"<<NVAL<<"valeurs\n";

for(i=0;i<NVAL;i++)cin>>t[i];

max=min=t[0];

for(i=1;i<NVAL;i++)

{if(t[i]>max)max=t[i];/*oumax=t[i]>max?t[i]:max*/

if(t[i]<min)min=t[i];/*oumin=t[i]<min?t[i]:min*/

}

cout<<"valeurmax:"<<max<<"\n";

cout<<"valeurmin:"<<min<<"\n";

}

b.Onpeutremplacersystématiquementt[i]par*(t+i).Voicifinalementleprogrammeobtenu:#include<iostream>

usingnamespacestd;

main()

{constintNVAL=10;/*nombredevaleursdutableau*/

inti,min,max;

intt[NVAL];

cout<<"donnez"<<NVAL<<"valeurs\n";

for(i=0;i<NVAL;i++)cin>>*(t+i);

max=min=*t;

for(i=1;i<NVAL;i++)

{if(*(t+i)>max)max=*(t+i);

if(*(t+i)<min)min=*(t+i);

79

Page 81: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

cout<<"valeurmax:"<<max<<"\n";

cout<<"valeurmin:"<<min<<"\n";

}

80

Page 82: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice36

ÉnoncéSoientdeuxtableauxt1ett2déclarésainsi:

floatt1[10],t2[10];

Écrirelesinstructionspermettantderecopier,danst1, touslesélémentspositifsdet2,encomplétantéventuellementt1pardeszéros.Ici,onnechercherapasàfournirunprogrammecompletetonutiliserasystématiquementleformalismetableau.

Onpeutcommencerparremplirt1dezéros,avantd’yrecopierlesélémentspositifsdet2:

inti,j;

for(i=0;i<10;i++)t1[i]=0;

/*isertàpointerdanst1etjdanst2*/

for(i=0,j=0;j<10;j++)

if(t2[j]>0)t1[i++]=t2[j];

Maisonpeutrecopierd’aborddanst1lesélémentspositifsdet2,avantdecompléteréventuellement par des zéros. Cette seconde formulation, moins simple que laprécédente,serévéleraittoutefoisplusefficacesurdegrandstableaux:

inti,j;

for(i=0,j=0;j<10;j++)

if(t2[j]>0)t1[i++]=t2[j];

for(j=i;j<10;j++)t1[j]=0;

81

Page 83: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice37

ÉnoncéQuelsrésultatsfourniraceprogramme:

#include<iostream>

usingnamespacestd;

main()

{intt[4]={10,20,30,40};

int*ad[4];

inti;

for(i=0;i<4;i++)ad[i]=t+i;/*1*/

for(i=0;i<4;i++)cout<<*ad[i]<<"";/*2*/

cout<<"\n";

cout<<*(ad[1]+1)<<""<<*ad[1]+1<<"\n";/*3*/

}

Letableauadestuntableaude4éléments;chacundecesélémentsestunpointeursurunint.L’instruction/*1*/ remplit le tableauad avec lesadressesdes4élémentsdutableaut.L’instruction/*2*/affichefinalementles4élémentsdutableaut;eneffet,*ad[i]représentelavaleursituéeàl’adressead[i]./*2*/estéquivalenteicià:

for(i=0;i<4;i++)cout<<t[i]<<"";

Enfin,dansl’instruction/*3*/,*(ad[1]+1)représentelavaleursituéeàl’entiersuivantceluid’adressead[1];ils’agitdoncdet[2].Enrevanche,*ad[1]+1représentelavaleursituéeàl’adressead[1]augmentéede1,autrementditt[1]+1.

Voici,endéfinitive,lesrésultatsfournisparceprogramme:10203040

3021

82

Page 84: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice38

ÉnoncéSoitletableautdéclaréainsi:

floatt[3][4];

Écrireles(seules)instructionspermettantdecalculer,dansunevariablenomméesom,lasommedesélémentsdet:

a.enutilisantle«formalismeusueldestableauxàdeuxindices»;b.enutilisantle«formalismepointeur».

a.Lapremièresolutionneposeaucunproblèmeparticulier:inti,j;

som=0;

for(i=0;i<3;i++)

for(j=0;j<4;j++)

som+=t[i][j];

b.Leformalismepointeuresticimoinsfacileàappliquerquedanslecasdestableauxàunindice.Eneffet,avec,parexemple,floatt[4],testdetypeint*etilcorrespondà un pointeur sur le premier élément du tableau. Il suffit donc d’incrémenterconvenablementtpourparcourirtouslesélémentsdutableau.

En revanche, avec notre tableau float t [3] [4], t est du type pointeur sur destableaux de 4 flottants (type : * float[4]). La notation *(t+i) est généralementinutilisablesouscetteformepuisque,d’unepart,ellecorrespondàdesvaleursdetableaux de 4 flottants et que, d’autre part, l’incrément i porte, non plus sur desflottants,maissurdesblocsde4flottants;parexemple,t+2représentel’adresseduhuitièmeflottant,comptéàpartirdeceluid’adresset.

Unesolutionconsisteà«convertir»lavaleurdetenunpointeurdetypefloat*.Onpourraitsecontenterdeprocéderainsi:float*adt;

.....

adt=t;

Eneffet,danscecas,l’affectationentraîneuneconversionforcéedetenfloat*,cequinechangepasl’adressecorrespondante(seulelanaturedupointeurachangé).

83

Page 85: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Celan’estvraiqueparcequel’onpassedepointeurssurdesgroupesd’élémentsàunpointeursurceséléments.Autrementdit,aucunecontrainted’alignementnerisquedenuireici.Iln’eniraitpasdemême,parexemple,pourdesconversionsdechar*enint*.

Généralement,onygagneraenlisibilitéenexplicitantlaconversionmiseenœuvreà l’aide de l’opérateur de cast. Notez que, par ailleurs, cela peut éviter certainsmessagesd’avertissement(warnings)delapartducompilateur.

Voicifinalementcequepourraientêtrelesinstructionsdemandées:inti;

int*adt;

som=0;

adt=(float*)t;

for(i=0;i<12;i++)

som+=*(adt+i);

84

Page 86: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice39

ÉnoncéÉcrire une fonction qui fournit en valeur de retour la somme des éléments d’untableaudeflottantstransmis,ainsiquesadimension,enargument.

Écrireunpetitprogrammed’essai.

En C++, par défaut, les arguments sont transmis par valeur.Mais, dans le cas d’untableau, cettevaleur, de typepointeur, n’est riend’autreque sonadresse.Quant à latransmission par référence, elle n’a pas de signification dans ce cas. Nous n’avonsdoncaucunchoixencequiconcernelemodedetransmissiondenotretableau.

En ce qui concerne le nombre d’éléments (de type int), nous le transmettronsclassiquementparvaleur.L’en-têtedenotrefonctionpourraseprésentersousl’unedesformessuivantes:

floatsomme(floatt[],intn)

floatsomme(float*t,intn)

floatsomme(floatt[5],intn)//déconseillécarlaissecroirequet

//estdedimensionfixe5

En effet, la dimension réelle de t n’a aucune incidence sur les instructions de lafonctionelle-même(ellen’intervientpasdans lecalculde l’adressed’unélémentdutableau1etellenesertpasà«allouer»unemplacementpuisqueletableauenquestionauraétéallouédanslafonctionappelantsomme).

Voicicequepourraitêtrelafonctiondemandée:floatsomme(floatt[],intn)//onpeutécriresomme(float*t,...

//ouencoresomme(floatt[4],...

//maispassomme(floatt[n],...

{inti;

floats=0;

for(i=0;i<n;i++)

s+=t[i];//onpourraitécrires+=*(t+i);

returns;

}

Pour ce qui est du programme d’utilisation de la fonction somme, on peut, là encore,écrirele«prototype»sousdifférentesformes:

floatsomme(float[],int);

floatsomme(float*,int);

floatsomme(float[5],int);//déconseillécarlaissecroirequet

//estdedimensionfixe5

85

Page 87: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Voiciunexempled’untelprogramme:#include<iostream>

usingnamespacestd;

main()

{

floatsomme(float*,int);

floatt[4]={3,2.5,5.1,3.5};

cout<<"sommedet:"<<somme(t,4);

}

86

Page 88: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice40

ÉnoncéÉcrireunefonctionquinerenvoieaucunevaleuretquidéterminelavaleurmaximaleet lavaleurminimaled’un tableaud’entiers(àun indice)de taillequelconque.Onprévoira4 arguments : le tableau, sadimension, lemaximumet leminimum.Pourchacun d’entre eux, on choisira le mode de transmission le plus approprié (parvaleurouparréférence).Danslecasoùlatransmissionparréférenceestnécessaire,proposer deux solutions : l’une utilisant effectivement cette notion de référence,l’autrela«simulant»àl’aidedepointeurs.

Écrireunpetitprogrammed’essai.

En C++, par défaut, les arguments sont transmis par valeur.Mais, dans le cas d’untableau,cettevaleur,detypepointeur,n’estriend’autrequel’adressedutableau.Quantàlatransmissionparréférence,ellen’apasdesignificationdanscecas.Nousn’avonsdoncaucunchoixconcernantlemodedetransmissiondenotretableau.

En ce qui concerne le nombre d’éléments du tableau, on peut indifféremment entransmettre l’adresse (sous forme d’un pointeur de type int *), ou la valeur ; ici, laseconde solution est la plus appropriée, puisque la fonction n’a pas besoin d’enmodifierlavaleur.

En revanche, en cequi concerne lemaximumet leminimum, ils nepeuventpas êtretransmisparvaleur,puisqu’ilsdoiventprécisémentêtredéterminéspar lafonction.Ilfautdoncobligatoirementprévoirdepasser:

soitdesréférences.L’en-têtedenotrefonctionseprésenteraainsi:voidmaxmin(intt[],intn,int&admax,int&admin)

soitdespointeurssurdesfloat.L’en-têtedenotrefonctionseprésenteraainsi:voidmaxmin(intt[],intn,int*admax,int*admin)

L’algorithme de recherche demaximumet deminimumpeut être calqué sur celui del’exercice39,enremplaçantmaxpar*admaxetminpar*admin.Voicicequepourraitêtrenotrefonction:

avectransmissionparréférence:

87

Page 89: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

voidmaxmin(intt[],intn,int&admax,int&admin)

{inti;

admax=t[1];

admin=t[1];

for(i=1;i<n;i++)

{if(t[i]>admax)admax=t[i];

if(t[i]<admin)admin=t[i];

}

}

avectransmissionparpointeursvoidmaxmin(intt[],intn,int*admax,int*admin)

{inti;

*admax=t[1];

*admin=t[1];

for(i=1;i<n;i++)

{if(t[i]>*admax)*admax=t[i];

if(t[i]<*admin)*admin=t[i];

}

}

Ici,sil’onsouhaiteéviterles«indirections»quiapparaissentsystématiquementdansles instructions de comparaison, on peut travailler temporairement sur des variableslocalesà la fonction(nommées icimaxetmin).Celanousconduitàune fonctionde laformesuivante:

voidmaxmin(intt[],intn,int*admax,int*admin)

{inti,max,min;

max=t[1];

min=t[1];

for(i=1;i<n;i++)

{if(t[i]>max)max=t[i];

if(t[i]<min)min=t[i];

}

*admax=max;

*admin=min;

}

Voiciunpetitexempledeprogrammed’utilisationdelapremièrefonction:#include<iostream>

usingnamespacestd;

main()

{voidmaxmin(int[],int,int&,int&);

intt[8]={2,5,7,2,9,3,9,4};

intmax,min;

maxmin(t,8,max,min);

cout<<"valeurmaxi:"<<max<<"\n";

cout<<"valeurmini:"<<min<<"\n";

}

Etvoicilemêmeexempleutilisantlasecondefonction:#include<iostream>

usingnamespacestd;

main()

{voidmaxmin(int[],int,int*,int*);

intt[8]={2,5,7,2,9,3,9,4};

intmax,min;

maxmin(t,8,&max,&min);

cout<<"valeurmaxi:"<<max<<"\n";

cout<<"valeurmini:"<<min<<"\n";

88

Page 90: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

89

Page 91: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice41

ÉnoncéÉcrire une fonction qui fournit en retour la somme des valeurs d’un tableau deflottantsàdeuxindicesdontlesdimensionssontfourniesenargument.

Paranalogieaveccequenousavionsfaitdansl’exercice39,nouspourrionssongeràdéclarerletableauconcernédansl’en-têtedelafonctionsouslaformet[][].Maiscelan’estpluspossiblecar,cettefois,pourdéterminerl’adressed’unélémentt[i][j]d’unteltableau,lecompilateurdoitenconnaîtreladeuxièmedimension.

Unesolutionconsisteàconsidérerqu’onreçoitunpointeur(detypefloat*)surledébutdutableauetd’enparcourirtousleséléments(aunombreden*psinetpdésignentlesdimensionsdutableau)commesil’onavaitaffaireàuntableauàunedimension.

Celanousconduitàcettefonction:floatsomme(float*adt,intn,intp)

{inti;

floats;

for(i=0;i<n*p;i++)s+=adt[i];/*ous+=*(adt+i)*/

returns;

}

Pour utiliser une telle fonction, la seule difficulté consiste à lui transmettreeffectivementl’adressededébutdutableau,souslaformed’unpointeurdetypeint*.Or,avec,parexemplet[3][4],t, s’il correspondbienà labonneadresse,estdu type« pointeur sur des tableaux de 4 flottants ». A priori, toutefois, compte tenu de laprésenceduprototype,laconversionvoulueseramiseenœuvreautomatiquementparlecompilateur.Toutefois,commenousl’avonsdéjàditdansl’exercice38,ongagneraenlisibilité(etenéventuelsmessagesd’avertissement!)enfaisantappelàl’opérateurde«cast».

Voicifinalementunexempled’untelprogrammed’utilisationdenotrefonction:#include<iostream>

usingnamespacestd;

main()

{floatsomme(float*,int,int);

floatt[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

cout<<"somme:"<<somme((float*)t,3,4)<<"\n";

}

90

Page 92: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice42

ÉnoncéÉcrire un programme allouant dynamiquement un emplacement pour un tableaud’entiers,dontlatailleestfournieendonnée.Utilisercetableaupouryplacerdesnombresentiersluségalementendonnée.Créerensuitedynamiquementunnouveautableau destiné à recevoir les carrés des nombres contenus dans le premier.Supprimerlepremiertableau,afficherlesvaleursdusecondetsupprimerletout.Onnechercherapasàtraiterunéventuelproblèmedemanquedemémoire.

Ilnousfaututiliserdeuxvariables (adt1etadt2)de typepointeur surdesentierspourconserver les adresses des emplacements alloués pour chacun des deux tableauxd’entiers.

#include<iostream>

usingnamespacestd;

main()

{intnval;//nombredevaleurs

int*adt1,*adt2;//attention,pasint*adt1,adt2

do{cout<<"combiendevaleurs:";

cin>>nval;

}

while(nval<=0);//onrefuselesvaleursnégatives

/*allocationpremiertableau,lecturevaleurs*/

adt1=newint[nval];

cout<<"donnez"<<nval<<"valeurs\n";

for(inti=0;i<nval;i++)cin>>adt1[i];//oucin*(adt1+i)

/*allocationsecondtableau,calculdescarrés*/

adt2=newint[nval];

for(inti=0;i<nval;i++)adt2[i]=adt1[i]*adt1[i];

/*suppressionpremiertableau,affichagevaleurs*/

deleteadt1;

cout<<"voicileurscarrés:\n";

for(inti=0;i<nval;i++)cout<<adt2[i]<<"";

/*suppressiondusecondtableau*/

deleteadt2;

}

Notez que, dans l’appel de l’opérateur delete, il n’est pas nécessaire de préciser lenombred’élémentsdutableaud’entiersàlibérer.Iln’eniraplusdemême,lorsquel’onauraaffaireàdestableauxd’objets.

Voiciunexempled’exécutiondeceprogramme:combiendevaleurs:0

combiendevaleurs:-2

combiendevaleurs:8

donnez8valeurs

91

Page 93: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

125973086-2

voicileurscarrés:

142581499064

92

Page 94: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice43

ÉnoncéQuelsrésultatsfourniraceprogramme:

#include<iostream>

usingnamespacestd;

main()

{

char*ad1;

ad1="bonjour";

cout<<ad1<<"\n";

ad1="monsieur";

cout<<ad1;

}

L’instructionad1="bonjour"placedanslavariablead1 l’adressedelachaîneconstante"bonjour". L’instruction cout << ad1 se contente d’afficher la valeur de la chaîne dontl’adresse figure dans ad1, c’est-à-dire en l’occurrence "bonjour". De manièrecomparable, l’instruction ad1 = "monsieur" place l’adresse de la chaîne constante"monsieur" dans ad1 ; l’instruction cout << ad1 affiche la valeur de la chaîne ayantmaintenantl’adressecontenuedansad1,c’est-à-dire"monsieur".

Finalement,ceprogrammeaffichetoutsimplement:bonjour

monsieur

Onauraitobtenuplussimplementlemêmerésultatenécrivant:cout<<"bonjour\nmonsieur";

93

Page 95: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice44

ÉnoncéQuelsrésultatsfourniraceprogramme:

#include<iostream>

usingnamespacestd;

main()

{

char*adr="bonjour";/*1*/

inti;

for(i=0;i<3;i++)cout<<adr[i];/*2*/

cout<<"\n";

i=0;

while(adr[i])cout<<adr[i++];/*3*/

}

Ladéclaration/*1*/placedanslavariableadrl’adressedelachaîneconstantebonjour.L’instruction /* 2 */ affiche les caractères adr[0], adr[1] et adr[2], c’est-à-dire les 3premierscaractèresdecettechaîne.L’instruction/*3*/ affiche tous lescaractèresàpartirdeceluid’adresseadr,tantquel’onapasaffaireàuncaractèrenul;commenotrechaîne "bonjour" est précisément terminée par un tel caractère nul, cette instructionaffichefinalement,unparun,touslescaractèresde"bonjour".

Endéfinitive,leprogrammefournitsimplementlesrésultatssuivants:bon

bonjour

94

Page 96: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice45

ÉnoncéÉcrireleprogrammeprécédent(exercice44),sansutiliserle«formalismetableau»(ilexisteplusieurssolutions).

Voicideuxsolutionspossibles:

a.Onpeutremplacersystématiquementlanotationadr[i]par*(adr+i),cequiconduitàceprogramme:#include<iostream>

usingnamespacestd;

main()

{

char*adr="bonjour";

inti;

for(i=0;i<3;i++)cout<<*(adr+i);

cout<<"\n";

i=0;

while(adr[i])cout<<*(adr+i++);

}

b.Onpeutégalementparcourirnotrechaîne,nonplusàl’aided’un«indice»i,maisenincrémentantunpointeurdetypechar*:ilpourraits’agirtoutsimplementdeadr,maisgénéralementonpréféreranepasdétruirecetteinformationetenemployerunecopie:#include<iostream>

usingnamespacestd;

main()

{

char*adr="bonjour";

char*adb;

for(adb=adr;adb<adr+3;adb++)cout<<*adb;

cout<<"\n";

adb=adr;

while(*adb)cout<<*(adb++);

}

Notez bien que si nous incrémentions directement adr dans la première instructiond’affichage, nous ne disposerions plus de la « bonne adresse » pour la seconceinstructiond’affichage.

95

Page 97: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice46

ÉnoncéÉcrire un programme qui demande à l’utilisateur de lui fournir un nombre entierentre1et7etquiaffichelenomdujourdelasemaineayantlenuméroindiqué(lundipour1,mardipour2,...dimanchepour7).

Une démarche consiste à créer un « tableau de 7 pointeurs sur des chaînes »,correspondant chacune aunomd’un jour de la semaine.Commeces chaînes sont iciconstantes, il estpossibledecréerun tel tableauparunedéclarationcomportantuneinitialisationdelaforme:

char*jour[7]={"lundi","mardi",...

N’oubliezpasalorsquejour[0]contiendral’adressedelapremièrechaîne,c’est-à-direl’adressedelachaîneconstante"lundi";jour[1]contiendral’adressede"mardi"...

Pourafficherlavaleurdelachaînederangi,ilsuffitderemarquerquesonadresseestsimplementjour[i-1].

D’oùleprogrammedemandé:#include<iostream>

usingnamespacestd;

main()

{

char*jour[7]={"lundi","mardi","mercredi","jeudi",

"vendredi","samedi","dimanche"

};

inti;

do

{cout<<"donnezunnombreentierentre1et7:";

cin>>i;

}

while(i<=0||i>7);

cout<<"lejournuméro"<<i<<"delasemaineest"<<jour[i-1];

}

1.Iln’eniraitpasdemêmepourdestableauxàplusieursindices.

96

Page 98: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre5Lesstructures

97

Page 99: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

Déclarationd’untypestructureetdesvariablesdecetypeC++permetdedéclareruntypestructure,decettemanière:

structenreg{intnumero;

intqte;

floatprix;

};

Cette déclaration définit un type (modèle) de structure mais ne réserve pas devariablescorrespondantàcettestructure.Cetypes’appelleicienregetilpréciselenometletypedechacundeschampsconstituantlastructure(numero,qteetprix).

Une foisun tel typede structuredéfini,nouspouvonsdéclarerdesvariablesdu typecorrespondant.Parexemple,l’instruction:

enregart1,art2;

réserve deux emplacements nommés art1 et art2, de type enreg, destinés à contenirchacundeuxentiersetunflottant.

Les champs d’une structure peuvent être de n’importe quel type de base, d’un typetableau ou structure. De même, les éléments d’un tableau peuvent être d’un typestructure.

Utilisationd’une(variabledetype)structureUnchampd’unestructurepeutêtremanipulécommen’importequellevariabledutypecorrespondant. On désigne un champ donné en faisant suivre le nom de la variablestructuredel’opérateur«point»(.),suividunomdechamp,commedanscesexemplesutilisantlesdéclarationsprécédentes:

art1.numero//champnumerodelastructureart1

art2.prix//champprixdelastructureart2

Contrairement au tableau, une variable de type structure peut être affectée à unevariabledemêmetype(mêmenomdemodèle).

InitialisationdesstructuresLes structures de classe automatique ne sont pas initialisées par défaut. Celles declasse statique voient leurs champs initialisés à « zéro » (entier nul, flottant nul,

98

Page 100: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

caractèredecodenul,pointeurnul).Entouterigueur,cetterègles’appliqueauxchampsquisontdesscalairesoudestableauxdescalaires.Sicertainschampssonteux-mêmesdesstructures,larègles’appliqueraàchacundeleurschamps,etainsidesuite.

Àl’instard’untableau,unestructurepeutêtreinitialiséelorsdesadéclaration,commedanscetteinstructionquiutiliseletypeenregdéfiniprécédemment:

enregart1={100,285,200};

Onpeutomettrecertainesvaleurs.

99

Page 101: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice47

ÉnoncéSoitlemodèle(type)destructuresuivant:

structpoint

{charc;

intx,y;

};

Écrireunefonctionquireçoitenargumentunestructuredetypepointetquienaffichelecontenusouslaforme:

pointBdecoordonnées1012

a.entransmettantenargumentlavaleurdelastructureconcernée,

b.entransmettantenargumentl’adressedelastructureconcernée,

c.entransmettantlastructureconcernéeparréférence.

Danslestroiscas,onécriraunpetitprogrammed’essaidelafonctionainsiréalisée.

a.Voicilafonctiondemandée:voidaffiche(pointp)

{cout<<"point"<<p.c<<"decoordonnées"

<<p.x<<""<<p.y<<"\n";

}

Notezquesacompilationnécessiteobligatoirementladéclarationdutypepoint.

Voiciunpetitprogrammecompletquiaffectelesvaleurs'A',10et12auxdifférentschamps d’une structure nommée s, avant d’en afficher les valeurs à l’aide de lafonctionprécédente:#include<iostream>

usingnamespacestd;

structpoint

{charc;

intx,y;

};

voidaffiche(pointp)

{cout<<"point"<<p.c<<"decoordonnées"

<<p.x<<""<<p.y<<"\n";

}

main()

{voidaffiche(point);//déclarationdeaffiche

points;

s.c='A';

s.x=10;

s.y=12;

100

Page 102: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

affiche(s);

}

b.Voiciladeuxièmefonctiondemandée,accompagnéedesonprogrammedetest:#include<iostream>

usingnamespacestd;

structpoint

{charc;

intx,y;

};

voidaffiche(point*adp)

{cout<<"point"<<adp->c<<"decoordonnées"

<<adp->x<<""<<adp->y;

}

main()

{voidaffiche(point*);

points;

s.c='A';

s.x=10;

s.y=12;

affiche(&s);

}

Notezquel’ondoit,cettefois,faireappelàl’opérateur->,àlaplacedel’opérateur«point»(.),puisque l’on travailleavecunpointeursurunestructure,etnonplusaveclavaleurdelastructureelle-même.Toutefoisl’usagede->n’estpastotalementindispensable,danslamesureoù,parexemple,adp->xestéquivalentà(*adp).x.

c.Voicilatroisièmefonctiondemandée,accompagnéedesonprogrammedetest:#include<iostream>

usingnamespacestd;

structpoint

{charc;

intx,y;

};

voidaffiche(point&p)

{cout<<"point"<<p.c<<"decoordonnées"

<<p.x<<""<<p.y;

}

main()

{voidaffiche(point&);

points;

s.c='A';

s.x=10;

s.y=12;

affiche(s);

}

Aulieud’affecterdesvaleursauxchampsc,xetydenotrestructures (dans les troisprogrammes d’essai), nous pourrions (ici) utiliser les possibilités d’initialisationoffertesparlelangageC,enécrivant:

points={'A',10,12};

101

Page 103: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice48

ÉnoncéSoitletypestructureenregdéfiniainsi:

constintNMOIS=12;

structenreg

{intstock;

floatprix;

intventes[NMOIS]

}

Écrireunefonctionnomméerazqui«metàzéro» leschampsstocketventesd’unestructuredecetype,transmiseenargument.Lafonctionnecomporterapasdevaleurderetour.

Écrireunpetitprogrammed’essaiquiaffectetoutd’aborddesvaleursauxdifférentschampsd’unetellestructure,avantdeleurappliquerlafonctionraz.Onafficheralesvaleurs de la structure, avant et après appel (on pourra s’aider d’une fonctiond’affichage).

Ici,pourquelafonctionpuissemodifierlavaleurd’unestructurereçueenargument,ilest nécessaire qu’elle en reçoive, soit la référence, soit l’adresse.Voici un exemplecompletutilisantlapremièrepossibilité:

#include<iostream>

usingnamespacestd;

constintNMOIS=12;

structenreg

{intstock;

floatprix;

intventes[NMOIS];

};

voidraz(enreg&s)

{s.stock=0;

for(inti=0;i<NMOIS;i++)

s.ventes[i]=0;

return;

}

voidaffiche(enregs)//transmissionparvaleurici

{cout<<"stock:"<<s.stock<<"\n";

cout<<"prix:"<<s.prix<<"\n";

cout<<"ventes:";

for(inti=0;i<NMOIS;i++)cout<<s.ventes[i]<<"";

cout<<"\n";

}

main()

{voidraz(enreg&);

102

Page 104: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

enrege={12,5.25,{12,23,4,8,4,9,5,2,7,2,8,7}};

cout<<"contenuavantraz:\n";

affiche(e);

raz(e);

cout<<"contenuapresraz:\n";

affiche(e);

}

Voiciunexempled’exécutiondeceprogramme:contenuavantraz:

stock:12

prix:5.25

ventes:12234849527287

contenuapresraz:

stock:0

prix:5.25

ventes:000000000000

À titre indicatif, voici ce que serait notre fonction raz, avec une transmission parpointeur:

voidraz(enreg*ads)

{ads->stock=0;

for(inti=0;i<NMOIS;i++)

ads->ventes[i]=0;

return;

}

Danslafonctionmain,sadéclarationetsonappeldeviendraient:voidraz(enreg*);

raz(&e)

103

Page 105: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice49

ÉnoncéSoitletypestructuresuivant,représentantunpointd’unplan:

structpoint{charc;//nomattribuéaupoint

intx,y;//sescoordonnées

}

Écrireunefonctionquireçoitenargumentl’adressed’unestructuredutypepointetquirenvoieenrésultatunestructuredemêmetypecorrespondantàunpointdemêmenometdecoordonnéesopposées.

Écrireunpetitprogrammed’essai.

Voicicequepourraitêtrenotrefonction(nousavonsreproduitladéclarationdepoint,laquelle pourrait éventuellement figurer dans un fichier en-tête séparé qu’onincorporeraitparunedirective#include):

#include<iostream>

usingnamespacestd;

structpoint

{charc;

intx,y;

};

pointsym(point*adp)

{pointres;

res.c=adp->c;

res.x=-adp->x;

res.y=-adp->y;

returnres;

}

Notez la « dissymétrie » d’instructions telles que res.c = adp->c ; on y fait appel àl’opérateur«.»àgaucheetàl’opérateur«->»àdroite(onpourraitcependantécrireres.c=(*adp).c).

Voici un exemple d’essai de notre fonction (ici, nous avons utilisé les possibilitésd’initialisationd’unestructurepourdonnerdesvaleursàp1):

main()

{pointsym(point*);

pointp1={'P',5,8};

pointp2;

p2=sym(&p1);

cout<<p1.c<<""<<p1.x<<""<<p1.y<<"\n";

cout<<p2.c<<""<<p2.x<<""<<p2.y<<"\n";

}

104

Page 106: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Sil’énoncénel’avaitpasimposé,nousaurionsputransmettreparréférencel’argumentde la fonctionsym.Nousaurionspuégalementutiliserune transmissionparvaleur,cequin’auraitguèreétépénalisantpouruneinformationdesipetitetaille.Enthéorie,cechoixdumodedetransmission(valeur,référenceouadresse)existeégalementpourlavaleur de retour. Toutefois, cette dernière est généralement (comme c’est le cas ici)créée dans une variable locale à la fonction. Dans ces conditions, en transmettrel’adresseoularéférencereviendraitàrenvoyerl’adressedequelquechosedestinéàdisparaître.En toute rigueur,onpourrait renvoyer l’adressed’unemplacementallouédynamiquement, mais encore faudrait-il définir clairement à qui incomberait laresponsabilitédesasuppressionultérieure.

105

Page 107: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice50

ÉnoncéSoitlastructuresuivante,représentantunpointd’unplan:

structpoint

{charc;//nomdupoint

intx,y;//coordonnées

};

1.Écrireladéclarationd’untableau(nommécourbe)deNPpoints (NP supposédéfiniparuneconstante).

2. Écrire une fonction (nommée affiche) qui affiche les valeurs des différents«points»dutableaucourbe,transmisenargument,souslaforme:pointDdecoordonnées102

3.Écrireunprogrammequi:–litendonnéesdesvaleurspourletableaucourbe;–faitappelàlafonctionprécédentepourlesafficher.

1.Ilsuffitdedéclareruntableaudestructures:structpointcourbe[NP];

2.Commecourbeestuntableau,onnepeutqu’entransmettrel’adresseenargumentdeaffiche.Ilestpréférabledeprévoirégalementenargumentlenombredepoints.Voicicequepourraitêtrenotrefonction:voidaffiche(pointcourbe[],intnp)

/*courbe:adressedelapremièrestructuredutableau*/

/*(onpourraitécrirepoint*courbe)*/

/*np:nombredepointsdelacourbe*/

{inti;

for(i=0;i<np;i++)

cout<<"point"<<courbe[i].c<<"decoordonnées"

<<courbe[i].x<<""<<courbe[i].x<<"\n";

}

Commepour n’importe quel tableau à une dimension transmis en argument, il estpossibledenepasenmentionner ladimensiondans l’en-têtede la fonction.Bienentendu, comme, en fait, l’identificateur courbe n’est qu’unpointeur de type point *(pointeur sur la première structure du tableau), nous aurions pu également écrirepoint*courbe.

Notez que, comme à l’accoutumée, le « formalisme tableau » et le « formalismepointeur»peuventêtreindifféremmentutilisés(voirecombinés).Parexemple,notrefonctionauraitpuégalements’écrire:

106

Page 108: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

voidaffiche(point*courbe,intnp)

{point*adp;

inti;

for(i=0,adp=courbe;i<np;i++,adp++)

cout<<"point"<<(courbe+i)->c<<"decoordonnées"

<<(courbe+i)->x<<(courbe+i)->y);

}

3.Voicicequepourraitdonnerleprogrammedemandé:#include<iostream>

usingnamespacestd;

constintNP=4;//nombredepointsd’unecourbe

structpoint

{charc;

intx,y;

};

voidaffiche(pointcourbe[],intnp)

{

inti;

for(i=0;i<np;i++)

cout<<"point"<<courbe[i].c<<"decoordonnées"

<<courbe[i].x<<""<<courbe[i].x<<"\n";

}

main()

{pointcourbe[NP];

inti;

voidaffiche(point[],int);

/*lecturedesdifférentspointsdelacourbe*/

for(i=0;i<NP;i++)

{cout<<"nom(1caractère)etcoordonnéespoint"<<i+1<<"\n";

cin>>courbe[i].c>>courbe[i].x>>courbe[i].y;

}

affiche(courbe,NP);

}

107

Page 109: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice51

ÉnoncéÉcrire le programme de la question 3 de l’exercice précédent, sans utiliser destructures.Onprévoiratoujoursunefonctionpourlirelesinformationsrelativesàunpoint.

Ici,ilnousfautobligatoirementprévoir3tableauxdifférentsdemêmetaille:unpourlesnomsdepoints,unpourleursabscissesetunpourleursordonnées.Leprogrammene présente pas de difficultés particulières (son principal intérêt est de pouvoir êtrecomparéauprécédent!).

#include<iostream>

usingnamespacestd;

constintNP=4;//nombredepointsd'unecourbe

main()

{charc[NP];//nomsdesdifférentspoints

intx[NP];//abscissesdesdifférentspoints

inty[NP];//ordonnéesdesdifférentspoints

inti;

voidaffiche(char[],int[],int[],int);

/*lecturedesdifférentspointsdelacourbe*/

for(i=0;i<NP;i++)

{cout<<"nom(1caractère)etcoordonnéespoint"

<<i+1<<":\n";

cin>>c[i]>>x[i]>>y[i];

}

affiche(c,x,y,NP);

}

voidaffiche(charc[],intx[],inty[],intnp)

{for(inti=0;i<np;i++)

cout<<"point"<<c[i]<<"decoordonnées"

<<x[i]<<""<<y[i]<<"\n";

}

108

Page 110: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice52

ÉnoncéSoientlesdeuxmodèlesdestructuredateetpersonnedéclarésainsi:

contintLG_NOM=30;

structdate

{intjour;

intmois;

intannee;

};

structpersonne

{charnom[LG_NOM+1];//chaînedecaractères(destyleC)

//représentantlenom

structdatedate_embauche;

structdatedate_poste;

};

Écrire une fonction qui reçoit en argument une structure de type personne et qui enremplitlesdifférentschampsavecundialogueseprésentantsousl’unedes2formessuivantes:

nom:DUPONT

dateembauche(jjmmaa):16175

dateposte=dateembauche?(O/N):O

nom:DUPONT

dateembauche(jjmmaa):10381

dateposte=dateembauche?(O/N):N

dateposte(jjmmaa):23891

Notre fonction doitmodifier le contenu d’une structure de type personne ; il est doncnécessaire qu’elle en reçoive la référence ou l’adresse en argument. Ici, l’énoncén’imposantriendeparticulier,nouschoisironsunetransmissionparréférence.Voicicequepourraitêtrelafonctiondemandée:

voidremplit(personne&p)

{charrep;//pourlireuneréponsedetypeO/N

cout<<"nom:";

cin>>p.nom;//attention,pasdecontrôledelongueur

cout<<"dateembauche(jjmmaa):";

cin>>p.date_embauche.jour

>>p.date_embauche.mois

>>p.date_embauche.annee;

cout<<"dateposte=dateembauche?(O/N):";

cin>>rep;

if(rep=='O')p.date_poste=p.date_embauche;

else{cout<<"dateposte(jjmmaa):";

cin>>p.date_poste.jour

>>p.date_poste.mois

109

Page 111: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

>>p.date_poste.annee;

}

}

Voici, à titre indicatif, un petit programme d’essai de notre fonction (sa compilationnécessitelesdéclarationsdesstructuresdateetpersonne):

main()

{voidremplit(personne&);//déclarationremplit

personnebloc;

remplit(bloc);

cout<<"nom:"<<bloc.nom<<"\ndateembauche:"

<<bloc.date_embauche.jour<<""

<<bloc.date_embauche.mois<<""

<<bloc.date_embauche.annee<<"\n"

<<"dateposte:"

<<bloc.date_poste.jour<<""

<<bloc.date_poste.mois<<""

<<bloc.date_poste.annee;

}

110

Page 112: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre6DeCàC++

N.B.Cechapitreconstitue le«pointd’entrée»de l’ouvragepour lesprogrammeursabordantl’étudedeC++,enayantdéjàuneconnaissancedulangageC.Iln’apasàêtreprisencompteparlesautresprogrammeursquipourronttoutsimplementl’ignorer.

111

Page 113: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

C++estpresqueunsur-ensembleduC,telqu’ilestdéfiniparlanormeANSI.Seulesquelques incompatibilités existent ; nous rappelons ici les principales. Par ailleurs,C++dispose,parrapportauCANSI,d’uncertainnombredespécificitésquinesontpas véritablement axées sur la programmation orientée objet. Elles sont égalementexaminéesici.

DéclarationsdefonctionsEnC++, toute fonctionutiliséedansun fichier sourcedoit obligatoirement avoir faitl’objet:

soitd’unedéclarationsousformed’unprototype (ilpréciseàlafois lenomdelafonction, le type de ses arguments éventuels et le type de sa valeur de retour),commedanscetexemple:floatfexp(int,double,char*);

soitd’unedéfinitionpréalableauseindumêmefichiersource(cederniercasétantd’ailleurspeuconseillé,danslamesureoùdesproblèmesrisquentd’apparaîtredèslorsqu’onsépareladitefonctiondufichiersourceenquestion).

EnC,unefonctionpouvaitnepasêtredéclarée(auquelcasonconsidérait,pardéfaut,quesavaleurderetourétaitdetypeint),ouencoredéclaréepartiellement(sansfournirletypedesesarguments),commedans:

floatfexp();

FonctionssansargumentsEnC++,unefonctionsansargumentsedéfinit(auniveaudel’en-tête)etsedéclare(auniveauduprototype)enfournissantune«listed’argumentsvide»commedans:

floatfct();

EnC,onpouvait indifféremmentutilisercettenotationoufaireappelaumot-clévoid,commedans:

floatfct(void)

Fonctionssansvaleurderetour

112

Page 114: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

EnC++,unefonctionsansvaleurderetoursedéfinit(en-tête)etsedéclare(prototype)obligatoirementàl’aidedumot-clévoid,commedans:

voidfct(int,double);

EnC,l’emploidumot-clévoidétait,danscecas,facultatif.

LequalificatifconstEnC++,unsymboleglobaldéclaréaveclequalificatifconst:

a une portée limitée au fichier source concerné, tandis qu’en C il pouvaitéventuellement être utilisé dans un autre fichier source (en utilisant le mot-cléextern);

peut être utilisé dans une expression constante (calculable au moment de lacompilation), alors qu’il ne pouvait pas l’être en C ; ce dernier point permetnotamment d’utiliser de tels symboles pour définir la taille d’un tableau (enC, ilfallaitobligatoirementavoir recoursàunedéfinitiondesymbolespar ladirective#define).

Letypevoid*EnC++,unpointeurdetypevoid*nepeutpasêtreconverti implicitement lorsd’uneaffectationenunpointeurd’unautretype;lachoseétaitpermiseenC.Bienentendu,enC++,ilrestepossibledefaireappelàl’opérateurdecast.

Nouvellespossibilitésd’entrées-sortiesC++ dispose de nouvelles facilités d’entrées-sorties. Bien qu’elles soient fortementliées à des aspects P.O.O. (surdéfinition d’opérateur en particulier), elles sontparfaitementutilisablesendehorsdececontexte.C’esttoutparticulièrementlecasdespossibilités d’entrées-sorties conversationnelles (clavier, écran) qui remplacentavantageusementlesfonctionsprintfetscanf.Ainsi:

cout<<expression1<<expression2<<.........<<expressionn

affichesurleflotcout (connectépardéfautàlasortiestandardstdout) lesvaleursdesdifférentes expressions indiquées, selonuneprésentationadaptée à leur type (on saitdistinguerlesattributsdesigneetonpeutafficherdesvaleursdepointeurs).Demême:

cin>>lvalue1>>lvalue2>>.........>>lvaluen

113

Page 115: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

litsurleflotcin(connectépardéfautàl’entréestandardstdin)desinformationsdel’undes typeschar,short,int,long,float,doubleouchar* (on sait distinguer les attributs designe ; en revanche, lespointeursne sontpasadmis).Lesconventionsd’analysedescaractèreslussontcomparablesàcellesdescanf,aveccetteprincipaledifférencequela lectured’uncaractèrecommencepar sauter lesespacesblancs (espace, tabulationhorizontale,tabulationverticale,findeligne,changementdepage).

NouvelleformedecommentairesLesdeuxcaractères//permettentd’introduiredescommentairesdefindeligne: toutce qui suit ces caractères, jusqu’à la fin de la ligne, est considéré comme uncommentaire.

EmplacementlibredesdéclarationsEnC++,iln’estplusnécessairederegrouperlesdéclarationsendébutdefonctionouen début de bloc. Il devient ainsi possible d’employer des expressions dans desinitialisations,commedanscetexemple:

intn;

.....

n=...;

.....

intq=2*n-1;

.....

Cespossibilitéss’appliquentégalementauxinstructionsstructuréesfor,switch,whileetdo...while,commedanscetexemple:

for(inti=0;...;...)//laportéedeiestlimitéeaublocquisuit

{.....

}

LatransmissionparréférenceEn faisant suivre du symbole & le type d’un argument dans l’en-tête (et dans leprototype)d’unefonction,onréaliseunetransmissionparréférence.Celasignifiequeleséventuellesmodificationseffectuéesauseindelafonctionporterontsurl’argumenteffectifdel’appeletnonplussurunecopie.Onnoteraqu’alorsl’argumenteffectifdoitobligatoirement être une lvalue du même type que l’argument muet correspondant.Toutefois,si l’argumentmuetest,desurcroît,déclaréavecl’attributconst, la fonctionreçoit quandmêmeune copie de l’argument effectif correspondant, lequel peut alorsêtreuneconstanteouuneexpressiond’untypesusceptibled’êtreconvertidansletype

114

Page 116: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

attendu.

Cespossibilitésdetransmissionparréférences’appliquentégalementàunevaleurderetour(danscecas,lanotiondeconstancen’aplusdesignification).

La notion de référence est théoriquement indépendante de celle de transmissiond’argument;enpratique,elleestrarementutiliséeendehorsdececontexte.

LesargumentspardéfautDans ladéclarationd’une fonction (prototype), il estpossibledeprévoirpourunouplusieursarguments(obligatoirement lesderniersde la liste)desvaleurspardéfaut ;elles sont indiquéespar le signe =, à la suite du type de l’argument commedans cetexemple:

floatfct(char,int=10,float=0.0);

Cesvaleurspardéfautserontalorsutiliséeslorsqu’onappelleraladitefonctionavecunnombre d’arguments inférieur à celui prévu. Par exemple, avec la précédentedéclaration,l’appelfct('a')seraéquivalentàfct('a',10,0.0) ;demême,l’appelfct('x',12)seraéquivalentàfct('x',12,0.0).Enrevanche,l’appelfct()seraillégal.

SurdéfinitiondefonctionsEn C++, il est possible, au sein d’un même programme, que plusieurs fonctionspossèdent lemêmenom.Danscecas, lorsque lecompilateurrencontre l’appeld’unetellefonction,ileffectuelechoixdela«bonne»fonctionentenantcomptedelanaturedes arguments effectifs. D’une manière générale, si les règles utilisées par lecompilateur pour sa recherche sont assez intuitives, leur énoncé précis est assezcomplexeetnousnelerappelleronspasici(onletrouvera,parexemple,dansl’annexede nos différents ouvrages consacrés à C++ et publiés également aux ÉditionsEyrolles.) Signalons simplement que ces règles peuvent faire intervenir toutes lesconversionsusuelles (promotionsnumériqueset conversions standards, cesdernièrespouvant être dégradantes), ainsi que les conversions définies par l’utilisateur en casd’argumentdetypeclasse,àconditionqu’aucuneambiguïtén’apparaisse.

Gestiondynamiquedelamémoire

115

Page 117: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

EnC++,lesfonctionsmalloc,calloc...etfree sont remplacéesavantageusementpar lesopérateursnewetdelete.

Sitypereprésenteladescriptiond’untypeabsolumentquelconqueetsinreprésenteuneexpressiond’untypeentier(généralementlongouunsignedlong),l’expression:

newtype[n]

allouel’emplacementnécessairepournélémentsdutypeindiquéetfournitenrésultatunpointeur(detypetype*)surlepremierélément.Encasd’échecd’allocation,ilyadéclenchement d’une exception bad_alloc (les exceptions font l’objet du chapitre 19).L’indication n est facultative : avec new type, on obtient un emplacement pour unélémentdutypeindiqué,commesil’onavaitécritnewtype[1].

L’expression:deleteadresse//ilexisteuneautresyntaxepourlestableauxd’objets

//voirchap.9

libèreunemplacementpréalablementallouéparnewà l’adresse indiquée. Iln’estpasnécessairederépéterlenombred’éléments,dumoinslorsqu’ilnes’agitpasd’objets,mêmesicelui-ciestdifférentde1.Lecasdestableauxd’objetsestexaminéauchapitre9.

LesfonctionsenligneUne fonction en ligne (on dit aussi « développée ») est une fonction dont lesinstructionssontincorporéesparlecompilateur(danslemoduleobjetcorrespondant)àchaqueappel.Celaévitelapertedetempsnécessaireàunappelusuel(changementdecontexte, copie des valeurs des arguments sur la « pile »...) ; en revanche, lesinstructionsenquestionsontgénéréesplusieursfois.

Les fonctions « en ligne» offrent lemême intérêt que lesmacros, sans présenter derisques d’effets de bord. Une fonction en ligne est nécessairement définie en mêmetempsqu’elleestdéclarée(ellenepeutplusêtrecompiléeséparément)etsonen-têteestprécédédumot-cléinline,commedans:

inlinefct(...){.....}

116

Page 118: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice53

ÉnoncéQuelleserreursserontdétectéesparuncompilateurC++danscefichiersourcequiestacceptéparuncompilateurC?

main()

{

inta=10,b=20,c;

c=g(a,b);

printf("valeurdeg(%d,%d)=%d",a,b,c);

}

g(intx,inty)

{

return(x*x+2*x*y+y*y);

}

1.Lafonctiongdoitobligatoirementfairel’objetd’unedéclaration(souslaformed’unprototype)dans la fonctionmain.Parexemple,onpourrait introduire (n’importeoùavantl’appeldeg):intg(int,int);

ouencore:intg(intx,inty);

Rappelonsque,danscederniercas,lesnomsxetysontfictifs:ilsn’ontaucunrôledanslasuiteetilsn’interfèrentnullementavecd’autresvariablesdemêmenomquipourraientêtredéclaréesdanslamêmefonction(icimain).

2.Lafonctionprintfdoit,elleaussi,comme toutes les fonctionsC++(lecompilateurn’étant pas enmesurededistinguer les fonctionsde la bibliothèquedes fonctionsdéfinies par l’utilisateur), faire l’objet d’un prototype.Naturellement, il n’est pasnécessairedel’écrireexplicitement; ilestobtenuparincorporationdufichieren-têtecorrespondant:#include<cstdio>

Lessymbolesdéfinisdanscefichierappartiennentàl’espacedenomsstd,desortequ’ilfautégalementprévoiruneinstruction:usingnamespacestd;

NotezquecertainscompilateursCrefusentl’absencedeprototypepourunefonctiondelabibliothèquestandardtellequeprintf(maislanormeANSIn’imposaitrienà

117

Page 119: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

cesujet!).

118

Page 120: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice54

ÉnoncéÉcrirecorrectementenCceprogrammequiestcorrectenC++:

#include<cstdio>

usingnamespacestd;

constintnb=10;

constintexclus=5;

main()

{

intvaleurs[nb];

inti,nbval=0;

printf("donnez%dvaleurs:\n",nb);

for(i=0;i<nb;i++)scanf("%d",&valeurs[i]);

for(i=0;i<nb;i++)

switch(valeurs[i])

{caseexclus-1:

caseexclus:

caseexclus+1:nbval++;

}

printf("%dvaleurssontinterdites",nbval);

}

EnC,lessymbolesnbetexclusnesontpasutilisablesdansdesexpressionsconstantes.Il fautdonc lesdéfinirsoitcommedesvariables,soità l’aided’unedirective#definecommesuit:

#include<stdio.h>

#defineNB10

#defineEXCLUS5

main()

{

intvaleurs[NB];

inti;

intnbval=0;

printf("donnez%dvaleurs:\n",NB);

for(i=0;i<NB;i++)scanf("%d",&valeurs[i]);

for(i=0;i<NB;i++)

switch(valeurs[i])

{caseEXCLUS-1:

caseEXCLUS:

caseEXCLUS+1:nbval++;

}

printf("%dvaleurssontinterdites",nbval);

}

119

Page 121: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice55

ÉnoncéModifierleprogrammeCsuivant,defaçonqu’ilsoitcorrectenC++etqu’ilnefasseappelqu’auxnouvellespossibilitésd’entrées-sortiesdeC++,c’est-à-direqu’ilévitelesappelsàprintfetscanf:

#include<stdio.h>

main()

{

intn;floatx;

printf("donnezunentieretunflottant\n");

scanf("%d%e",&n,&x);

printf("leproduitde%dpar%e\n'est:%e",n,x,n*x);

}

#include<iostream>

usingnamespacestd;

main()

{

intn;floatx;

cout<<"donnezunentieretunflottant\n";

cin>>n>>x;

cout<<"leproduitde"<<n<<"par"<<x<<"\n'est:"<<n*x;

}

120

Page 122: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice56

ÉnoncéÉcrire une fonction permettant d’échanger les contenus de 2 variables de type intfourniesenargument:

a. en transmettant l’adresse des variables concernées (seuleméthode utilisable enC);

b.enutilisantlatransmissionparréférence.

Danslesdeuxcas,onécriraunpetitprogrammed’essai(main)delafonction.

a.Aveclatransmissiondesadressesdesvariables#include<iostream>

usingnamespacestd;

main()

{voidechange(int*,int*);//prototypedelafonctionechange

intn=15,p=23;

cout<<"avant:"<<n<<""<<p<<"\n";

echange(&n,&p);

cout<<"après:"<<n<<""<<p<<"\n";

}

voidechange(int*a,int*b)

{intc;//pourlapermutation

c=*a;

*a=*b;

*b=c;

}

b.Avecunetransmissionparréférence#include<iostream>

usingnamespacestd;

main()

{voidechange(int&,int&);//prototypedelafonctionechange

intn=15,p=23;

cout<<"avant:"<<n<<""<<p<<"\n";

echange(n,p);//attentionnetnon&n,petnon&p

cout<<"après:"<<n<<""<<p<<"\n";

}

voidechange(int&a,int&b)

{intc;//pourlapermutation

c=a;

a=b;

b=c;

}

121

Page 123: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice57

ÉnoncéSoitlemodèledestructuresuivant:

structessai

{intn;

floatx;

};

Écrire une fonctionnommée raz permettant de remettre à zéro les 2 champs d’unestructuredecetypetransmiseenargument:

a.paradresse;

b.parréférence.

Danslesdeuxcas,onécriraunpetitprogrammed’essaidelafonction;ilafficheralesvaleursd’unestructuredecetype,aprèsappeldeladitefonction.

a.Avecunetransmissiond’adresse#include<iostream>

usingnamespacestd;

structessai

{intn;

floatx;

};

voidraz(structessai*ads)

{ads->n=0;//ouencore(*ads).n=0;

ads->x=0.0;//ouencore(*ads).x=0.0;

}

main()

{structessais;

raz(&s);

cout<<"valeursaprèsraz:"<<s.n<<""<<s.x;

}

b.Avecunetransmissionparréférence#include<iostream>

usingnamespacestd;

structessai

{intn;

floatx;

};

voidraz(structessai&s)

{s.n=0;

s.x=0.0;

}

122

Page 124: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

main()

{structessais;

raz(s);//notezbiensetnon&s!

cout<<"valeursaprèsraz:"<<s.n<<""<<s.x;

}

123

Page 125: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice58

ÉnoncéSoientlesdéclarations(C++)suivantes:

intfct(int);//fonctionI

intfct(float);//fonctionII

voidfct(int,float);//fonctionIII

voidfct(float,int);//fonctionIV

intn,p;

floatx,y;

charc;

doublez;

Les appels suivants sont-ils corrects et, si oui, quelles seront les fonctionseffectivementappeléesetlesconversionséventuellementmisesenplace?

a.fct(n);

b.fct(x);

c.fct(n,x);

d.fct(x,n);

e.fct(c);

f.fct(n,p);

g.fct(n,c);

h.fct(n,z);

i.fct(z,z);

Lescasa,b,cetdneposentaucunproblème.IlyarespectivementappeldesfonctionsI,II,IIIetIV,sansqu’aucuneconversiond’argumentnesoitnécessaire.

e.AppeldelafonctionI,aprèsconversiondelavaleurdecenint.f.Appelincorrect,comptetenudesonambiguïté;deuxpossibilitésexistenteneffet:

conservern,convertirpenfloatetappelerlafonctionIIIou,aucontraire,convertirnenfloat,conserverpetappelerlafonctionIV.

g.AppeldelafonctionIII,aprèsconversiondecenfloat.h.AppeldelafonctionIII,aprèsconversion(dégradante)dezenfloat.

124

Page 126: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

i.Appelincorrect,comptetenudesonambiguïté;deuxpossibilitésexistenteneffet:convertirlepremierargumentenfloatet lesecondenintetappeler lafonctionIIIou,aucontraire,convertirlepremierargumentenintetlesecondenfloatetappelerlafonctionIV.Notezque,danslesdeuxcas,ils’agitdeconversionsdégradantes.

125

Page 127: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice59

ÉnoncéÉcrireplussimplementenC++lesinstructionssuivantes,enutilisantlesopérateursnewetdelete:

int*adi;

double*add;

.....

adi=malloc(sizeof(int));

add=malloc(sizeof(double)*100);

int*adi;

double*add;

.....

adi=newint;

add=newdouble[100];

On peut éventuellement tenir compte des possibilités de déclarations dynamiquesoffertesparC++(c’est-à-direquel’onpeutintroduireunedéclarationàn’importequelemplacementd’unprogramme),etécrire,parexemple:

int*adi=newint;

double*add=newdouble[100];

126

Page 128: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice60

ÉnoncéÉcrire plus simplement en C++, en utilisant les spécificités de ce langage, lesinstructionsCsuivantes:

double*adtab;

intnval;

.....

printf("combiendevaleurs?");

scanf("%d",&nval);

adtab=malloc(sizeof(double)*nval);

double*adtab;

intnval;

.....

cout<<"combiendevaleurs?";

cin>>nval;

adtab=newdouble[nval];

127

Page 129: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice61

Énoncé

a.Transformerleprogrammesuivantpourquelafonctionfctdevienneunefonctionenligne.#include<iostream>

usingnamespacestd;

main()

{

intfct(char,int);//déclaration(prototype)defct

intn=150,p;

charc='s';

p=fct(c,n);

cout<<"fct(\'"<<c<<"\',"<<n<<")vaut:"<<p;

}

intfct(charc,intn)//définitiondefct

{

intres;

if(c=='a')res=n+c;

elseif(c=='s')res=n-c;

elseres=n*c;

returnres;

}

b.Commentfaudrait-ilprocédersil’onsouhaitaitquelafonctionfct soitcompiléeséparément?

a. Nous devons donc d’abord déclarer (et définir en même temps) la fonction fctcommeunefonctionenligne.Leprogrammemains’écritdelamêmemanière,sicen’est que la déclaration de fct n’y est plus nécessaire puisqu’elle apparaîtauparavant.#include<iostream>

usingnamespacestd;

inlineintfct(charc,intn)

{intres;

if(c=='a')res=n+c;

elseif(c=='s')res=n-c;

elseres=n*c;

returnres;

}

main()

{intn=150,p;

charc='s';

p=fct(c,n);

cout<<"fct(\'"<<c<<"\',"<<n<<")vaut:"<<p;

}

b.Ils’agitenfaitd’unequestionpiège.Eneffet,lafonctionfctétantenligne,ellene

128

Page 130: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

peutplusêtrecompiléeséparément.Ilestcependantpossibledelaconserverdansun fichier d’extension h et d’incorporer simplement ce fichier par #include pourcompilerlemain.Cettedémarcheserencontrerad’ailleursfréquemmentdanslecasdeclassescomportantdesfonctionsenligne.Alors,dansunfichierd’extensionh,ontrouvera la déclaration de la classe en question, à l’intérieur de laquelleapparaîtrontles«déclarations-définitions»desfonctionsenligne.

129

Page 131: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre7Notionsdeclasse,constructeuret

destructeur

130

Page 132: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

LespossibilitésdeProgrammationOrientéeObjetdeC++reposentsurleconceptdeclasse.Uneclasseestlagénéralisationdelanotiondetypedéfiniparl’utilisateur,danslequelsetrouventassociéesàlafoisdesdonnées(onparlede«membresdonnée»)etdesfonctions(onparlede«fonctionsmembre»oudeméthodes).EnP.O.O.pure,lesdonnéessont«encapsulées»,cequisignifiequeleuraccèsnepeutsefairequeparlebiais des méthodes. C++ vous autorise à n’encapsuler qu’une partie seulement desdonnéesd’uneclasse.

En toute rigueur,C++vouspermetégalementd’associerdes fonctionsmembreàdesstructuresouàdesunions.Danscecas,toutefois,aucuneencapsulationn’estpossible(cequirevientàdirequetouslesmembressont«publics»).

Déclarationetdéfinitiond’uneclasseLadéclaration d’une classe précise quels sont lesmembres (données ou fonctions)publics(c’est-à-direaccessiblesàl’utilisateurdelaclasse)etquelssontlesmembresprivés (inaccessibles à l’utilisateur de la classe).On utilise pour cela lesmots-cléspublic et private, comme dans cet exemple dans lequel la classe point comporte deuxmembresdonnéeprivésxetyettroisfonctionsmembrespubliquesinitialise,deplaceetaffiche:

/*------------Déclarationdelaclassepoint-------------*/

classpoint

{/*déclarationdesmembresprivés*/

private:

intx;

inty;

/*déclarationdesmembrespublics*/

public:

voidinitialise(int,int);

voiddeplace(int,int);

voidaffiche();

};

Ladéfinitiond’uneclasseconsisteàfournirlesdéfinitionsdesfonctionsmembre.Onindiquealorslenomdelaclassecorrespondante,àl’aidedel’opérateurderésolutionde portée (::). Au sein de la définition même, les membres (privés ou publics —données ou fonctions) sont directement accessibles sans qu’il soit nécessaire depréciserlenomdelaclasse.Voici,parexemple,cequepourraitêtreladéfinitiondela

131

Page 133: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

fonctioninitialisedelaclasseprécédente:voidpoint::initialise(intabs,intord)

{

x=abs;y=ord;

}

Ici,xetyreprésententimplicitementlesmembresxetyd’unobjetdelaclassepoint.

1. Les mots-clés public et private peuvent apparaître à plusieurs reprises dans ladéclarationd’unemêmeclasse.Unedéclarationtellequelasuivanteesttoutàfaitenvisageable:classX

{private:

...

public:

...

private:

...

public:

...

};

2.Commeonleverradanslechapitreconsacréàl’héritage,ilexisteuntroisièmemot-clé,protected,quin’intervientqu’encasd’existencedeclassesdérivées.

Utilisationd’uneclasseOndéclareun«objet»d’untypeclassedonnéenfaisantprécédersonnomdeceluidelaclasse,commedansl’instructionsuivantequidéclaredeuxobjetsaetbdetypepoint:

pointa,b;

Onpeutaccéderàn’importequelmembrepublic(donnéeoufonction)d’uneclasseenutilisantl’opérateur".".Parexemple:

a.initialise(5,2);

appellelafonctionmembreinitialisedelaclasseàlaquelleappartientl’objeta,c’est-à-dire,ici,laclassepoint.

AffectationentreobjetsC++autorise l’affectationd’unobjetd’un typedonnéàunautreobjetdemême type.Dans ce cas, il y a (tout naturellement) recopie des valeurs des champs de données(qu’ils soient publics ou privés). Toutefois, si, parmi ces champs, se trouvent des

132

Page 134: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

pointeurs,lesemplacementspointésneserontpassoumisàcetterecopie.Siunteleffetestnécessaire(etilleserasouvent!),ilnepourraêtreobtenuqu’en«surdéfinissant»l’opérateur d’affectation pour la classe concernée (voyez le chapitre consacré à lasurdéfinitiond’opérateurs).

ConstructeuretdestructeurUnefonctionmembreportant lemêmenomquesaclassesenommeunconstructeur.Dès qu’une classe comporte un constructeur (aumoins un), il n’est plus possible dedéclarerunobjetdu typecorrespondant, sans fournirdesvaleurspour les argumentsrequis par ce constructeur (sauf si ce dernier ne possède aucun argument). Leconstructeurestappeléaprèsl’allocationdel’espacemémoiredestinéàl’objet.

Pardéfinition,unconstructeurnerenvoiepasdevaleur(aucuneindicationdetype,pasmêmevoid,nedoitfigurerdevantsadéclarationousadéfinition).

Unefonctionmembreportantlemêmenomquesaclasse,précédédusymboletilde(~),se nomme undestructeur. Le destructeur est appelé avant la libération de l’espacemémoire associé à l’objet. Par définition, un destructeur ne peut pas comporterd’arguments et il ne renvoie pas de valeur (aucune indication de type ne doit êtreprévue).

MembresdonnéestatiquesUnmembredonnéedéclaréavec l’attribut static estpartagépar tous lesobjetsde lamêmeclasse. Il existemême lorsque aucunobjet de cette classe n’a été déclaré.Unmembre donnée statique doit être initialisé explicitement, à l’extérieur de la classe(mêmes’ilestprivé),enutilisantl’opérateurderésolutiondeportée(::)pourspécifiersaclasse.Engénéral,soninitialisationsefaitdansladéfinitiondelaclasse.

Exploitationd’uneclasseEnpratique,àl’utilisateurd’uneclasse(onditsouventle«client»),onfournira:

un fichier en-tête contenant la déclaration de la classe : l’utilisateur l’employantdevral’incluredanstoutprogrammefaisantappelàlaclasseenquestion;

unmoduleobjetrésultantdelacompilationdufichiersourcecontenantladéfinitiondelaclasse,c’est-à-direladéfinitiondesesfonctionsmembre.

133

Page 135: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice62

ÉnoncéRéaliseruneclassepointpermettantdemanipulerunpointd’unplan.Onprévoira:

•unconstructeurrecevantenargumentslescoordonnées(float)d’unpoint;

• une fonction membre deplace effectuant une translation définie par ses deuxarguments(float);

• une fonction membre affiche se contentant d’afficher les coordonnéescartésiennesdupoint.

Lescoordonnéesdupointserontdesmembresdonnéeprivés.

Onécriraséparément:

•unfichiersourceconstituantladéclarationdelaclasse;

•unfichiersourcecorrespondantàsadéfinition.

Écrire,parailleurs,unpetitprogrammed’essai(main)déclarantunpoint,l’affichant,ledéplaçantetl’affichantànouveau.

a.Fichiersource(nommépoint.h)contenantladéclarationdelaclasse/*fichierPOINT1.H*/

/*déclarationdelaclassepoint*/

classpoint

{

floatx,y;//coordonnées(cartésiennes)dupoint

public:

point(float,float);//constructeur

voiddeplace(float,float);//déplacement

voidaffiche();//affichage

};

b.Fichiersourcecontenantladéfinitiondelaclasse/*définitiondelaclassepoint*/

#include"point1.h"

#include<iostream>

usingnamespacestd;

point::point(floatabs,floatord)

{x=abs;y=ord;

}

voidpoint::deplace(floatdx,floatdy)

{x=x+dx;y=+dy;

134

Page 136: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

voidpoint::affiche()

{cout<<"Mescoordonnéescartésiennessont"<<x<<""<<y<<"\n";

}

Notezque,pourcompilercefichiersource,ilestnécessaired’inclurelefichiersource,nomméicipoint1.h,contenantladéclarationdelaclassepoint.

c.Exempled'utilisationdelaclasse#include<iostream>

usingnamespacestd;

#include"point1.h"

main()

{

pointp(1.25,2.5);//constructiond'unpointdecoordonnées1.252.5

p.affiche();//affichagedecepoint

p.deplace(2.1,3.4);//déplacementdecepoint

p.affiche();//nouvelaffichage

}

Bienentendu,pourpouvoirexécuterceprogramme,ilseranécessaired’introduire,lorsde l’édition de liens, le module objet résultant de la compilation du fichier sourcecontenantladéfinitiondelaclassepoint.

Notezque,généralement,lefichierpoint1.hcontiendradesdirectivesconditionnellesdecompilation, afin d’éviter les risques d’inclusion multiple. Par exemple, on pourraprocéderainsi:

#ifndefPOINT1_H

#definePOINT1_H

.....

déclarationdelaclasse.....

.....

#endif

135

Page 137: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice63

ÉnoncéRéaliser une classe point, analogue à la précédente, mais ne comportant pas defonction affiche. Pour respecter le principe d’encapsulation des données, prévoirdeuxfonctionsmembrepubliques(nomméesabscisseetordonnee)fournissantenretourl’abscisse et l’ordonnéed’unpoint.Adapter le petit programmed’essai précédentpourqu’ilfonctionneaveccettenouvelleclasse.

Il suffit d’introduire deux nouvelles fonctions membre abscisse et ordonnee et desupprimerlafonctionaffiche.Lanouvelledéclarationdelaclasseestalors:

/*fichierPOINT2.H*/

/*déclarationdelaclassepoint*/

classpoint

{

floatx,y;//coordonnées(cartésiennes)dupoint

public:

point(float,float);//constructeur

voiddeplace(float,float);//déplacement

floatabscisse();//abscissedupoint

floatordonnee();//ordonnéedupoint

};

Voicisanouvelledéfinition:/*définitiondelaclassepoint*/

#include"point2.h"

#include<iostream>

usingnamespacestd;

point::point(floatabs,floatord)

{x=abs;y=ord;

}

voidpoint::deplace(floatdx,floatdy)

{x=x+dx;y=+dy;

}

floatpoint::abscisse()

{returnx;

}

floatpoint::ordonnee()

{returny;

}

Etlenouveauprogrammed’essai:/*exempled'utilisationdelaclassepoint*/

#include"point2.h"

#include<iostream>

usingnamespacestd;

main()

136

Page 138: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

{

pointp(1.25,2.5);//construction

//affichage

cout<<"Coordonnéescartésiennes:"<<p.abscisse()<<""

<<p.ordonnee()<<"\n";

p.deplace(2.1,3.4);//déplacement

//affichage

cout<<"Coordonnéescartésiennes:"<<p.abscisse()<<""

<<p.ordonnee()<<"\n";

}

DiscussionCetexemplemontrequ’ilesttoujourspossiblederespecterleprinciped’encapsulationen introduisant ce que l’on nomme des « fonctions d’accès ». Il s’agit de fonctionsmembre destinées à accéder (aussi bien en consultation — comme ici — qu’enmodification) auxmembres privés. L’intérêt de leur emploi (par rapport à un accèsdirectauxdonnéesqu’il faudraitalors rendrepubliques) résidedans lasouplessedemodificationde l’implémentationde laclassequiendécoule.Ainsi, ici, il est toutàfait possible de modifier la manière dont un point est représenté (par exemple, enutilisantsescoordonnéespolairesplutôtquesescoordonnéescartésiennes),sansquel’utilisateurdelaclassen’aitàsesoucierdecetaspect.C’estd’ailleurscequevousmontreral’exercice65ci-après.

137

Page 139: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice64

ÉnoncéAjouteràlaclasseprécédente(comportantunconstructeurettroisfonctionsmembredeplace,abscisseetordonnee)denouvellesfonctionsmembre:

•homothetiequieffectueunehomothétiedontlerapportestfournienargumentþ;

•rotationquieffectueunerotationdontl’angleestfournienargumentþ;

•rhoetthetaquifournissentenretourlescoordonnéespolairesdupoint.

Ladéclarationdelanouvelleclassepointdécouledirectementdel’énoncé:/*fichierPOINT3.H*/

/*déclarationdelaclassepoint*/

classpoint

{floatx,y;//coordonnées(cartésiennes)dupoint

public:

point(float,float);//constructeur

voiddeplace(float,float);//déplacement

voidhomothetie(float);//homothétie

voidrotation(float);//rotation

floatabscisse();//abscissedupoint

floatordonnee();//ordonnéedupoint

floatrho();//rayonvecteur

floattheta();//angle

};

Sa définition mérite quelques remarques. En effet, si homothetie ne présente aucunedifficulté, la fonction membre rotation nécessite quant à elle une transformationintermédiaire des coordonnées cartésiennes du point en coordonnées polaires. Demême,lafonctionmembrerhodoitcalculerlerayonvecteurd’unpointdontonconnaîtlescoordonnéescartésiennestandisquelafonctionmembrethetadoitcalculerl’angled’unpointdontonconnaîtlescoordonnéescartésiennes.

Le calcul de rayon vecteur étant simple, nous l’avons laissé figurer dans les deuxfonctionsconcernées(rotationetrho).Enrevanche,lecalculd’angleaétéréaliséparceque nous nommons une « fonction de service », c’est-à-dire une fonction qui n’ad’intérêt que dans la définition de la classe elle-même. Ici, il s’agit d’une fonctionindépendantemais,bienentendu,onpeutprévoirdesfonctionsdeservicesousformedefonctionsmembre(ellesserontalorsgénéralementprivées).

Voicifinalementladéfinitiondenotreclassepoint:

138

Page 140: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

/****************déclarationsdeservice*****************/

#include"point3.h"

#include<cmath>//poursqrtetatan

#include<iostream>

usingnamespacestd;

constfloatpi=3.141592653;//valeurdepi

floatangle(float,float);//fonctiondeservice(nonmembre)

/***************définitiondesfonctionsmembre**********/

point::point(floatabs,floatord)

{x=abs;y=ord;

}

voidpoint::deplace(floatdx,floatdy)

{x+=dx;y+=dy;

}

voidpoint::homothetie(floathm)

{x*=hm;y*=hm;

}

voidpoint::rotation(floatth)

{floatr=sqrt(x*x+y*y);//passageen

floatt=angle(x,y);//coordonnéespolaires

t+=th;//rotationth

x=r*cos(t);//retouren

y=r*sin(t);//coordonnéescartésiennes

}

floatpoint::abscisse()

{returnx;

}

floatpoint::ordonnee()

{returny;

}

floatpoint::rho()

{returnsqrt(x*x+y*y);

}

floatpoint::theta()

{returnangle(x,y);

}

/**********définitiondesfonctionsdeservice***********/

/*fonctiondecalculdel'anglecorrespondantauxcoordonnées*/

/*cartésiennesfourniesenargument*/

/*Onchoisitunedéterminationentre-piet+pi(0six=0)*/

floatangle(floatx,floaty)

{floata=x?atan(y/x):0;

if(y<0)if(x>=0)returna+pi;

elsereturna-pi;

returna;

}

139

Page 141: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice65

ÉnoncéModifier la classe point précédente, de manière que les données (privées) soientmaintenant les coordonnées polaires d’un point, et non plus ses coordonnéescartésiennes.Onéviterademodifier la déclarationdesmembrespublics, de sortequel’interfacedelaclasse(cequiestvisiblepourl’utilisateur)nechangepas.

Ladéclarationdelanouvelleclassedécouledirectementdel’énoncé:/*fichierPOINT4.H:déclarationdelaclassepoint*/

classpoint

{floatr,t;//coordonnées(polaires)dupoint

public:

point(float,float);//constructeur

voiddeplace(float,float);//déplacement

voidhomothetie(float);//homothétie

voidrotation(float);//rotation

floatabscisse();//abscissedupoint

floatordonnee();//ordonnéedupoint

floatrho();//rayonvecteur

floattheta();//angle

};

Encequiconcernesadéfinition,ilestmaintenantnécessairederemarquerque:

leconstructeurreçoittoujoursenargumentlescoordonnéescartésiennesd’unpoint;ildoitdoncopérerlestransformationsappropriées;

la fonction deplace reçoit un déplacement exprimé en coordonnées cartésiennes ; ilfaut donc tout d’abord déterminer les coordonnées cartésiennes du point aprèsdéplacement,avantderepasserencoordonnéespolaires.

Enrevanche,lesfonctionshomothetieetrotations’exprimenttrèssimplement.

Voiciladéfinitiondenotrenouvelleclasse(nousavonsfaitappelàlamêmefonctiondeserviceanglequedansl’exerciceprécédent):

#include"point4.h"

#include<cmath>//pourcos,sin,sqrtetatan

#include<iostream>

usingnamespacestd;

constintpi=3.141592635;//valeurdepi

/**********définitiondesfonctionsdeservice***********/

/*fonctiondecalculdel'anglecorrespondantauxcoordonnées*/

/*cartésiennesfourniesenargument*/

/*Onchoisitunedéterminationentre-piet+pi(0six=0)*/

140

Page 142: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

floatangle(floatx,floaty)

{floata=x?atan(y/x):0;

if(y<0)if(x>=0)returna+pi;

elsereturna-pi;

returna;

}

/**********définitiondesfonctionsmembre*****************/

point::point(floatabs,floatord)

{r=sqrt(abs*abs+ord*ord);

t=atan(ord/abs);

}

voidpoint::deplace(floatdx,floatdy)

{floatx=r*cos(t)+dx;//nouvelleabscisse

floaty=r*sin(t)+dy;//nouvelleordonnée

r=sqrt(x*x+y*y);

t=angle(x,y);

}

voidpoint::homothetie(floathm)

{r*=hm;

}

voidpoint::rotation(floatth)

{t+=th;

}

floatpoint::abscisse()

{returnr*cos(t);

}

floatpoint::ordonnee()

{returnr*sin(t);

}

floatpoint::rho()

{returnr;

}

floatpoint::theta()

{returnt;

}

141

Page 143: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice66

ÉnoncéSoitlaclassepointcrééedansl’exercice62,dontladéclarationétaitlasuivante:

classpoint

{floatx,y;

public:

point(float,float);

voiddeplace(float,float);

voidaffiche();

}

Adapter cette classe,demanièreque la fonctionmembre affiche fournisse, enplusdescoordonnéesdupoint,lenombred’objetsdetypepoint.

Il faut doncdéfinir un compteurdunombred’objets existant àunmomentdonné.Cecompteur doit être incrémenté à chaque création d’un nouvel objet, donc par leconstructeurpoint.Demême, il doit êtredécrémenté à chaquedestructiond’unobjet,donc par le destructeur de la classe point ; il faudra donc ajouter ici une fonctionmembrenommée~point.

Quantaucompteurproprementdit,nouspourrionscertesenfaireunevariableglobale,définieparexempleenmêmetempsque laclasse ;cettedémarcheprésente toutefoisdesrisquesd’effetsdebord(modificationaccidentelledelavaleurdecettevariable,depuis n’importe quel programme utilisateur). Il est plus judicieux d’en faire unmembreprivéstatique.

Voicilanouvelledéclarationdenotreclassepoint:/*fichierPOINT5.H*/

/*déclarationdelaclassepoint*/

classpoint

{

staticnb_pts;//compteurdunombred'objetscréés

floatx,y;//coordonnées(cartésiennes)dupoint

public:

point(float,float);//constructeur

~point();//destructeur

voiddeplace(float,float);//déplacement

voidaffiche();//affichage

};

Etvoicisanouvelledéfinition(notezl’initialisationdumembrestatique):#include"point5.h"

#include<iostream>

142

Page 144: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

usingnamespacestd;

intpoint::nb_pts=0;//initialisationobligatoirestatiquenb_pts

point::point(floatabs,floatord)//constructeur

x=abs;y=ord;

nb_pts++;//actualisationnbpoints

}

point::~point()//destructeur

{nb_pts--;//actualisationnbpoints

}

voidpoint::deplace(floatdx,floatdy)

{x=x+dx;y=+dy;

}

voidpoint::affiche()

{cout<<"Jesuisunpointparmi"<<nb_pts

<<"decoordonnées"<<x<<""<<y<<"\n";

}

Ilpourraitêtrejudicieuxdemunirnotreclassepointd’unefonctionmembrefournissantlenombred’objetsde typepointexistantàunmomentdonné.C’estcequenousvousproposeronsdansunexerciceduprochainchapitre.

143

Page 145: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice67

ÉnoncéRéaliser une classe nommée set_char permettant de manipuler des ensembles decaractères.Ondevrapouvoirréalisersuruntelensemblelesopérationsclassiquessuivantes : lui ajouter un nouvel élément, connaître son « cardinal » (nombred’éléments),savoirsiuncaractèredonnéluiappartient.

Ici,onn’effectueraaucuneallocationdynamiqued’emplacementsmémoire.Ilfaudradoncprévoir,enmembredonnée,untableaudetaillefixe.

Écrire,enoutre,unprogramme(main)utilisant laclasse set_char pourdéterminer lenombredecaractèresdifférentscontenusdansunmotluendonnée.

N.B. Le chapitre 21 vous montrera comment résoudre cet exercice à l’aide descomposantsstandardintroduitsparlanorme,etqu’ilnefautpaschercheràutiliserici.

Compte tenu des contraintes imposées par l’énoncé (pas de gestion dynamique), unesolutionconsiste àprévoirun tableaudans lequelunélémentde rang i précise si lecaractèredecodeiappartientounonàl’ensemble.Notezqu’ilestnécessairequeisoitpositifounul;ontravailleradonctoujourssurdescaractèresnonsignés.Latailledutableaudoitêtreégaleaunombredecaractèresqu’ilestpossibledereprésenterdansuneimplémentationdonnée(généralement256).

Lerestedeladéclarationdelaclassedécouledel’énoncé./*fichierSETCHAR1.H*/

/*déclarationdelaclasseset_char*/

#defineN_CAR_MAX256//onpourraitutiliserUCHAR_MAXdéfini

//dans<limits.h>

classset_char

{

unsignedcharens[N_CAR_MAX];

//tableaudesindicateurs(présent/absent)

//pourchacundescaractèrespossibles

public:

set_char();//constructeur

voidajoute(unsignedchar);//ajoutd'unélément

intappartient(unsignedchar);//appartenanced'unélément

intcardinal();//cardinaldel'ensemble

};

144

Page 146: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Ladéfinitiondelaclasseendécouleasseznaturellement:/*définitiondelaclasseset_char*/

#include"setchar1.h"

set_char::set_char()

{inti;

for(i=0;i<N_CAR_MAX;i++)ens[i]=0;

}

voidset_char::ajoute(unsignedcharc)

{ens[c]=1;

}

intset_char::appartient(unsignedcharc)

{returnens[c];

}

intset_char::cardinal()

{inti,n;

for(i=0,n=0;i<N_CAR_MAX;i++)if(ens[i])n++;

returnn;

}

Ilenvademêmepourleprogrammed’utilisation:/*utilisationdelaclasseset_char*/

#include<cstring>

#include"setchar1.h"

#include<iostream>

usingnamespacestd;

main()

{set_charens;

charmot[81];

cout<<"donnezunmot";

cin>>mot;

inti;

for(i=0;i<strlen(mot);i++)ens.ajoute(mot[i]);

cout<<"ilcontient"<<ens.cardinal()<<"caractèresdifférents";

if(ens.appartient('e'))cout<<"lecaractèreeestprésent\n";

elsecout<<"lecaractèreen'estpasprésent\n";

}

Sil’onavaitdéclarédetypecharlesargumentsdeajouteetappartient,onauraitalorspuaboutirsoitautypeunsignedchar,soitautypesignedchar, selon l’environnementutilisé.Dans le dernier cas, on aurait couru le risque de transmettre à l’une des fonctionsmembrecitéesunevaleurnégative,etpartantd’accéderàl’extérieurdutableauens.

DiscussionLetableauens[N_CHAR_MAX]occupeunoctetparcaractère;chacundecesoctetsneprendquel’unedesvaleurs0ou1;onpourraitéconomiserdel’espacemémoireenprévoyantseulement 1 bit par caractère. Les fonctions membre y perdraient toutefois ensimplicité,ainsiqu’envitesse.

145

Page 147: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Bien entendu, beaucoup d’autres implémentations sont possibles ; c’est ainsi, parexemple,que l’onpourrait fournir auconstructeurunnombremaximald’éléments, etallouerdynamiquementl’emplacementmémoirecorrespondant;toutefois,làencore,onperdrait lebénéficede lacorrespondance immédiateentreuncaractèreet lapositionde son indicateur. Notez toutefois que ce sera la seule possibilité réaliste lorsqu’ils’agiradereprésenterdesensemblesdanslesquelslenombremaximald’élémentsseratrèsgrand.

146

Page 148: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice68

ÉnoncéModifierlaclasseset_charprécédente,demanièreàdisposerdecequel’onnommeun«itérateur»sur lesdifférentsélémentsdel’ensemble.Ils’agitd’unmécanismepermettant d’accéder séquentiellement aux différents éléments. On prévoira troisnouvellesfonctionsmembre:init,quiinitialiseleprocessusd’exploration;prochain,qui fournit lavaleurde l’élément suivant lorsqu’il existe et existe, quiprécise s’ilexisteencoreunélémentnonexploré.

Oncompléteraalorsleprogrammed’utilisationprécédent,demanièrequ’ilaffichelesdifférentscaractèrescontenusdanslemotfourniendonnée.

N.B. Le chapitre 21 vous montrera comment résoudre cet exercice à l’aide descomposantsstandardintroduitsparlanorme,etqu’ilnefautpaschercheràutiliserici.

Comptetenudel’implémentationdenotreclasse,lagestiondumécanismed’itérationnécessite l’emploi d’un pointeur (que nous nommerons courant) sur un élément dutableauens.Nousconviendronsquecourantdésignelepremierélémentdeensnonencoretraitédans l’itération,c’est-à-direnonencorerenvoyépar la fonctionmembresuivant(nousaurionspuadopterlaconventioncontraire,àsavoirquecourantdésigneledernierélémenttraité).

Enoutre, pour faciliter la reconnaissancede la finde l’itération, nousutiliseronsunmembre donnée supplémentaire (fin) valant 0 dans les cas usuels, et 1 lorsqu’aucunélémentneseradisponible(poursuivant).

Lerôledelafonctioninitseradoncdefairepointercourantsurlapremièrevaleurnonnulledeenss’ilenexisteune;danslecascontraire,finseraplacéà1.

La fonction suivant fournira en retour l’élémentpointépar courant lorsqu’il existe (finnonnul)ou lavaleur0dans lecascontraire (il s’agit làd’uneconventiondestinéeàprotéger l’utilisateur ayant appelé cette fonction, alors qu’aucun élément n’était plusdisponible).Danslepremiercas,suivantrechercheraleprochainélémentdel’ensemble(enmodifiantlavaleurdefinlorsqu’untelélémentn’existepas).Notezbienqu’icilafonctionsuivantdoitrenvoyernonpasleprochainélément,maisl’élémentcourant.

147

Page 149: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Enfin, la fonction existe se contentera de renvoyer la valeur de fin puisque cettedernièreindiquel’existenceoul’inexistenced’unélémentcourant.

Voicilanouvelledéfinitiondelaclasseset_char:/*fichierSETCHAR2.H*/

/*déclarationdelaclasseset_char*/

#defineN_CAR_MAX256//onpourraitutiliserUCHAR_MAXdéfini

//dans<climits>

classset_char

{

unsignedcharens[N_CAR_MAX];

//tableaudesindicateurs(présent/absent)

//pourchacundescaractèrespossibles

intcourant;//positioncourantedansletableauens

intfin;//indiquesifinatteinte

public:

set_char();//constructeur

voidajoute(unsignedchar);//ajoutd'unélément

intappartient(unsignedchar);//appartenanced'unélément

intcardinal();//cardinaldel'ensemble

voidinit();//initialisationitération

unsignedcharsuivant();//caractèresuivant

intexiste();

};

Voiciladéfinitiondestroisnouvellesfonctionsmembreinit,suivantetexiste:voidset_char::init()

{courant=0;fin=0;

while((++courant<N_CAR_MAX)&&(!ens[courant]));

//silafindeensestatteinte,courantvautN_CAR_MAX

if(courant>=N_CAR_MAX)fin=1;

}

unsignedcharset_char::suivant()

{if(fin)return0;//aucasoùonseraitdéjàenfindeens

unsignedcharc=courant;//conservationducaractèrecourant

//etrecherchedusuivants'ilexiste

while((++courant<N_CAR_MAX)&&(!ens[courant]));

//silafindeensestatteinte,courantvautN_CAR_MAX

if(courant>=N_CAR_MAX)fin=1;//s'iln'yaplusdecaractère

returnc;

}

intset_char::existe()

{return(!fin);

}

Voicienfinl’adaptationduprogrammed’utilisation:#include<cstring>

#include"setchar2.h"

#include<iostream>

usingnamespacestd;

main()

{set_charens;

charmot[81];

cout<<"donnezunmot";

cin>>mot;

inti;

for(i=0;i<strlen(mot);i++)ens.ajoute(mot[i]);

cout<<"ilcontient"<<ens.cardinal()<<"caractèresdifférents"

<<"quisont:\n";

148

Page 150: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

ens.init();//inititérationsurlescaractèresdel'ensemble

while(ens.existe())

cout<<ens.suivant();

}

149

Page 151: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre8Propriétésdesfonctionsmembre

150

Page 152: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

SurdéfinitiondesfonctionsmembreetargumentspardéfautIl s’agit simplement de la généralisation aux fonctionsmembre des possibilités déjàoffertesparC++pourlesfonctions«ordinaires».

FonctionsmembreenligneIls’agitégalementdelagénéralisationauxfonctionsmembred’unepossibilitéoffertepour les fonctions ordinaires, avec une petite nuance concernant samise enœuvre ;pourrendre«enligne»unefonctionmembre,onpeut:

soit fournirdirectement ladéfinitiondelafonctiondans ladéclarationmêmedelaclasse ; dans ce cas, le qualificatif inline n’a pas à être utilisé, comme dans cetexemple:classtruc

{...

intfctenlig(int,float)

{définitiondefctenlig}

.....

};

soit procéder comme pour une fonction ordinaire, en fournissant une définition endehors de la déclaration de la classe ; dans ce cas, le qualificatif inline doitapparaître,àlafoisdevantladéclarationetdevantl’en-tête.

Casdesobjetstransmisenargumentsd’unefonctionmembreUnefonctionmembrereçoitimplicitementl’adressedel’objetl’ayantappelé.Mais,enoutre, il est toujours possible de lui transmettre explicitement un argument (ouplusieurs)dutypedesaclasse,oumêmedutyped’uneautreclasse.Danslepremiercas,lafonctionmembreauraaccèsauxmembresprivésdel’argumentenquestion(car,enC++, l’unitéd’encapsulationest laclasseelle-mêmeetnon l’objet).En revanche,dans le second cas, la fonction membre n’aura accès qu’aux membres publics de

151

Page 153: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

l’argument.

Un tel argument peut être transmis classiquement par valeur, par adresse ou parréférence.Avec la transmission par valeur, il y a recopie des valeurs desmembresdonnéedansunemplacementlocalàlafonctionappelée.Desproblèmespeuventsurgirdès lors que l’objet transmis en argument contient des pointeurs sur des partiesdynamiques. Ils seront réglés par l’emploi d’un « constructeur par recopie » (voirchapitresuivant).

Bienquecesoitd’unusagepluslimité,unefonctionordinairepeutégalementrecevoirun argument de type classe. Bien entendu, elle n’aura alors accès qu’aux membrespublics de cet argument. (Àmoins d’avoir été déclarée fonction amie, comme on leverradanslechapitrecorrespondant.)

CasdesfonctionsmembrefournissantunobjetenretourUnefonctionmembrepeutfournircommevaleurderetourunobjetdutypedesaclasseou d’un autre type classe (dans ce dernier cas, elle n’accédera bien sûr qu’auxmembrespublicsdel’objetenquestion).Latransmissionpeut,làencore,sefaireparvaleur,paradresseouparréférence.

Latransmissionparvaleurimpliqueunerecopiequiposedonclesmêmesproblèmesqueceuxévoqués ci-dessuspour lesobjets comportant despointeurs surdespartiesdynamiques.Quantaux transmissionsparadresseoupar référence,ellesdoiventêtreutilisées avec beaucoup de précautions, dans lamesure où, dans ce cas, on renvoie(généralement)l’adressed’unobjetallouéautomatiquement,c’est-à-diredontladuréedeviecoïncideaveccelledelafonction.

Autoréférence:lemot-cléthisAu sein d’une fonctionmembre, this représente un pointeur sur l’objet ayant appeléladitefonctionmembre.

FonctionsmembrestatiquesLorsqu’une fonctionmembre a une action indépendante d’un quelconque objet de saclasse, onpeut ladéclarer avec l’attribut static.Dans ce cas, une telle fonctionpeut

152

Page 154: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

êtreappelée,sansmentionnerd’objetparticulier,enpréfixantsimplementsonnomdunomdelaclasseconcernée,suividel’opérateurderésolutiondeportée(::).

FonctionsmembreconstantesOn peut déclarer des objets constants (à l’aide du qualificatif const). Dans ce cas,seuleslesfonctionsmembredéclarées(etdéfinies)aveccemêmequalificatif(exemplededéclaration:voidaffiche()const)peuventrecevoir(implicitementouexplicitement)enargumentunobjetconstant.

153

Page 155: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice69

ÉnoncéOnsouhaiteréaliseruneclassevecteur3dpermettantdemanipulerdesvecteursàtroiscomposantes.Onprévoitquesadéclarationseprésenteainsi:

classvecteur3d

{floatx,y,z;//pourles3composantes(cartésiennes)

.....

};

Onsouhaitepouvoirdéclarerunvecteur,soitenfournissantexplicitementses troiscomposantes, soit en en fournissant aucune, auquel cas le vecteur créé posséderatroiscomposantesnulles.Écrireleoulesconstructeurscorrespondants:

a.enutilisantdesfonctionsmembresurdéfinies;

b.enutilisantuneseulefonctionmembre;

c.enutilisantuneseulefonctionenligne.

Il s’agit de simples applications des possibilités de surdéfinition, d’arguments pardéfautetd’écritureenlignedesfonctionsmembres.

a./*déclarationdelaclassevecteur3d*/

classvecteur3d

{floatx,y,z;

public:

vecteur3d();//constructeursansarguments

vecteur3d(float,float,float);//constructeur3composantes

.....

};

/*définitiondesconstructeursdelaclassevecteur3d*/

vecteur3d::vecteur3d()

{x=0;y=0;z=0;

}

vecteur3d::vecteur3d(floatc1,floatc2,floatc3)

{x=c1;y=c2;z=c3;

}

b./*déclarationdelaclassevecteur3d*/

classvecteur3d

{floatx,y,z;

public:

154

Page 156: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

vecteur3d(float=0.0,float=0.0,float=0.0);//constructeur(unique)

.....

};

/*définitionduconstructeurdelaclassevecteur3d*/

vecteur3d::vecteur3d(floatc1,floatc2,floatc3)

{x=c1;y=c2;z=c3;

}

On notera toutefois qu’avec ce constructeur il est possible de déclarer un point enfournissant non seulement zéro ou trois composantes,mais éventuellement seulementuneoudeux.Cettesolutionn’estdoncpasrigoureusementéquivalenteàlaprécédente.

c./*déclarationdelaclassevecteur3d*/

classvecteur3d

{floatx,y,z;

public:

//constructeurunique"enligne"

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{x=c1;y=c2;z=c3;

}

.....

};

Ici,iln’yaplusaucunedéfinitiondeconstructeur,puisquecedernierestenligne.

155

Page 157: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice70

ÉnoncéSoituneclassevecteur3ddéfiniecommesuit:

classvecteur3d

{floatx,y,z;

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{x=c1;y=c2;z=c3;

}

.....

};

Introduire une fonction membre nommée coincide permettant de savoir si deuxvecteursontlesmêmescomposantes:

a.enutilisantunetransmissionparvaleur;

b.enutilisantunetransmissionparadresse;

c.enutilisantunetransmissionparréférence.

Si v1 et v2 désignent 2 vecteurs de type vecteur3d, comment s’écrit le test decoïncidencedeces2vecteurs,danschacundes3casconsidérés?

Lafonctioncoincideestmembredelaclassevecteur3d;ellerecevradoncimplicitementl’adresseduvecteurl’ayantappelé.Elleneposséderadoncqu’unseulargument, lui-mêmedetypevecteur3d.Noussupposeronsqu’ellefournitunevaleurderetourdetypeint(1pourlacoïncidence,0danslecascontraire).

a.Ladéclarationdecoincidepourraseprésenterainsi:intcoincide(vecteur3d);

Voicicequepourraitêtresadéfinition:intvecteur3d::coincide(vecteur3dv)

{if((v.x==x)&&(v.y==y)&&(v.z==z))return1;

elsereturn0;

}

b.Ladéclarationdecoincidedevient:intcoincide(vecteur3d*);

Etsanouvelledéfinitionpourraitêtre:

156

Page 158: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

intvecteur3d::coincide(vecteur3d*adv)

{if((adv->x==x)&&(adv->y==y)&&(adv->z==z)

return1;

elsereturn0;

}

Enutilisantthis, ladéfinitionde coincide pourrait fairemoins de distinction entre sesdeuxarguments(l’unimplicite,l’autreexplicite):

intvecteur3d::coincide(vecteur3d*adv)

{if((adv->x==this->x)&&(adv->y==this->y)&&(adv->z==this->z))

return1;

elsereturn0;

}

c.Ladéclarationdecoincidedevient:intcoincide(vecteur3d&);

Etsanouvelledéfinitionest:intvecteur3d::coincide(vecteur3d&v)

{if((v.x==x)&&(v.y==y)&&(v.z==z))return1;

elsereturn0;

}

Notezquelecorpsdelafonctionestrestélemêmequ’ena.

Voici les trois appels de coincide correspondant respectivement aux trois définitionsprécédentes:

a.v1.coincide(v2);ouv2.coincide(v1);

b.v1.coincide(&v2)ouv2.coincide(&v1);

c.v1.coincide(v2)ouv2.coincide(v1);

DiscussionLa surdéfinition d’opérateur offrira une mise en œuvre plus agréable de ce test decoïncidencededeuxvecteurs.C’estainsiqu’ilserapossibledesurdéfinirl’opérateurde comparaison == (pour la classe vecteur3d) et, partant, d’exprimer ce test sous lasimpleformev1==v2.

157

Page 159: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice71

ÉnoncéSoituneclassevecteur3ddéfiniecommesuit:

classvecteur3d

{floatx,y,z;

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{x=c1;y=c2;z=c3;

}

.....

};

Introduire, dans cette classe, une fonction membre nommée normax permettantd’obtenir,parmideuxvecteurs,celuiquialaplusgrandenorme.Onprévoiratroissituations:

a.lerésultatestrenvoyéparvaleurþ;

b. le résultat est renvoyé par référence, l’argument (explicite) étant égalementtransmisparréférenceþ;

c.lerésultatestrenvoyéparadresse,l’argument(explicite)étantégalementtransmisparadresse.

a. La seule difficulté réside dans lamanière de renvoyer la valeur de l’objet ayantappeléunefonctionmembre,àsavoir*this.Voiciladéfinitiondelafonctionnormax(ladéclarationendécouleimmédiatement):vecteur3dvecteur3d::normax(vecteur3dv)

{floatnorm1=x*x+y*y+z*z;

floatnorm2=v.x*v.x+v.y*v.y+v.z*v.z;

if(norm1>norm2)return*this;

elsereturnv;

}

Voiciunexempled’utilisation(onsupposequev1,v2etwsontdetypevecteur3d):w=v1.normax(v2);/*onobtientdanswceluidesdeuxvecteursv1etv2*/

/*ayantlaplusgrandenorme*/

Notez bien que l’affectation ne pose aucun problème ici, puisque notre classe necomporteaucunpointeursurdespartiesdynamiques.

b.Aucun nouveau problème ne se pose. Il suffit demodifier ainsi l’en-tête de notre

158

Page 160: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

fonction,sansenmodifierlecorps:vecteur3d&vecteur3d::normax(vecteur3d&v)

Lafonctionnormaxs’utilisecommeprécédemment.

c.Ilfaut,cettefois,adapterenconséquencel’en-têteetlecorpsdelafonction:vecteur3d*vecteur3d::normax(vecteur3d*adv)

{

floatnorm1=x*x+y*y+z*z;

floatnorm2=adv->x*adv->x+adv->y*adv->y+adv->z*adv->z;

if(norm1>norm2)returnthis;

elsereturnadv;

}

Ici,l’utilisationdelafonctionnécessitequelquesprécautions.Envoiciunexemple(v1,v2etwsonttoujoursdetypevecteur3d):

w=*(v1.normax(&v2));

DiscussionEncequiconcernelatransmissiondel’uniqueargumentexplicitedenormax,ilfautnoterqu’il est impossible de la prévoir par valeur, dès lors que normax doit restituer sonrésultat par adresse ou par référence.En effet, dans ce cas, on obtiendrait en retourl’adresseou la référenced’unvecteurallouéautomatiquementauseinde la fonction.Notez qu’un tel problème ne se pose pas pour l’argument implicite (this), car ilcorrespondtoujoursàl’adressed’unvecteur(transmisautomatiquementparréférence),etnonàunevaleur.

Par ailleurs, on ne perdra pas de vue qu’il est rare qu’une fonction puisse renvoyerl’adresse ou la référence d’un objet. Ici, la chose n’est possible que parce que cerésultatn’apasétécréédynamiquementdanslafonction.

159

Page 161: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice72

ÉnoncéRéaliseruneclassevecteur3dpermettantdemanipulerdesvecteursà3composantes(detypefloat).Onyprévoira:

•unconstructeur,avecdesvaleurspardéfaut(0),

•unefonctiond’affichagedes3composantesduvecteur,souslaforme:<composante1,composante2,composante3>

•unefonctionpermettantd’obtenirlasommede2vecteurs;

•unefonctionpermettantd’obtenirleproduitscalairede2vecteurs.

On choisira les modes de transmission les mieux appropriés. On écrira un petitprogrammeutilisantlaclasseainsiréalisée.

La fonctionmembre calculant la sommede deux vecteurs (nous la nommerons somme)reçoit implicitement (par référence) un argument de type vecteur3d. Elle comporteradoncunseulargument,luiaussidetypevecteur3d.Onpeut,apriori,letransmettreparadresse,parvaleurouparréférence.Enfait,latransmissionparadresse,enC++,n’aplus guère de raison d’être, dans lamesure où la transmission par référence fait lamêmechose,moyennantuneécritureplusagréable.

Lechoixdoitdonc se faireentre transmissionparvaleuroupar référence.Lorsqu’ils’agitdetransmettreunobjet(comportantplusieursmembresdonnée),latransmissionpar référence estplus efficace (en tempsd’exécution).Quiplus est, la fonction sommereçoitdéjàimplicitementunvecteurparréférence,desortequ’iln’yaaucuneraisondeluitransmettredifféremmentlesecondvecteur.

Lemêmeraisonnements’appliqueàlafonctiondecalculduproduitscalaire(quenousnommonsprodscal).

Encequiconcernelavaleurderetourdesomme,égalementdetypevecteur3d,iln’estenrevanchepaspossibledelatransmettreparréférence.Eneffet,ce«résultat»(detypevecteur3d) sera créé au sein de la fonction elle-même, ce qui signifie que l’objetcorrespondant sera de classe automatique, donc détruit à la fin de l’exécution de lafonction.Ilfautdoncabsolumententransmettrelavaleur.

160

Page 162: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Voici ce que pourrait être la déclaration de notre classe vecteur3d (ici, seul leconstructeuraétéprévuenligne):

/*déclarationdelaclassevecteur3d*/

classvecteur3d

{

floatx,y,z;

public:

vecteur3d(floatc1=0,floatc2=0,floatc3=0)//constructeur

{x=c1;y=c2;z=c3;

}

vecteur3dsomme(vecteur3d&);//somme(résultatparvaleur)

floatprodscal(vecteur3d&);//produitscalaire

voidaffiche();//affichagecomposantes

};

Voicisadéfinition:/*définitiondelaclassevect3d*/

#include<iostream>

usingnamespacestd;

vecteur3dvecteur3d::somme(vecteur3d&v)

{vecteur3dres;

res.x=x+v.x;

res.y=y+v.y;

res.z=z+v.z;

returnres;

}

floatvecteur3d::prodscal(vecteur3d&v)

{return(v.x*x+v.y*y+v.z*z);

}

voidvecteur3d::affiche()

{cout<<"<"<<x<<","<<y<<","<<z<<">";

}

Voici un petit programme d’essai de la classe vecteur3d, accompagné des résultatsproduitsparsonexécution:

/*programmed'essaidelaclassevecteur3d*/

#include<iostream>//voirN.B.duparagrapheNouvellespossibilités

//d'entrées-sortiesduchapitre2

usingnamespacestd;

main()

{vecteur3dv1(1,2,3),v2(3,0,2),w;

cout<<"v1=";v1.affiche();cout<<"\n";

cout<<"v2=";v2.affiche();cout<<"\n";

cout<<"w=";w.affiche();cout<<"\n";

w=v1.somme(v2);

cout<<"w=";w.affiche();cout<<"\n";

cout<<"V1.V2="<<v1.prodscal(v2)<<"\n";

}

v1=<1,2,3>

v2=<3,0,2>

w=<0,0,0>

w=<4,2,5>

V1.V2=9

161

Page 163: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice73

ÉnoncéCommentpourrait-onadapter laclassepointcrééedansl’exercice66,pourqu’elledisposed’unefonctionmembrenombre fournissant lenombredepointsexistantàuninstantdonné?

Onpourraitcertes introduireunefonctionmembreclassique.Toutefois,cettesolutionprésenterait l’inconvénient d’obliger l’utilisateur à appliquer une telle fonction à unobjetdetypepoint.Quepenseralorsd’unappeltelquep.compte()(pétantunpoint)pourconnaîtrelenombredepoints?Quiplusest,commentfaireappelàcomptes’iln’existeaucunpoint?

La solution la plus agréable consiste à faire de compte une fonction statique. On ladéclareradoncainsi:

staticintcompte();

Voicisadéfinition:intpoint::compte()

{returnnb_pts;

}

Voiciunexempled’appeldecompteauseind’unprogrammedanslequellaclassepointaétédéclarée:

cout<<"ilya"<<point::compte()<<"points\n";

162

Page 164: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre9Construction,destructionetinitialisation

desobjets

163

Page 165: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

AppelsduconstructeuretdudestructeurDans tous les cas (objets statiques, automatiques ou dynamiques), s’il y a appel duconstructeur, celui-ci a lieu après l’allocation de l’emplacementmémoire destiné àl’objet.Demême,s’ilexisteundestructeur,cedernierestappeléavantlalibérationdel’espacemémoireassociéàl’objet.

LesobjetsautomatiquesetstatiquesLesobjetsautomatiquessontcréésparunedéclarationsoitdansunefonction,soitauseind’unbloc.Ilssontcréésaumomentdel’exécutiondeladéclaration(laquelle,enC++,peutapparaîtren’importeoùdansunprogramme).Ilssontdétruitslorsqu’onsortdelafonctionoudubloc.

Lesobjetsstatiquessontcréésparunedéclarationsituéeendehorsdetoutefonctionouparunedéclarationprécédéedumot-cléstatic(dansunefonctionoudansunbloc).Ils sont créés avant l’entrée dans la fonction main et détruits après la fin de sonexécution.

LesobjetstemporairesL’appel explicite, au sein d’une expression, du constructeur d’un objet provoque lacréation d’un objet temporaire (on n’a pas accès à son adresse) qui pourra êtreautomatiquementdétruit dèsqu’il ne seraplusutile.Par exemple, si une classe pointpossèdeleconstructeurpoint(float,float)etsiaestdetypepoint,nouspouvonsécrire:

a=point(1.5,2.25);

Ne confondez pas une telle affectation avec une initialisation d’un objet lors de sadéclaration.

Cetteinstructionprovoquelacréationd’unobjettemporairedetypepoint(avecappelduconstructeurconcerné),suiviedel’affectationdecetobjetàa.

164

Page 166: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

LesobjetsdynamiquesIlssontcréésparl’opérateurnew,auquelondoitfournir,lecaséchéant,lesvaleursdesargumentsdestinésàunconstructeur,commedanscetexemple(quisupposequ’ilexisteuneclassepointpossédantleconstructeurpoint(float,float)):

point*adp;

.....

adp=newpoint(2.5,5.32);//créationd'unobjetdetypepoint,par

//appeld'unconstructeuràdeuxarguments

Commepourlesvariablesordinaires,onpeutpréciserunnombred’objets,mais,desrestrictions apparaissent alors, qui portent sur le constructeur qu’il est possibled’appeler(voyezci-aprèslarubrique«tableauxd’objets»).

L’accès aux membres d’un objet dynamique est réalisé comme pour les variablesordinaires. Par exemple, si point possède une méthode nommée affiche, on pourral’appelerpar(*adp).affiche()ouencoreparadp->affiche().

Lesobjetsdynamiquesn’ontpasdeduréedeviedéfinieapriori.Ilssontdétruitsàlademande en utilisant l’opérateur delete comme dans : delete adr. (Dans le cas d’untableaud’objets,lasyntaxeseradifférente,voirunpeuplusloin.)

Constructiond’objetscontenantdesobjetsmembreUneclassepeutposséderunmembredonnéequiestlui-mêmedetypeclasse.Envoiciunexemple.Sinousavonsdéfini:

classpoint

{floatx,y;

public:

point(float,float);

.....

};

nouspouvonsdéfiniruneclassepointcol,dontunmembreestdetypepoint:classpointcol

{pointp;

intcouleur;

public:

pointcol(float,float,int);

.....

};

Danscecas,lorsdelacréationd’unobjetdetypepointcol,ilyauratoutd’abordappeld’unconstructeurdepointcol,puisappeld’unconstructeurdepoint;cedernierrecevra

165

Page 167: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

lesargumentsqu’onauramentionnésdansl’en-têtedeladéfinitionduconstructeurdepointcol.Parexemple,avec:

pointcol::pointcol(floatabs,floatord,intcoul):p(abs,ord)

{...

}

onprécisequeleconstructeurdumembreprecevraenargumentlesvaleursabsetord.

Si l’en-tête de pointcol nementionnait rien concernant p, il faudrait alors que le typepointpossèdeunconstructeursansargumentpourquecelasoitcorrect.

Initialisationd’objetsEnC++,onparled’initialisationd’unobjetdansdessituationstellesque:

pointa=5;//ildoitexisterunconstructeuràunargumentdetypeentier

pointb=a;//ildoitexisterunconstructeuràunargumentdetypepoint

Ledeuxièmecas correspondà l’initialisationd’unobjet à l’aided’unautreobjet demêmetype,qu’onnommeparfois«initialisationparrecopie».L’opérationestréaliséeparappeldecequel’onnommeun«constructeurparrecopie».

Mais il existe d’autres situations, plus courantes, qui mettent en œuvre un telmécanisme,àsavoir:

latransmissiond’unobjetparvaleurenargumentd’appeld’unefonctionþ;

latransmissiond’unobjetparvaleurenvaleurderetourd’unefonction.

Dans toutes ces situations d’initialisation par recopie, le constructeur par recopieemployéest:

soitunconstructeurdelaformetype(type&)outype(consttype&)s’ilenexisteun;cedernierdoit alorsprendre encharge la recopiede tous lesmembresde l’objet, ycomprisceuxquisontdesobjets;ilpeutcependants’appuyersurlespossibilitésdetransmissiond’informationentreconstructeurs,présentéeauparavant;

La transmission par référence est obligatoire ici. D’autre part, le fait d’utiliserl’attribut const permet d’appliquer le constructeur à un objet constant ou à uneexpression ; rappelons que, dans ce cas, il y création d'un objet temporaire dont ontransmetlaréférenceauconstructeur.

soit, dans le cas contraire, ce que l’on nomme un « constructeur de recopie par

166

Page 168: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

défaut»,quirecopielesdifférentsmembresdel’objet.Sicertainsdecesmembressont eux-mêmes des objets, la recopie sera réalisée par appel de son propreconstructeur par recopie (qui pourra être soit un constructeur par défaut, soit unconstructeurdéfinidanslaclassecorrespondante).

Dans les très anciennes versions (antérieures à 2.0), la recopie se faisait de façonglobale;autrementdit,la«valeur»d’unobjetétaitreportée(bitparbit)dansunautre,sans tenir compte de sa structure. Les choses étaient alors relativement peusatisfaisantes...

Lestableauxd’objetsSi point est un type objet possédant un constructeur sans argument (ou, situationgénéralementdéconseillée,sansconstructeur),ladéclaration:

pointcourbe[20];

crée un tableau courbe de 20 objets de type point en appelant, le cas échéant, leconstructeurpourchacund’entreeux.Noteztoutefoisquecourben’estpaslui-mêmeunobjet.

Demême:point*adcourbe=newpoint[20];

allouel’emplacementmémoirenécessaireàvingtobjets(consécutifs)detypepoint,enappelant,lecaséchéant,leconstructeurpourchacund’entreeux,puisplacel’adressedupremierdansadcourbe.

La destruction d’un tableau d’objets se fait en utilisant une syntaxe particulière del’opérateurnew.Parexemple,pourdétruireletableauprécédent,onécrira:

delete[]adcourbe;

En théorie, onpeut compléter ladéclarationd’un tableaud’objetsparun initialiseurcontenantune listedevaleurs (ellespeuventéventuellementêtrede typesdifférents).Chaquevaleurestalorstransmiseàunconstructeurapproprié.Cesvaleursdoiventêtreconstantespour les tableauxdeclassestatique ; ilpeuts’agird’expressionspour lestableauxdeclasseautomatique.Onpeutnepaspréciserdevaleurspour lesderniersélémentsdutableau.

167

Page 169: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Onnoteraqu’untelinitialiseurnepeutpasêtreutiliséavecdestableauxdynamiques(ilenvademêmepourlestableauxordinaires!).

168

Page 170: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice74

ÉnoncéCommentconcevoirletypeclassechosedefaçonquecepetitprogramme:

main()

{chosex;

cout<<"bonjour\n";

}

fournisselesrésultatssuivants:créationobjetdetypechose

bonjour

destructionobjetdetypechose

Quefourniraalorsl’exécutiondeceprogramme(utilisantlemêmetypechose):main()

{chose*adc=newchose

}

Ilsuffitdeprévoir,dansleconstructeurdechose,l’instruction:cout<<"créationobjetdetypechose\n";

et,dansledestructeur,l’instruction:cout<<"destructionobjetdetypechose\n";

Danscecas,ledeuxièmeprogrammefournirasimplementàl’exécution(puisqu’ilcréeunobjetdetypechosesansjamaisledétruire):

créationobjetdetypechose

169

Page 171: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice75

ÉnoncéQuels seront les résultats fournis par l’exécution du programme suivant (ici, ladéclaration de la classe demo, sa définition et le programme d’utilisation ont étéregroupésenunseulfichier):

#include<iostream>

usingnamespacestd;

classdemo

{intx,y;

public:

demo(intabs=1,intord=0)//constructeurI(0,1ou2arguments)

{x=abs;y=ord;

cout<<"constructeurI:"<<x<<""<<y<<"\n";

}

demo(demo&);//constructeurII(parrecopie)

~demo();//destructeur

};

demo::demo(demo&d)//oudemo::demo(constdemo&d)

{cout<<"constructeurII(recopie):"<<d.x<<""<<d.y<<"\n";

x=d.x;y=d.y;

}

demo::~demo()

{cout<<"destruction:"<<x<<""<<y<<"\n";

}

main()

{

voidfct(demo,demo*);//protofonctionindépendantefct

cout<<"débutmain\n";

demoa;

demob=2;

democ=a;

demo*adr=newdemo(3,3);

fct(a,adr);

demod=demo(4,4);

c=demo(5,5);

cout<<"finmain\n";

}

voidfct(demod,demo*add)

{cout<<"entréefct\n";

deleteadd;

cout<<"sortiefct\n";

}

Voicilesrésultatsquefournitleprogramme(ici,nousavonsutilisélecompilateurC++BuilderX;cepointn’ayantd’importancequepourlesobjetstemporaires,danslecasdes implémentations qui ne respecteraient pas la norme quant à l’instant de leurdestruction).

170

Page 172: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

La plupart des lignes sont assorties d’explications complémentaires présentéesconventionnellement sous forme de commentaires et qui précisent les instructionsconcernées.

débutmain

constructeurI:10/*demoa;*/

constructeurI:20/*demob=2;*/

constructeurII(recopie):10/*democ=a;*/

constructeurI:33/*newdemo(3,3)*/

constructeurII(recopie):10/*recopiedelavaleurdeadansfct(a,...)*/

/*cequicréeunobjettemporaire*/

entréefct

destruction:33/*deleteadd;(dansfct)*/

sortiefct

destruction:10/*destructionobjettemporairecréépour*/

/*l'appeldefct*/

constructeurI:44/*demod=demo(4,4)*/

constructeurI:55/*c=demo(5,5)(contructionobjettemporaire)*/

destruction:55/*destructionobjettemporaireprécédent*/

finmain

destruction:44/*destructiond*/

destruction:55/*destructionc*/

destruction:20/*destructionb*/

destruction:10/*destructiona*/

Notezbienquel’affectationc=demo(5,5)entraînelacréationd’unobjettemporaireparappelduconstructeurdedemo (arguments5et5) ; cetobjet est ensuite affecté à a. Onconstated’ailleursquecetobjetesteffectivementdétruitaussitôtaprès.Maisilexistecertaines implémentationsquine respectentpas lanormeetoùcelapeut seproduireplustard.

Parailleurs,l’appeldefctaentraînélaconstructiond’unobjettemporaire,parappeldu constructeur par recopie. Cet objet est ici libéré dès la sortie de la fonction. Làencore,danscertainesimplémentations,celapeutseproduireplustard.

171

Page 173: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice76

ÉnoncéCréeruneclassepointnecontenantqu’unconstructeursansarguments,undestructeuretunmembredonnéeprivéreprésentantunnumérodepoint(lepremiercrééporteralenuméro1,lesuivantlenuméro2...).Leconstructeurafficheralenumérodupointcrééetledestructeurafficheralenumérodupointdétruit.Écrireunpetitprogrammed’utilisationcréantdynamiquementuntableaude4pointsetledétruisant.

Pourpouvoirnuméroternospoints,ilnousfautpouvoircompterlenombredefoisoùleconstructeuraétéappelé,cequinouspermettrabiend’attribuerunnumérodifférentàchaquepoint.Pourcefaire,nousdéfinissons,auseindelaclassepoint,unmembredonnéestatiquenb_points.Ici,ilseraincrémentéparleconstructeurmaisledestructeurn’aurapasd’actionsurlui.Commetoutmembrestatique,nb_pointsdevraêtreinitialisé.

Voici la déclaration (définition) de notre classe, accompagnée du programmed’utilisationdemandé:

#include<iostream>

usingnamespacestd;

classpoint

{intnum;

staticintnb_points;

public:

point()

{num=++nb_points;

cout<<"créationpointnuméro:"<<num<<"\n";

}

~point()

{cout<<"Destructionpointnuméro:"<<num<<"\n";

}

};

intpoint::nb_points=0;//initialisationobligatoire

main()

{point*adcourb=newpoint[4];

delete[]adcourb;

}

Àtitreindicatif,voicilesrésultatsfournisparceprogramme:créationpointnuméro:1

créationpointnuméro:2

créationpointnuméro:3

créationpointnuméro:4

Destructionpointnuméro:4

Destructionpointnuméro:3

Destructionpointnuméro:2

Destructionpointnuméro:1

172

Page 174: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice77

Énoncé1. Réaliser une classe nommée set_int permettant de manipuler des ensembles de

nombres entiers. On devra pouvoir réaliser sur un tel ensemble les opérationsclassiques suivantes : lui ajouter un nouvel élément, connaître son cardinal(nombred’éléments),savoirsiunentierdonnéluiappartient.

Ici, on conservera les différents éléments de l’ensemble dans un tableau allouédynamiquement par le constructeur.Un argument (auquel on pourra prévoir unevaleurpardéfaut)luipréciseralenombremaximald’élémentsdel’ensemble.

2.Écrire,enoutre,unprogramme(main)utilisantlaclasseset_intpourdéterminerlenombred’entiersdifférentscontenusdans20entierslusendonnées.

3. Que faudrait-il faire pour qu’un objet du type set_int puisse être transmis parvaleur, soit comme argument d’appel, soit comme valeur de retour d’unefonction?

N.B. Le chapitre 17 vous montrera comment résoudre cet exercice à l’aide descomposantsstandardintroduitsparlanorme,qu’ilnefautpaschercheràutiliserici.

1.Ladéclarationdelaclassedécouledel’énoncé:/*fichierSETINT1.H*/

/*déclarationdelaclasseset_int*/

classset_int

{

int*adval;//adressedutableaudesvaleurs

intnmax;//nombremaxid'éléments

intnelem;//nombrecourantd'éléments

public:

set_int(int=20);//constructeur

~set_int();//destructeur

voidajoute(int);//ajoutd'unélément

intappartient(int);//appartenanced'unélément

intcardinal();//cardinaldel'ensemble

};

Lemembredonnéeadvalestdestinéàpointersurletableaud’entiersquiseraallouéparle constructeur. Lemembre nmax représentera la taille de ce tableau, tandis que nelemfournira lenombreeffectifd’entiersstockésdanscetableau.Cesentiersseront,cettefois, rangésdans l’ordreoù ils seront fournis à ajoute, et nonplus à un emplacement

173

Page 175: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

prédéterminé, comme nous l’avions fait pour les caractères (dans les exercices duchapitreprécédent).

Commelacréationd’unobjetentraîneiciuneallocationdynamiqued’unemplacementmémoire, il est raisonnable de prévoir la libération de cet emplacement lors de ladestructiondel’objet;cetteopérationdoitdoncêtrepriseenchargeparledestructeur,d’oùlaprésencedecettefonctionmembre.

Voiciladéfinitiondenotreclasse:#include"setint1.h"

set_int::set_int(intdim)

{adval=newint[nmax=dim];//allocationtableaudevaleurs

nelem=0;

}

set_int::~set_int()

{deleteadval;//libérationtableaudevaleurs

}

voidset_int::ajoute(intnb)

{//onexaminesinbappartientdéjààl'ensemble

//enutilisantlafonctionmembreappartient

//s'iln'yappartientpasetsil'ensemblen'estpasplein

//onl'ajoute

if(!appartient(nb)&&(nelem<nmax))adval[nelem++]=nb;

}

intset_int::appartient(intnb)

{inti=0;

//onexaminesinbappartientdéjààl'ensemble

//(sicen'estpaslecas,ivaudraneleenfindeboucle)

while((i<nelem)&&(adval[i]!=nb))i++;

return(i<nelem);

}

intset_int::cardinal()

{returnnelem;

}

Notez que, dans la fonction membre ajoute, nous avons utilisé la fonction membreappartient pour vérifier que le nombre à ajouter ne figurait pas déjà dans notreensemble.

Parailleurs,l’énoncéneprévoitrienpourlecasoùl’onchercheàajouterunélémentàunensembledéjà«plein»; ici,nousnoussommescontentédenerienfairedanscecas. Dans la pratique, il faudrait soit prévoir un moyen pour que l’utilisateur soitprévenudecettesituation,soit,mieux,prévoirautomatiquementl’agrandissementdelazonedynamiqueassociéeàl’ensemble.

2.Voicileprogrammed’utilisationdemandé:#include"setint1.h"

#include<iostream>

usingnamespacestd;

main()

{set_intens;

cout<<"donnez20entiers\n";

inti,n;

for(i=0;i<20;i++)

{cin>>n;

ens.ajoute(n);

174

Page 176: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

cout<<"ilya:"<<ens.cardinal()<<"entiersdifférents\n";

}

Àtitreindicatif,voiciunexempled’exécution:donnez20entiers

02528518207255450045

ilya:7entiersdifférents

3. Telle qu’est actuellement prévue notre classe set_int, si un objet de ce type esttransmisparvaleur, soit enargumentd’appel, soit en retourd’une fonction, ilyaappelduconstructeurde recopiepardéfaut.Orcedernier secontented’effectuerunecopiedesmembresdonnéedel’objetconcerné,cequisignifiequ’onseretrouveenprésencededeuxobjetscontenantdeuxpointeursdifférentssurunmêmetableaud’entiers.Unproblèmevadoncseposer,dèslorsquel’objetcopiéseradétruit(cequipeutseproduiredèslasortiedelafonction);eneffet,danscecas,letableaudynamique d’entiers sera détruit, alors même que l’objet d’origine continuera à«pointer»dessus.

Indépendamment de cela, d’autres problèmes similaires pourraient se poser si lafonctionétaitamenéeàmodifierlecontenudel’ensemble;eneffet,onmodifieraitalorsletableaud’entiersoriginal,choseàlaquelleonnes’attendpasdanslecasdelatransmissionparvaleur.

Pourréglercesproblèmes,ilestnécessairedemunirnotreclassed’unconstructeurpar recopie approprié, c’est-à-dire tenant compte de la « partie dynamique » del’objet(onparleparfoisde«copieprofonde»).Pourcefaire,onalloueunsecondemplacement pour un tableau d’entiers, dans lequel on recopie les valeurs dupremierensemble.Naturellement,ilnefautpasoublierdeprocéderégalementàlarecopiedesmembresdonnée,puisquecelle-cin’estplusassuréeparleconstructeurde recopie par défaut (lequel n’est plus appelé, dès lors qu’un constructeur parrecopieaétédéfini).

Nousajouteronsdoncdansladéclarationdenotreclasse:set_int(set_int&);//constructeurparrecopie

Et,danssadéfinition:set_int::set_int(set_int&e)//ouset_int::set_int(constset_int&e)

{

adval=newint[nmax=e.nmax];//allocationnouveautableau

nelem=e.nelem;

inti;

for(i=0;i<nelem;i++)//copieancientableaudansnouveau

adval[i]=e.adval[i];

}

Discussion

175

Page 177: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

La surdéfinition du constructeur par recopie est quasiment indispensable pour touteclasse comportant un pointeur sur une partie dynamique. En l’état actuel de C++, iln’estpaspossibled’interdirelatransmissionparvaleurd’unobjetquin’enposséderaitpas ! Et il ne semble pas raisonnable de livrer à un « client » un tel objet, en luidemandant de ne jamais le transmettre par valeur ! Cela signifie que la plupart desclasses«intéressantes»devrontdéfiniruntelconstructeurparrecopie.

Nous verrons que lesmêmes réflexions s’appliqueront à l’affectation entre objets etqu’elles conduiront à la conclusion que la plupart des classes intéressantes devrontredéfinirl’opérateurd’affectation.

176

Page 178: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice78

ÉnoncéModifier l’implémentation de la classe précédente (avec son constructeur parrecopie)defaçonquel’ensembled’entierssoitmaintenantreprésentéparune listechaînée (chaqueentierest rangédansunestructurecomportantunchampdestinéàcontenir un nombre et un champ destiné à contenir un pointeur sur la structuresuivante).L’interfacedelaclasse(lapartiepubliquedesadéclaration)devraresterinchangée, ce qui signifie qu’un client de la classe continuera à l’employer de lamêmefaçon.

Comme nous le suggère l’énoncé, nous allons donc définir une structure que nousnommeronsélément:

structnoeud

{intvaleur;//valeurd'unélémentdel'ensemble

noeud*suivant;//pointeursurlenœudsuivantdelaliste

};

Notrestructurenoeudpeutêtredéfinie indifféremmentdans ladéclarationde laclasseset_intouendehors.

Encequiconcernelesmembresdonnéeprivésdelaclasse,nousneconserveronsquenelem qui, bien que non indispensable, nous évitera de parcourir toute la liste pourdéterminerlecardinaldenotreensemble.

Enrevanche,nousyintroduironsunpointeurnommédebut,destinéàcontenirl’adressedupremierélémentdelaliste,s’ilexiste(audépart,ilserainitialiséàNULL).

Encequiconcerneleconstructeurdeset_int,nousluiconserveronssonargument(detypeint),bienqu’iciiln’aitplusaucunintérêt,etceladanslebutdenepasmodifierl’interfacedenotreclasse(commeledemandaitl’énoncé).

Voicidonclanouvelledéclarationdenotreclasse:/*fichierSETINT3.H*/

/*déclarationdelaclasseset_int*/

structnoeud

{

intvaleur;//valeurd'unélémentdel'ensemble

noeud*suivant;//pointeursurlenœudsuivantdelaliste

};

classset_int

177

Page 179: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

{

noeud*debut;//pointeursurledébutdelaliste

intnelem;//nombrecourantd'éléments

public:

set_int(int=20);//constructeur(argumentinutileici)

set_int(set_int&);//constructeurparrecopie

~set_int();//destructeur

voidajoute(int);//ajoutd'unélément

intappartient(int);//appartenanced'unélément

intcardinal();//cardinaldel'ensemble

};

Ladéfinitiondunouveauconstructeurneprésentepasdedifficulté.Lafonctionmembreajoute réalise une classique insertion d’un noeud en début de liste et incrémente lenombre d’éléments de l’ensemble. La fonction appartient effectue une exploration deliste tant qu’elle n’a pas trouvé la valeur concernée ou atteint la fin de la liste. Enrevanche,lenouveauconstructeurparrecopiedoitrecopierlalistechaînée.Ilréaliseàlafoisuneexplorationdelalisted’origineetuneinsertiondanslalistecopiée.Quantaudestructeur, ildoitmaintenant libérersystématiquement tous lesemplacementsdesdifférentsnœudscrééspourlaliste.

Voicilanouvelledéfinitiondenotreclasse:#include<stdlib.h>//pourNULL

#include"setint3.h"

set_int::set_int(intdim)//dimestconservépourlacompatibilité

//avecl'ancienneclasse

{debut=NULL;

nelem=0;

}

set_int::set_int(set_int&e)//ou:set_int::set_int(constset_int&e)

{nelem=e.nelem;

//créationd'unenouvellelisteidentiqueàl'ancienne

noeud*adsource=e.debut;

noeud*adbut;

debut=NULL;

while(adsource)

{adbut=newnoeud;//créationnouveaunœud

adbut->valeur=adsource->valeur;//copievaleur

adbut->suivant=debut;//insertionnouveaunœud

debut=adbut;//dansnouvelleliste

adsource=adsource->suivant;//nœudsuivantancienneliste

}

}

set_int::~set_int()

{noeud*adn;

noeud*courant=debut;

while(courant)

{adn=courant;//libérationdetous

courant=courant->suivant;//lesnœuds

deleteadn;//delaliste

}

}

voidset_int::ajoute(intnb)

{if(!appartient(nb))//sinbn'appartientpasàlaliste

{noeud*adn=newnoeud;//onl'ajouteendébutdeliste

adn->valeur=nb;

adn->suivant=debut;

debut=adn;

178

Page 180: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

nelem++;

}

}

intset_int::appartient(intnb)

{noeud*courant=debut;

//attentionàl'ordredesdeuxconditions

while(courant&&(courant->valeur!=nb))courant=courant->suivant;

return(courant!=NULL);

}

intset_int::cardinal()

{returnnelem;

}

Notez que le programme d’utilisation proposé dans l’exercice 26 reste valable ici,puisquenousn’avonsprécisémentpasmodifiél’interfacedenotreclasse.

Par ailleurs, le problème évoqué à propos de l’ajout d’un élément à un ensemble«plein»neseposeplusici,comptetenudelanouvelleimplémentationdenotreclasse(hormisunéventuelmanquedemémoire).

179

Page 181: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice79

ÉnoncéModifierlaclasseset_intprécédente(implémentéesouslaformed’unelistechaînée,avec ou sans son constructeur par recopie) pour qu’elle dispose de ce que l’onnommeun« itérateur» sur lesdifférents élémentsde l’ensemble.Rappelonsqu’ils’agitd’unmécanismepermettantd’accéderséquentiellementauxdifférentsélémentsdel’ensemble.Onprévoiratroisnouvellesfonctionsmembre:init,pourinitialiserleprocessusd’itérationþ;prochain,pour fournir l’élémentsuivant lorsqu’ilexisteetexiste,pourtesters’ilexisteencoreunélémentnonexploré.

On complétera alors le programme d’utilisation précédent (en fait, celui del’exercice 26), de manière qu’il affiche les différents entiers contenus dans lesvaleursfourniesendonnée.

N.B. Le chapitre 21 vous montrera comment résoudre cet exercice à l’aide descomposantsstandardintroduitsparlanorme,qu’ilnefautpaschercheràutiliserici.D’autre part, cet exercice sera plus profitable s’il est traité après l’exercice duchapitre3quiproposaitl’introductiond’untelitérateurdansuneclassereprésentantdes ensembles de caractères (mais dont l’implémentation était différente del’actuelleclasse).

Ici, la gestion du mécanisme d’itération nécessite l’emploi d’un pointeur (que nousnommeronscourant) surunnœuddenotre liste.Nousconviendronsqu’ilpointesur lepremier élément non encore traité dans l’itération, c’est-à-dire dont la valeurcorrespondanten’apasencoreétérenvoyéeparlafonctionprochain. Iln’estpasutile,ici,deprévoirunmembredonnéepourindiquersilafindelisteaétéatteinte;eneffet,aveclaconventionadoptée,ilnoussuffitdetesterlavaleurdecourant(quiseraégaleàNULL,lorsquel’onseraenfindeliste).

Lerôledelafonctioninitselimiteàl’initialisationdecourantàlavaleurdupointeursurledébutdelaliste(debut).

La fonction suivant fournira en retour la valeur entière associée au nœud pointé parcourantlorsqu’ilexiste(courantdifférentdeNULL)oulavaleur0danslecascontraire(ils’agit, làencore,d’uneconventiondestinéeàprotéger l’utilisateurayantappelécettefonction alors que la fin de liste était déjà atteinte et, donc, qu’aucun élément de

180

Page 182: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

l’ensemblen’étaitdisponible).Deplus,danslepremiercas(usuel),lafonctionsuivantactualiseralavaleurdecourant,demanièrequ’ilpointesurlenœudsuivant.

Enfin,lafonctionexisteexaminerasimplementlavaleurdedebutpoursavoirs’ilexisteencoreunélémentàtraiter.

Voiciladéclarationcomplètedenotrenouvelleclasse:/*fichierSETINT4.H*/

/*déclarationdelaclasseset_int*/

structnoeud

{intvaleur;//valeurd'unélémentdel'ensemble

noeud*suivant;//pointeursurlenœudsuivantdelaliste

};

classset_int

{noeud*debut;//pointeursurledébutdelaliste

intnelem;//nombrecourantd'éléments

noeud*courant;//pointeursurnœudcourant

public:

set_int(int=20);//constructeur

set_int(set_int&);//constructeurparrecopie

~set_int();//destructeur

voidajoute(int);//ajoutd'unélément

intappartient(int);//appartenanced'unélément

intcardinal();//cardinaldel'ensemble

voidinit();//initialisationitération

intprochain();//entiersuivant

intexiste();//testfinliste

};

Voiciladéfinitiondestroisnouvellesfonctionsmembreinit,suivantetexiste:voidset_int::init()

{courant=debut;

}

intset_int::prochain()

{if(courant)

{intval=courant->valeur;

courant=courant->suivant;

returnval;

}

elsereturn0;//parconvention

}

intset_int::existe()

{return(courant!=NULL);

}

Voicilenouveauprogrammed’utilisationdemandé:/*utilisationdelaclasseset_int*/

#include"setint1.h"

#include<iostream>

usingnamespacestd;

main()

{set_intens;

cout<<"donnez20entiers\n";

inti,n;

for(i=0;i<20;i++)

{cin>>n;

ens.ajoute(n);

}

cout<<"ilya:"<<ens.cardinal()<<"entiersdifférents\n";

cout<<"Cesont:\n";

181

Page 183: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

ens.init();

while(ens.existe())

cout<<ens.prochain()<<"";

}

Àtitreindicatif,voiciunexempled’exécution:donnez20entiers

02154120214512021452

ilya:5entiersdifférents

Cesont:

45120

182

Page 184: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice80

ÉnoncéQuelsserontlesrésultatsfournisparl’exécutiondeceprogramme:

#include<iostream>

usingnamespacestd;

classpoint

{intx,y;

public:

point()

{x=0;y=0;

cout<<"**constructeur0argument\n";

}

point(intabs)

{x=abs;y=0;

cout<<"**constructeur1argument\n";

}

point(intabs,intord)

{x=abs;y=ord;

cout<<"**constructeur2arguments\n";

}

point(point&p)

{x=p.x;y=p.y;

cout<<"**constructeurparrecopie\n";

}

voidaffiche()

{cout<<"pointdecoordonnees:"<<x<<""<<y<<"\n";

}

};

main()

{pointa(10,20);

pointb(30,40);

pointcourbe[6]={4,a,0,b};

for(inti=0;i<6;i++)courbe[i].affiche();

}

**constructeur2arguments//construction(classique)dea

**constructeur2arguments//construction(classique)deb

**constructeur1argument//constructioncourbe[0]

**constructeurparrecopie//constructioncourbe[1]

**constructeur1argument//constructioncourbe[2]

**constructeurparrecopie//constructioncourbe[3]

**constructeur0argument//constructioncourbe[4]

**constructeur0argument//constructioncourbe[5]

pointdecoordonnees:40

pointdecoordonnees:1020

pointdecoordonnees:00

pointdecoordonnees:3040

pointdecoordonnees:00

pointdecoordonnees:00

Aprèslaconstructionclassiquedespointsaetb,ilyacréationd’untableaude6objetsdetypepoint.Lesquatrepremierspointssontconstruitspar:

183

Page 185: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

appeld’unconstructeuràunargumentdetypeint(valeur4)pourlepremierpointþ;

appeld’unconstructeuràunargumentdetypepoint(valeura)pourledeuxièmeþ;

appeld’unconstructeuràunargumentdetypeint(valeur0)pourletroisièmeþ;

appeld’unconstructeuràunargumentdetypepoint(valeurb)pourlequatrième.

Les deux derniers points du tableau ne disposent pas d’initialiseur ; ils sont doncconstruitsparappeld’unconstructeursansargument.

184

Page 186: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre10Lesfonctionsamies

185

Page 187: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

EnC++, l’unité de protection est la classe, et non pas l’objet. Cela signifie qu’unefonctionmembre d’une classe peut accéder à tous lesmembres privés de n’importequel objet de sa classe. En revanche, ces membres privés restent inaccessibles àn’importe quelle fonctionmembre d’une autre classe ou à n’importe quelle fonctionindépendante.

Lanotiondefonctionamie,ouplusexactementde«déclarationd’amitié»,permetdedéclarerdansuneclasselesfonctionsquel’onautoriseàaccéderàsesmembresprivés(donnéesoufonctions).Ilexisteplusieurssituationsd’amitié.

Fonctionindépendante,amied’uneclasseAclassA

{

......

friend---fct(-----);

.....

}

LafonctionfctayantleprototypespécifiéestautoriséeàaccéderauxmembresprivésdelaclasseA.

Fonctionmembred’uneclasseB,amied’uneautreclasseA

classA

{

.....

friend---B:fct(-----);

.....

};

La fonction fct, membre de la classe B, ayant le prototype spécifié, est autorisée àaccéderauxmembresprivésdelaclasseA.

Pourqu’ilpuissecompilerconvenablementladéclarationdeA,doncenparticulier ladéclarationd’amitié relativeàfct, lecompilateurdevraconnaître ladéclarationdeB(maispasnécessairementsadéfinition).

Généralement,lafonctionmembrefctposséderaunargumentouunevaleurderetourdetypeA(cequijustifierasadéclarationd’amitié).Pourcompilersadéclaration(auseindeladéclarationdeA),ilsuffiraaucompilateurdesavoirqueAestuneclasse;sisa

186

Page 188: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

déclarationn’estpasconnueàceniveau,onpourrasecontenterde:classA;

En revanche, pour compiler la définition de fct, le compilateur devra posséder lescaractéristiquesdeA,doncdisposerdesadéclaration.

Touteslesfonctionsd’uneclasseBsontamiesd’uneautreclasseADans ce cas, plutôt que d’utiliser autant de déclarations d’amitié que de fonctionsmembre, on utilise (dans la déclaration de la classe A) la déclaration (globale)suivante:

friendclassB;

PourcompilerladéclarationdeA,onpréciserasimplementqueBestuneclassepar:classB;

QuantàladéclarationdelaclasseB,ellenécessiteragénéralement(dèsqu’unedesesfonctions membre possédera un argument ou une valeur de retour de type A) ladéclarationdelaclasseA.

187

Page 189: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice81

ÉnoncéSoitlaclassepointsuivante:

classpoint

{intx,y;

public:

point(intabs=0,intord=0)

{x=abs;y=ord;

}

};

Écrire une fonction indépendante affiche, amie de la classe point, permettantd’afficher les coordonnées d’un point. On fournira séparément un fichier sourcecontenantlanouvelledéclaration(définition)depointetunfichiersourcecontenantladéfinitiondelafonctionaffiche.Écrireunpetitprogramme(main)quicréeunpointde classe automatique et un point de classe dynamique et qui en affiche lescoordonnées.

Nous devons donc réaliser une fonction indépendante, nommée affiche, amie de laclassepoint. Une telle fonction, contrairement à une fonctionmembre, ne reçoit plusd’argument implicite ; affiche devra donc recevoir un argument de type point. Sonprototypeseradoncdelaforme:

voidaffiche(point);

sil’onsouhaitetransmettreunpointparvaleur,oudelaforme:voidaffiche(point&);

si l’onsouhaite transmettreunpointpar référence. Ici,nouschoisironscettedernièrepossibilitéet,commeaffiche n’a aucune raisondemodifier lesvaleursdu point reçu,nousleprotégeronsàl’aideduqualificatifconst:

voidaffiche(constpoint&);

Lequalificatifconstpermetd’appliquerlafonctionafficheàunobjetconstant.Maisellepourraégalementêtreappliquéeàuneexpressiondetypepoint,voireàuneexpressiond’untypesusceptibled’êtreconvertiimplicitementenunpoint(voirlechapitrerelatif

188

Page 190: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

aux conversions définies par l’utilisateur). Ce dernier aspect ne constitue plusnécessairementunavantage!

Manifestement,affiche devrapouvoir accéderauxmembresprivés xety de la classepoint.Ilfautdoncprévoirunedéclarationd’amitiéauseindecetteclasse,dontvoicilanouvelledéclaration:

/*fichierPOINT1.H*/

/*déclarationdelaclassepoint*/

classpoint

{intx,y;

public:

friendvoidaffiche(constpoint&);//constnonobligatoire

point(intabs=0,intord=0)

{x=abs;y=ord;

}

};

Pourécrireaffiche,ilnoussuffitd’accéderauxmembres(privés)xetydesonargumentde type point. Si ce dernier se nomme p, les membres en question se notent(classiquement)p.xetp.y.Voiciladéfinitiondeaffiche:

#include"point1.h"//nécessairepourcompileraffiche

#include<iostream>

usingnamespacestd;

voidaffiche(constpoint&p)

{cout<<"Coordonnées:"<<p.x<<""<<p.y<<"\n";

}

Notezbienquelacompilationdeaffichenécessiteladéclarationdelaclassepoint,etpasseulementunedéclarationtellequeclasspoint,carlecompilateurdoitconnaîtrelescaractéristiquesdelaclassepoint(notamment,ici,lalocalisationdesmembresxety).

Voicilepetitprogrammed’essaidemandé:#include"point1.h"

main()

{pointa(1,5);

affiche(a);

point*adp;

adp=newpoint(2,12);

affiche(*adp);//attention*adpetnonadp

}

Notez que nous n’avons pas eu à fournir le prototype de la fonction indépendanteaffiche, car il figure dans la déclaration de la classe point. Le faire constitueraitd’ailleursuneerreur.

189

Page 191: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice82

ÉnoncéSoitlaclassevecteur3ddéfiniedansl’exercice70par:

classvecteur3d

{floatx,y,z;

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{x=c1;y=c2;z=c3;}

.....

};

Écrireunefonctionindépendantecoincide,amiede laclassevecteur3d,permettantdesavoir si deux vecteurs ont lesmêmes composantes (cette fonction remplacera lafonctionmembrecoincidequ’ondemandaitd’écriredansl’exercice70).

Siv1etv2 désignent deuxvecteurs de type vecteur3d, comment s’écritmaintenant letestdecoïncidencedecesdeuxvecteurs?

La fonction coincide va donc disposer de deux arguments de type vecteur3d. Si l’onprévoit de les transmettre par référence, en interdisant leur éventuelle modificationdanslafonction,leprototypedecoincidesera:

intcoincide(constvecteur3d&,constvecteur3d&);

Là encore, le qualificatif const a un double rôle : il permet d’appliquer la fonctioncoincide à des objets constants ou à des expressions de type point.Mais il permettraégalementdel’appliqueràtoutevaleursusceptibled’êtreconvertiedansletypepoint(voirlechapitrerelatifauxconversionsdéfiniesparl’utilisateur).

Voicilanouvelledéclarationdenotreclasse:/*fichiervect3D.H*/

/*déclarationdelaclassevecteur3d*/

classvecteur3d

{floatx,y,z;

public:

friendintcoincide(constvecteur3d&,constvecteur3d&);

vecteur3d(floatc1=0,floatc2=0,floatc3=0)

{x=c1;y=c2;z=c3;

}

190

Page 192: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

};

Voiciladéfinitiondelafonctioncoincide:#include"vect3d.h"//nécessairepourcompilercoincide

intcoincide(constvecteur3d&v1,constvecteur3d&v2)

{if((v1.x==v2.x)&&(v1.y==v2.y)&&(v1.z==v2.z))

return1;

elsereturn0;

}

Letestdecoïncidencededeuxvecteurss’écritmaintenant:coincide(v1,v2)

Onnoteraque,tantdansladéfinitiondecoincidequedanscetest,onvoitsemanifesterlasymétrieduproblème,cequin’étaitpaslecaslorsquenousavionsfaitdecoincideunefonctionmembredelaclassevecteur3d.

191

Page 193: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice83

ÉnoncéCréerdeuxclasses(dontlesmembresdonnéesontprivés):

•l’une,nomméevect,permettantdereprésenterdesvecteursà3composantesdetype double ; elle comportera un constructeur et une fonction membred’affichage;

• l’autre, nommée matrice, permettant de représenter des matrices carrées dedimension3x3;ellecomporteraunconstructeuravecunargument(adressed’un tableau de 3 x 3 valeurs) qui initialisera la matrice avec les valeurscorrespondantes.

Réaliser une fonction indépendante prod permettant de fournir le vecteurcorrespondantauproduitd’unematriceparunvecteur.Écrireunpetitprogrammedetest.Onfourniraséparémentlesdeuxdéclarationsdechacunedesclasses,leursdeuxdéfinitions,ladéfinitiondeprodetleprogrammedetest.

Ici,pournousfaciliterl’écriture,nousreprésenteronslescomposantesd’unvecteurparuntableauàtroisélémentsetlesvaleursd’unematriceparuntableauà2indices.Lafonctiondecalculduproduitd’unvecteurparunematricedoitobligatoirementpouvoiraccéder aux données des deux classes, ce qui signifie qu’elle devra être déclarée«amie»danschacunedecesdeuxclasses.

En ce qui concerne ses deux arguments (de type vect et mat), nous avons choisi latransmission la plus efficace, c’est-à-dire la transmission par référence. Quant aurésultat (de type vect), il doit obligatoirement être renvoyé par valeur (nous enreparleronsdansladéfinitiondeprod).

Voiciladéclarationdelaclassevect:/*fichiervect1.h*/

classmatrice;//pourpouvoircompilerladéclarationdevect

classvect

{

doublev[3];//vecteurà3composantes

public:

vect(doublev1=0,doublev2=0,doublev3=0)//constructeur

{v[0]=v1;v[1]=v2;v[2]=v3;}

friendvectprod(constmatrice&,constvect&);//fonctionamie

//indépendante

192

Page 194: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

voidaffiche();

};

etladéclarationdelaclassemat:/*fichiermat1.h*/

classvect;//pourpouvoircompilerladéclarationdematrice

classmatrice

{

doublemat[3][3];//matrice3X3

public:

matrice();//constructeuravecinitialisationàzéro

matrice(doublet[3][3]);//constructeuràpartird'untableau

//3x3

friendvectprod(constmatrice&,constvect&);//fonctionamie

//indépendante

};

Nous avons déclaré constants les arguments de la fonction vect, ce qui nous protèged'uneéventuellefautedeprogrammationdanslesinstructionsdecettefonction.Danscecas, la fonction pourra être appelée avec en arguments effectifs non seulement desobjetsconstants,maisaussidesexpressionsd'untypesusceptibled'êtreconvertidansletypevoulu(commeonleverradanslechapitrerelatifauxconversionsdéfiniesparl'utilisateur).

Ladéfinitiondesfonctionsmembre(enfaitaffiche)delaclassevectneprésentepasdedifficultés:

#include"vect1.h"

#include<iostream>

usingnamespacestd;

voidvect::affiche()

{inti;

for(i=0;i<3;i++)cout<<v[i]<<"";

cout<<"\n";

}

Ilenvademêmepourlesfonctionsmembre(enfaitleconstructeur)delaclassemat:#include"mat1.h"

#include<iostream>

usingnamespacestd;

matrice::matrice(doublet[3][3])

{

inti;intj;

for(i=0;i<3;i++)

for(j=0;j<3;j++)

mat[i][j]=t[i][j];

}

Ladéfinitionprodnécessitelesfichierscontenantlesdéclarationsdevectetdemat:#include"vect1.h"

#include"mat1.h"

vectprod(constmatrice&m,constvect&x)

193

Page 195: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

{

inti,j;

doublesom;

vectres;//pourlerésultatduproduit

for(i=0;i<3;i++)

{for(j=0,som=0;j<3;j++)

som+=m.mat[i][j]*x.v[j];

res.v[i]=som;

}

returnres;

}

Notezquecette fonctioncréeunobjetautomatiqueresdeclassevect. Ilnepeutdoncêtrerenvoyéqueparvaleur.Dans lecascontraire, lafonctionappelanterécupéreraitl’adressed’unemplacementlibéréaumomentdelasortiedelafonction.

Voici,enfin,unexempledeprogrammedetest,accompagnédesonrésultat:#include"vect1.h"

#include"mat1.h"

main()

{vectw(1,2,3);

vectres;

doubletb[3][3]={1,2,3,4,5,6,7,8,9};

matricea=tb;

res=prod(a,w);

res.affiche();

}

143250

194

Page 196: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre11Surdéfinitiond’opérateurs

195

Page 197: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

C++vouspermetdesurdéfinirlesopérateursexistants,c’est-à-diredeleurdonnerunenouvellesignificationlorsqu’ilsportent(enpartieouentotalité)surdesobjetsdetypeclasse.

Lemécanismedelasurdéfinitiond’opérateursPoursurdéfinirunopérateurexistantop,ondéfinitunefonctionnomméeoperatorop (onpeutplacerunouplusieursespacesentre lemotoperator et l’opérateur,maiscen’estpasuneobligation):

soit sous forme d’une fonction indépendante (généralement amie d’une ou deplusieursclasses);

soitsousformed’unefonctionmembred’uneclasse.

Danslepremiercas,siopestunopérateurbinaire,lanotationaopbestéquivalenteà:operatorop(a,b)

Danslesecondcas,lamêmenotationestéquivalenteà:a.operatorop(b)

Lespossibilitésetleslimitesdelasurdéfinitiond'opérateursOn doit se limiter aux opérateurs existants, en conservant leur « pluralité » (unaire,binaire). Les opérateurs ainsi surdéfinis gardent leur priorité et leur associativitéhabituelle(voirtableaurécapitulatif,unpeuplusloin).

Unopérateursurdéfinidoit toujoursposséderunopérandede typeclasse(onnepeutdoncpasmodifierlessignificationsdesopérateursusuels).Ildoitdoncs’agir:

soitd’unefonctionmembre,auquelcaselledisposeobligatoirementd’unargumentimplicitedutypedesaclasse(this);

soitd’unefonctionindépendante(ouplutôtamie)possédantaumoinsunargumentdetypeclasse.

Il ne faut pas faire d’hypothèse sur la signification a priori d’un opérateur ; par

196

Page 198: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

exemple,lasignificationde+=pouruneclassenepeutenaucuncasêtredéduitedelasignificaitonde+etde=pourcettemêmeclasse.

CasparticuliersLes opérateurs [], (), ->, new et delete doivent obligatoirement être définis commefonctionsmembre.

Lesopérateurs=(affectation)et&(pointeursur)possèdentunesignificationprédéfiniepour les objets de n’importe quel type classe.Cela ne les empêchenullement d’êtresurdéfinis.Encequiconcernel’opérateurd’affectation,onpeutchoisirdetransmettresonuniqueargumentparvaleurouparréférence.Danslederniercas,onneperdrapasdevuequeleseulmoyend’autoriserl’affectationd’uneexpressionconsisteàdéclarercetargumentconstant.

La surdéfinition de new, pour un type classe donné, se fait par une fonction deprototype:

void*new(size_t)

Ellereçoit,enuniqueargument,latailledel’objetàallouer(cetargumentseragénéréautomatiquement par le compilateur, lors d’un appel de new), et elle doit fournir enretourl’adressedel’objetalloué.

Lasurdéfinitiondedelete,pouruntypedonné,sefaitparunefonctiondeprototype:voiddelete(type*)

Ellereçoit,enuniqueargument,l’adressedel’objetàlibérer.

Tableaurécapitulatif

LesopérateurssurdéfinissablesenC++(classésparprioritédécroissante)

Pluralité Opérateurs AssociativitéBinaire ()

(3)[]

(3)->

(3) ->

Unaire+-++

(5)--

(5)!~*&

(1)new

(4)(6)

new[](4)(6)

delete(4)(6)

delete[](4)(6)

(cast)

<-

Binaire */% ->

Binaire +- ->

197

Page 199: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Binaire <<>> ->

Binaire <<=>>= ->

Binaire ==!= ->

Binaire & ->

Binaire ^ ->

Binaire || ->

Binaire && ->

Binaire | ->

Binaire =(1)(3)

+=-=*=/=%=&=^=|=<<=

>>=<-

Binaire , ->

(1)S’iln’estpassurdéfini,ilpossèdeunesignificationpardéfaut.

(3)Doitêtredéfinicommefonctionmembre.

(4)Soitàun«niveauglobal»(fonctionindépendante),soitpouruneclasse(fonctionmembre).

(5)Lorsqu’ilssontdéfinisdefaçonunaire,cesopérateurscorrespondentàlanotation«pré»;maisilenexisteunedéfinitionbinaire(avecdeuxièmeopérandefictifdetypeint)quicorrespondàlanotation«post».

(6)Ondistinguebiennewdenew[]etdeletededelete[]

Mêmelorsqu’onasurdéfinilesopérateursnewetdeletepouruneclasse,ilrestepossibledefaireappelauxopérateursnewetdeleteusuels,enutilisantl’opérateurderésolutiondeportée(::).

198

Page 200: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice84

ÉnoncéSoituneclassevecteur3ddéfiniecommesuit:

classvecteur3d

{floatx,y,z;

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{x=c1;y=c2;z=c3;

}

};

Définirlesopérateurs==et!=demanièrequ’ilspermettentdetesterlacoïncidenceoulanon-coïncidencededeuxpoints:

a.enutilisantdesfonctionsmembreþ;

b.enutilisantdesfonctionsamies.

a.Avecdesfonctionsmembre

Ilsuffitdoncdeprévoir,danslaclassevecteur3d,deuxfonctionsmembredenomoperator== et operator !=, recevant un argument de type vecteur3d correspondant au secondargumentdesopérateurs(lepremieropérandeétantfourniparl’argumentimplicitethisdesfonctionsmembre).Voiciladéclarationcomplètedenotreclasse,accompagnéedesdéfinitionsdesopérateurs:

classvecteur3d

{floatx,y,z;

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{x=c1;y=c2;z=c3;

}

intoperator==(vecteur3d);

intoperator!=(vecteur3d);

};

intvecteur3d::operator==(vecteur3dv)

{if((v.x==x)&&(v.y==y)&&(v.z==z))return1;

elsereturn0;

}

intvecteur3d::operator!=(vecteur3dv)

{return!((*this)==v);

}

Notez que, dans la définition de !=, nous nous sommes servi de l’opérateur ==. Enpratique,onserasouventamenéàréécrireentièrementladéfinitiond’untelopérateur,

199

Page 201: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

pour de simples raisons d’efficacité (d’ailleurs, pour lesmêmes raisons, on placera«enligne»lesfonctionsoperator==etoperator!=).

b.Avecdesfonctionsamies

Ilfautdoncprévoirdedéclarercommeamies,danslaclassevecteur3d,deuxfonctions(operator==etoperator!=) recevantdeuxargumentsde typevecteur3dcorrespondantauxdeux opérandes des opérateurs Voici la nouvelle déclaration de notre classe,accompagnéedesdéfinitionsdesopérateurs:

classvecteur3d

{floatx,y,z;

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{x=c1;y=c2;z=c3;

}

friendintoperator==(vecteur3d,vecteur3d);

friendintoperator!=(vecteur3d,vecteur3d);

};

intoperator==(vecteur3dv,vecteur3dw)

{if((v.x==w.x)&&(v.y==w.y)&&(v.z==w.z))return1;

elsereturn0;

}

intoperator!=(vecteur3dv,vecteur3dw)

{return!(v==w);

}

Voici, à titre indicatif, un exempledeprogramme, accompagnédu résultat fourni parsonexécution,utilisantn’importe laquelledesdeuxclassesvecteur3d quenousvenonsdedéfinir:

#include"vecteur3d.h"

#include<iostream>

usingnamespacestd;

main()

{vecteur3dv1(3,4,5),v2(4,5,6),v3(3,4,5);

cout<<"v1==v2:"<<(v1==v2)<<"v1!=v2:"<<(v1!=v2)<<"\n";

cout<<"v1==v3:"<<(v1==v3)<<"v1!=v3:"<<(v1!=v3)<<"\n";

}

v1==v2:0v1!=v2:1

v1==v3:1v1!=v3:0

200

Page 202: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice85

ÉnoncéSoitlaclassevecteur3dainsidéfinie:

classvecteur3d

{floatx,y,z;

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{x=c1;y=c2;z=c3;

}

};

Définir l’opérateur binaire + pour qu’il fournisse la somme de deux vecteurs, etl’opérateur binaire * pour qu’il fournisse le produit scalaire dedeuxvecteurs.Onchoisiraicidesfonctionsamies.

Ilsuffitdecréerdeuxfonctionsamiesnomméesoperator+etoperator*.Ellesrecevrontdeux arguments de type vecteur3d ; la première fournira en retour un objet de typevecteur3d,lasecondefourniraenretourunfloat.

classvecteur3d

{floatx,y,z;

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{x=c1;y=c2;z=c3;

}

friendvecteur3doperator+(vecteur3d,vecteur3d);

friendfloatoperator*(vecteur3d,vecteur3d);

};

vecteur3doperator+(vecteur3dv,vecteur3dw)

{vecteur3dres;

res.x=v.x+w.x;

res.y=v.y+w.y;

returnres;

}

floatoperator*(vecteur3dv,vecteur3dw)

{return(v.x*w.x+v.y*w.y+v.z*w.z);

}

Il est possible de transmettre par référence les arguments des deux fonctions amiesconcernées.

Souvent,danscecas,onutiliseralequalificatifconstpuisquelafonctionestsupposéenepasmodifierlesvaleursdesesarguments.Parexemple:

201

Page 203: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

vecteur3doperator+(constvecteur3d&v,constvecteur3d&w)

Danscecas,lafonctionpeutcertesêtreappeléeavecdesargumentseffectifsconstants.Mais il ne faudra pas perdre de vue qu'elle peut aussi être appelée avec argumentsfournissousformed'expressionsdetypevecteur3d,voireavecdesargumentsd'untypeautrequevecteur3d,pourpeuqu'ilexisteuneconversion impliciteappropriée (voir lechapitrerelatifauxconversionsdéfiniesparl'utilisateur).

Enrevanche,iln’estpaspossiblededemanderàoperator+detransmettresonrésultatparréférence,puisquecedernierestcréédanslafonctionmême,sousformed’unobjetde classe automatique. En toute rigueur, operator * pourrait transmettre son résultat(float)parréférence,maiscelan’aguèred’intérêtenpratique.

202

Page 204: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice86

ÉnoncéSoitlaclassevecteur3dainsidéfinie:

classvecteur3d

{

floatv[3];

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{//àcompléter

}

};

Compléter la définitiondu constructeur (en ligne), puis définir l’opérateur [] pourqu’il permette d’accéder à l’unedes trois composantes d’unvecteur, et cela aussibienauseind’uneexpression(...=v1[i])qu’àgauched’unopérateurd’affectation(v1[i] = ...) ; de plus, on cherchera à se protéger contre d’éventuels risques dedébordementd’indice.

Ladéfinitionduconstructeurneposeaucunproblème.Encequiconcernel’opérateur[], C++ ne permet de le surdéfinir que sous la forme d’une fonctionmembre (cetteexceptionestjustifiéeparlefaitquel’objetconcernénedoitpasrisquerd’êtresoumisàuneconversion, lorsqu’ilapparaîtàgauched’uneaffectation).Elleposséderadoncunseulargumentdetypeintetellerenverraunrésultatdetypevecteur3d.

Pour que notre opérateur puisse être utilisé à gauche d’une affectation, il fautabsolumentquelerésultatsoitrenvoyéparréférence.Pournousprotégerd’unéventueldébordementd’indice,nous avons simplementprévuque toute tentatived’accès àunélémentendehorsdeslimitesconduiraitàaccéderaupremierélément.

Voici la déclaration de notre classe, accompagnée de la définition de la fonctionoperator[]:

classvecteur3d

{

floatv[3];

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{v[0]=c1;v[1]=c2;v[2]=c3;

}

float&operator[](int);

};

float&vecteur3d::operator[](inti)

{if((i<0)||(i>2))i=0;//pouréviterun"débordement"

203

Page 205: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

returnv[i];

}

À titre indicatif, voici un petit exemple de programme faisant appel à notre classevecteur3d,accompagnédurésultatdesonexécution:

#include"vecteur3d.h"

#include<iostream>

usingnamespacestd;

main()

{

vecteur3dv1(3,4,5);

inti;

cout<<"v1=";

for(i=0;i<3;i++)cout<<v1[i]<<"";

for(i=0;i<3;i++)v1[i]=i;

cout<<"\nv1=";

for(i=0;i<3;i++)cout<<v1[i]<<"";

}

v1=345

v1=012

204

Page 206: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice87

ÉnoncéL’exercice 77 vous avait proposé de créer une classe set_int permettant dereprésenterdesensemblesdenombresentiers:

classset_int

{

int*adval;//adressedutableaudesvaleurs

intnmax;//nombremaxid'éléments

intnelem;//nombrecourantd'éléments

public:

set_int(int=20);//constructeur

~set_int();//destructeur

......//autresfonctionsmembre

};

Son implémentation prévoyait de placer les différents éléments dans un tableauallouédynamiquement;aussil’affectationentreobjetsdetypeset_intposait-elledesproblèmes,puisqu’elleaboutissaitàdesobjetsdifférentscomportantdespointeurssurunmêmeemplacementdynamique.

Modifier la classe set_int pour qu’elle ne présente plus de telles lacunes. Onprévoiraquetoutobjetdetypeset_intcomportesonpropreemplacementdynamique,comme on l’avait fait pour permettre la transmission par valeur. De plus, ons’arrangerapourquel’affectationmultiplesoitutilisable.

Noussommesenprésenced’unproblèmevoisindeceluiposéparleconstructeurparrecopie.Nousl’avionsrésoluenprévoyantcequel’onappelleune«copieprofonde»del’objetconcerné(c’est-à-direunecopienonseulementdel’objetlui-même,maisdetoutes ses parties dynamiques). Quelques différences supplémentaires surgissentnéanmoins.Eneffet,ici:

onpeutsetrouverenprésenced’uneaffectationd’unobjetàlui-même;

avant affectation, il existe deuxobjets « complets » (avec leur partie dynamique),alorsquedans lecasduconstructeurparrecopie, iln’existaitqu’unseulobjet, lesecondétantàcréer.

Voicicommenttraiterl’affectationb=a,danslecasoùbestdifférentdea:

libérationdel’emplacementpointéparb;

205

Page 207: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

créationdynamiqued’unnouvelemplacementdanslequelonrecopielesvaleursdel’emplacementdésignépara;

miseenplacedesvaleursdesmembresdonnéedeb.

Siaetbdésignent lemêmeobjet (ons’enassureradans l’opérateurd’affectation,encomparantlesadressesdesobjetsconcernés),onéviterad’appliquercemécanismequiconduirait à un emplacement dynamique pointé par « personne », et qui, donc, nepourraitjamaisêtrelibéré.Enfait,onsecontenterade...nerienfaire!

Par ailleurs, pour que l’affectation multiple (telle que c = b = a) fonctionnecorrectement, ilestnécessairequenotreopérateur renvoieunevaleurde retour (elleseradetypeset_int)représentantlavaleurdesonpremieropérande(aprèsaffectation,c’est-à-direlavaleurdebaprèsb=a).

Voicicequepourraitêtreladéfinitiondenotrefonctionoperator=(encequiconcerneladéclarationdelaclasseset_int,ilsuffiraitd’yajouterset_int&operator=(set_int&):

set_int&set_int::operator=(set_int&e)//ou:constset_int&e

//surdéfinitiondel'affectation-lescommentairescorrespondentàb=a

{

if(this!=&e)//onnefaitrienpoura=a

{deleteadval;//libérationpartiedynamiquedeb

adval=newint[nmax=e.nmax];//allocationnouvelensemble

//poura

nelem=e.nelem;//danslequelonrecopie

inti;//entièrementl'ensembleb

for(i=0;i<nelem;i++)//avecsapartiedynamique

adval[i]=e.adval[i];

}

return*this;

}

1.Onassocieraobligatoirementconstà latransmissionparréférence,si l’onsouhaitepouvoir appliquer l’affectation à une expression de type set_int. Cela n’est pasnécessairementjustifiéici.

2.Une telle surdéfinition d’un opérateur d’affectation pour une classe possédant desparties dynamiques ne sera valable que si elle est associée à une surdéfinitioncomparableduconstructeurparrecopie(pourlaclasseset_int,celleproposéedanslasolutiondel’exercice26convientparfaitement).

3.Apriori,seulelavaleurdupremieropérandeavraimentbesoind’êtretransmiseparréférence (pour que = puisse le modifier !) ; cette condition est obligatoirementremplie puisque notre opérateur doit être surdéfini comme fonction membre.Toutefois,enpratique,onutiliseraégalementlatransmissionparréférence,àlafoispourlesecondopérandeetpourlavaleurderetour,defaçonàêtreplusefficace(en

206

Page 208: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

temps).Notezd’ailleursquesi l’opérateur= renvoyaitsonrésultatparvaleur, ilyauraitalorsappelduconstructeurderecopie(laremarqueprécédentes’appliqueraitalorsàunesimpleaffectation).

207

Page 209: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice88

ÉnoncéConsidérerànouveaulaclasseset_intcrééedansl’exercice77(etsurlaquelleestégalementfondél’exerciceprécédent):

classset_int

{int*adval;//adressedutableaudesvaleurs

intnmax;//nombremaxid'éléments

intnelem;//nombrecourantd'éléments

public:

set_int(int=20);//constructeur

~set_int();//destructeur

......

};

Adaptercetteclasse,demanièreque:

•l’onpuisseajouterunélémentàunensembledetypeset_intpar(edésignantunobjetdetypeset_intetnunentier):e<n;

•e[n]prennelavaleur1sinappartientàeetlavaleur0danslecascontraire.Ons’arrangera pour qu’une instruction de la forme e[i] = ... soit rejetée à lacompilation.

Il nous faut donc surdéfinir l’opérateur binaire <, de façon qu’il reçoive commeopérandesunobjetdetypeset_intetunentier.Bienquel’énoncéneprévoierien,ilestprobable que l’on souhaitera pouvoir écrire des choses telles que (e étant de typeset_int,netpdesentiers):

e<n<p;

Cela signifie donc que notre opérateur devra fournir comme valeur de retourl’ensembleconcerné,aprèsqu’onluiauraajoutél’élémentvoulu.

Nouspouvons ici utiliser indifféremmentune fonctionmembreouune fonction amie.Nouschoisironslapremièrepossibilité.Parailleurs,noustransmettronslesopérandesetlavaleurderetourparréférence(c’estpossibleicicarl’objetcorrespondantn’estpas créé au sein de l’opérateur même, c’est-à-dire qu’il n’est pas de classeautomatique);ainsi,notreopérateurfonctionneramêmesileconstructeurparrecopien’a pas été surdéfini (en pratique toutefois, il faudra le faire dès qu’on souhaiterapouvoirtransmettrelavaleurd’unobjetdetypeset_intenargument).

208

Page 210: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

En ce qui concerne l’opérateur [], il doit être surdéfini comme fonction membre,comme l’impose leC++, et cela bien qu’ici une affectation telle e[i]= soit interdite(alorsque c’est précisémentpour l’autoriserqueC++obliged’en faireune fonctionmembre!).Pourinterdiredetellesaffectations,ladémarchelaplussimpleconsisteàfaire en sorte que le résultat fourni par l’opérateur ne soit pas une lvalue, en latransmettantparvaleur.

Voici ce que pourraient être la définition et la déclaration de notre nouvelle classemuniedecesdeuxopérateurs(notezquenousutilisons[]pourdéfinir<):

classset_int

{int*adval;//adressedutableaudesvaleurs

intnmax;//nombremaxid'éléments

intnelem;//nombrecourantd'éléments

public:

set_int(int=20);//constructeur

~set_int();//destructeur

set_int&operator<(int);

intoperator[](int);//attentionrésultatparvaleur

};

set_int::set_int(intdim)

{adval=newint[nmax=dim];

nelem=0;

}

set_int::~set_int()

{deleteadval;

}

/*surdéfinitionde<*/

set_int&set_int::operator<(intnb)

{//onexaminesinbappartientdéjààl'ensemble

//enutilisantl'opérateur[]

if(!(*this)[nb]&&(nelem<nmax))adval[nelem++]=nb;

return(*this);

}

/*surdéfinitionde[]*/

intset_int::operator[](intnb)//attentionrésultatparvaleur

{inti=0;

//onexaminesinbappartientdéjààl'ensemble

//(danscecasivaudraneleenfindeboucle)

while((i<nelem)&&(adval[i]!=nb))i++;

return(i<nelem);

}

À titre indicatif, voici un exemple d’utilisation accompagné du résultat de sonexécution:

#include"set_int.h"

#include<iostream>

usingnamespacestd;

main()

{set_intens(10);

ens<25<2<25<3;

cout<<(ens[25])<<""<<(ens[5])<<"\n";

}

10

209

Page 211: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice89

ÉnoncéSoituneclassevecteur3ddéfiniecommesuit:

classvecteur3d

{floatv[3];

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{v[0]=c1;v[1]=c2;v[2]=c3;

}

//àcompléter

};

Définirl’opérateur[]demanièreque:

•ilpermetted’accéder«normalement»àunélémentd’unobjetnonconstantdetypevecteur3d,etcelaaussibiendansuneexpressionqu’enopérandedegauched’uneaffectation;

•ilnepermettequelaconsultation(etnonlamodification)d’unobjetconstantdetypevecteur3d (autrementdit, siv estun telobjet,une instructionde la formev[i]=...devraêtrerejetéeàlacompilation).

Rappelonsquelorsquel’ondéfinitdesobjetsconstants(qualificatifconst),iln’estpaspossiblede leurappliquerunefonctionmembrepublique,saufsicettedernièreaétédéclarée avec le qualificatif const (auquel cas, elle peut indifféremment être utiliséeavecdesobjetsconstantsounonconstants).Ici,nousdevonsdoncdéfinirunefonctionmembreconstantedenomoperator[].

Par ailleurs, pour qu’une affectation de la forme v[i] = ... soit interdite, il estnécessaire que notre opérateur renvoie son résultat par valeur (et non par adressecommeonagénéralementl’habitudedelefaire).

Dans ces conditions, on voit qu’il est nécessaire de prévoir deux fonctionsmembredifférentes,pourtraiterchacundesdeuxcas:objetconstantouobjetnonconstant.Lechoix de la « bonne fonction » sera assuré par le compilateur, selon la présence oul’absencedel’attributconstpourl’objetconcerné.

Voici la définition complète de notre classe, accompagnée de la définition des deuxfonctionsoperator[]:

classvecteur3d

210

Page 212: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

{floatv[3];

public:

vecteur3d(floatc1=0.0,floatc2=0.0,floatc3=0.0)

{v[0]=c1;v[1]=c2;v[2]=c3;

}

floatoperator[](int)const;//[]pourunvecteurconstant

float&operator[](int);//[]pourunvecteurnonconstant

};

/********operator[]pourunobjetnonconstant*******/

float&vecteur3d::operator[](inti)

{if((i<0)||(i>2))i=0;//pouréviterundébordement

returnv[i];

}

/**********operator[]pourunobjetconstant*********/

floatvecteur3d::operator[](inti)const

{if((i<0)||(i>2))i=0;//pouréviterundébordement

returnv[i];

}

À titre indicatif, voici un petit programme utilisant la classe vecteur3d ainsi définie,accompagnédurésultatproduitparsonexécution:

#include"vecteur3d.h"

#include<iostream>//voirN.B.duparagrapheNouvellespossibilités

//d'entrées-sortiesduchapitre2

usingnamespacestd;

main()

{inti;

vecteur3dv1(3,4,5);

constvecteur3dv2(1,2,3);

cout<<"V1:";

for(i=0;i<3;i++)cout<<v1[i]<<"";

cout<<"\nV2:";

for(i=0;i<3;i++)cout<<v2[i]<<"";

for(i=0;i<3;i++)v1[i]=i;

cout<<"\nV1:";

for(i=0;i<3;i++)cout<<v1[i]<<"";

//v2[1]=3;estbienrejetéàlacompilation

}

V1:345

V2:123

V1:012

211

Page 213: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice90

ÉnoncéDéfinir une classe vect permettant de représenter des « vecteurs dynamiquesd’entiers»,c’est-à-diredontlenombred’élémentspeutnepasêtreconnulorsdelacompilation. Plus précisément, on prévoira de déclarer de tels vecteurs par uneinstructiondelaforme:

vectt(exp);

danslaquelleexpdésigneuneexpressionquelconque(detypeentier).

Ondéfinira,defaçonappropriée,l’opérateur[]demanièrequ’ilpermetted’accéderà des éléments d’un objet d’un type vect comme on le ferait avec un tableauclassique.

Onnechercherapasàrésoudrelesproblèmesposéséventuellementparl’affectationoulatransmissionparvaleurd’objetsdetypevect.Enrevanche,ons’arrangerapourqu’aucunrisquede«débordement»d’indicen’existe.

NB. Le chapitre 21 vous montrera comment résoudre cet exercice à l’aide descomposantsstandardintroduitsparlanorme,qu’ilnefautpaschercheràutiliserici.Il montrera également comment se protéger des débordements d’indice par unetechniquedegestiond’exceptions.

Lesélémentsd’unobjetde type vect doivent obligatoirement être rangés enmémoiredynamique.L’emplacementcorrespondantseradoncallouépar leconstructeurquienrecevra la taille en argument. Le destructeur devra donc, naturellement, libérer cetemplacement. En ce qui concerne l’accès à un élément, il se fera en surdéfinissantl’opérateur [], comme nous l’avons déjà fait au cours des précédents exercices ;rappelonsqu’ilfaudraobligatoirementlefairesousformed’unefonctionmembre.

Pour nous protéger d’un éventuel débordement d’indice, nous ferons en sorte qu’unetentative d’accès à un élément situé en dehors du vecteur conduise à accéder àl’élémentderang0.

Voicicequepourraientêtreladéclarationetladéfinitiondenotreclasse:/**********déclarationdelaclassevect********/

classvect

{intnelem;//nombred'éléments

212

Page 214: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

int*adr;//adressezonedynamiquecontenantleséléments

public:

vect(int);//constructeur

~vect();//destructeur

int&operator[](int);//accèsàunélémentparson"indice"

};

/***********définitiondelaclassevect********/

vect::vect(intn)

{adr=newint[nelem=n];

inti;

for(i=0;i<nelem;i++)adr[i]=0;

}

vect::~vect()

{deleteadr;

}

int&vect::operator[](inti)

{if((i<0)||(i>=nelem))i=0;//protection

returnadr[i];

}

Voiciunpetit exempledeprogrammed’utilisationd’une telleclasse,accompagnédurésultatproduitparsonexécution:

#include"vect.h"

#include<iostream>

usingnamespacestd;

main()

{vectv(6);

inti;

for(i=0;i<6;i++)v[i]=i;

for(i=0;i<6;i++)cout<<v[i]<<"";

}

012345

213

Page 215: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice91

ÉnoncéEns’inspirantdel’exerciceprécédent,onsouhaitecréeruneclasseint2dpermettantde représenterdes tableauxdynamiquesd’entiers àdeux indices, c’est-à-diredontlesdimensionspeuventnepasêtreconnueslorsdelacompilation.Plusprécisément,onprévoiradedéclarerdetelstableauxparunedéclarationdelaforme:

int2dt(exp1,exp2);

danslaquelleexp1etexp2désignentuneexpressionquelconque(detypeentier).

On surdéfinira l’opérateur (), demanière qu’il permette d’accéder à des élémentsd’unobjetd’untypeint2dcommeonleferaitavecuntableauclassique.

Làencore,onnechercherapasàrésoudrelesproblèmesposéséventuellementparl’affectation ou la transmission par valeur d’objets de type int2d. En revanche, ons’arrangerapourqu’iln’existeaucunrisquededébordementd’indice.

N.B. Le chapitre 21 vous montrera comment résoudre cet exercice à l’aide descomposantsstandardintroduitsparlanorme,qu’ilnefautpaschercheràutiliserici.Ilmontreraégalementcommentseprotégercontrelesdébordementsd’indiceparunetechniquedegestiond’exceptions.

Comme dans l’exercice précédent, les éléments d’un objet de type int2d doiventobligatoirementêtrerangésenmémoiredynamique.L’emplacementcorrespondantseradoncallouéparleconstructeurquiendéduiralatailledesdeuxdimensionsreçuesenargumentsLedestructeurlibéreracetemplacement.

Nous devons décider de la manière dont seront rangés les différents éléments enmémoire,àsavoir«parligne»ou«parcolonne»(entouterigueur,cetteterminologiefait référence à la façon dont on a l’habitude de visualiser des tableaux à deuxdimensions, ceque l’onnommeune ligne correspondant en fait àdes éléments ayantmêmevaleurdupremierindice).Nouschoisironsicilapremièresolution(c’estcelleutiliséeparCouC++pourlestableauxàdeuxdimensions).Ainsi,unélémentrepérépar lesvaleursietj des2 indices sera situé à l’adresse (adv désignant l’adresse del’emplacementdynamiqueallouéautableau):

adv+i*ncol+j

214

Page 216: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

En ce qui concerne l’accès à un élément, il se fera en surdéfinissant l’opérateur (),d’unemanièrecomparableàcequenousavionsfaitpour[] ; làencore,C++imposeque()soitdéfinicommefonctionmembre.

Pour nous protéger d’un éventuel débordement d’indice, nous ferons en sorte qu’unevaleur incorrecte d’un indice conduise à « faire comme si » on lui avait attribué lavaleur0.

Voicicequepourraientêtreladéclarationetladéfinitiondenotreclasse:/********déclarationdelaclasseint2d********/

classint2d

{intnlig;//nombrede"lignes"

intncol;//nombrede"colonnes"

int*adv;//adresseemplacementdynamiquecontenantlesvaleurs

public:

int2d(intnl,intnc);//constructeur

~int2d();//destructeur

int&operator()(int,int);//accèsàunélément,parses2"indices"

};

/***********définitionduconstructeur**********/

int2d::int2d(intnl,intnc)

{nlig=nl;

ncol=nc;

adv=newint[nl*nc];

inti;

for(i=0;i<nl*nc;i++)adv[i]=0;//miseàzéro

}

/***********définitiondudestructeur***********/

int2d::~int2d()

{deleteadv;

}

/**********définitiondel'opérateur()*********/

int&int2d::operator()(inti,intj)

{if((i<0)||(i>=nlig))i=0;//protectionssurpremierindice

if((j<0)||(j>=ncol))j=0;//protectionssursecondindice

return*(adv+i*ncol+j);

}

Voiciunpetitexempled’utilisationdenotreclasseint2d,accompagnédurésultatfourniparsonexécution:

#include"int2d.h"

#include<iostream>

usingnamespacestd;

main()

{int2dt1(4,3);

inti,j;

for(i=0;i<4;i++)

for(j=0;j<3;j++)

t1(i,j)=i+j;

for(i=0;i<4;i++)

{for(j=0;j<3;j++)

cout<<t1(i,j)<<"";

cout<<"\n";

}

}

215

Page 217: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

012

123

234

345

1.Dans lapratique,onseraamenéàdéfinirdeuxopérateurs()commenous l’avionsfait dans l’exercice précédent pour l’opérateur [] : l’un pour des objets nonconstants (celui défini ici), l’autre pour des objets constants (il sera prévu demanièreànepaspouvoirmodifierl’objetsurlequelilporte).

2.Généralement,par soucid’efficacité, lesopérateurs telsque() serontdéfinis«enligne».

3.Onauraitpusongeràemployerl’opérateur[]dontl’utilisations’avèreplusnaturelleque celle de l’opérateur (). Cependant, [] ne peut être surdéfini que sous formebinaire.Iln’estdoncpaspossibledel’employersouslaformet[i,j]pouraccéderàunélémentd’un tableaudynamique.Onpourraitalorspenserà l’utilisersous laformehabituellet[i][j].Or,cetteécrituredoitêtreinterprétéecomme(t[i])[j]),cequisignifiequ’onn’yappliquepluslesecondopérateuràunobjetdetypeint2d.

Comme, de surcroît, [] doit être défini comme fonction membre, l’écriture enquestiondemanderaitdedéfinir[]pouruneclasseint2d,ens’arrangeantpourqu’ilfournisseun résultat («vecteur ligne») lui-mêmede typeclasse.Danscederniercas,ilfaudraitégalementsurdéfinirl’opérateur[]pourqu’ilfournisseunrésultatdetype int. Pour obtenir le résultat souhaité, il serait alors nécessaire de définird’autresclassesqueint2d.

216

Page 218: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice92

ÉnoncéCréeruneclassenommée histo permettantdemanipulerdes«histogrammes».Onrappellequel’onobtientunhistogrammeàpartird’unensembledevaleursx(i),endéfinissant n tranches (intervalles) contiguës (souvent de même amplitude) et encomptabilisantlenombredevaleursx(i)appartenantàchacunedecestranches.

Onprévoira:

• un constructeur de la forme histo (float min, float max, int ninter), dont lesargumentsprécisentlesbornes(minetmax)desvaleursàprendreencompteetlenombredetranches(ninter)supposéesdemêmeamplitude;

•unopérateur<<définitelqueh<<xajoutelavaleurxàl’histogrammeh,c’est-à-dire qu’elle incrémente de 1 le compteur relatif à la tranche à laquelleappartient x. Les valeurs sortant des limites (min - max) ne seront pascomptabilisées;

•unopérateur[]définitelqueh[i]représentelenombredevaleursrépertoriéesdanslatranchederangi(lapremièretrancheportantlenuméro1;unnuméroincorrect de tranche conduira à considérer celle de rang 1). On s'arrangerapourqu'uneinstructiondelaformeh[i]=...soitrejetéeencompilation.

On ne cherchera pas ici à régler les problèmes posés par l’affectation ou latransmissionparvaleurd’objetsdutypehisto.

Nousn’avonspasbesoindeconserverlesdifférentesvaleursx(i),maisseulementlescompteurs relatifs à chaque tranche. En revanche, il faut prévoir d’allouerdynamiquement (dans le constructeur) l’emplacement nécessaire à ces compteurs,puisquelenombredetranchesn’estpasconnulorsdelacompilationdelaclassehisto.Il faudra naturellement prévoir de libérer cet emplacement dans le destructeur de laclasse. Lesmembres donnée de histo seront donc les valeurs extrêmes (minimale etmaximale), le nombre de tranches et un pointeur sur l’emplacement contenant lescompteurs.

L’opérateur<< peut être surdéfini, soit comme fonctionmembre, soit comme fonctionamie ; nous choisirons ici la première solution. En revanche, l’opérateur [] doit

217

Page 219: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

absolument être surdéfini comme fonction membre. Pour qu’il ne soit pas possibled’écrire des affectations de la forme h[i] = ..., on fera en sorte que cet opérateurfournissesonrésultatparvaleur.

Voicicequepourraitêtreladéclarationdenotreclassehisto:/***fichierhisto.h:déclarationdelaclassehisto***/

classhisto

{floatmin;//borneinférieure

floatmax;//bornesupérieure

intnint;//nombredetranchesutiles

int*adc;//pointeursurlescompteursassociésàchaqueintervalle

//adc[i-1]=compteurvaleursdelatranchederangi

floatecart;//largd'unetranche(pouréviterunrecalculsystématique)

public:

histo(float=0.0,float=1.0,int=10);//constructeur

~histo();//destructeur

histo&operator<<(float);//ajouteunevaleur

intoperator[](int);//nombredevaleursdanschaquetranche

};

Notezquenousavonsprévudesvaleurspardéfautpourlesargumentsduconstructeur(celles-cin’étaientpasimposéesparl’énoncé).

En ce qui concerne la définition des différents membres, il faut noter qu’il estindispensable qu’une telle classe soit protégée contre toute utilisation incorrecte.Certes, cela passe par un contrôle de la valeur du numéro de tranche fourni àl’opérateur [], ou par le refus de prendre en compte une valeur hors limites (quifourniraitunnumérodetrancheconduisantàunemplacementsituéendehorsdeceluiallouéparleconstructeur).

Mais nous devons de surcroît nous assurer que les valeurs des arguments fournis auconstructeur ne risquent pas de mettre en cause le fonctionnement ultérieur desdifférentes fonctionsmembre.Enparticulier, il est bondevérifier que le nombredetranchesn’estpasnégatif(notamment,unevaleurnulleconduiraitdans<<àunedivisionpar zéro) et que les valeurs du minimum et du maximum sont convenablementordonnées et différentes l’unede l’autre (dans cedernier cas, onaboutirait encore àunedivisionparzéro).Ici,nousavonsprévuderéglerceproblèmeenattribuantlecaséchéantdesvaleurspardéfautarbitraires(max=min+1,nint=1).

Voicicequepourraitêtreladéfinitiondesdifférentesfonctionsmembredenotreclassehisto:

/**************définitiondelaclassehisto**********/

#include"histo.h"

#include<iostream>

usingnamespacestd;

/*********************constructeur********************/

histo::histo(floatmini,floatmaxi,intninter)

{//protectioncontreargumentserronés

if(maxi<mini)

{floattemp=maxi;maxi=mini;mini=temp;}

if(maxi==mini)maxi=mini+1.0;//arbitraire

218

Page 220: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

if(ninter<1)nint=1;

min=mini;max=maxi;nint=ninter;

adc=newint[nint];//allocemplacementscompteurs

inti;

for(i=0;i<=nint-1;i++)adc[i]=0;//etr.a.z.

ecart=(max-min)/nint;//largeurd'unetranche

}

/*********************destructeur*********************/

histo::~histo()

{deleteadc;

}

/*********************opérateur<<********************/

histo&histo::operator<<(floatv)

{intnt=(v-min)/ecart;

//onnecomptabilisequelesvaleurs"convenables"

if((nt>=0)&&(nt<=nint-1))adc[nt]++;

return(*this);

}

/*********************opérateur[]********************/

inthisto::operator[](intn)//résultatparvaleurici

{if((n<1)||(n>nint))n=1;//protection"débordement"

returnadc[n-1];

}

Voici, à titre indicatif, unpetit programmed’essaide la classe histo, accompagnédurésultatfourniparsonexécution:

#include"histo.h"

#include<iostream>

usingnamespacestd;

main()

{constintnint=4;

inti;

histoh(0.,5.,nint);//4tranchesentre0et5

h<<1.5<<2.4<<3.8<<3.0<<2.0<<3.5<<2.8<<4.6;

h<<12.0<<-3.5;

for(i=0;i<10;i++)h<<i/2.0;

cout<<"valeursdestranches\n";

for(i=1;i<=nint;i++)

cout<<"numéro"<<i<<":"<<h[i]<<"\n";

//uneaffectationtellequeh[2]=...seraitrejetéeencompilation

}

valeursdestranches

numéro1:3

numéro2:5

numéro3:6

numéro4:4

219

Page 221: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice93

ÉnoncéRéaliser une classe nommée stack_int permettant de gérer une pile d’entiers. Cesderniers seront conservés dans un emplacement alloué dynamiquement ; sadimensionseradéterminéeparl’argumentfourniàsonconstructeur(onluiprévoiraunevaleurpardéfautde20).Cetteclassedevracomporter lesopérateurssuivants(noussupposonsquepestunobjetdetypestack_intetnunentier):

•<<,telquep<<najoutel'entiernàlapilep(silapileestpleine,riennesepasse);

•>>,telquep>>nplacedansnlavaleurduhautdelapilep,enlasupprimantdelapile(silapileestvide,lavaleurdenneserapasmodifiée);

•++,telquep++vale1silapilepestpleineet0danslecascontraire;

•--,telquep--vale1silapilepestvideet0danslecascontraire.

On prévoira que les opérateurs << et >> pourront être utilisés sous les formessuivantes(n1,n2etn3étantdesentiers):

p<<n1<<n2<<n3;p>>n1>>n2<<n3;

Onferaensortequ’ilsoitpossibledetransmettreunepileparvaleur.Enrevanche,l’affectation entre piles ne sera pas permise, et on s’arrangera pour que cettesituationaboutisseàunarrêtdel’exécution.

N.B.Lechapitre17vousmontreracomment résoudre leprésentexerciceà l’aidedescomposantsstandardintroduitsparlanorme,qu'ilnefautpaschercheràutiliserici.

La classe stack_int contiendra comme membres donnée : la taille de l’emplacementréservépourlapile(nmax),lenombred’élémentsplacésàunmomentdonnésurlapile(nelem) et un pointeur sur l’emplacement qui sera alloué par le constructeur pour yranger les éléments de la pile (adv).Notez qu’il n’est pas nécessaire de prévoir unedonnéesupplémentairepourunéventuel«pointeur»depile,danslamesureoùc’estlenombred’élémentsnelemquijoueicicerôle.

Lesopérateursrequispeuventindifféremmentêtredéfiniscommefonctionsmembreoucomme fonctions amies. Nous choisirons ici la première solution. Pour que les

220

Page 222: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

opérateurs<<et>>puissentêtreutilisésàplusieursreprisesdansunemêmeexpression,ilestnécessaireque,parexemple:

p<<n1<<n2<<n3;

soitéquivalentà:(((p<<n1)<<n2)<<n3);

Pour ce faire, lesopérateurs << et >> doivent fournir comme résultat la pile reçue enpremieropérande, aprèsqu’ellea subi l’opérationvoulue (empilageoudépilage). Ilestpréférablequecerésultatsoittransmisparréférence(onéviteralapertedetempsdueauconstructeurparrecopie).

En ce qui concerne la transmission par valeur d’un objet de type stack_int, nous nepouvonspasnouscontenterduconstructeurparrecopiepardéfautpuisquecedernierne recopierait pas la partie dynamique de l’objet, ce qui poserait les problèmeshabituels. Nous devons donc surdéfinir le constructeur par recopie de façon qu’ilréaliseune«copieprofonde»del’objet.

Pour satisfaire à la contrainte imposée par l’énoncé sur l’affectation, nous allonssurdéfinir l’opérateurd’affectation.Commecedernierdoit secontenterd’afficherunmessaged’erreuretd’interromprel’exécution,iln’estpasnécessairequ’ilrenvoieunequelconquevaleur(d’oùladéclarationvoid).

Voicicequepourraitêtreladéclarationdenotreclasse:#include<stdlib.h>

#include<iostream>

usingnamespacestd;

classstack_int

{intnmax;//nombremaximaldelavaleursdelapile

intnelem;//nombrecourantdevaleursdelapile

int*adv;//pointeursurlesvaleurs

public:

stack_int(int=20);//constructeur

~stack_int();//destructeur

stack_int(stack_int&);//constructeurparrecopie

//voirremarque1ci-après

voidoperator=(stack_int&);//affectation–voirremarque2

stack_int&operator<<(int);//opérateurd'empilage

stack_int&operator>>(int&);//opérateurdedépilage

//(attentionint&)

intoperator++();//opérateurdetestpilepleine

intoperator--();//opérateurdetestpilevide

};

1. Il est nécessaire que l’argument de stack_int soit transmis par référence. On peututiliserconst ;danscecas,onpourra initialiserunobjetavecunobjetconstantouune expression d’un type susceptible d’être converti implicitement dans le type

221

Page 223: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

stack_int(cequiesticilecasdestypesentiersouflottants;voirlechapitrerelatifauxconversionsdéfiniesparl’utilisateur).

2. En revanche, il n’est pas nécessaire que l’argument de operator= soit transmis parréférence.Sil’onsouhaitaitpouvoiraffecterdesobjetsconstants,ilfaudraitajouterle qualificatif const ; dans ce cas, on pourrait alors, du même coup, affecter desexpressionsd’un type convertibledans le type stack_int, c’est-à-dire ici d’un typeentierouflottant(voirlechapitrerelatifauxconversionsdéfiniesparl’utilisateur).

3. L’opérateur >> doit absolument recevoir son deuxième opérande par référence,puisqu’ildoitpouvoirenmodifierlavaleur.

Voiciladéfinitiondesfonctionsmembredestack_int:#include"stack-int.h"

stack_int::stack_int(intn)

{nmax=n;

adv=newint[nmax];

nelem=0;

}

stack_int::~stack_int()

{deleteadv;

}

stack_int::stack_int(stack_int&p)

{nmax=p.nmax;nelem=p.nelem;

adv=newint[nmax];

inti;

for(i=0;i<nelem;i++)

adv[i]=p.adv[i];

}

voidstack_int::operator=(stack_int&p)

{cout<<"***Tentatived'affectationentrepiles-arrêtexécution***\n";

exit(1);

}

stack_int&stack_int::operator<<(intn)

{if(nelem<nmax)adv[nelem++]=n;

return(*this);

}

stack_int&stack_int::operator>>(int&n)

{if(nelem>0)n=adv[--nelem];

return(*this);

}

intstack_int::operator++()

{return(nelem==nmax);

}

intstack_int::operator--()

{return(nelem==0);

}

À titre indicatif, voici un petit programme d’utilisation de notre classe, accompagnéd’unexempled’exécution:

/************programmed'essaidestack_int*********/

#include"stack_int.h"

#include<iostream>

usingnamespacestd;

main()

{voidfct(stack_int);

222

Page 224: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

stack_intpile(40);

cout<<"pleine:"<<pile++<<"vide:"<<pile--<<"\n";

pile<<1<<2<<3<<4;

fct(pile);

intn,p;

pile>>n>>p;//ondépile2valeurs

cout<<"hautdelapileauretourdefct:"<<n<<""<<p<<"\n";

stack_intpileb(25);

pileb=pile;//tentatived'affectation

}

voidfct(stack_intpl)

{cout<<"hautdelapilereçueparfct:";

intn,p;

pl>>n>>p;//ondépile2valeurs

cout<<n<<""<<p<<"\n";

pl<<12;//onenajouteune

}

pleine:0vide:1

hautdelapilereçueparfct:43

hautdelapileauretourdefct:43

***Tentatived'affectationentrepiles-arrêtexécution***

223

Page 225: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice94

ÉnoncéAdapterlaclassepoint:

classpoint

{

intx,y;

//.......

public:

point(intabs=0,intord=0)//constructeur

//.....

};

defaçonqu’elledisposededeuxfonctionsmembrepermettantdeconnaître,à toutinstant, le nombre total d’objets de type point, ainsi que le nombre d’objetsdynamiques(c’est-à-direcréésparnew)decemêmetype.

Ici,iln’estpluspossibledesecontenterdecomptabiliserlesappelsauconstructeuretau destructeur. Il faut, en outre, comptabiliser les appels à new et delete. La seulepossibilité pour y parvenir consiste à surdéfinir ces deux opérateurs pour la classepoint.Lenombredepoints total et lenombredepointsdynamiques seront conservésdansdesmembresstatiques(n’existantqu’uneseulefoispourl’ensembledesobjetsdutype point). Quant aux fonctions membre fournissant les informations voulues, il estpréférable d’en faire des fonctions membre statiques (déclarées, elles aussi, avecl’attributstatic),cequipermettradelesemployerplusfacilementquesionenavaitfaitdes fonctionsmembreordinaires (puisqu’onpourra lesappelersansavoirà les faireportersurunobjetparticulier).

Voicicequepourraitêtrenotreclassepoint(déclarationetdéfinition):#include<stddef.h>//poursize_t

#include<iostream>

usingnamespacestd;

classpoint

{staticintnpt;//nombretotaldepoints

staticintnptd;//nombredepointsdynamiques

intx,y;

public:

point(intabs=0,intord=0)//constructeur

{x=abs;y=ord;

npt++;

}

~point()//destructeur

{npt--;

}

void*operatornew(size_tsz)//newsurdéfini

224

Page 226: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

{nptd++;

return::newchar[sz];//appellenewprédéfini

}

voidoperatordelete(void*dp)

{nptd--;

::delete(dp);//appelledeleteprédéfini

}

staticintnpt_tot()

{returnnpt;

}

staticintnpt_dyn()

{returnnptd;

}

};

intpoint::npt=0;//initialisationdesmembresstatiquesdepoint

intpoint::nptd=0;

Notezque,dans lasurdéfinitiondenewetdelete,nousavons faitappelauxopérateursprédéfinis(paremploide::)pourcequiconcernelagestiondelamémoire.

Voici un exemple de programme utilisant notre classe point, accompagné du résultatfourniparsonexécution:

#include"point.h"

#include<iostream>

usingnamespacestd;

main()

{

point*ad1,*ad2;

pointa(3,5);

cout<<"A:"<<point::npt_tot()<<""<<point::npt_dyn()<<"\n";

ad1=newpoint(1,3);

pointb;

cout<<"B:"<<point::npt_tot()<<""<<point::npt_dyn()<<"\n";

ad2=newpoint(2,0);

deletead1;

cout<<"C:"<<point::npt_tot()<<""<<point::npt_dyn()<<"\n";

pointc(2);

deletead2;

cout<<"D:"<<point::npt_tot()<<""<<point::npt_dyn()<<"\n";

}

A:10

B:31

C:31

D:30

225

Page 227: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre12Lesconversionsdetypedéfiniespar

l’utilisateur

226

Page 228: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

C++vouspermetdedéfinirdesconversionsd’untypeclasseversunautretypeclasseou un type de base. On parle de conversions définies par l’utilisateur (en abrégé :C.D.U.).Cesconversionspeuventalorséventuellementêtremisesenœuvredefaçonimpliciteparlecompilateur,afindedonnerunesignificationàunappeldefonctionouà un opérateur (sans conversion, l’appel ou l’opérateur serait illégal). On retrouveainsidespossibilitéscomparablesàcellesquinoussontoffertesparleCenmatièredeconversionsimplicites.

Deuxsortesdefonctions(obligatoirementdesfonctionsmembre)permettentdedéfinirdesC.D.U.:

lesconstructeursàunargument(quelquesoitletypedecetargument)réalisentuneconversiondu typede cet argumentdans le typede sa classe ; onpeut cependantutiliser lemot-clé explicit devant la déclaration du constructeur pour en interdirel’utilisationdansuneconversionimplicite;

lesopérateursdecast;dansuneclasseA,ondéfiniraunopérateurdeconversiond’untypex(quelconque,c’est-à-direaussibienuntypedebasequ’unautretypeclasse)enintroduisantlafonctionmembredeprototype:operatorx();

Notezqueletypedelavaleurderetour(obligatoirementdéfiniparsonnom)nedoitpasfigurerdansl’en-têteouleprototyped’unetellefonction.

Lesrèglesd’utilisationdesC.D.U.rejoignentcellesconcernantlechoixd’unefonctionsurdéfinie:

LesC.D.U.nesontmisesenœuvrequesicelaestnécessaire.

Une seule C.D.U. peut intervenir dans une chaîne de conversions (d’un argumentd’unefonctionoud’unopéranded’unopérateur).

Il ne doit pas y avoir d’ambiguïté, c’est-à-dire plusieurs chaînes de conversionsconduisantaumêmetype,pourunargumentouunopérandedonné.

N. B. Aucune conversion n’est réalisable sur un argument effectif en cas detransmission par référence, sauf si l’argument muet correspondant est déclaré avecl’attributconst;danscederniercas,onretrouvelesmêmespossibilitésdeconversionqu’encasdetransmissionparvaleur.

227

Page 229: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice95

ÉnoncéSoitlaclassepointsuivante:

classpoint

{intx,y;

public:

point(intabs=0,intord=0)

{x=abs;y=ord;

}

//.....

};

a. La munir d’un opérateur de cast permettant de convertir un point en un entier(correspondantàsonabscisse).

b.Soientalorscesdéclarations:pointp;

intn;

voidfct(int);

Quefontcesinstructions:n=p;//instruction1

fct(p);//instruction2

a. Il suffit de définir une fonction membre, de nom operator int, sans argument etrenvoyantlavaleurdel’abscissedupointl’ayantappelée.Rappelonsqueletypedela valeur de retour (queC++ déduit du nom de la fonction - ici int) ne doit pasfigurerdansl’en-têteouleprototype.Voicicequepourraientêtreladéclarationetladéfinitiondecettefonction,iciréuniesenuneseuledéclarationd’unefonctionenligne:operatorint()

{returnx;}

b. L’instruction 1 est traduite par le compilateur en une conversion de p en int (parappeldeoperatorint),suivied’uneaffectationdurésultatàn.Notezbienqu’iln’yapas d’appel d’un quelconque opérateur d’affection de la classe point, ni d’unconstructeurparrecopie(carleseulargumenttransmisàlafonctionoperatorintestl’argumentimplicitethis).

L’instruction 2 est traduite par le compilateur en une conversion de p en int (parappeldeoperatorint),suivied’unappeldelafonctionfct,àlaquelleontransmetle

228

Page 230: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

résultat de cette conversion. Notez qu’il n’y pas, là non plus, d’appel d’unconstructeurpar recopie, ni pour fct (puisqu’elle reçoit un argument d’un type debase),nipouroperatorint(pourlamêmeraisonqueprécédemment).

229

Page 231: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice96

ÉnoncéQuelsrésultatsfourniraleprogrammesuivant:

#include<iostream>

usingnamespacestd;

classpoint

{intx,y;

public:

point(intabs,intord)//constructeur2arguments

{x=abs;y=ord;

}

operatorint()//"cast"point-->int

{cout<<"***appelint()pourlepoint"<<x<<""<<y<<"\n";

returnx;

}

};

main()

{pointa(1,5),b(2,8);

intn1,n2,n3;

n1=a+3;//instruction1

cout<<"n1="<<n1<<"\n";

n2=a+b;//instruction2

cout<<"n2="<<n2<<"\n";

doublez1,z2;

z1=a+3;//instruction3

cout<<"z1="<<z1<<"\n";

z2=a+b;//instruction4

cout<<"z2="<<z2<<"\n";

}

Lorsque le compilateur rencontre, dans l’instruction 1, l’expression a + 3, il cherchetoutd’abords’ilexisteunopérateursurdéfinicorrespondantauxtypespointetint(danscetordre).Ici,iln’entrouvepas.Ilvaalorschercheràmettreenplacedesconversionspermettantd’aboutiràuneopérationexistante;ici,l’opérateurdecast(operatorint)luipermetdeserameneràuneadditiond’entiers(parconversiondepenint),etc’estlaseulepossibilité.Le résultat est alors,biensûr,de type int et il est affecté à n1 sansconversion.

Lemêmeraisonnements’appliqueàl’instruction2;l’évaluationdea+bsefaitalorspar conversion de a et de b en int (seule possibilité de donner une signification àl’opérateur+).Lerésultatestdetypeintetilestaffectésansconversionàn2.

Lesexpressionsfigurantàdroitedesaffectationsdesinstructions3et4sontévaluéesexactement suivant lesmêmes règles que les expressions des instructions 1 et 2.Cen’estqu’aumomentde l’affectationdurésultat (de typeint)à lavariablementionnée

230

Page 232: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

quel’onopèreuneconversionint-->double(analogueàcellesquisontmisesenplaceenlangageC).

Àtitreindicatif,voicicequefournitprécisémentl’exécutionduprogramme:***appelint()pourlepoint15

n1=4

***appelint()pourlepoint15

***appelint()pourlepoint28

n2=3

***appelint()pourlepoint15

z1=4

***appelint()pourlepoint15

***appelint()pourlepoint28

z2=3

231

Page 233: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice97

ÉnoncéQuelsrésultatsfourniraleprogrammesuivant:

#include<iostream>

usingnamespacestd;

classpoint

{

intx,y;

public:

point(intabs,intord)//constructeur2arguments

{x=abs;y=ord;

}

operatorint()//"cast"point-->int

{cout<<"**appelint()pourlepoint"<<x<<""<<y<<"\n";

returnx;

}

};

voidfct(doublev)

{cout<<"$$appelfctavecargument:"<<v<<"\n";

}

main()

{pointa(1,4);

intn1;

doublez1,z2;

n1=a+1.75;//instruction1

cout<<"n1="<<n1<<"\n";

z1=a+1.75;//instruction2

cout<<"z1="<<z1<<"\n";

z2=a;//instruction3

cout<<"z2="<<z2<<"\n";

fct(a);//instruction4

}

Pourévaluerl’expressiona+1.75de l’instruction1, lecompilateurmetenplaceunechaînedeconversionsdeaendouble(point-->intsuiviedeint-->double)demanièreàaboutiràl’additiondedeuxvaleursdetypedouble;lerésultat,detypedouble,estensuiteconvertipourêtreaffectéàn1 (conversionforcéepar l’affectation,commed’habitudeenC).

Notezbienqu’iln’estpasquestionpourlecompilateurdeprévoirlaconversionenintdelavaleur1.75(defaçonàserameneràl’additiondedeuxint,aprèsconversiondeaenint)carils’agitlàd’une«conversiondégradante»quin’estjamaismiseenœuvrede manière implicite dans un calcul d’expression. Il n’y a donc pas d’autre choixpossible(notezques’ilyenavaiteffectivementunautre,ilnes’agiraitpaspourautantd’une situation d’ambiguïté dans la mesure où le compilateur appliquerait alors les

232

Page 234: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

règleshabituellesdechoixd’unefonctionsurdéfinie).

L’instruction2correspondàunraisonnementsimilaire,aveccetteseuledifférencequelerésultatdel’addition(detypedouble)peutêtreaffectéàz1sansconversion.

Enfinlesinstructions3et4entraînentuneconversiondepointendouble,parunesuitedeconversionspoint-->intetint-->double.

Voicilerésultatdel’exécutionduprogramme:**appelint()pourlepoint14

n1=2

**appelint()pourlepoint14

z1=2.75

**appelint()pourlepoint14

z2=1

**appelint()pourlepoint14

$$appelfctavecargument:1

233

Page 235: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice98

ÉnoncéQue se passerait-il si, dans la classe point du précédent exercice, nous avionsintroduit, en plus de l’opérateur operator int, un autre opérateur de cast operator

double?

Dans ce cas, les instructions 1 et 2 auraient conduit à une situation d’ambiguïté. Eneffet, le compilateur aurait disposé de deux chaînes de conversions permettant deconvertirunpointendouble:soitpoint-->double,soitpoint-->intsuiviedeint->double.Enrevanche,lesinstructions3et4auraienttoujoursétéacceptées.

234

Page 236: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice99

ÉnoncéConsidérerlaclassesuivante:

classcomplexe

{doublex,y;

public:

complexe(doubler=0,doublei=0);

complexe(complexe&);//oucomplexe(constcomplexe&)

};

Dansunprogrammecontenantlesdéclarations:complexez(1,3);

voidfct(complexe);

queproduirontlesinstructionssuivantes:z=3.75;//instruction1

fct(2.8);//instruction2

z=2;//instruction3

fct(4);//instruction4

L’instruction1conduitàuneconversiondelavaleurdouble3.75enuncomplexeparappeldu constructeur à un argument (compte tenu des arguments par défaut), suivie d’uneaffectationàz.

L’instruction2conduitàuneconversionde lavaleurdouble2.8enuncomplexe (commeprécédemment par appel du constructeur à un argument) ; il y aura ensuite créationd’unecopie,parappelduconstructeurparrecopiedelaclassecomplexe,copiequiseratransmiseàlafonctionfct.

Lesinstructions3et4jouentlemêmerôlequelesdeuxprécédentes,aveccetteseuledifférence qu’elles font intervenir des conversions int --> complexe obtenues par uneconversionint-->doublesuivied’uneconversiondouble-->complexe.

235

Page 237: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice100

Énoncéa.Quelsrésultatsfourniraleprogrammesuivant:

#include<iostream>

usingnamespacestd;

classpoint

{intx,y;

public:

point(intabs=0,intord=0)//constructeur0,1ou2arguments

{x=abs;y=ord;

cout<<"$$constructionpoint:"<<x<<""<<y<<"\n";

}

friendpointoperator+(point,point);//point+point-->point

voidaffiche()

{cout<<"Coordonnées:"<<x<<""<<y<<"\n";}

};

pointoperator+(pointa,pointb)

{pointr;

r.x=a.x+b.x;r.y=a.y+b.y;

returnr;

}

main()

{pointa,b(2,4);

a=b+6;//affectation1

a.affiche();

a=6+b;//affectation2

b.affiche();

}

b.Mêmequestionensupposantquel’opérateur+aétésurdéfinicommeunefonctionmembreetnonpluscommeunefonctionamie.

c.Mêmequestionensupposantquel’opérateur+estdéfiniainsi:friendpointoperator+(point&,point&);

d.Mêmequestionensupposantquel’opérateur+estdéfiniainsi:friendpointoperator+(constpoint&,constpoint&);

a. L’évaluation de l’expression b + 6 de la première affectation se fait en utilisantl’opérateur+surdéfinipourlaclassepoint,aprèsavoirconvertil’entier6enunpoint(parappelduconstructeuràunargument).Lerésultat,detypepoint,estalorsaffectéàa.

L’instructiond’affectation2sedérouledefaçonsimilaire(conversiondel’entier6

236

Page 238: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

enunpoint).

Endéfinitive,onobtientlesrésultatssuivants:$$constructionpoint:00

$$constructionpoint:24

$$constructionpoint:60

$$constructionpoint:00

Coordonnées:84

$$constructionpoint:60

$$constructionpoint:00

Coordonnées:24

b.Enrevanche,sil’opérateur+avaitétésurdéfiniparunefonctionmembre,lasecondeinstructiond’affectationauraitétérejetéeàlacompilation;eneffet,elleauraitétéinterprétéecomme:.operator+(b)

Onvoiticiqueseulelafonctionamiepermetdetraiterdefaçonidentiquelesdeuxopérandesd’unopérateurbinaire,notammentencequiconcernelespossibilitésdeconversionsimplicites.

c.Latransmissionparréférenceimposequel’argumenteffectifd’unefonctionsoitdumême type que l’argument muet correspondant, aucune conversion n’étant alorspossible.Lesaffectations1et2serontrejetéesencompilation.

d.Laprésencedeconstpermetaucompilateurdetransmettreàlafonction(operator+)unobjet temporaire obtenu par d’éventuelles conversions des arguments effectifs.Touteslesaffectationsredeviennentcorrectes.

237

Page 239: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice101

ÉnoncéSoientlesdeuxclassessuivantes:

classB

{//...

public:

B();//constructeursansargument

B(int);//constructeuràunargumententier

//...

};

classA

{//...

friendoperator+(A,A);

public:

A();//constructeursansargument

A(int);//constructeuràunargumententier

A(B);//constructeuràunargumentdetypeB

//...

};

a.Dansunprogrammecontenantlesdéclarations:Aa1,a2,a3;

Bb1,b2,b3;

lesinstructionssuivantesseront-ellescorrecteset,sioui,queferont-elles?a1=b1;//instruction1

b1=a1;//instruction2

a3=a1+a2;//instruction3

a3=b1+b2;//instruction4

b3=a1+a2;//instruction5

b.Commentobtenirlemêmerésultatsansdéfinir,dansA,leconstructeurA(B)?

a.

Instruction1

Ilfautdistinguer2cas:

SiAn’apassurdéfinid’opérateurd’affectationd’unobjetdetypeBàunobjetdetypeA,il y aura conversion de b1 dans le type de A (par appel du constructeur A(B)), suivied’une affectation à a1 (en utilisant soit l’opérateur d’affectation par défaut de A, soitéventuellementceluiquiauraitpuyêtresurdéfini).

238

Page 240: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

La formeusuelle de l’en-tête de l’opérateur A est A & operator = (const & A),mais lesréférencesnesontpasindispensables,pasplusqueconst.Demême,lavaleurderetourpeut ne pas exister dès lors qu’on ne cherche pas à pas réaliser des affectationsmultiples.

Enrevanche,siAasurdéfiniunopérateurd’affectationd’unobjetdetypeBàunobjetdetypeA,cedernierserautilisépourréaliserl’instruction1,et,danscecas,iln’yauradoncpasd’appeldeconstructeurdeA,nid’éventuelautreopérateurd’affectation.Cecomportements’expliqueparlefaitquelesC.D.U.nesontmisesenœuvrequelorsquecelaestnécessaire.

Instruction2

Làencore,ilfautdistinguerlesdeuxcasprécédents.SiBn’apassurdéfinid’opérateurd’affectationd’unobjetde type B àunobjetde type A, l’instruction2 conduira àuneerreurdecompilationpuisqu’iln’existeraaucunepossibilitédeconvertirunobjetdetypeAenunobjetdetypeB.Enrevanche,sil’opérateurd’affectationenquestionexiste,ilseratoutsimplementutilisé.

Instruction3

Elleneposeaucunproblèmeparticulier.

Instruction4

Ici,b1etb2serontconvertisdansletypeAenutilisantleconstructeurA(B)avantd’êtretransmis à l’opérateur + (de A). Le résultat, de type A, sera affecté à a3, en utilisantl’opérateurd’affectationdeA—soitceluipardéfaut,soitceluiéventuellementsurdéfini.

Instruction5

L’expression a1 + a2 ne pose pas de problème puisqu’elle est évaluée à l’aide del’opérateur + de la classe A ; elle fournit un résultat de type A. En revanche, pourl’affectationdurésultatàb3,ilfautànouveaudistinguerlesdeuxcasdéjàévoqués.SiBasurdéfiniunopérateurd’affectationd’unobjetdetypeBàunobjetdetypeA,ilserautilisé ; si un tel opérateur n’a pas été surdéfini, l’instruction sera rejetée par lecompilateur(puisqu’iln’existeraalorsaucunepossibilitédeconversiondeBenA).

b.Enintroduisant,danslaclasseB,unopérateurdecastpermettantlaconversiondeBenA,delaforme:operatorB()

239

Page 241: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre13Latechniquedel’héritage

240

Page 242: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

Le concept d’héritage constitue l’un des fondements de la Programmation OrientéeObjet.IlpermetdedéfinirunenouvelleclasseBdite«dérivée»,àpartird’uneclasseexistanteA,dite«debase»;pourcefaire,onprocèdeainsi:

classB:publicA//ou:privateAou(depuislaversion3)protectedA

{//définitiondesmembressupplémentaires(donnéesoufonctions)

//ouredéfinitiondemembresexistantsdansA(donnéesoufonctions)

};

Avec public A, on parle de « dérivation publique » ; avec private A, on parle de«dérivationprivée»;avecprotectedA,onparlede«dérivationprotégée».

Modalitésd’accèsàlaclassedebaseLes membres privés d’une classe de base ne sont jamais accessibles aux fonctionsmembredesaclassedérivée.

Outre les « statuts » public ou privé (présentés au chapitre 3), il existe un statut« protégé ». Un membre protégé se comporte comme un membre privé pour unutilisateur quelconque de la classe ou de la classe dérivée,mais commeunmembrepublicpourlaclassedérivée.

D’autrepart,ilexistetroissortesdedérivation:

publique – lesmembresde la classedebase conservent leur statut dans la classedérivée;c’estlasituationlaplususuelle;

privée – tous lesmembres de la classe de base deviennent privés dans la classedérivée;

protégée (depuis la version 3) – les membres publics de la classe de basedeviennentmembresprotégésdelaclassedérivée;lesautresmembresconserventleurstatut.

Lorsqu’unmembre (donnéeou fonction)est redéfinidansuneclassedérivée, il restetoujourspossible(soitdanslesfonctionsmembredecetteclasse,soitpourunclientdecetteclasse)d’accéderauxmembresdemêmenomdelaclassedebase;ilsuffitpourcelad’utiliserl’opérateurderésolutiondeportée(::),sousréserve,biensûr,qu’untelaccèssoitautorisé.

241

Page 243: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

AppeldesconstructeursetdesdestructeursSoitBuneclassedérivéed’uneclassedebaseA.Naturellement,dèslorsqueBpossèdeau moins un constructeur, la création d’un objet de type B implique obligatoirementl’appel d’un constructeur de B.Mais, de plus, ce constructeur de B doit prévoir desargumentsàdestinationd’unconstructeurdeA(uneexceptionalieusoitsiAn’apasdeconstructeur, soit si A possède un constructeur sans argument). Ces arguments sontprécisésdansladéfinitionduconstructeurdeB,commedanscetexemple:

B(intx,inty,charcoul):A(x,y);

Les arguments mentionnés pour A peuvent éventuellement l’être sous formed’expressions.

CasparticulierduconstructeurparrecopieEnplusdesrèglesci-dessus,ilfautajouterquesilaclassedérivéeBnepossèdepasdeconstructeurpar recopie, ilyauraappelduconstructeurpar recopiepardéfautdeB,lequelprocéderaainsi:

appel du constructeur par recopie de A (soit celui qui y a été défini, soit leconstructeurparrecopiepardéfaut);

initialisationdesmembresdonnéedeBquinesontpashéritésdeA.

En revanche, unproblème se pose lorsque la classe dérivéedéfinit explicitement unconstructeurparrecopie.Eneffet,danscecas,ilfauttenircomptedecequel’appeldececonstructeurparrecopieentraîneral’appel:

du constructeur de la classe de basementionné dans son en-tête, comme dans cetexemple (il s’agit ici d’un constructeur par recopie de la classe de base,mais ilpourraits’agirden’importequelautreconstructeur):B(B&b):A(b);//appelduconstructeurparrecopiedeA

//auquelseratransmiselapartiedeBhéritéedeA

//(grâceauxrèglesdecompatibilitéentre

//classedérivéeetclassedebase)

d’un constructeur sans argument, si aucun constructeur de la classe de base n’estmentionné dans l’en-tête ; dans ce cas, il est nécessaire que la classe de basedisposed’untelconstructeursansargument,fautedequoionobtiendraituneerreurdecompilation.

Conséquencesdel’héritage

242

Page 244: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Considérons la situation suivante, dans laquelle la classe A possède une fonctionmembref (dontnousneprécisonspaslesarguments)fournissantunrésultatdetypet(quelconque:typedebaseoutypedéfiniparl’utilisateur,éventuellementtypeclasse):

classAclassB:publicA

{.....{.....

public:};

tf(...);

.....

};

Aa;//aestdutypeA

Bb;//bestdutypeB,dérivédeA

Naturellement,unappeltelquea.f(...)aunsensetilfournitunrésultatdetypet.LefaitqueBhéritepubliquementdeApermetalorsdedonnerunsensàunappeltelque:

b.f(...)

La fonction f agira sur b, comme s’il était de type A.Le résultat fourni par f seracependanttoujoursdetypet,même,notamment,lorsqueletypetestprécisémentletypeA(lerésultatdefpourratoutefoisêtresoumisàd’éventuellesconversionss’ilestaffectéàunelvalue).

Casparticulierdel’opérateurd’affectationConsidéronsuneclasseBdérivantd’uneclasseA.

SilaclassedérivéeBn’apassurdéfinil’opérateurd’affectation,l’affectationdedeuxobjetsdetypeBsedéroulemembreàmembre,enconsidérantquela«partiehéritéedeA»constitueunmembre.Ainsi, lesmembrespropresàB sont traitéspar l’affectationprévuepourleurtype(pardéfautousurdéfinie,suivantlecas).LapartiehéritéedeAesttraitéeparl’affectationprévuedanslaclasseA.

Si laclassedérivéeBasurdéfini l’opérateur=, l’affectationdedeuxobjetsde typeBferanécessairementappelàl’opérateur=définidansB.CeluideAneserapasappelé,même s’il a été surdéfini. Il faudradoncque l’opérateur=deBprenne en chargetoutcequiconcernel’affectationd’objetsdetypeB,ycomprispourcequiestdesmembreshéritésdeA(quitteàfaireappelàl’opérateurd’affectationdeA).

Compatibilitéentreobjetsd’uneclassedebaseetobjetsd’uneclassedérivéeConsidérons:

classAclassB:publicA

{.....{.....

243

Page 245: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

};};

Aa;//aestdutypeA

Bb;//bestdutypeB,dérivédeA

A*ada;//adaestunpointeursurdesobjetsdetypeA

B*adb;//adbestunpointeursurdesobjetsdetypeB

Ilexistedeuxconversionsimplicites:

d’unobjetd’untypedérivédansunobjetd’untypedebase.Ainsil’affectationa=best légale : elle revient à convertir b dans le type A (c’est-à-dire, en fait, à neconsidérerdebquecequiestdutypeA)etàaffectercerésultatàa(avecappel,soitde l’opérateur d’affectation de A si celui-ci a été surdéfini, soit de l’opérateurd’affectationpardéfautdeA).L’affectationinverseb=aest,quantàelle,illégale;

d’un pointeur sur une classe dérivée en un pointeur sur une classe de base.Ainsil’affectationada=adbestlégale,tandisqueadb=adaestillégale(ellepeutcependantêtreforcéeparemploidel’opérateurdecast:adb=(B*)ada).

244

Page 246: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice102

ÉnoncéOndisposed’unfichiernommépoint.hcontenantladéclarationsuivantedelaclassepoint:

classpoint

{floatx,y;

public:

voidinitialise(floatabs=0.0,floatord=0.0)

{x=abs;y=ord;

}

voidaffiche()

{cout<<"Pointdecoordonnées:"<<x<<""<<y<<"\n";

}

floatabs(){returnx;}

floatord(){returny;}

};

a. Créer une classe pointb, dérivée de point comportant simplement une nouvellefonctionmembre nommée rho, fournissant la valeur du rayon vecteur (premièrecoordonnéepolaire)d’unpoint.

b.Même question, en supposant que lesmembres x et y ont été déclarés protégés(protected)danspoint,etnonplusprivés.

c.Introduireunconstructeurdanslaclassepointb.

d.Quellessontlesfonctionsmembreutilisablespourunobjetdetypepointb?

a. Ilsuffitdeprévoir,dansladéclarationdepointb,unenouvelle fonctionmembredeprototype:floatrho();

Toutefois,commelesmembresxetysontprivés,ilsrestentprivéspourlesfonctionsmembredesaclassedérivéepointb;cequisignifiequ’auseindeladéfinitionderho,ilfautfaireappelaux«fonctionsd’accès»depointquesontabsetord.Voicicequepourraitêtrenotreclassepointb(ici,nousavonsfournirhosousformed’unefonctionenligne):#include"point.h"//pourladéclarationdelaclassepoint

#include<math.h>

classpointb:publicpoint

{public:

floatrho()

245

Page 247: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

{returnsqrt(abs()*abs()+ord()*ord());

}

};

Notezque, tellequ’elleaétédéfinie, laclassepointn’apasdonnénaissanceàunfichierobjet(puisquetoutessesfonctionsmembresontenligne).Ilenvademêmeicipourpointb.Aussi,pourutiliserpointbauseind’unprogramme,ilsuffirad’inclurelesfichierscontenant lesdéclarationsdepointb.Naturellement,dans lapratique, ilenirararementainsi;engénéral,ondevrafournirnonseulementlesdéclarationsdela classe de base et de sa classe dérivée, mais également les fichiers objetcorrespondantàleurscompilationsrespectives.

b.Ladéfinitionprécédenterestevalablemais,néanmoins,commelesmembresxetydepoint ont été déclarés protégés, ils sont accessibles aux fonctions membre de saclasse dérivée ; aussi est-il possible, dans la définition de rho, de les utiliser«directement».Voicicequepourraitdevenirnotrefonctionrho (toujours ici«enligne»):floatrho()//ilfautquexetysoient

{returnsqrt(x*x+y*y);//déclarés"protected"danspoint

}

c. Voici ce que pourrait être un constructeur à deux arguments (avec valeurs pardéfaut):pointb(floatc1=0.0,floatc2=0.0)

{initialise(c1,c2);

}

Là encore, si les membres x et y de point ont été déclarés «þprotégésþ», il estpossibled’écrireainsinotreconstructeur:pointb(floatc1=0.0,floatc2=0.0)

{x=c1;y=c2;//ilfautquexetysoient

}//déclarés"protected"danspoint

Notezqu’iciiln’estpaspossibleauconstructeurdepointbd’appelerunquelconqueconstructeurdepointpuisquecederniertypenepossèdepasdeconstructeur.

d. Un objet de type pointb peut utiliser n’importe laquelle des fonctions membrepubliques de point, c’est-à-dire initialise, affiche, abs et ord, ainsi que n’importelaquelledesfonctionsmembrepubliquesdepointb,c’est-à-direrhoouleconstructeurpointb.Notezd’ailleursqu’icileconstructeuretinitialisefontdoubleemploi:celaprovientd’unepartdecequepointnedisposepasdevéritableconstructeur,d’autrepartdeceque pointb n’apasdéfinidemembresdonnée supplémentaires,de sortequ’il n’y a rien de plus à faire pour initialiser un objet de type pointb que pourinitialiserunobjetdetypepoint.

246

Page 248: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice103

ÉnoncéOndisposed’unfichierpoint.hcontenantladéclarationsuivantedelaclassepoint:

#include<iostream>

usingnamespacestd;

classpoint

{floatx,y;

public:

point(floatabs=0.0,floatord=0.0)

{x=abs;y=ord;

}

voidaffiche()

{cout<<"Coordonnées:"<<x<<""<<y<<"\n";

}

voiddeplace(floatdx,floatdy)

{x=x+dx;y=y+dy;

}

};

a.Créeruneclassepointcol,dérivéedepoint,comportant:

•unmembredonnéesupplémentairecl,detypeint,contenantla«couleur»d’unpoint;

•lesfonctionsmembresuivantes:affiche(redéfinie),quiaffichelescoordonnéesetlacouleurd’un

objetdetypepointcol;

colore(intcouleur),quipermetdedéfinirlacouleurd’unobjetde

typepointcol,

unconstructeurpermettantdedéfinirlacouleuretlescoordonnées(onneledéfinirapasenligne).

b.Queferaalorsprécisémentcetteinstruction:pointcol(2.5,3.25,5);

a.Lafonctioncoloreneposeaucunproblèmeparticulierpuisqu’elleagituniquementsurunmembredonnéepropreà pointcool.Encequiconcerne affiche, il est nécessairequ’ellepuisseafficherlesvaleursdesmembresxety,héritésdepoint.Commecesmembressontprivés(etnonprotégés),iln’estpaspossiblequelanouvelleméthodeaffiche de pointcol accède à eux directement. Elle doit donc obligatoirement faireappelàlaméthodeaffichedutypepoint;ilsuffit,pourcela,d’utiliserl’opérateurde

247

Page 249: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

résolution de portée. Enfin, le constructeur de pointcol doit retransmettre auconstructeur de point les coordonnées qu’il aura reçues par ses deux premiersarguments.

Voicicequepourraitêtrenotreclassepointcol(ici,touteslesfonctionsmembre,saufleconstructeur,sontenligne):/******fichierpointcol.h:déclarationdepointcol******/

#include"point.h"

#include<iostream>

usingnamespacestd;

classpointcol:publicpoint

{intcl;

public:

pointcol(float=0.0,float=0.0,int=0);

voidcolore(intcoul)

{cl=coul;

}

voidaffiche()//affichedoitappeleraffichede

{point::affiche();//pointpourlescoordonnées

cout<<"couleur:"<<cl;//maiselleaaccèsàlacouleur

}

};

/******définitionduconstructeurdepointcol*****/

#include"point.h"

#include"pointcol.h"

pointcol::pointcol(floatabs,floatord,intcoul):point(abs,ord)

{cl=coul;//onpourraitaussiécrirecolore(coul);

}

Notezbienquel’onpréciseleconstructeurdepointdevantêtreappeléparceluidepointcol,auniveauduconstructeurdepointcol,etnondesadéclaration.

b.Ladéclarationpointcol(2.5,3.25,5)entraîne lacréationd’unemplacementpourunobjetdetypepointcol,lequelestinitialiséparappel,successivement:

duconstructeurdepoint,quireçoitenargumentlesvaleurs2.5et3.25(commeprévudansl’en-têteduconstructeurdepointcol)þ;

duconstructeurdepointcol.

248

Page 250: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice104

ÉnoncéOnsupposequ’ondisposede lamêmeclassepoint (etdoncdu fichier point.h) quedans l’exercice précédent. Créer une classe pointcol possédant les mêmescaractéristiques que ci-dessus, mais sans faire appel à l’héritage. Quellesdifférencesapparaîtrontentrecetteclassepointcoletcelledel’exerciceprécédent,auniveaudespossibilitésd’utilisation?

La seuledémarchepossible consiste à créer une classe pointcol dans laquelle undesmembres donnée est lui-même de type point. Sa déclaration et sa définition seprésenteraientalorsainsi:

/*****fichierpointcol.h:déclarationdepointcol*****/

#include"point.h"

#include<iostream>

usingnamespacestd;

classpointcol

{pointp;

intcl;

public:

pointcol(float=0.0,float=0.0,int=0);

voidcolore(intcoul)

{cl=coul;

}

voidaffiche()

{p.affiche();//affichedoitappeleraffiche

cout<<"couleur:"<<cl;//dupointppourles

//coordonnées

}

};

/******définitionduconstructeurdepointcol*****/

#include"point.h"

#include"pointcol.h"

pointcol::pointcol(floatabs,floatord,intcoul):p(abs,ord)

{cl=coul;

}

Apparemment, il existe une analogie étroite entre cette classe pointcol et celle del’exercice précédent. Néanmoins, l’utilisateur de cette nouvelle classe ne peut plusfairedirectementappelauxfonctionsmembrehéritéesdepoint.Ainsi,pourappliquerlaméthodedeplaceàunobjetadetypepoint,ildevraitabsolumentécrire:a.p.deplace(...);or,celan’estpasautoriséici,puisquepestunmembreprivédepointcol.

249

Page 251: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice105

ÉnoncéSoit une classe point ainsi définie (nous ne fournissons pas la définition de sonconstructeur):

classpoint

{intx,y;

public:

point(int=0,int=0);

friendintoperator==(point,point);

};

intoperator==(pointa,pointb)

{returna.x==b.x&&a.y==b.y;

}

Soitlaclassepointcol,dérivéedepoint:classpointcol:publicpoint

{intcl;

public:

pointcol(int=0,int=0,int=0);

//éventuellesfonctionsmembre

};

a.Siaetbsontdetypepointcoletpdetypepoint,lesinstructionssuivantessont-ellescorrecteset,sioui,quefont-elles?if(a==b)...//instruction1

if(a==p)...//instruction2

if(p==a)...//instruction3

if(a==5)...//instruction4

if(5==a)...//instruction5

b.Mêmesquestions,ensupposant,cettefois,quel’opérateur+aétédéfiniauseindepoint,sousformed’unefonctionmembre.

a. Les 5 instructions proposées sont correctes. D’une manière générale, x == y estinterprétécommeoperator==(x,y).Sixetysontdetypepoint,aucunproblèmenesepose bien sûr. Si l’un des opérandes est de type pointcol (ou les deux), il seraconverti implicitementdans le type point.Si l’undesopérandes estde type int, ilsera converti implicitementdans le type point (par utilisationdu constructeur à unargumentdepoint).

Encequiconcernelasignificationdelacomparaison,onvoitqu’ellerevientàneconsidérer d’un objet de type pointcol que ses coordonnées. Pour un entier, elle

250

Page 252: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

revientàleconsidérercommeunpointayantcetentierpourabscisseetuneordonnéenulle.

N.B.Silesargumentsdeoperator=étaienttransmisparréférence,lesdeuxdernièresaffectationsseraientrejetées,àmoinsd’avoirenplusprévul’attributconst.

b.Cettefois,x==yestinterprétécommex.operator==(y).Sixestdetypepointetyd’untype pouvant se ramener au type point (c’est-à-dire soit du type pointcol qui seraconverti implicitement en un type de base point, soit d’un type entier qui seraconverti implicitement enun type point par l’intermédiairedu constructeur), aucunproblèmenesepose(c’estlecasdelatroisièmeinstruction).

Sixestdetypepointcoletyd’untypepouvantseramenerautypepoint,onretrouvelecas précédent, dans lamesure où la fonctionmembre operator ==, héritée de point,peuttoujourss’appliqueràunobjetdetypepoint(c’estlecasdesinstructions1,2et4).

Enrevanche,sixestdetypeint,iln’estpluspossibledeluiappliquerunefonctionmembre.C’estcequisepassedansladernièreinstruction,quiseradoncrejetéeàlacompilation.

N.B. Si l’unique argument de operator= était transmis par référence, la quatrièmeaffectationseraitrejetée,àmoinsd’avoirprévuenplusl’attributconst.

251

Page 253: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice106

ÉnoncéSoituneclassevectpermettantdemanipulerdes«vecteursdynamiques»d’entiers(c’est-à-dire dont la dimension peut être fixée aumoment de l’exécution) dont ladéclaration(fourniedanslefichiervect.h)seprésenteainsi:

classvect

{intnelem;//nombred'éléments

int*adr;//adressezonedynamiquecontenantleséléments

public:

vect(int);//constructeur(préciselatailleduvecteur)

~vect();//destructeur

int&operator[](int);//accèsàunélémentparson"indice"

};

Onsupposequeleconstructeuralloueeffectivementl’emplacementnécessairepourle nombre d’entiers reçu en argument et que l’opérateur [] peut être utiliséindifféremmentdansuneexpressionouàgauched’uneaffectation.

Créer une classe vectb, dérivée de vect, permettant de manipuler des vecteursdynamiques,danslesquelsonpeutfixerles«bornes»desindices,lesquellesserontfournies au constructeur de vectb. La classe vect apparaîtra ainsi comme un casparticulierdevectb(unobjetdetypevectétantunobjetdetypevectbdans lequel lalimiteinférieuredel’indiceest0).

Onnesepréoccuperapas,ici,desproblèmeséventuellementposésparlarecopieoul’affectationd’objetsdetypevectb.

Nousprévoirons,dansvectb,deuxmembresdonnéesupplémentaires(debutetfin)pourconserverlesbornesdel’indice(entouterigueur,onpourraitsecontenterd’unmembresupplémentairecontenantlalimiteinférieure,sachantquelavaleursupérieurepourraits’endéduireàpartirdelaconnaissancedelatailleduvecteur;toutefois,cettedernièreinformationn’étantpaspublique,nousrencontrerionsdesproblèmesd’accès!).

Manifestement,vectbnécessiteunconstructeuràdeuxargumentsentierscorrespondantauxbornesdel’indice;sonen-têtepourraitcommencerainsi:

vectb(intd,intf)

Commel’appeldececonstructeurentraîneraautomatiquementceluiduconstructeurdevect, iln’estpasquestiondefaire l’allocationdynamiquedenotrevecteurdansvectb.

252

Page 254: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Au contraire, nous réutilisons le travail effectué par vect, auquel nous transmettronssimplement lenombred’éléments souhaités, c’est-à-dire ici f-d+1.Voici l’en-têtecompletduconstructeurdevectb:

vectb(intd,intf):vect(f-d+1)

Latâchespécifiquedevectb se limiteraà renseigner lesvaleursdesmembresdonnéedebutetfin.

Aucun destructeur n’est nécessaire pour vectb, dans la mesure où son constructeurn’alloueaucunautreemplacementdynamiquequeceluiallouéparvect.

Encequiconcernel’opérateur[],onpeutpenserquevectb l’héritedevectetque,parconséquent, il n’est pas nécessaire de le surdéfinir. Toutefois, la notation t[i] nedésigneplusforcémentl’élémentderangid’unobjetdetypevectb.Or,manifestement,on souhaitera qu’il en aille toujours ainsi. Il faut donc redéfinir [] pour vectb, quitted’ailleursàréutiliserl’opérateurdéfinidansvect.

Voicicequepourraitêtrenotreclassevectb(ici,onnetrouvequ’unedéfinition,puisquelesdeuxfonctionsmembreontétédéfiniesenligne):

#include"vect.h"

classvectb:publicvect

{intdebut,fin;

public:

vectb(intd,intf):vect(f-d+1)

{debut=d;fin=f;

}

int&operator[](inti)

{returnvect::operator[](i-debut);

}

};

1.Silemembredonnéeadravaitétédéclaréprotégé(protected)danslaclassevect,nousaurionspuredéfinirl’opérateur[]devectb,sansfaireappelàceluidevect:int&operator[](inti)

{returnadr[i-debut];

}

2.Aucuneprotectiond’indicesn’est àprévoirdans vectb, dès lorsqu’elle adéjà étéprévuedansvect.

253

Page 255: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice107

ÉnoncéSoit une classe int2d (telle que celle créée dans l’exercice 91) permettant demanipuler des « tableaux dynamiques » d’entiers à deux dimensions dont ladéclaration(fourniedanslefichierint2d.h)seprésenteainsi:

classint2d

{intnlig;//nombrede"lignes"

intncol;//nombrede"colonnes"

int*adv;//adresseemplacementdynamiquecontenantlesvaleurs

public:

int2d(intnl,intnc);//constructeur

~int2d();//destructeur

int&operator()(int,int);//accèsàunélément,parses2"indices"

};

Onsupposequeleconstructeuralloueeffectivementl’emplacementnécessaireetquel’opérateur[]peutêtreutiliséindifféremmentdansuneexpressionouàgauched’uneaffectation.

Créer une classe int2db, dérivée de int2d, permettant de manipuler des tableauxdynamiques,dans lesquelsonpeut fixer les«bornes» (valeurminimaleetvaleurmaximale)desdeuxindices; lesquatrevaleurscorrespondantesserontfourniesenargumentsduconstructeurdeint2db.

Onnesepréoccuperapas,ici,desproblèmeséventuellementposésparlarecopieoul’affectationd’objetsdetypeint2db.

Il suffit, en fait, de généraliser à la classe int2d le travail réalisé dans l’exerciceprécédentpourlaclassevect.Voicicequepourraitêtrenotreclasseint2db (ici,onnetrouve qu’une définition, dans la mesure où les fonctions membre de int2db ont étéfourniesenligne):

#include"int2d.h"

classint2db:publicint2d

{intligdeb,ligfin;//bornes(mini,maxi)premierindice

intcoldeb,colfin;//bornes(mini,maxi)secondindice

public://constructeur

int2db(intld,intlf,intcd,intcf):int2d(lf-ld+1,cf-cd+1)

{ligdeb=ld;ligfin=lf;

coldeb=cd;colfin=cf;

}

int&int2db::operator()(inti,intj//redéfinitiondeoperator()

{returnint2d::operator()(i-ligdeb,j-coldeb);

}

};

254

Page 256: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Notezque,lànonplus,aucuneprotectiond’indicesupplémentairen’estàprévoirdansint2db,dèslorsqu’elleadéjàétéprévuedansint2d.

255

Page 257: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice108

ÉnoncéSoituneclassevectpermettantdemanipulerdes«vecteursdynamiques»d’entiers,dont la déclaration (fournie dans un fichier vect.h) se présente ainsi (notez laprésencedemembresprotégés):

classvect

{protected://enprévisiond'uneéventuelleclassedérivée

intnelem;//nombred'éléments

int*adr;//adressezonedynamiquecontenantleséléments

public:

vect(int);//constructeur

~vect();//destructeur

int&operator[](int);//accèsàunélémentparson"indice"

};

Onsupposequeleconstructeuralloueeffectivementl’emplacementnécessaireetquel’opérateur[]peutêtreutiliséindifféremmentdansuneexpressionouàgauched’uneaffectation. En revanche, comme on peut le voir, cette classe n’a pas prévu deconstructeur par recopie et elle n’a pas surdéfini l’opérateur d’affectation.L’affectation et la transmission par valeur d’objets de type vect posent donc les«problèmeshabituels».

Créeruneclassevectok,dérivéedevect,tellequel’affectationetlatransmissionparvaleur d’objets de type vectok s’y déroulent convenablement. Pour faciliterl’utilisation de cette nouvelle classe, introduire une fonction membre taille

fournissantladimensiond’unvecteur.

Écrireunpetitprogrammed’essai.

Manifestement,laclassevectokn’apasbesoindedéfinirdenouveauxmembresdonnée.Pourdéclarerdesobjetsdetypevectok,ilfaudrapouvoirenpréciserladimension,cequi signifie que vectok devra absolument disposer d’un constructeur approprié. Cedernier se contentera toutefois de retransmettre la valeur reçue en argument auconstructeurdevect ; il auradoncuncorpsvide.Notezqu’iln’estpasnécessairedeprévoirundestructeur(carceluidevectseraappeléencasdedestructiond’unobjetdetypevectoketiln’yariendeplusàfaire).

Notezquepourgérer convenablement la recopieou l’affectationd’objets, nousnouscontenterons de la méthode déjà rencontrée qui consiste à dupliquer les objets

256

Page 258: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

concernés(enenfaisantune«copieprofonde»).

Poursatisfaireauxcontraintesde l’énoncé, ilnousfautdoncprévoirdedéfinir,dansvectok,unconstructeurparrecopie.Ilfautalorstenircomptedecequelarecopied’unobjetdetypevectok(quiferadoncappelàceconstructeur)entraîneraalorsl’appelduconstructeur de vect qui sera indiqué dans l’en-tête du constructeur par recopie devectok,dumoinssiunetelleinformationestprécisée(danslecascontraire,ilyauraitappeld’unconstructeursansargumentdevect,cequin’estpaspossibleici).Ici,nousdisposonsdoncdedeuxpossibilités:

demanderl’appelduconstructeurparrecopiedevect,cequiconduitàceten-tête:vectok::vectok(vectok&v):vect(v)

(rappelonsquel’argumentv,detypevectok,seraimplicitementconvertientypevect,pourpouvoirêtretransmisauconstructeurvect).

Cettefaçondefaireconduitàlacréationd’unobjetdetypevect,obtenuparrecopie(par défaut) de v. Il faudra alors compléter le travail en créant un nouvelemplacementdynamiquepourlevecteuretenadaptantcorrectementlavaleurdeadr.

demanderl’appelduconstructeuràunargumentdevect,cequiconduitàceten-tête:vectok::vectok(vectok&v):vect(v.nelem)

Cette fois, il y aura création d’un nouvel objet de type vect, avec son propreemplacement dynamique, dans lequel il faudra néanmoins recopier les valeurs de v.C’estcettedernièresolutionquenouschoisironsici.

Toujours pour satisfaire aux contraintes de l’énoncé, nous devons surdéfinirl’affectation dans la classe vectok. Ici, aucun choix ne se présente. Nous utiliseronsl’algorithmeprésentédansl’exercice87.

Voicicequepourraientêtreladéclarationetladéfinitiondenotreclassevectok:/****déclarationdelaclassevectok****/

#include"vect.h"

classvectok:publicvect

{//pasdenouveauxmembresdonnée

public:

vectok(intdim):vect(dim)//constructeurdevectok:secontente

{}//depasserdimauconstructeurdevect

vectok(vectok&);//constructeurparrecopiedevectok

vectok&operator=(vectok&);//surdéfinitiondel'affectationdevectok

inttaille()

{returnnelem;

}

};

/*****définitionduconstructeurparrecopiedevectok*****/

//ildoitobligatoirementprévoirdesargumentspourunconstructeur

//(quelconque)devect(icileconstructeuràunargument)

vectok::vectok(vectok&v):vect(v.nelem)//ouconstvectok&v

{inti;

for(i=0;i<nelem;i++)adr[i]=v.adr[i];

257

Page 259: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

/*****définitiondel'affectationentrevectok*****/

vectok&vectok::operator=(vectok&v)//ouconstvectok&v

{if(this!=&v)

{deleteadr;

adr=newint[nelem=v.nelem];

inti;

for(i=0;i<nelem;i++)adr[i]=v.adr[i];

}

return(*this);

}

Voiciunexempledeprogrammeutilisantlaclassevectok,accompagnédurésultatdesonexécution:

#include<iostream>//voirN.B.duparagrapheNouvellespossibilités

//d'entrées-sortiesduchapitre2

usingnamespacestd;

#include"vectok.h"

main()

{voidfct(vectok);

vectokv(6);

inti;

for(i=0;i<v.taille();i++)v[i]=i;

cout<<"vecteurv:";

for(i=0;i<v.taille();i++)cout<<v[i]<<"";

cout<<"\n";

vectokw(3);

w=v;

cout<<"vecteurw:";

for(i=0;i<w.taille();i++)cout<<w[i]<<"";

cout<<"\n";

fct(v);

}

voidfct(vectokv)

{cout<<"vecteurreçuparfct:"<<"\n";

inti;

for(i=0;i<v.taille();i++)cout<<v[i]<<"";

}

vecteurv:012345

vecteurw:012345

vecteurreçuparfct:

012345

Voici,àtitreindicatif,cequeseraitladéfinitionduconstructeurparrecopiedevectok,danslecasoùl’onferaitappelauconstructeurparrecopie(pardéfaut)devect:

vectok::vectok(vectok&v):vect(v)//ouconstvectok&v

{nelem=v.nelem;

adr=newint[nelem];

inti;

for(i=0;i<nelem;i++)

adr[i]=v.adr[i];

}

Ici,ilafalluallouerunnouvelemplacementpourunvecteur,cequin'étaitpaslecas

258

Page 260: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

lorsque l’on faisait appel au constructeur à un argument de vect (puisque ce dernierfaisaitdéjàunetelleallocation).

259

Page 261: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre14L’héritagemultiple

260

Page 262: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

Depuis la version 2.0, C++ autorise l’héritagemultiple : une classe peut hériter deplusieurs autres classes, comme dans cet exemple où la classe pointcol héritesimultanémentdesclassespointetcoul:

classpointcol:publicpoint,publiccoul//chaquedérivation,icipublique

//pourraitêtreprivéeouprotégée

{//définitiondesmembressupplémentaires(donnéesoufonctions)

//ouredéfinitiondemembresexistantsdéjàdanspointoucoul

};

Chacunedesdérivationspeutêtrepublique,privéeouprotégée.Lesmodalitésd’accèsauxmembresdechacunedesclassesdebaserestentlesmêmesquedanslecasd’unedérivation«simple».L’opérateurderésolutiondeportée(::)peutêtreutilisé:

soitlorsquel’onveutaccéderàunmembred’unedesclassesdebase,alorsqu’ilestredéfinidanslaclassedérivée,

soit lorsquedeuxclassesdebasepossèdentunmembredemêmenometqu’il fautalorspréciserceluiquinousintéresse.

AppeldesconstructeursetdesdestructeursLacréationd’unobjetentraînel’appelduconstructeurdechacunedesclassesdebase,dans l’ordre où ces constructeurs sont mentionnés dans la déclaration de la classedérivée(ici,pointpuiscoulpuisquenousavonsécritclasspointcol :publicpoint,publiccoul).Lesdestructeurssontappelésdansl’ordreinverse.

Leconstructeurdelaclassedérivéepeutmentionner,danssonen-tête,desinformationsà retransmettreàchacundesconstructeursdesclassesdebase (ceseragénéralementindispensable,saufsiuneclassedebasepossèdeunconstructeursansargumentousiellenedisposepasdutoutdeconstructeur).Envoiciunexemple:

pointcoul(.....):point(.....),coul(.....)

|||

|||

argumentsargumentsarguments

pointcoulpointcoul

ClassesvirtuellesParlebiaisdedérivationssuccessives,ilesttoutàfaitpossiblequ’uneclassehéritedeuxfoisd’unemêmeclasse.EnvoiciunexempledanslequelDhéritedeuxfoisdeA:

261

Page 263: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

classB:publicA

{.....};

classC:publicA

{.....};

classD:publicB,publicC

{.....};

Dans ce cas, les membres donnée de la classe en question (A dans notre exemple)apparaissent deux fois dans la classe dérivée de deuxième niveau (ici D).Naturellement,ilestnécessairedefaireappelàl’opérateurderésolutiondeportée(::)pour lever l’ambiguïté. Si l’on souhaite que de tels membres n’apparaissent qu’uneseulefoisdanslaclassedérivéededeuxièmeniveau,ilfaut,danslesdéclarationsdesdérivéesdepremierniveau(iciBetC)déclareravecl’attributvirtuallaclassedontonveutéviterladuplication(iciA).

Voici comment on procéderait dans l’exemple précédent (le mot virtual peut êtreindifféremmentplacéavantouaprèslemotpublicoulemotprivate):

classB:publicvirtualA

{.....};

classC:publicvirtualA

{.....};

classD:publicB,publicC

{.....};

Lorsqu’onaainsidéclaréuneclassevirtuelle, ilestnécessaireque lesconstructeursd’éventuelles classes dérivées puissent préciser les informations à transmettre auconstructeurdecetteclassevirtuelle(danslecasusueloùl’onautoriseladuplication,ceproblèmeneseposeplus ;eneffet,chaqueconstructeur transmet les informationsauxclassesascendantesdontlesconstructeurstransmettent,àleurtour,lesinformationsaux constructeurs de chacune des occurrences de la classe en question – cesinformationspouvantéventuellementêtredifférentes).Danscecas,onleprécisedansl’en-tête du constructeur de la classe dérivée, en plus des arguments destinés auxconstructeurs des classes du niveau immédiatement supérieur, comme dans cetexemple:

D(.....):B(.....),C(.....),A(.....)

||||

||||

argumentsargumentsargumentsarguments

deDpourBpourCpourA

Deplus,danscecas, lesconstructeursdesclasses BetC (quiontdéclaréque A était«virtuelle»)n’aurontplusàspécifierd’informationspourunconstructeurdeA.

Enfin,leconstructeurd’uneclassevirtuelleesttoujoursappeléavantlesautres.

262

Page 264: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice109

ÉnoncéQuelsserontlesrésultatsfournisparceprogramme:

#include<iostream>

usingnamespacestd;

classA

{intn;

floatx;

public:

A(intp=2)

{n=p;x=1;

cout<<"**constructionobjetA:"<<n<<""<<x<<"\n";

}

};

classB

{intn;

floaty;

public:

B(floatv=0.0)

{n=1;y=v;

cout<<"**constructionobjetB:"<<n<<""<<y<<"\n";

}

};

classC:publicB,publicA

{intn;

intp;

public:

C(intn1=1,intn2=2,intn3=3,floatv=0.0):A(n1),B(v)

{n=n3;p=n1+n2;

cout<<"**constructionobjetC:"<<n<<""<<p<<"\n";

}

};

main()

{Cc1;

Cc2(10,11,12,5.0);

}

L’objetc1estcrééparappelssuccessifsdesconstructeursdeB,puisdeA(ordreimposéparladéclarationdelaclasseC,etnonparl’en-têteduconstructeurdeC!).Lejeudelatransmissiondesargumentsetdesargumentspardéfautconduitaurésultatsuivant:

**constructionobjetB:10

**constructionobjetA:11

**constructionobjetC:33

**constructionobjetB:15

**constructionobjetA:101

**constructionobjetC:1221

263

Page 265: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice110

ÉnoncéMême question que précédemment, en remplaçant simplement l’en-tête duconstructeurdeCpar:

C(intn1=1,intn2=2,intn3=3,floatv=0.0):B(v)

Ici,commeleconstructeurdeCn’aprévuaucunargumentpourunéventuelconstructeurdeA, il y aura appel d’un constructeur sans argument, c’est-à-dire, en fait, appel duconstructeurdeA,avectouteslesvaleursprévuespardéfaut.Voicilerésultatobtenu:

**constructionobjetB:10

**constructionobjetA:21

**constructionobjetC:33

**constructionobjetB:15

**constructionobjetA:21

**constructionobjetC:1221

264

Page 266: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice111

EnoncéMêmequestionquedansl’exercice58,ensupposantquel’en-têteduconstructeurdeCestlasuivante:

C(intn1=1,intn2=2,intn3=3,floatv=0.0)

Cettefois,laconstructiond’unobjetdetypeCentraîneral’appeld’unconstructeursansargument,àlafoispourBetpourA.Voicilesrésultatsobtenus:

**constructionobjetB:10

**constructionobjetA:21

**constructionobjetC:33

**constructionobjetB:10

**constructionobjetA:21

**constructionobjetC:1221

265

Page 267: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice112

ÉnoncéQuelsserontlesrésultatsfournisparceprogramme:

#include<iostream>

usingnamespacestd;

classA

{intna;

public:

A(intnn=1)

{na=nn;cout<<"$$constructionobjetA"<<na<<"\n";

}

};

classB:publicA

{floatxb;

public:

B(floatxx=0.0)

{xb=xx;cout<<"$$constructionobjetB"<<xb<<"\n";

}

};

classC:publicA

{intnc;

public:

C(intnn=2):A(2*nn+1)

{nc=nn;

cout<<"$$constructionobjetC"<<nc<<"\n";

}

};

classD:publicB,publicC

{intnd;

public:

D(intn1,intn2,floatx):C(n1),B(x)

{nd=n2;

cout<<"$$constructionobjetD"<<nd<<"\n";

}

};

main()

{Dd(10,20,5.0);

}

Laconstructiond’unobjetdetypeDentraînera l’appeldesconstructeursdeBetdeC,lesquels,avantleurexécution,appellerontchacununconstructeurdeA:danslecasdeB,ilyauraappeld’unconstructeursansargument(puisquel’en-têtedeBnementionnepas de liste d’arguments pour A) ; en revanche, dans le cas de C, il s’agira (plusclassiquement)d’unconstructeuràunargument,commementionnédansl’en-têtedeC.

Notezbienqu’ilyacréationdedeuxobjetsdetypeA.Voicilesrésultatsobtenus:$$constructionobjetA1

$$constructionobjetB5

266

Page 268: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

$$constructionobjetA21

$$constructionobjetC10

$$constructionobjetD20

267

Page 269: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice113

ÉnoncéTransformerleprogrammeprécédent,demanièrequ’unobjetdetypeDnecontiennequ’une seule fois les membres de A (qui se réduisent en fait à l’entier na). Ons’arrangera pour que le constructeur de A soit appelé avec la valeur 2*nn+1, danslaquellenndésignel’argumentduconstructeurdeC.

DansladéclarationdesclassesBetC,ilfautindiquerquelaclasseAest«virtuelle»,de façon qu’elle ne soit incluse qu’une fois dans d’éventuelles descendantes de cesclasses.D’autre part, le constructeur de D doit prévoir, outre les arguments pour lesconstructeursdeBetdeC,ceuxdestinésàunconstructeurdeA.

Enrésumé,ladéclarationdeAresteinchangée,celledeBesttransforméeen:classB:publicvirtualA

{//leresteestinchangé

}

CelledeCesttransforméedefaçonanalogue:classC:publicvirtualA

{//leresteestinchangé

}

Enfin,dansD,l’en-têteduconstructeurdevient:D(intn1,intn2,floatx):C(n1),B(x),A(2*n1+1)

À titre indicatif, voici les résultats que fournirait le programme précédent ainsitransformé:

$$constructionobjetA21

$$constructionobjetB5

$$constructionobjetC10

$$constructionobjetD20

268

Page 270: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice114

ÉnoncéOnsouhaitecréeruneclasse liste permettantdemanipulerdes« listes chaînées»dans lesquelles la nature de l’information associée à chaque « nœud» de la listen’estpasconnue(parlaclasse).Unetellelistecorrespondraauschémasuivant:

Ladéclarationdelaclasselisteseprésenteraainsi:structelement//structured'unélémentdeliste

{element*suivant;//pointeursurl'élémentsuivant

void*contenu;//pointeursurunobjetquelconque

};

classliste

{element*debut;//pointeursurpremierélément

//autresmembresdonnéeséventuels

public:

liste();//constructeur

~liste();//destructeur

voidajoute(void*);//ajouteunélémentendébutde

liste

void*premier();//positionnesurpremierélément

void*prochain();//positionnesurprochainélément

intfini();

};

La fonction ajoute devra ajouter, en début de liste, un élément pointant surl’informationdont l’adresse est fournie en argument (void*). Pour « explorer » laliste,onaprévutroisfonctions:

•premier,quifourniral’adressedel’informationassociéeaupremiernœuddelalisteetqui,enmêmetemps,prépareraleprocessusdeparcoursdelaliste;

•prochain,quifourniral’adressedel’informationassociéeau«prochainnœud»;desappelssuccessifsdeprochaindevrontpermettredeparcourirlaliste(sans

269

Page 271: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

qu’ilsoitnécessaired’appeleruneautrefonction);

•fini,quipermettradesavoirsilafindelisteestatteinteounon.

1.Compléterladéclarationprécédentedelaclasselisteetenfournirladéfinitiondemanièrequ’ellefonctionnecommedemandé.

2.Soitlaclassepointsuivante:classpoint

{intx,y;

public:

point(intabs=0,intord=0){x=abs;y=ord;}

voidaffiche(){cout<<"Coordonnées:"<<x<<""<<y<<"\n";}

};

Créeruneclasseliste_points,dérivéeàlafoisdelisteetdepoint,pourqu’ellepuissepermettre de manipuler des listes chaînées de points, c’est-à-dire des listescomparablesàcellesprésentéesci-dessus,etdanslesquellesl’informationassociéeestdetypepoint.Ondevrapouvoir,notamment:

•ajouterunpointendébutd’unetelleliste;

• disposerd’une fonctionmembre affiche affichant les informations associées àchacundespointsdelalistedepoints.

3.Écrireunpetitprogrammed’essai.

1. Manifestement, les fonctions premier et prochain nécessitent un « pointeur sur unélément courant ». Il sera membre donnée de la classe liste. Nous conviendrons(classiquement) que la fin de liste est matérialisée par un nœud comportant unpointeur « nul ».La classe liste devra disposer d’un constructeur dont le rôle selimiteraàl’initialiseràune«listevide»,cequis’obtiendrasimplementenplaçantunpointeurnulcommeadressededébutdeliste(cettefaçondeprocédersimplifiegrandement l’algorithme d’ajout d’un élément en début de liste, puisqu’elle évited’avoiràdistinguerdesautreslecasdelalistevide).

Comme un objet de type liste est amené à créer différents emplacementsdynamiques,ilestnécessairedeprévoirlalibérationdecesemplacementslorsquel’objet est détruit. Il faudra donc prévoir un destructeur, chargé de détruire lesdifférents nœuds de la liste. À ce propos, notez qu’il n’est pas possible ici dedemanderaudestructeurdedétruireégalementlesinformationsassociées;eneffet,cen’estpas l’objetde type liste qui a alloué ces emplacements : ils sont sous laresponsabilitédel’utilisateurdelaclasseliste.

Voicicequepourraitêtrenotreclasselistecomplète:

270

Page 272: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

#include<stdlib.h>//pourNULL

structelement//structured'unélémentdeliste

{element*suivant;//pointeursurl'élémentsuivant

void*contenu;//pointeursurunobjetquelconque

};

classliste

{element*debut;//pointeursurpremierélément

element*courant;//pointeursurélémentcourant

public:

liste()//constructeur

{debut=NULL;

courant=debut;//parsécurité

}

~liste();//destructeur

voidajoute(void*);//ajouteunélémentendébutdeliste

void*premier()//positionnesurpremierélément

{courant=debut;

if(courant!=NULL)return(courant->contenu);

elsereturnNULL;

}

void*prochain()//positionnesurprochainélément

{if(courant!=NULL)

{courant=courant->suivant;

if(courant!=NULL)return(courant->contenu);

}

returnNULL;

}

intfini(){return(courant==NULL);}

};

liste::~liste()

{element*suiv;

courant=debut;

while(courant!=NULL)

{suiv=courant->suivant;deletecourant;courant=suiv;}

}

voidliste::ajoute(void*chose)

{element*adel=newelement;

adel->suivant=debut;

adel->contenu=chose;

debut=adel;

}

2. Comme nous le demande l’énoncé, nous allons donc créer une classe liste_pointspar:classliste_points:publicliste,publicpoint

Notezquecethéritage,apparemmentnaturel,conduitnéanmoinsàintroduire,danslaclasseliste_points,deuxmembresdonnée(xety)n’ayantaucunintérêtparlasuite.

En revanche, la création des fonctions membre demandées devient extrêmementsimple. En effet, la fonction d’insertion d’un point en début de liste peut être lafonction ajoute de la classe liste : nous n’aurons donc même pas besoin de lasurdéfinir.Encequiconcernelafonctiond’affichagedetouslespointsdelaliste(quenousnommeronségalementaffiche),illuisuffiradefaireappel:

–auxfonctionspremier,prochainetfinidelaclasselistepourleparcoursdelalistedepointsþ;

–àlafonctionaffichedelaclassepointpourl’affichaged’unpoint.

271

Page 273: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Nousaboutissonsàceci:classliste_points:publicliste,publicpoint

{public:

liste_points(){}

voidaffiche();

};

voidliste_points::affiche()

{point*ptr=(point*)premier();

while(!fini()){ptr->affiche();ptr=(point*)prochain();}

}

3.Exempledeprogrammed’essai:#include"listepts.h"

main()

{liste_pointsl;

pointa(2,3),b(5,9),c(0,8);

l.ajoute(&a);l.affiche();cout<<"---------\n";

l.ajoute(&b);l.affiche();cout<<"---------\n";

l.ajoute(&c);l.affiche();cout<<"---------\n";

}

Àtitreindicatif,voicilesrésultatsfournisparceprogramme:Coordonnées:23

---------

Coordonnées:59

Coordonnées:23

---------

Coordonnées:08

Coordonnées:59

Coordonnées:23

---------

272

Page 274: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre15Lesfonctionsvirtuelles

273

Page 275: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

Typagestatiquedesobjets(ouligaturedynamiquedesfonctions)Lesrèglesdecompatibilitéentreuneclassedebaseetuneclassedérivéepermettentd’affecteràunpointeursuruneclassedebase lavaleurd’unpointeursuruneclassedérivée. Toutefois, par défaut, le type des objets pointés est défini lors de lacompilation.Parexemple,avec:

classAclassB:publicA

{.....{.....

public:public:

voidfct(...);voidfct(...);

..........

};};

A*pta;

B*ptb;

uneaffectationtellequepta=ptbestautorisée.Néanmoins,quelquesoitlecontenudepta(autrementdit,quelquesoitl’objetpointéparpta),pta->fct(...)appelletoujourslafonctionfctdelaclasseA.

LesfonctionsvirtuellesL’emploi des fonctions virtuelles permet d’éviter les problèmes inhérents au typagestatique.Lorsqu’une fonction est déclarée virtuelle (mot-clé virtual) dans une classe,les appels à une telle fonction ou à n’importe laquelle de ses redéfinitions dans desclasses dérivées sont « résolus » aumoment de l’exécution, selon le typede l’objetconcerné. On parle de typage dynamique des objets (ou de ligature dynamique desfonctions).Parexemple,avec:

classAclassB:publicA

{.....{.....

public:public:

virtualvoidfct(...);voidfct(...);

..........

};};

A*pta;

l’instruction pta->fct (...) appellera la fonction fct de la classe correspondantréellementautypedel’objetpointéparpta.

N.B. Ilpeutyavoir ligaturedynamiquemêmeendehorsde l’utilisationdepointeurs

274

Page 276: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

(voyez,parexemple,l’exercice65).

Règles Le mot-clé virtual ne s’emploie qu’une fois pour une fonction donnée ; plusprécisément,ilnedoitpasaccompagnerlesredéfinitionsdecettefonctiondanslesclassesdérivées.

Uneméthodedéclaréevirtuelledansuneclassedebasepeutnepasêtre redéfiniedanssesclassesdérivées.

Unefonctionvirtuellepeutêtresurdéfinie(chaquefonctionsurdéfiniepouvantêtreounepasêtrevirtuelle).

Unconstructeurnepeutpasêtrevirtuel,undestructeurpeutl’être.

Parsanaturemême,lemécanismedeligaturedynamiqueestlimitéàunehiérarchiede classes ; souvent, pour qu’il puisse s’appliquer à toute une bibliothèque declasses, on sera amené à faire hériter toutes les classes de la bibliothèque d’unemêmeclassedebase.

LesfonctionsvirtuellespuresUne«fonctionvirtuellepure»sedéclareavecuneinitialisationàzéro,commedans:

virtualvoidaffiche()=0;

Lorsqu’uneclassecomporteaumoinsune fonctionvirtuellepure,elleestconsidéréecomme«abstraite»,c’est-à-direqu’iln’estpluspossibledecréerdesobjetsdesontype.

Unefonctiondéclaréevirtuellepuredansuneclassedebasepeutnepasêtredéclaréedans une classe dérivée et, dans ce cas, elle est à nouveau implicitement fonctionvirtuellepuredecetteclassedérivée(avantlaversion3.0,unefonctionvirtuellepuredevait obligatoirement être redéfinie dans une classe dérivée ou déclarée à nouveauvirtuellepure).

275

Page 277: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice115

ÉnoncéQuelsrésultatsproduiraceprogramme:

#include<iostream>

usingnamespacestd;

classpoint

{protected://pourquexetysoientaccessiblesàpointcol

intx,y;

public:

point(intabs=0,intord=0){x=abs;y=ord;}

virtualvoidaffiche()

{cout<<"Jesuisunpoint\n";

cout<<"mescoordonnéessont:"<<x<<""<<y<<"\n";

}

};

classpointcol:publicpoint

{shortcouleur;

public:

pointcol(intabs=0,intord=0,shortcl=1):point(abs,ord)

{couleur=cl;

}

voidaffiche()

{cout<<"Jesuisunpointcoloré\n";

cout<<"mescoordonnéessont:"<<x<<""<<y;

cout<<"etmacouleurest:"<<couleur<<"\n";

}

};

main()

{

pointp(3,5);point*adp=&p;

pointcolpc(8,6,2);pointcol*adpc=&pc;

adp->affiche();adpc->affiche();//instructions1

cout<<"-----------------\n";

adp=adpc;

adp->affiche();adpc->affiche();//instructions2

}

Danslesinstructions1,adp(detypepoint*)pointesurunobjetdetypepoint,tandisqueadpc(detypepointcol*)pointesurunobjetdetypepointcol.Ilyaappeldelafonctionaffiche, respectivement de point et de pointcol ; l’existence du « typage dynamique »n’apparaît pas clairement puisque,même en son absence (c’est-à-dire si la fonctionaffichen’avaitpasétédéclaréevirtuelle),onauraitobtenulemêmerésultat.

Enrevanche,danslesinstructions2,adp(detypepoint*)pointemaintenantsurunobjetde type pointcol. Grâce au typage dynamique, adp->affiche() appelle bien la fonctionaffichedutypepointcol.

276

Page 278: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Voicilesrésultatscompletsfournisparceprogramme:Jesuisunpoint

mescoordonnéessont:35

Jesuisunpointcoloré

mescoordonnéessont:86etmacouleurest:2

-----------------

Jesuisunpointcoloré

mescoordonnéessont:86etmacouleurest:2

Jesuisunpointcoloré

mescoordonnéessont:86etmacouleurest:2

277

Page 279: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice116

ÉnoncéQuelsrésultatsproduiraceprogramme:

#include<iostream>

usingnamespacestd;

classpoint

{intx,y;

public:

point(intabs=0,intord=0){x=abs;y=ord;}

virtualvoididentifie()

{cout<<"Jesuisunpoint\n";

}

voidaffiche()

{identifie();

cout<<"Mescoordonnéessont:"<<x<<""<<y<<"\n";

}

};

classpointcol:publicpoint

{shortcouleur;

public:

pointcol(intabs=0,intord=0,intcl=1):point(abs,ord)

{couleur=cl;}

voididentifie()

{cout<<"Jesuisunpointcolorédecouleur:"<<couleur<<"\n";

}

};

main()

{pointp(3,4);

pointcolpc(5,9,5);

p.affiche();

pc.affiche();

cout<<"---------------\n";

point*adp=&p;

pointcol*adpc=&pc;

adp->affiche();adpc->affiche();

cout<<"---------------\n";

adp=adpc;

adp->affiche();adpc->affiche();

}

Dans la fonction affiche de point, l’appel de identifie fait l’objet d’une ligaturedynamique (puisquecettedernière fonctionaétédéclaréevirtuelle).Lorsqu’unobjetdetypepointcolappelleunefonctionaffiche,ceserabienlafonctionaffichedepointquiseraappelée(puisqueaffichen’estpasredéfiniedanspointcol).Maiscettedernièreferaappel,danscecas,àlafonctionidentifiedepointcol.Bienentendu,lorsqu’unobjetdetypepoint appelle affiche, cette dernière fera toujours appel à la fonction identifie depoint(lemêmerésultatseraitobtenusansligaturedynamique).

278

Page 280: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Voicilesrésultatscompletsfournisparnotreprogramme:Jesuisunpoint

Mescoordonnéessont:34

Jesuisunpointcolorédecouleur:5

Mescoordonnéessont:59

---------------

Jesuisunpoint

Mescoordonnéessont:34

Jesuisunpointcolorédecouleur:5

Mescoordonnéessont:59

---------------

Jesuisunpointcolorédecouleur:5

Mescoordonnéessont:59

Jesuisunpointcolorédecouleur:5

Mescoordonnéessont:59

279

Page 281: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice117

ÉnoncéOn souhaite créer une classe nommée ens_heter permettant de manipuler desensembles dont le typedes éléments est non seulement inconnude la classe,maiségalement susceptible de varier d’un élément à un autre. Pour que la chose soitpossible,on imposera simplement lacontrainte suivante : tous les typesconcernésdevrontdériverd’unmêmetypedebasenommébase.Letypebaseserasupposéconnuaumomentoùl’ondéfinitlaclasseens_heter.

Laclassebasedisposeraaumoinsd’unefonctionvirtuellepurenomméeaffiche;cettefonction devra être redéfinie dans les classes dérivées pour afficher lescaractéristiquesdel’objetconcerné.

Laclasseens_heterdisposeradesfonctionsmembresuivantes:

•ajoutepourajouterunnouvelélémentà l’ensemble(elledevras’assurerqu’iln’existepasdéjà)þ;

•appartientpourtesterl’appartenanced’unélémentàl’ensembleþ;

•cardinalquifourniralenombred’élémentsdel’ensemble.

Deplus,laclassedevraêtremunied’un«itérateur»,c’est-à-dired’unmécanismepermettant de parcourir les différents éléments de l’ensemble. On prévoira 3fonctions:

•initpourinitialiserlemécanismed’itérationþ;

• suivant qui fournira en retour le prochain élément (objet d’un type dérivé debase)þ;

•existepourprécisers’ilexisteencoreunélémentnonexaminé.

Enfin,unefonctionnomméelistepermettrad’afficherlescaractéristiquesdetouslesélémentsdel’ensemble(ellefera,biensûr,appelauxfonctionsaffichedesdifférentsobjetsconcernés).

Onréaliseraensuiteunpetitprogrammed’essaide laclasseens_heter,encréantunensemblecomportantdesobjetsdetypepoint(deuxcoordonnéesentières)etcomplexe(unepartieréelleetunepartieimaginaire,toutesdeuxdetypefloat).Naturellement,pointetcomplexedevrontdériverdebase.

280

Page 282: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

On ne se préoccupera pas des éventuels problèmes posés par l’affectation ou latransmissionparvaleurd’objetsdutypeens_heter.

N.B. Pour ne pas trop compliquer le programme, on fondera l’égalité de deuxélémentsd’unensemblesurl’égalitédeleursadressesetnonpasdeleursvaleurs.Autrementdit,deuxélémentsdemêmetypeetdemêmevaleurpourrontapparteniràunmêmeensemble,àconditionqu’ils’agissebiendedeuxobjetsdifférents.

Manifestement, la classe ens_heter ne contiendra pas les objets correspondant auxéléments de l’ensemble,mais seulement des pointeurs sur ces différents éléments.Àmoinsdefixerlenombremaximald’élémentsapriori, ilestnécessairedeconserverces pointeurs dans un emplacement alloué dynamiquement par le constructeur ; cedernier comportera donc un argument permettant de préciser le nombre maximald’élémentsrequispourl’ensemble.

Outrel’adresse(adel)decetableaudepointeurs,ontrouveraenmembresdonnée:

lenombremaximald’éléments(nmax);

lenombrecourantd’éléments(nelem);

unentier(courant)quiserviraaumécanismed’itération(ildésignerauneadressedutableaudepointeurs).

En ce qui concerne les fonctions ajoute et appartient, elles devront manifestementrecevoir en argument un objet d’un type dérivé de base. Puisqu’on ne fait aucunehypothèse a priori sur la nature de tels objets, il est préférable d’éviter unetransmission d’argument par valeur. Par souci de simplicité, nous choisirons unetransmissionparréférence.

Lesmêmesremarquess’appliquentàlavaleurderetourdelafonctionsuivant.

Voici,endéfinitive,ladéclarationdenotreclasseens_heter:/*fichierensheter.H*/

/*déclarationdelaclasseens_heter*/

classbase;

classens_heter

{intnmax;//nombremaximald'éléments

intnelem;//nombrecourantd'éléments

base**adel;//adressezonedepointeurssurlesobjetséléments

intcourant;//numérod'élémentcourant(pourl'itération)

public:

ens_heter(int=20);//constructeur

~ens_heter();//destructeur

voidajoute(base&);//ajoutd'unélément

intappartient(base&);//appartenanced'unélément

intcardinal();//cardinaldel'ensemble

voidinit();//initialisationitération

281

Page 283: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

base&suivant();//passageélémentsuivant

intexiste();//

voidliste();//afficheles"valeurs"desdifférentséléments

};

Laclassebase(déclarationetdéfinition)découledirectementdel'énoncé:classbase

{public:

virtualvoidaffiche()=0;

};

Voici la définition des fonctions membre de ens_heter (notez que leur compilationnécessiteladéclaration(définition)précédentedelaclassebase(etnonpasseulementunesimpleindicationdelaformeclassbase):

/*définitiondelaclasseens_heter*/

#include"ensheter.h"

ens_heter::ens_heter(intdim)

{nmax=dim;

adel=newbase*[dim];

nelem=0;

courant=0;//précaution

}

ens_heter::~ens_heter()

{deleteadel;

}

voidens_heter::ajoute(base&obj)

{if((nelem<nmax)&&(!appartient(obj)))adel[nelem++]=&obj;

}

intens_heter::appartient(base&obj)

{inttrouve=0;

init();

while(existe()&&!trouve)if(&suivant()==&obj)trouve=1;

returntrouve;

}

intens_heter::cardinal()

{returnnelem;

}

voidens_heter::init()

{courant=0;

}

base&ens_heter::suivant()

{if(courant<nelem)return(*adel[courant++]);

//enpratique,ilfaudraitrenvoyerunobjet"bidon"sifin

//ensembleatteinte

}

intens_heter::existe()

{return(courant<nelem);

}

voidens_heter::liste()

{init();

while(existe())

suivant().affiche();

}

Voiciunpetitprogrammequidéfinitdeuxclassespointetcomplexe,dérivéesdebaseetquicréeunpetitensemblehétérogène(sacompilationnécessiteladéclarationdelaclassebase).Àlasuitefigurelerésultatdesonexécution:

282

Page 284: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

#include"ens_heter.h"

#include"base.h"

#include<iostream>

usingnamespacestd;

classpoint:publicbase

{intx,y;

public:

point(intabs=0,intord=0)

{x=abs;y=ord;

}

voidaffiche()

{cout<<"Pointdecoordonnées:"<<x<<""<<y<<"\n";

}

};

classcomplexe:publicbase

{floatre,im;

public:

complexe(floatreel=0.0,floatimag=0.0)

{re=reel;im=imag;

}

voidaffiche()

{cout<<"Complexe-partieréelle:"<<re

<<",partieimaginaire:"<<im<<"\n";

}

};

/*utilisationdelaclasseens_heter*/

main()

{pointp(1,3);

complexez(0.5,3);

ens_hetere;

cout<<"cardinaldee:"<<e.cardinal()<<"\n";

cout<<"contenudee\n";

e.liste();

e.ajoute(p);

cout<<"cardinaldee:"<<e.cardinal()<<"\n";

cout<<"contenudee\n";

e.liste();

e.ajoute(z);

cout<<"cardinaldee:"<<e.cardinal()<<"\n";

cout<<"contenudee\n";

e.liste();

e.init();intn=0;

while(e.existe()){e.suivant();

n++;

}

cout<<"avecl'itérateur,ontrouve:"<<n<<"éléments\n";

}

cardinaldee:0

contenudee

cardinaldee:1

contenudee

Pointdecoordonnées:13

cardinaldee:2

contenudee

Pointdecoordonnées:13

Complexe-partieréelle:0.5,partieimaginaire:3

avecl'itérateur,ontrouve:2éléments

283

Page 285: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Sil’oncherchaitàrésoudrelesproblèmesposésparl’affectationetlatransmissionparvaleurd’objetsdetypeens_heter,onseraitamenéàeffectuerdes«copiescomplètes»(onditsouvent«profondes»)del’objet,c’est-à-diretenantcomptenonseulementdesesmembres,maisaussidesespartiesdynamiques.

Cela conduirait effectivement à recopier la partie dynamiquede l’ensemble, c’est-à-dire le tableau de pointeurs d’adresse base *. Mais que faudrait-il faire pour leséléments eux-mêmes (pointés par les différentes valeurs du tableau) ?Même si l’onadmetqu’ilfautégalementlesdupliquer,iln’estpaspossibledelefairesansconnaîtrela«structure»desobjetsconcernés.

D’unemanièregénérale,vousvoyezquecetexemple,parlesquestionsqu’ilsoulève,plaidelargementenfaveurdelaconstitutiondebibliothèquesd’objets,danslesquellessont définies, a priori, un certain nombre de règles communes. Parmi ces règles, onpourraitnotammentimposeràchaqueobjetdeposséderunefonction(denomunique)enassurant lacopieprofonde ;naturellement,pourpouvoir l’utilisercorrectement, ilfaudrait que la ligature dynamique soit possible, c’est-à-dire que tous les objetsconcernésdériventd’unmêmeobjetdebase.

284

Page 286: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre16Lesflotsd’entréeetdesortie

285

Page 287: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

Unflotestuncanal recevant (flotd’«entrée»)oufournissant (flotde«sortie»)del’information.Cecanalestassociéàunpériphériqueouàunfichier.Unflotd’entréeestunobjetdetypeistreamtandisqu’unflotdesortieestunobjetdetypeostream.Leflotcoutestunflotdesortieprédéfini,connectéàlasortiestandardstdout;demême,leflotcinestunflotd’entréeprédéfini,connectéàl’entréestandardstdin.

LaclasseostreamEllesurdéfinitl’opérateur<<souslaformed’unefonctionmembre:

ostream&operator<<(expression)

L’expression correspondant à son deuxième opérande peut être d’un type de basequelconque,ycomprischar,char* (onobtient lachaînepointée)ouunpointeursuruntypequelconqueautrequechar(onobtientlavaleurdupointeur);pourobtenirlavaleurdel’adressed’unechaîne,onlaconvertitartificiellementenunpointeurdetypevoid*.

Fonctionsmembres:

ostream&put(charc)transmetauflotcorrespondantlecaractèrec.

ostream & write (void * adr, int long) envoie long caractères, prélevés à partir del’adresseadr.

LaclasseistreamEllesurdéfinitl’opérateur>>souslaformed’unefonctionmembre:

istream&operator>>(type_de_base&)

Letype_de_basepeutêtrequelconque,pourpeuqu’ilnes’agissepasd’unpointeur(char*est cependant accepté ; il correspond à l’entrée d’une chaîne de caractères, et nond’uneadresse).

Les«espacesblancs»(espace,tabulationhorizontale\touverticale\v,findeligne\net changement depage \f) servent de«délimiteurs » (commedans scanf), y comprispourleschaînesdecaractères.

Principalesfonctionsmembres:

istream&get(char&c)extraituncaractèreduflotd’entréeetlerangedansc.

286

Page 288: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

intget()extraituncaractèreduflotd’entréeetenrenvoielavaleur(sousformed’unentier);fournitEOFencasdefindefichier.

istream&read(void*adr,inttaille)littaillecaractèressurleflotetlesrangeàpartirdel’adresseadr.

LaclasseiostreamElle est dérivée de istream et ostream. Elle permet de réaliser des entrées sorties«conversationnelles».

Lestatutd’erreurd’unflotÀchaqueflotestassociéunensembledebitsd’unentierformantle«statutd’erreurduflot».

Lesbitsd’erreur

Laclasseios(dontdériventistreametostream)définitlesconstantessuivantes:

eofbit:findefichier(leflotn’aplusdecaractèresdisponibles);

failbit:laprochaineopérationsurleflotnepourrapasaboutir;

badbit:leflotestdansunétatirrécupérable;

goodbit(valant,enfait0):aucunedeserrreursprécédentes.

Une opération sur le flot a réussi lorsqu’un des bits goodbit ou eofbit est activé. Laprochaineopérationsurleflotnepourraréussirquesigoodbitestactivé(maisiln’estpascertainqu’elleréussisse!).

Lorsqu’un flot est dansun état d’erreur, aucuneopérationnepeut aboutir tant que laconditiond’erreurn’apasétécorrigéeetquelebitd’erreurcorrespondantn’apasétéremisàzéro(àl’aidedelafonctionclear).

Accèsauxbitsd’erreur

Laclasseioscontientcinqfonctionsmembre:

eof():valeurdeeofbit;

bad():valeurdebadbit;

fail():valeurdefailbit;

287

Page 289: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

good():1siaucunbitdustatutd’erreurn’estactivé;

rdstate():valeurdustatutd’erreur(entier).

Modificationdustatutd’erreur

void clear (int i=0) donne la valeur i au statut d’erreur. Pour activer un seul bit (parexemplebadbit),onprocéderaainsi(flétantunflot):

fl.clear(ios::badbit|flrdstate());

Surdéfinitionde()etde!

Siflestunflot,(fl)estvraisiaucundesbitsd’erreurn’estactivé(c’est-à-diresigoodestvrai);demême,!flestvraisiundesbitsd’erreurprécédentsestactivé(c’est-à-diresigoodestfaux).

Surdéfinitionde<<et>>pourdestypesclasseOnsurdéfinira<<et>>pouruneclassequelconque,sousformedefonctionsamies,enutilisantces«canevas»:

ostream&operator<<(ostreamsortie,type_classeobjet1)

{

//Envoisurleflotsortiedesmembresdeobjetenutilisant

//lespossibilitésclassiquesde<<pourlestypesdebase

//c'est-à-diredesinstructionsdelaforme:

//sortie<<.....;

returnsortie;

}

istream&operator>>(istream&entree,type_de_base&objet)

{

//Lecturedesinformationscorrespondantauxdifférentsmembresdeobjet

//enutilisantlespossibilitésclassiquesde>>pourlestypesdebase

//c'est-à-diredesinstructionsdelaforme:

//entree>>.....;

returnentree;

}

Lemotd’étatdustatutdeformatageÀchaqueflot,estassociéun«statutdeformatage»constituéd’unmotd’étatetdetroisvaleursnumériques(gabarit,précisionetcaractèrederemplissage).

Voicilesprincipauxbitsdumotd’état:

Lemotd’étatdustatutdeformatage(partiel)

288

Page 290: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Nomdechamp Nomdubit(s’ilexiste)

Signification(quandactivé)

ios::basefield

ios::dec conversiondécimaleios::oct conversionoctaleios::hex conversionhexadécimale

ios::showbaseaffichageindicateurdebase(ensortie)

ios::showpointaffichagepointdécimal(ensortie)

ios::floatfield

ios::scientific notation«þscientifiqueþ»ios::fixed notation«þpointfixeþ»

ActionsurlestatutdeformatageOn peut utiliser, soit des « manipulateurs » qui peuvent être « simples » ou«paramétriques»,soitdesfonctionsmembre.

a.Lesmanipulateursnonparamétriques

Ilss’emploientsouslaforme:flot<<manipulateur

flot>>manipulateur

Lesprincipauxmanipulateursnonparamétriquessont:

Lesprincipauxmanipulateursnonparamétriques

Manipulateur Utilisation ACTIONdec Entrée/Sortie Activelebitdeconversiondécimale

hex Entrée/SortieActivelebitdeconversionhexadécimale

oct Entrée/Sortie Activelebitdeconversionoctale

endl SortieInsèreunsautdeligneetvideletampon

ends SortieInsèreuncaractèredefindechaîne(\0)

289

Page 291: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

b.Lesmanipulateursparamétriques

Ilss’utilisentsouslaforme:istream&manipulateur(argument)

ostream&manipulateur(argument)

Voicilesprincipauxmanipulateursparamétriques:

Lesprincipauxmanipulateursparamétriques

Manipulateur Utilisation Rôlesetbase(int) Entrée/Sortie Définitlabasedeconversion

setprecision(int) Entrée/SortieDéfinitlaprécisiondesnombresflottants

setw(int) Entrée/SortieDéfinitlegabarit.Ilretombeà0aprèschaqueopération

L’utilisation des manipulateurs paramétriques nécessite l’inclusion du fichier en-têteiomanip.Danscertainesimplémentations,ilpeutencores’agirdeiomanip.h(associéalorsàiostream.hetnonàiostream,etsansl’utilisationdel’espacedenomsstd).

Associationd’unflotàunfichierLa classe ofstream, dérivant de ostream, permet de créer un flot de sortie associé à unfichier:

ofstreamflot(char*nomfich,mode_d_ouverture)

Lafonctionmembreseekp(déplacement,origine)permetd’agirsurlepointeurdefichier.

Demême,laclasseifstream,dérivantdeistream,permetdecréerunflotd’entréeassociéàunfichier:

ifstreamflot(char*nomfich,mode_d_ouverture)

Lafonctionmembreseekg(déplacement,origine)permetd’agirsurlepointeurdefichier.

Danstouslescas,lafonctionclosepermetdefermerlefichier.

L’utilisationdesclassesofstreametifstreamdemandel’inclusiondufichierfstream.Danscertainesimplémentations,ilpeutencores’agirdefstream.h(associéalorsàiostream.hetnonàiostream,etsansl’utilisationdel’espacedenomsstd).

290

Page 292: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Modesd’ouvertured’unfichier

Lesdifférentsmodesd’ouvertured’unfichier

Bitdemoded’ouverture Action

ios::in Ouvertureenlecture(obligatoirepourlaclasseifstream)ios::out Ouvertureenécriture(obligatoirepourlaclasseofstream)ios::app Ouvertureenajoutdedonnées(écritureenfindefichier)ios::ate Seplaceenfindefichieraprèsouverture

ios::truncSilefichierexiste,soncontenuestperdu(obligatoiresiios::outestactivésansios::ateniios::app)

ios::binary(Utiledanscertainesimplémentationsuniquement.)Lefichierestouvertenmodedit«binaire»ouencore«nontranslaté»

291

Page 293: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice118

ÉnoncéÉcrire un programme qui lit un nombre réel et qui en affiche le carré sur un«gabarit»minimalde12caractères,de22façonsdifférentes:

•en«pointfixe»,avecunnombrededécimalesvariantde0à10,

•ennotationscientifique,avecunnombrededécimalesvariantde0à10.

Danstouslescas,onafficheralesrésultatsaveccetteprésentation:précisiondexxchiffres:cccccccccccc

Il faut donc activer d’abord le bit fixed, ensuite le bit scientific du champ floatfield.Nousutiliseronslafonctionsetf,membredelaclasseios.Notezbienqu’ilfautéviterd’écrire,parexemple:

setf(ios::fixed);

Eneffet,celaactiveraitlebitfixed,sansmodifier lesautresdonc,enparticulier,sansmodifierlesautresbitsduchampfloatfield.

Le gabarit d’affichage est déterminé par le manipulateur setw. Notez qu’il fauttransmettre ce manipulateur au flot concerné, juste avant d’afficher l’informationvoulue.

#include<iomanip>//pourles"manipulateursparamétriques"

#include<iostream>//voirN.B.duparagrapheNouvellespossibilités

//d'entrées-sortiesduchapitre2

usingnamespacestd;

main()

{floatval,carre;

cout<<"donnezunnombreréel:";

cin>>val;

carre=val*val;

cout<<"Voicisoncarré:\n";

inti;

cout<<"ennotationpointfixe:\n";

cout.setf(ios::fixed,ios::floatfield);//metà1lebitios::fixed

//duchampios::floatfield

for(i=0;i<10;i++)

cout<<"précisionde"<<setw(2)<<i<<"chiffres:"

<<setprecision(i)<<setw(12)<<carre<<"\n";

cout<<"ennotationscientifique:\n";

cout.setf(ios::scientific,ios::floatfield);

for(i=0;i<10;i++)

cout<<"précisionde"<<setw(2)<<i<<"chiffres:"

292

Page 294: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

<<setprecision(i)<<setw(12)<<carre<<"\n";

}

Àtitreindicatif,voiciunexempled’exécutiondeceprogramme:donnezunnombreréel:12.3456

Voicisoncarré:

ennotationpointfixe:

précisionde0chiffres:152

précisionde1chiffres:152.4

précisionde2chiffres:152.41

précisionde3chiffres:152.414

précisionde4chiffres:152.4138

précisionde5chiffres:152.41385

précisionde6chiffres:152.413849

précisionde7chiffres:152.4138489

précisionde8chiffres:152.41384888

précisionde9chiffres:152.413848877

ennotationscientifique:

précisionde0chiffres:1.524138e+002

précisionde1chiffres:1.5e+002

précisionde2chiffres:1.52e+002

précisionde3chiffres:1.524e+002

précisionde4chiffres:1.5241e+002

précisionde5chiffres:1.52414e+002

précisionde6chiffres:1.524138e+002

précisionde7chiffres:1.5241385e+002

précisionde8chiffres:1.52413849e+002

précisionde9chiffres:1.524138489e+002

293

Page 295: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice119

ÉnoncéSoitlaclassepointsuivante:

classpoint

{intx,y;

public:

//fonctionsmembre

};

Surdéfinirlesopérateurs<<et>>demanièrequ’ilsoitpossibledelireunpointsurunflotd’entréeoud’écrireunpointsurun flotdesortie.Onprévoiraqu’un telpointsoitreprésentésouslaforme:

<entier,entier>

avecéventuellementdesséparateurs«espaces_blancs»supplémentaires,departetd’autredesnombresentiers.

Nousdevonsdonc surdéfinir lesopérateurs << et >> pour qu’ils puissent recevoir, endeuxième opérande, un argument de type point. Il ne pourra s’agir que de fonctionsamies,dontlesprototypesseprésenterontainsi:

ostream&operator<<(ostream&,point);

istream&operator>>(istream&,point);

L’écriture de operator << ne présente pas de difficultés particulières : on se contented’écrire,surleflotconcerné,lescoordonnéesdupoint,accompagnéesdessymboles<et>.

Enrevanche,l’écrituredeoperator>>nécessiteunpeuplusd’attention.Eneffet,ilfauts’assurerquel’informationseprésentebiensouslaformerequiseet,sicen’estpaslecas, prévoirdedonner au flot concerné l’état bad, afinque l’utilisateurpuisse savoirquel’opérations’estmaldéroulée(entestant«naturellement»l’étatduflot).

Voici ce que pourrait être la déclaration de notre classe (nous l’avons simplementmunied’unconstructeur)etladéfinitiondesdeuxfonctionsamiesvoulues:

#include<iostream>

usingnamespacestd;

classpoint

{

intx,y;

294

Page 296: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

public:

point(intabs=0,intord=0)

{x=abs;y=ord;}

intabscisse(){returnx;}

friendostream&operator<<(ostream&,point);

friendistream&operator>>(istream&,point&);

};

ostream&operator<<(ostream&sortie,pointp)

{

sortie<<"<"<<p.x<<","<<p.y<<">";

returnsortie;

}

istream&operator>>(istream&entree,point&p)

{

charc='\0';

floatx,y;

intok=1;

entree>>c;

if(c!='<')ok=0;

else

{entree>>x>>c;

if(c!=',')ok=0;

else

{entree>>y>>c;

if(c!='>')ok=0;

}

}

if(ok){p.x=x;p.y=y;}//onn'affecteàpquesitoutestOK

elseentree.clear(ios::badbit|entree.rdstate());

returnentree;

}

À titre indicatif, voici un petit programme d’essai, accompagné d’un exempled’exécution:

main()

{

charligne[121];

pointa(2,3),b;

cout<<"pointa:"<<a<<"pointb:"<<b<<"\n";

do

{cout<<"donnezunpoint:";

if(cin>>a)cout<<"mercipourlepoint:"<<a<<"\n";

else{cout<<"**informationincorrecte\n";

cin.clear();

cin.getline(ligne,120,‘\n’);

}

}

while(a.abscisse());

}

pointa:<2,3>pointb:<0,0>

donnezunpoint:4,5

**informationincorrecte

donnezunpoint:<4,5<

**informationincorrecte

donnezunpoint:<4,5>

mercipourlepoint:<4,5>

donnezunpoint:<8,9>

mercipourlepoint:<8,9>

donnezunpoint:bof

**informationincorrecte

donnezunpoint:<0,0>

295

Page 297: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

mercipourlepoint:<0,0>

296

Page 298: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice120

ÉnoncéÉcrireunprogrammequienregistre (sous forme«binaire»,etnonpas formatée),dansunfichierdenomfourniparl’utilisateur,unesuitedenombresentiersfournissurl’entréestandard.Onconviendraquel’utilisateurfourniralavaleur0(quineserapasenregistréedanslefichier)pourpréciserqu’iln’aplusd’entiersàentrer.

Sinomfichdésigneunechaînedecaractères,ladéclaration:ofstreamsortie(nomfich,ios::out);

permetdecréerunflotdenomsortie,del’associeraufichierdontlenomfiguredansnomfichetd’ouvrircefichierenécriture.

L’écriture dans le fichier en question se fera par la fonction write, appliquée au flotsortie.

Voicileprogrammedemandé:constintLGMAX=20;

#include<cstdlib>//pourexit

#include<fstream>

#include<iomanip>

#include<iostream>

usingnamespacestd;

main()

{

charnomfich[LGMAX+1];

intn;

cout<<"nomdufichieràcréer:";

cin>>setw(LGMAX)>>nomfich;

ofstreamsortie(nomfich,ios::out);

if(!sortie){cout<<"créationimpossible\n";

exit(1);

}

do

{cout<<"donnezunentier:";

cin>>n;

if(n)sortie.write((char*)&n,sizeof(int));

}

while(n&&sortie);

sortie.close();

}

Notezque if(!sortie) est équivalent à if(!sortie.good()) et que while (n && sortie) estéquivalentàwhile(n&&sortie.good()).

297

Page 299: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice121

ÉnoncéÉcrireunprogrammepermettantdelister(surlasortiestandard)lesentierscontenusdansunfichiertelqueceluicrééparl’exerciceprécédent.

constintLGMAX=20;

#include<cstdlib>//pourexit

#include<fstream>

#include<iomanip>

#include<iostream>

usingnamespacestd;

main()

{

charnomfich[LGMAX+1];

intn;

cout<<"nomdufichieràlister:";

cin>>setw(LGMAX)>>nomfich;

ifstreamentree(nomfich,ios::in);

if(!entree){cout<<"ouvertureimpossible\n";

exit(1);

}

while(entree.read((char*)&n,sizeof(int)))

cout<<n<<"\n";

entree.close();

}

298

Page 300: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice122

ÉnoncéÉcrireunprogrammepermettantàunutilisateurderetrouver,dansunfichiertelqueceluicréédansl’exercice120,lesentiersdontilfournitle«rang».Onconviendraqu’unrangégalà0signifiequel’utilisateursouhaitemettrefinauprogramme.

constintLGMAX_NOM_FICH=20;

#include<cstdlib>//pourexit

#include<fstream>

#include<iomanip>

#include<iostream>

usingnamespacestd;

main()

{

charnomfich[LGMAX_NOM_FICH+1];

intn,num;

cout<<"nomdufichieràconsulter:";

cin>>setw(LGMAX_NOM_FICH)>>nomfich;

ifstreamentree(nomfich,ios::in);

if(!entree){cout<<"Ouvertureimpossible\n";

exit(1);

}

do

{cout<<"Numérodel'entierrecherché:";

cin>>num;

if(num)

{entree.seekg(sizeof(int)*(num-1),ios::beg);

entree.read((char*)&n,sizeof(int));

if(entree)cout<<"--Valeur:"<<n<<"\n";

else{cout<<"--Erreur\n";

entree.clear();

}

}

}

while(num);

entree.close();

}

1.Ici,latransmissionpeutsefaireparvaleurouparréférence.

299

Page 301: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre17Lespatronsdefonctions

300

Page 302: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

Introduiteparlaversion3,lanotiondepatrondefonctionspermetdedéfinircequ’onnommesouventdes«fonctionsgénériques».Plusprécisément,àl’aided’uneuniquedéfinition comportant des « paramètres de type », on décrit toute une famille defonctions; lecompilateur«fabrique»(onditaussi«instancie»)laoulesfonctionsnécessairesàlademande(onnommesouventcesinstances«fonctionspatron»).

Laversion3limitaitlesparamètresd’unpatrondefonctionsàdesparamètresdetype.LanormeANSIa,enoutre,introduitles«paramètresexpression».

Définitiond’unpatrondefonctionsOn précise les paramètres (muets) de type, en faisant précéder chacun du mot(relativement arbitraire) class sous la forme template <class ..., class ..., ...>. Ladéfinitiondelafonctionestclassique,hormislefaitquelesparamètresmuetsdetypepeuventêtreemployésn’importeoùuntypeeffectifestpermis.

Parexemple:template<classT,classU>voidfct(Ta,T*b,Uc)

{

Tx;//variablelocalexdetypeT

U*adr;//variablelocaleadrdetypeU*

...

adr=newT[10];//allocationtableaude10élémentsdetypeT

...

n=sizeof(T);//uneinstructionutilisantletypeT

...

}

Uneinstructiontelleque(Tdésignantuntypequelconque):Tx(3);

estlégalemêmesiTn'estpasuntypeclasse;danscederniercas,elleestsimplementéquivalenteà:

Tx=3;

Instanciationd’unefonctionpatron

301

Page 303: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chaquefoisqu’onutiliseunefonctionayantunnomdepatron,lecompilateurchercheàutiliser ce patron pour créer (instancier) une fonction adéquate. Pour ce faire, ilchercheà réaliserunecorrespondanceabsolue des types : aucune conversion, qu’ils’agisse depromotionnumériqueoude conversion standardn’est permise ; qui plusest,lesqualifieursconstetvolatiledoiventêtreexactementlesmêmes.

Voicidesexemplesutilisantnotrepatronprécédent:intn,p;floatx;charc;

int*adi;float*adf;

classpoint;pointp;point*adp;

fct(n,adi,x);//instancielafonctionvoidfct(int,int*,float)

fct(n,adi,p)//instancielafonctionvoidfct(int,int*,int)

fct(x,adf,p);//instancielafonctionvoidfct(float,float*,int)

fct(c,adi,x);//erreurcharetint*necorrespondentpasàTetT*

//(pasdeconversion)

fct(&n,&adi,x);//instancielafonctionvoidfct(int*,int**,float)

fct(p,adp,n);//instancielafonctionvoidfct(point,point*,int)

D’unemanièregénérale, ilestnécessairequechaqueparamètredetypeapparaisseaumoinsunefoisdansl’en-têtedupatron.

Ladéfinitiond’unpatrondefonctionsnepeutpasêtrecompiléeseule;detoutefaçon,elle doit être connue du compilateur pour qu’il puisse instancier la bonne fonctionpatron.Engénéral,lesdéfinitionsdepatronsdefonctionsfigurerontdansdesfichiersd’extensionh,defaçonàéviterd’avoiràenfournirsystématiquementlaliste.

Lesparamètresexpressiond'unpatrondefonctionsIlsontétéintroduitsparlanorme.Unparamètreexpressiond’unpatrondefonctionsseprésente comme un argument usuel de fonction ; il n’apparaît pas dans la liste deparamètresdetype(template)etildoitapparaîtredansl’en-têtedupatron.Parexemple:

template<classT>intcompte(T*tab,intn)

{//ici,onpeutseservirdelavaleurdel'entiern

//commeonleferaitdansn'importequellefonctionordinaire

}

Unpatrondefonctionspeutdisposerd’unoudeplusieursparamètresexpression.Lorsde l’appel, leur typen’aplusbesoinde correspondre exactement à celui attendu : ilsuffit qu’il soit acceptable par affectation, comme dans n’importe quel appel d’unefonctionordinaire.

Surdéfinitiondepatronsdefonctionsetspécialisationde

302

Page 304: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

fonctionsdepatronsOnpeutdéfinirplusieurspatronsdemêmenom,possédantdesparamètres(detypeouexpression) différents. La seule règle à respecter dans ce cas est que l’appel d’unefonctiondecenomnedoitpasconduireàuneambiguïté:unseulpatrondefonctionsdoitpouvoirêtreutiliséàchaquefois.

Par ailleurs, il est possible de fournir la définition d’une ou plusieurs fonctionsparticulièresquiserontutiliséesenlieuetplacedecelleinstanciéeparunpatron.Parexemple,avec:

template<classT>Tmin(Ta,Tb)//patrondefonctions

{...}

char*min(char*cha,char*chb)//versionspécialiséepourletypechar*

{...}

intn,p;

char*adr1,*adr2;

min(n,p)//appellelafonctioninstanciéeparlepatrongénéral

//soitici:intmin(int,int)

min(adr1,adr2)//appellelafonctionspécialisée

//char*min(char*,char*)

Algorithmed’instanciationoud’appeld’unefonctionPrécisons comment doivent être aménagées les règles de recherche d’une fonctionsurdéfinie,danslecasoùilexisteunouplusieurspatronsdefonctions.

Lorsd’unappeldefonction,lecompilateurrecherchetoutd’abordunecorrespondanceexacte avec les fonctions « ordinaires ». S’il y a ambiguïté, la recherche échoue(comme à l’accoutumée). Si aucune fonction « ordinaire » ne convient, on examinealors tous les patrons ayant le nom voulu (en ne considérant que les paramètres detype).Siuneseulecorrespondanceexacteest trouvée, la fonctioncorrespondanteestinstanciée(dumoins,siellenel’apasdéjàété)etleproblèmeestrésolu.S’ilyenaplusieurs,larechercheéchoue.

Enfin, si aucun patron de fonction ne convient, on examine à nouveau toutes lesfonctions « ordinaires » en les traitant cette fois comme de simples fonctionssurdéfinies(promotionsnumériques,conversionsstandard…).

303

Page 305: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice123

ÉnoncéCréer un patron de fonctions permettant de calculer le carré d’une valeur de typequelconque(lerésultatposséderalemêmetype).Écrireunpetitprogrammeutilisantcepatron.

Ici,notrepatronnecomporteraqu’unseulparamètredetype(correspondantàlafoisàl’uniqueargumentetàlavaleurderetourdelafonction).Sadéfinitionneposepasdeproblèmeparticulier.

#include<iostream>

usingnamespacestd;

template<classT>Tcarre(Ta)

{returna*a;

}

main()

{intn=5;

floatx=1.5;

cout<<"carrede"<<n<<"="<<carre(n)<<"\n";

cout<<"carrede"<<x<<"="<<carre(x)<<"\n";

}

304

Page 306: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice124

ÉnoncéSoitcettedéfinitiondepatrondefonctions:

template<classT,classU>Tfct(Ta,Ub,Tc)

{.....

}

Aveclesdéclarationssuivantes:

intn,p,q;

floatx;

chart[20];

charc;

Quelssontlesappelscorrectset,danscecas,quelssontlesprototypesdesfonctionsinstanciées?

fct(n,p,q);//appelI

fct(n,x,q);//appelII

fct(x,n,q);//appelIII

fct(t,n,&c);//appelIV

a.L’appelIestcorrect;ilinstancielafonction:intfct(int,int,int)

b.L’appelIIestcorrect;ilinstancielafonction:intfct(int,float,int)

c.L’appelIIIestincorrect.d.L’appelIVestcorrectselonlanorme.Ilinstancielafonction:

char*fct(char*,int,char*)

L’appelþIVn’étaitpasacceptédanslaversion3quineconsidéraitpaschar*commeunecorrespondanceabsoluepourchar[20].

305

Page 307: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice125

ÉnoncéCréer un patron de fonctions permettant de calculer la somme d’un tableaud’éléments de type quelconque, le nombre d’éléments du tableau étant fourni enparamètre (on supposera que l’environnement utilisé accepte les « paramètresexpression»).Écrireunpetitprogrammeutilisantcepatron.

//définitiondupatrondefonctions

template<classT>Tsomme(T*tab,intnelem)

{Tsom;

inti;

som=0;

for(i=0;i<nelem;i++)som=som+tab[i];

returnsom;

}

//exempled'utilisation

#include<iostream>

usingnamespacestd;

main()

{intti[]={3,5,2,1};

floattf[]={2.5,3.2,1.8};

chartc[]={'a','e','i','o','u'};

cout<<somme(ti,4)<<"\n";

cout<<somme(tf,3)<<"\n";

cout<<somme(tc,5)<<"\n";

}

1.telqu’ilaétéconçu,lepatronsommenepeutêtreappliquéqu’àuntypeTpourlequel:

–l’opérationd’additionaunsens;celasignifiedoncqu’ilnepeutpass’agird’untype pointeur ; il peut s’agir d’un type classe, à condition que cette dernière aitsurdéfinil’opérateurd’addition;

–ladéclarationTsomestcorrecte ;celasignifiequesiTestun typeclasse, ilestnécessairequ'ildisposed'unconstructeursansargument;

–l’affectationsom=0estcorrecte ;celasignifiequesiTestun typeclasse, ilestnécessairequ’ilaitsurdéfinil’affectation.

À ce propos, notons qu’il est possible d’initialiser som lors de sa déclaration, enprocédantainsi:

306

Page 308: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Tsom(0);

CelaestéquivalentàTsom=0siTestuntypeprédéfini.Enrevanche,siTestdetypeclasse,celaprovoquel'appeld'unconstructeurà1argumentdeT,enluitransmettantlavaleur0;leproblèmerelatifàl'affectationsom=0neseposeplusalors.

2.L’exécutionde l’exempleproposéfournidesrésultatspeusatisfaisantsdans lecasoùl’onappliquesommeàuntableaudecaractères,comptetenudelacapacitélimitéedecetype.Onpourraitaméliorerlasituationen«spécialisant»notrepatronpourlestableauxdecaractères(enprévoyant,parexemple,unevaleurderetourdetypeint).

307

Page 309: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice126

ÉnoncéSoientlesdéfinitionssuivantesdepatronsdefonctions:

template<classT,classU>voidfct(Ta,Ub){...}//patronI

template<classT,classU>voidfct(T*a,Ub){...}//patronII

template<classT>voidfct(T,T,T){...}//patronIII

voidfct(inta,floatb){.....}//fonctionIV

Aveccesdéclarations:intn,p,q;

floatx,y;

doublez;

Quelssont lesappelscorrectset,danscecas,quelssont lespatronsutiliséset lesprototypesdesfonctionsinstanciées?

fct(n,p);//appelI

fct(x,y);//appelII

fct(n,x);//appelIII

fct(n,z);//appelIV

fct(&n,p);//appelV

fct(&n,x);//appelVI

fct(&n,&p,&q)//appelVII

Ici,onfaitappelàlafoisàunesurdéfinition(patronsI,IIetIII)etàunespécialisationdepatron(fonctionIV).

I)patronIvoidfct(int,int);

II)patronIvoidfct(float,float);

III)fonctionIVvoidfct(int,float);

IV)patronIvoidfct(int,double);

V)erreur:ambigüitéentrefct(T,U)etfct(T*,U)

VI)erreur:ambigüitéentrefct(T,U)etfct(T*,U)

VII)patronIIIvoidfct(int*,int*,int*);

Lepatron II ne peut jamais être utilisé.En effet, chaque fois qu’il pourrait l’être, lepatronIpeutl’êtreégalement,desortequ’ilyaambiguïté.LepatronIIestdonc,ici,parfaitementinutile.

Notezquesinousavionsdéfinisimultanémentlesdeuxpatrons:template<classT,classU>voidfct(Ta,Ub);

template<classT>voidfct(Ta,Tb)

308

Page 310: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

lemêmephénomèned’ambiguïté (entrecesdeuxpatrons)seraitapparu lorsd’appelstelsquefct(n,p)oufct(x,y).

Rappelonsquel’ambiguïtén’estdétectéequelorsquelecompilateurdoitinstancierunefonctionetnonsimplementauvudesdéfinitionsdepatronselles-mêmes:cesdernièresrestentdoncacceptéestantquel’ambiguïtén’estpasmiseenévidenceparunappellarévélant.

309

Page 311: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre18Lespatronsdeclasses

310

Page 312: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

Introduiteparlaversion3,lanotiondepatrondeclassespermetdedéfinircequel’onnomme aussi des « classes génériques ». Plus précisément, à l’aide d’une seuledéfinitioncomportantdesparamètresde typeetdesparamètresexpression,ondécrittoute une famille de classes ; le compilateur fabrique (instancie) la ou les classesnécessairesàlademande(onnommesouventcesinstancesdes«classespatron»).

Cette fois, les paramètres expression étaient déjà prévus par la version 3 alors que,danslecasdespatronsdefonctions,ilsn'ontétéintroduitsqueparlanormeANSI.

Définitiond'unpatrondeclassesOn précise les paramètres de type en les faisant précéder du mot clé class et lesparamètresexpressionenmentionnantleurtypedansunelistedeparamètresintroduiteparlemottemplate(commepourlespatronsdefonctions,aveccettedifférencequ’ici,touslesparamètres–typeouexpression–apparaissent).

Parexemple:template<classT,classU,intn>classgene

{//ici,Tdésigneuntypequelconque,nunevaleurentièrequelconque

};

Siunefonctionmembreestdéfinie(cequiestlecasusuel)àl’extérieurdeladéfinitiondupatron, il faut rappeler aucompilateur la listedeparamètres (template) et préfixerl’en-têtedelafonctionmembredunomdupatronaccompagnédesesparamètres.(Entouterigueur,ils’agitd’uneredondanceconstatée,maisnonjustifiée,parlefondateurdulangagelui-même,Stroustrup.)Parexemple,pourunconstructeurdenotrepatrondeclassesprécédent:

template<classT,classU,intn>gene<T,U,n>::gene(...)

{.....}

Instanciationd’uneclassepatronOndéclareuneclassepatronenfournissantàlasuitedunomdepatronunnombredeparamètres effectifs (noms de types ou expressions) correspondant aux paramètres

311

Page 313: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

figurantdanslaliste(template).Lesparamètresexpressiondoiventobligatoirementêtredesexpressionsconstantesdumêmetypequeceluifigurantdanslaliste.Parexemple,avecnotreprécédentpatron(onsupposequeptestuneclasse):

classgene<int,float,5>c1;//T=int,U=float,n=5

classgene<int,int,12>c2;//T=int,U=int,n=12

constintNV=100;

classgene<pt,double,NV>c3;//T=pt,U=double,n=100

intn=5;

classgene<int,double,n>c4;//erreur:nn'estpasconstant

constcharC='e';

classgene<int,double,C>c5;//erreur:Cdetypecharetnonint

Un paramètre de type effectif peut lui-même être une classe patron. Par exemple, sinousavonsdéfiniunpatrondeclassespointpar:

template<classT>classpoint{.....};

Voicidesinstancespossiblesdegene:classgene<point<int>,float,10>c5;//T=point<int>,U=float,n=10

classgene<point<char>,point<float>,5>c6;//T=point<int>,U=point<float>,n=5

Unpatronde classespeut comporterdesmembres (donnéesou fonctions) statiques ;dans ce cas, chaque instance de la classe dispose de son propre jeu de membresstatiques.

Spécialisationd’unpatrondeclassesUnpatrondeclassesnepeutpasêtresurdéfini(onnepeutpasdéfinirdeuxpatronsdemême nom). En revanche, on peut spécialiser un patron de classes de différentesmanières.

--Enspécialisantunefonctionmembre

Parexemple,aveccepatron:template<classT,intn>classtableau{.....};

Onpourraécrireuneversionspécialiséedeconstructeurpour lecasoùT est le typepointetoùnvaut10enprocédantainsi:

tableau<point,10>::tableau(...){.....}

--Enspécialisantuneclasse

Dans ce cas, on peut éventuellement spécialiser tout ou une partie des fonctionsmembre,maiscen’estpasnécessaire.Parexemple,aveccepatron:

template<classT>classpoint{.....};

onpeut fourniruneversionspécialiséepour lecasoùTest le typechar enprocédant

312

Page 314: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

ainsi:classpoint<char>

{//nouvelledéfinitiondelaclassepointpourlescaractères

};

IdentitédeclassespatronOnnepeutaffecterentreeuxquedeuxobjetsdemêmetype.Danslecasd’objetsd’untypeclassepatron,onconsidèrequ’ilya identitéde type lorsque leursparamètresdetypessontidentiquesetquelesparamètresexpressionontlesmêmesvaleurs.

ClassespatronethéritageOn peut « combiner » de plusieurs façons l’héritage avec la notion de patron declasses:

Classe«ordinaire»dérivéed’uneclassepatron;parexemple,siAestuneclassepatrondéfiniepartemplate<classT>A:classB:publicA<int>//BdérivedelaclassepatronA<int>

OnobtientuneseuleclassenomméeB.

Patron de classes dérivé d’une classe « ordinaire » ; par exemple, si A est uneclasseordinaire:template<classT>classB:publicA

Onobtientunefamilledeclasses(deparamètredetypeT).

Patrondeclassesdérivéd’unpatrondeclasses;parexemple,siAestuneclassepatrondéfiniepartemplate<classT>A,onpeut:

–définirunenouvellefamilledefonctionsdérivéespar:template<classT>classB:publicA<T>

Danscecas, ilexisteautantdeclassesdérivéespossiblesquedeclassesdebasepossibles.

–définirunenouvellefamilledefonctionsdérivéespar:template<classT,classU>classB:publicA<T>

Dansce cas,onpeutdirequechaqueclassedebasepossiblepeut engendrerunefamilledeclassesdérivées(deparamètredetypeU).

313

Page 315: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice127

ÉnoncéSoitladéfinitionsuivanted’unpatrondeclasses:

template<classT,intn>classessai

{Ttab[n];

public:

essai(T);//constructeur

};

a.Donnezladéfinitionduconstructeuressai,ensupposant:

•qu’elleestfournie«l'extérieur»deladéfinitionprécédente;

• que le constructeur recopie la valeur reçue en argument dans chacun desélémentsdutableautab.

b.Disposantainsideladéfinitionprécédentedupatronessai,desonconstructeuretdecesdéclarations:constintn=3;

intp=5;

Quelles sont les instructions correctes et les classes instanciées ? On en fournira(dans chaque cas) une définition équivalente sous la forme d’une « classeordinaire»,c’est-à-diredanslaquellelanotiondeparamètreadisparu.

essai<int,10>ei(3);//I

essai<float,n>ef(0.0);//II

essai<double,p>ed(2.5);//III

a.Ladéfinitionduconstructeurestanalogueàcellequel’onauraitécrite«enligne»;ilfautsimplement«préfixer»sonen-têted’unelistedeparamètresintroduitepartemplate.Deplus, ilfautpréfixerl’en-têtedelafonctionmembredunomdupatronaccompagnédesesparamètres(bienquecelasoitredondant):template<classT,intn>essai<T,n>::essai(Ta)

{inti;

for(i=0;i<n;i++)tab[i]=a;

}

b.AppelI:correct.classessai

{inttab[10];

public:

essai(int);//constructeur

314

Page 316: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

};

essai::essai(inta)

{inti;

for(i=0;i<n;i++)tab[i]=a;

}

b.AppelII:correct.classessai

{floattab[n];

public:

essai(float);//constructeur

};

essai::essai(floata)

{inti;

for(i=0;i<n;i++)tab[i]=a;

}

b.AppelIII:incorrectcarpn’estpasuneexpressionconstante.

315

Page 317: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice128

Énoncéa. Créer un patron de classes nommé pointcol, tel que chaque classe instanciée

permettedemanipulerdespointscolorés(deuxcoordonnéesetunecouleur)pourlesquels on puisse « choisir » à la fois le type des coordonnées et celui de lacouleur.Onselimiteraàdeuxfonctionsmembre:unconstructeurpossédanttroisarguments (sans valeur par défaut) et une fonction affiche affichant lescoordonnéesetlacouleurd’un«pointcoloré».

b. Dans quelles conditions peut-on instancier une classe patron pointcol pour desparamètresdetypeclasse?

a.Voicicequepourraitêtreladéfinitiondupatrondemandé,enprévoyantlesfonctionsmembre«enligne»:template<classT,classU>classpointcol

{

Tx,y;//coordonnees

Ucoul;//couleur

public:

pointcol(Tabs,Tord,Ucl)

{x=abs;y=ord;coul=cl;

}

voidaffiche()

{cout<<"pointcolore-coordonnees"<<x<<""<<y

<<"couleur"<<coul<<"\n";

}

};

À titre indicatif, voici un exemple d’utilisation (on suppose que la définitionprécédentefiguredanspointcol.h):

#include"pointcol.h"

#include<iostream>

usingnamespacestd;

main()

{pointcol<int,shortint>p1(5,5,2);p1.affiche();

pointcol<float,int>p2(4,6,2);p2.affiche();

pointcol<double,unsignedshort>p3(1,5,2);p3.affiche();

}

b. Il suffit que le type classe en question ait convenablement surdéfini l’opérateur <<,afind’assurerconvenablementl’affichagesurcoutdesinformationscorrespondantes.

316

Page 318: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice129

ÉnoncéOnadéfinilepatrondeclassessuivant:

template<classT>classpoint

{Tx,y;//coordonnees

public:

point(Tabs,Tord){x=abs;y=ord;}

voidaffiche();

};

template<classT>voidpoint<T>::affiche()

{cout<<"Coordonnees:"<<x<<""<<y<<"\n";

}

a.Quesepasse-t-ilaveccesinstructions:point<char>p(60,65);

p.affiche();

b.Comment faut-ilmodifier ladéfinitiondenotrepatronpourque les instructionsprécédentesaffichentbien:Coordonnees:6065

a. On obtient l’affichage des caractères de code 60 et 65 (c’est-à-dire dans uneimplémentationutilisantlecodeASCII:<etA)etnonlesnombres60et65.

b.IlfautspécialisernotrepatronpointpourlecasoùletypeTestletypechar.Pourcefaire,onpeut:

soitfournirunedéfinitioncomplètedepoint<char>,avecsesfonctionsmembre;

soit,puisqu’iciseule lafonctionafficheestconcernée,secontenterdesurdéfinir lafonction point<char>::affiche, ce qui conduit à cette nouvelle définition de notrepatron://définitiongeneraledupatronpoint

template<classT>classpoint

{

Tx,y;//coordonnees

public:

point(Tabs,Tord){x=abs;y=ord;}

voidaffiche();

};

template<classT>voidpoint<T>::affiche()

{cout<<"Coordonnees:"<<x<<""<<y<<"\n";

}

317

Page 319: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

//versionspecialiseedelafonctionaffichepourletypechar

voidpoint<char>::affiche()

{cout<<"Coordonnees:"<<(int)x<<""<<(int)y<<"\n";

}

318

Page 320: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice130

ÉnoncéCréerunpatrondeclassespermettantde représenterdes«vecteursdynamiques»c’est-à-dire des vecteurs dont la dimension peut ne pas être connue lors de lacompilation (ce n’est donc pas obligatoirement une expression constante commedans le cas de tableaux usuels). On prévoira que les éléments de ces vecteurspuissentêtredetypequelconque.

On surdéfinira convenablement l’opérateur [] pour qu’il permette l’accès auxélémentsduvecteur(aussibienenconsultationqu’enmodification)etons’arrangerapourqu’il n’existe aucun risquede«débordementd’indice».En revanche,onnecherchera pas à régler les problèmes posés éventuellement par l’affectation ou latransmissionparvaleurd’objetsdutypeconcerné.

N.B. Il ne faut pas chercher à utiliser les composants standard introduits par lanorme.Eneffet,lepatronvectorrépondraitintégralementàlaquestion.

En généralisant ce qui a été fait dans l’exercice 90 (sans toutefois initialiser leséléments du vecteur lors de sa construction), nous aboutissons au patron de classessuivant:

template<classT>classvect

{intnelem;//nombred'elements

T*adr;//adressezonedynamiquecontenantleselements

public:

vect(int);//constructeur

~vect();//destructeur

T&operator[](int);//operateurd'accesaunelement

};

template<classT>vect<T>::vect(intn)

{adr=newT[nelem=n];

}

template<classT>vect<T>::~vect()

{deleteadr;

}

template<classT>T&vect<T>::operator[](inti)

{if((i<0)||(i>nelem))i=0;//protectionindicehorslimites

returnadr[i];

}

Ladéfinitiondupatronde classes serait plus simple si les fonctionsmembre étaient

319

Page 321: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

«enligne».

Notezque, iciencore,nousavons faitensortequ’une tentatived’accèsàunélémentsituéendehorsduvecteurconduiseàaccéderàl’élémentderang0.Danslapratique,onauraintérêtàutiliserdesprotectionsplusélaborées.

À titre indicatif, voici un petit programme, accompagné du résultat fourni par sonexécution,utilisantcepatron(dontonsupposequeladéfinitionfiguredansvectgen.h):

#include"vectgen.h"

#include<iostream>

usingnamespacestd;

main()

{vect<int>vi(10);

vi[5]=5;vi[2]=2;

cout<<vi[2]<<""<<vi[5]<<"\n";

vect<double>vd(3);

vd[0]=0.0;vd[1]=0.1;vd[2]=0.2;

cout<<vd[0]<<""<<vd[1]<<""<<vd[2]<<"\n";

cout<<vd[12]<<"\n";

vd[12]=1.2;cout<<vd[12]<<""<<vd[0];

}

25

00.10.2

0

1.21.2

Notre patron vect permet d’instancier des vecteurs dynamiques dans lesquels lesélémentssontdetypeabsolumentquelconque,enparticulierdetypeclasse(pourvuqueladiteclassedisposed’unconstructeursansargument). Iln’enseraitpasalléainsisinousavionsinitialisélesélémentsdutableaulorsdeleurconstructionpar:

inti;

for(i=0;i<nelem;i++)adr[i]=0;

En effet, dans ce cas, ces instructions auraient convenablement fonctionné pourn’importe quel type de base (par conversion de l’entier 0 dans le type voulu). Enrevanche,pourêtreapplicableàdesélémentsde typeclasse, il aurait fallu, enoutre,quelaclasseconcernéedisposed’uneconversiond’unintdanscetypeclasse,c’est-à-dired’unconstructeuràunargumentdetypenumérique.

320

Page 322: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice131

ÉnoncéComme dans l’exercice précédent, réaliser un patron de classes permettant demanipulerdesvecteursdontlesélémentssontdetypequelconquemaispourlesquelsladimension,supposéeêtrecettefoisuneexpressionconstante,apparaîtracommeunparamètre(expression)dupatron.Hormiscettedifférence,les«fonctionnalités»dupatronresterontlesmêmes.

Il n’est plus nécessaire d’allouer un emplacement dynamique pour notre vecteur quipeutdoncfigurerdirectementdanslesmembresdonnéedenotrepatron.Leconstructeurn’est plus nécessaire (voir toutefois la remarque ci-dessous), pas plus que ledestructeur.Voicicequepourraitêtreladéfinitiondenotrepatron:

template<classT,intn>classvect

{Tv[n];//vecteurdenelementsdetypeT

public:

T&operator[](int);//operateurd'accesaunelement

};

template<classT,intn>T&vect<T,n>::operator[](inti)

{if((i<0)||(i>n))i=0;//protectionindicehorslimites

returnv[i];

}

Voici,toujoursàtitreindicatif,cequedeviendraitlepetitprogrammed’essai:#include"vectgen1.h"

#include<iostream>

usingnamespacestd;

main()

{vect<int,10>vi;

vi[5]=5;vi[2]=2;

cout<<vi[2]<<""<<vi[5]<<"\n";

vect<double,3>vd;

vd[0]=0.0;vd[1]=0.1;vd[2]=0.2;

cout<<vd[0]<<""<<vd[1]<<""<<vd[2]<<"\n";

cout<<vd[12]<<"\n";

vd[12]=1.2;cout<<vd[12]<<""<<vd[0];

}

Ici,nousn’avonspaseubesoindefairedunombred’élémentsunmembredonnéedenosclassespatron:eneffet,lorsqu’onenabesoin,onl’obtientcommeétantlavaleurdusecondparamètrefournilorsdel’instanciation.Sinousavionsvouluconserverce

321

Page 323: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

nombred’élémentssousformed’unmembredonnée,ilauraitéténécessairedeprévoirunconstructeur,parexemple:

template<classT,intn>classvect

{intnelem;//nombred'elements

Tv[n];//vecteurdenelementsdetypeT

public:

vect();

T&operator[](int);//operateurd'accesaunelement

};

template<classT,intn>vect<T,n>::vect()

{nelem=n;

}

322

Page 324: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice132

ÉnoncéOndisposedupatrondeclassessuivant:

template<classT>classpoint

{Tx,y;//coordonnees

public:

point(Tabs,Tord){x=abs;y=ord;}

voidaffiche()

{cout<<"Coordonnees:"<<x<<""<<y<<"\n";

}

};

a.Créer, pardérivation,unpatronde classes pointcol permettant demanipuler des«pointscolorés»danslesquelslescoordonnéesetlacouleursontdemêmetype.On redéfinira convenablement les fonctionsmembre en réutilisant les fonctionsmembredelaclassedebase.

b.Mêmequestion,maisenprévoyantquelescoordonnéesetlacouleurpuissentêtrededeuxtypesdifférents.

c.Toujourspardérivation,créercettefoisune«classeordinaire»(c’est-à-direuneclassequinesoitplusunpatrondeclasses,autrementditquinedépendeplusdeparamètres...) dans laquelle les coordonnées sont de type int, tandis que lacouleurestdetypeshort.

a. Aucun problème particulier ne se pose ; il suffit de faire dériver pointcol<T> depoint<T>.Voicicequepeutêtreladéfinitiondenotrepatron(ici,nousavonslaisséleconstructeur«enligne»maisnousavonsdéfiniafficheendehorsdelaclasse):template<classT>classpointcol:publicpoint<T>

{Tcl;

public:

pointcol(Tabs,Tord,Tcoul):point<T>(abs,ord)

{cl=coul;

}

voidaffiche();

};

template<classT>voidpointcol<T>::affiche()

{point<T>::affiche();

cout<<"couleur:"<<cl<<"\n";

}

Voici un petit exemple d’utilisation (il nécessite les déclarations appropriées ou

323

Page 325: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

l’incorporationdesfichiersen-têtecorrespondants):main()

{pointcol<int>p1(2,5,1);p1.affiche();

pointcol<float>p2(2.5,5.25,4);p2.affiche();

}

b.Cettefois,laclassedérivéedépenddedeuxparamètres(nommésiciTetU).Voiciceque pourrait être la définition de notre patron (avec, toujours, un constructeur enligneetunefonctionaffichedéfinieàl’extérieurdelaclasse):template<classT,classU>classpointcol:publicpoint<T>

{Ucl;

public:

pointcol(Tabs,Tord,Ucoul):point<T>(abs,ord)

{cl=coul;

}

voidaffiche();

};

template<classT,classU>voidpointcol<T,U>::affiche()

{point<T>::affiche();

cout<<"couleur:"<<cl<<"\n";

}

Voici un exemple d’utilisation (on suppose qu’il est muni des déclarationsappropriées):main()

{

pointcol<int,short>p1(2,5,1);p1.affiche();

pointcol<float,int>p2(2.5,5.25,4);p2.affiche();

}

c.Cettefois,pointcolestunesimpleclasse,nedépendantplusd’aucunparamètre.Voicicequepourraitêtresadéfinition:classpointcol:publicpoint<int>

{

shortcl;

public:

pointcol(intabs,intord,shortcoul):point<int>(abs,ord)

{cl=coul;

}

voidaffiche()

{point<int>::affiche();

cout<<"couleur:"<<cl<<"\n";

}

};

Etunpetitexempled’utilisation:main()

{

pointcolp1(2,5,1);p1.affiche();

pointcolp2(2.5,5.25,4);p2.affiche();

}

324

Page 326: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice133

ÉnoncéOndisposedumêmepatrondeclassesqueprécédemment:

template<classT>classpoint

{

Tx,y;//coordonnees

public:

point(Tabs,Tord){x=abs;y=ord;}

voidaffiche()

{cout<<"Coordonnees:"<<x<<""<<y<<"\n";

}

};

a.LuiajouteruneversionspécialiséedeaffichepourlecasoùTestletypecaractère.

b. Comme dans la questiona de l’exercice précédent, créer un patron de classespointcol permettant de manipuler des « points colorés » dans lesquels lescoordonnéeset lacouleursontdemêmetype.Onredéfiniraconvenablementlesfonctionsmembreenréutilisantlesfonctionsmembredelaclassedebaseetl’onprévoira une version spécialisée de affiche de pointcol dans le cas du typecaractère.

a.voidpoint<char>::affiche()

{cout<<"Coordonnees:"<<(int)x<<""<<(int)y<<"\n";

}

b.template<classT>classpointcol:publicpoint<T>

{Tcl;

public:

pointcol(Tabs,Tord,Tcoul):point<T>(abs,ord)

{cl=coul;

}

voidaffiche()

{point<T>::affiche();

cout<<"couleur:"<<cl<<"\n";

}

};

voidpointcol<char>::affiche()

{point<char>::affiche();

cout<<"couleur:"<<(int)cl<<"\n";

}

325

Page 327: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Seule la question a de l’exercice 132 se prêtait à une spécialisation pour le typecaractère.Eneffet,pour laclassedemandéeenc,n’ayantplusaffaireàunpatrondeclasses,laquestionn’auraitaucunsens.Encequiconcernelaclassedemandéeenb,enrevanche,onsetrouveenprésenced’uneclassedérivéedépendantde2paramètresTetU.Ilfaudraitalorspouvoirspécialiseruneclasse,nonpluspourdesvaleursdonnéesdetous les (deux) paramètres,mais pour une valeur donnée (char) de l’un d’entre eux ;cettenotiondefamilledespécialisationn’existepasdanslanormeactuelledeC++.

326

Page 328: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice134

ÉnoncéOndisposedupatrondeclassessuivant:

template<classT>classpoint

{Tx,y;//coordonnees

public:

point(Tabs,Tord){x=abs;y=ord;}

voidaffiche()

{cout<<"Coordonnees:"<<x<<""<<y<<"\n";

}

};

Onsouhaitecréerunpatrondeclasses cercle permettantdemanipulerdes cercles,définisparleurcentre(detypepoint)etunrayon.Onn’yprévoira,commefonctionsmembre, qu’un constructeur et une fonction affiche se contentant d’afficher lescoordonnéesducentreetlavaleurdurayon.

a.Lefaireparhéritage(uncercleestunpointquipossèdeunrayon).

b. Le faire par composition d’objets membre (un cercle possède un point et unrayon).

a.Ladémarcheestanalogueàcelledelaquestionadel’exercice132.Onaaffaireàunpatrondépendantdedeuxparamètres(iciTetU):template<classT,classU>classcercle:publicpoint<T>

{Ur;//rayon

public:

cercle(Tabs,Tord,Uray):point<T>(abs,ord)

{r=ray;

}

voidaffiche()

{point<T>::affiche();

cout<<"rayon:"<<r;

}

};

b.Lepatrondépendtoujoursdedeuxparamètres(TetU)mais iln’yaplusdenotiond’héritage:template<classT,classU>classcercle

{point<T>c;//centre

Ur;//rayon

public:

cercle(Tabs,Tord,Uray):c(abs,ord)//pourraitr(ray)

{r=ray;

}

327

Page 329: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

voidaffiche()

{c.affiche();

cout<<"rayon:"<<r;

}

};

Notezque,dansladéfinitionduconstructeurcercle,nousavonstransmislesargumentsabsetordàunconstructeurdepointpourlemembrec.Nousaurionspuutiliserlamêmenotationpourr,bienquecemembresoitd’un typedebaseetnond’un typeclasse ;celanousauraitconduitauconstructeursuivant(decorpsvide):

cercle(Tabs,Tord,Uray):c(abs,ord),r(ray)

{}

328

Page 330: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre19Gestiondesexceptions

329

Page 331: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Rappels

LemécanismegénéralDepuislaversion3,C++disposed’unmécanismeditdegestiondesexceptions.Uneexceptionestunerupturedeséquence(pasunappeldefonction!)déclenchée(onditaussi « levée ») par un programme à l’aide de l’instruction throw dans laquelle onmentionne une expression quelconque. Il y a alors branchement à un ensembled’instructions, dit « gestionnaire d’exceptions », choisi en fonction de la nature del’expressionindiquéeàthrow.

Pourqu’uneportiondeprogrammepuisse intercepteruneexception, ilestnécessairequ’ellefigureàl’intérieurd’unblocprécédédumot-clétry.Cederniersoitêtresuivid’une ou de plusieurs instructions catch représentant les différents gestionnairescorrespondants,commedansceschéma:

try

{.....//instructionssusceptiblesdeleveruneexception,soit

//directementparthrow(exp),soitparlebiaisdefonctions

//appelées

}

catch(type_a...)

{.....//traitementdel'exceptioncorrespondantautypetype_a

}

catch(type_b...)

{.....//traitementdel'exceptioncorrespondantautypetype_b

}

.....

catch(type_n...)

{.....//traitementdel'exceptioncorrespondantautypetype_n

}

Ungestionnaired’exceptionspeutcontenirdesinstructionsexitouabortquimettentfinàl’exécution du programme.Une instruction return fait sortir de la fonction ayant levél’exception.Danslesautrescas(rarementutilisés),onpasseauxinstructionssuivantlebloctryconcerné.

Algorithmedechoixd’ungestionnaired’exceptionsLorsqu’uneexceptionestlevéeparthrow,avecletypeT,onrecherche,danscetordre,un gestionnaire correspondant à l’un des types suivants : type T, type de base de T,pointeur sur une classe dérivée (si T est d’un type pointeur sur une classe), typeindéterminé(indiquéparcatch(...))danslegestionnaire.

330

Page 332: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

CheminementdesexceptionsLorsqu’une exception est levée par une fonction, on cherche tout d’abord ungestionnairedansl’éventuelbloctryassociéàcettefonction;si l’onn’entrouvepas(ousiaucunbloctryn’estassocié),onpoursuitlarecherchedansunéventuelbloctryassociéàune fonctionappelanteetainsidesuite.Siaucungestionnaired’exceptionsn’est trouvé, on appelle la fonction terminate qui, par défaut appelle abort, laquellefournitunmessagedugenreabnormalprogramtermination.

Spécificationd’interfaceUnefonction(ycomprismain)peut spécifier lesexceptionsqu’elleest susceptibledeprovoquer(elle-même,oudanslesfonctionsqu’elleappelleàsontour).Ellelefaitàl’aidedumot-cléthrow,suivi,entreparenthèses,delalistedesexceptionsconcernées.Danscecas,touteexceptionnonprévueetlevéeàl’intérieurdelafonction(oud’unefonction appelée) entraîne l’appel d’une fonction particulière nommée unexpected. Pardéfaut,cettefonctionappellelafonctionterminate.

331

Page 333: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice135

ÉnoncéQuelsrésultatsfourniraceprogrammelorsqu’onluifournitcommedonnées:

a.lavaleur1þ?

b.lavaleur0?#include<iostream>

usingnamespacestd;

main()

{intn;

cout<<"donnezunentier:";

cin>>n;

try

{cout<<"debutpremierbloctry\n";

if(n)thrown;

cout<<"finpremierbloctry\n";

}

catch(intn)

{cout<<"catch1-n="<<n<<"\n";

}

cout<<"suiteprogramme\n";

try

{cout<<"debutsecondbloctry\n";

thrown;

cout<<"finsecondbloctry\n";

}

catch(intn)

{cout<<"catch2-n="<<n<<"\n";

}

cout<<"finprogramme\n";

}

a.Aveclavaleur1:donnezunentier:1

debutpremierbloctry

catch1-n=1//fourniparlebloccatch(int)associéaupremier

//bloctry

suiteprogramme

debutsecondbloctry

catch2-n=1//fourniparlebloccatch(int)associéausecondbloc

//try

finprogramme

On notera bien que, dans le premier bloc try, l’exception de type int provoque unbranchement au bloc catch(int) correspondant. Comme ce dernier ne comporte pasd’instruction d’interruption de programme ou de fonction, on passe aux instructions

332

Page 334: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

suivantlederniergestionnaireassocié,c’est-à-direiciaubloctrysuivant.Làencore,une exception de type int provoque le branchement au bloc catch(int) correspondant(différentduprécédent).Puis l’onpasse aux instructions suivantes, c’est-à-dire ici àl’instructionaffichantfinprogramme.

b.Aveclavaleur0:donnezunentier:0

debutpremierbloctry

finpremierbloctry

suiteprogramme

debutsecondbloctry

catch2-n=0

finprogramme

Ici,contrairementàcequiseproduisaitdansl’exécutionprécédente, lepremierbloctry ne déclenche plus d’exception. Son exécution se poursuit donc en entier, d’où lemessagefinpremierbloctry.Lasuiteestidentique.

333

Page 335: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice136

ÉnoncéQuelsrésultatsdonneceprogrammelorsqu’onluifournitcommedonnées:

a.lavaleur1þ?

b.lavaleur2þ?

c.lavaleur3þ?

d.lavaleur4þ?#include<stdlib.h>//pourexit

#include<iostream>

usingnamespacestd;

main()

{intn;floatx;doublez;

cout<<"donnezunentier:";

cin>>n;

try

{switch(n)

{case1:thrown;break;

case2:x=n;throwx;break;

case3:z=n;throwz;break;

}

}

catch(intn)

{cout<<"catchentier-n="<<n<<"\n";

}

catch(floatx)

{cout<<"catchflottant-x="<<x<<"\n";

exit(-1);

}

cout<<"suiteetfinduprogramme\n";

}

a.donnezunentier:1

catchentier-n=1

suiteetfinduprogramme

L’exception de type int levée dans le bloc try est interceptée par le gestionnairecatch(int)quiafficheunmessage.Onpassealorsàl’instructionsuivantlebloctry,quiaffichelemessagesuiteetfinduprogramme.

b.donnezunentier:2

catchflottant-x=2

334

Page 336: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

L’exception de type float levée dans le bloc try est interceptée par le gestionnairecatch(float)quiafficheunmessageavantdemettrefinàl’exécutionduprogramme.

c.donnezunentier:3

abnormalprogramtermination/*ouquelquechosedesemblable*/

L’exception de type double levée dans le bloc try ne dispose d’aucun gestionnaireapproprié (ilauraitdûêtredu typecatch(double).Onappelledonc la fonctionterminatequi,pardéfaut,appellelafonctionabort, laquellemetfinàl’exécutionduprogrammeenaffichantunmessageapproprié.

d.donnezunentier:4

suiteetfinduprogramme

Ici,aucuneexceptionn’aétélevéeparlebloctryetonexécutel’instructionquilesuit,laquelleaffichelemessagedefindeprogramme.

335

Page 337: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice137

ÉnoncéQuelsrésultatsproduiraceprogramme?

#include<iostream>

usingnamespacestd;

classerreur

{public:

intnum;

};

classerreur_d:publicerreur

{public:

intcode;

};

classA

{public:

A(intn)

{if(n==1){erreur_derd;erd.num=999;

erd.code=12;throwerd;}

}

};

main()

{

try

{Aa(1);

cout<<"aprescreationa(1)\n";

}

catch(erreurer)

{cout<<"exceptionerreur:"<<er.num<<"\n";

}

catch(erreur_derd)

{cout<<"exceptionerreur_d:"<<erd.num<<""<<erd.code<<"\n";

}

cout<<"suite\n";

try

{Ab(1);

cout<<"aprescreationb(1)\n";

}

catch(erreur_derd)

{cout<<"exceptionerreur_d:"<<erd.num<<""<<erd.code<<"\

n";

}

catch(erreurer)

{cout<<"exceptionerreur:"<<er.num<<"\n";

}

}

exceptionerreur:999

suite

exceptionerreur_d:99912

336

Page 338: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Dans le premier bloc try, la construction de l’objet a(1) lève une exception de typeerreur_d,entransmettantuneexpression(erd)decetypedanslaquelleleschampsnumetcode valent respectivement 999 et 12. Le premier gestionnaire trouvé (catch(erreur er))

convientpuisqueerreurestuntypedebasedeerreur_d.C’estdoncluiquiestexécuté,cequi explique que la valeur du champ code ne soit pas affichée, et ceci malgrél’existence,unpeuplusloin,d’ungestionnairemieuxapproprié(catch(erreur_d)).

Dans lesecondbloctry, en revanche, lesmêmesgestionnairesont étédisposésdansl’ordre inverse. L’exception levée par la construction de b est alors traitée par legestionnaireappropriéetlavaleurduchampcodeesteffectivementaffichée.

337

Page 339: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice138

ÉnoncéSoitladéfinitionsuivantedesclasseserreuretA:

classerreur

{public:

intnum;

};

classA

{public:

A(intn)

{if(n==1){erreurer;er.num=999;thrower;}

}

};

1.Quelsrésultatsfourniraceprogrammeutilisantcesdeuxclasses:#include<iostream>

usingnamespacestd;

main()

{voidf();

try

{f();

}

catch(erreurer)

{cout<<"dansmain:"<<er.num<<"\n";

}

cout<<"suitemain\n";

}

voidf()

{try

{Aa(1);

}

catch(erreurer)

{cout<<"dansf:"<<er.num<<"\n";

}

cout<<"suitef\n";

}

2.Mêmequestionenremplaçantladéfinitiondefpar:voidf()

{try

{Aa(1);

}

catch(erreurer)

{cout<<"dansf:"<<er.num<<"\n";

return;

}

cout<<"suitef\n";

}

3.Mêmequestionenremplaçantladéfinitiondefpar:voidf()

{Aa(1);

338

Page 340: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

1.dansf:999

suitef

suitemain

L’exception levée par la construction dans f de a(1) est traitée par le gestionnairecatch(erreur) associé au bloc try correspondant de la fonction elle-même.On exécuteensuitel’instructionsuivantcebloc,laquelleaffichesuitef,avantd’effectuerunretourclassiquedelafonctionfdansmain.Onnoteraquelebloctrydemainn’intervientpasici.

2.dansf:999

suitemain

Là encore, l’exception levée f par la construction dans f de a(1) est traitée par legestionnairecatch(erreur) associéaubloc try correspondant de la fonction elle-même.Mais cette fois, celui-ci se termine par une instruction return, laquelle provoque leretourdelafonctionconcernée,àsavoirf(attention,paslegestionnaired’exceptions,quin’estpasunefonction!).

3.dansmain:999

suitemain

Cettefois,laconstructiondea(1)dansfnefigurepasdansunbloctry.Larecherchedugestionnairedel’exceptionalorslevéesefaitdansunbloctryenglobant,àsavoiriciceluidanslequelaeulieul’appeldef.

339

Page 341: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice139

ÉnoncéQuelsrésultatsfourniraceprogramme:

#include<iostream>

usingnamespacestd;

classA

{public:

A(intn)

{try

{if(n==1)thrown;

}

catch(intn)

{cout<<"dansconstructeurA:"<<n<<"\n";

throw;

}

}

};

main()

{voidf();

try

{f();

}

catch(intn)

{cout<<"dansmain:"<<n<<"\n";

}

cout<<"finmain\n";

}

voidf()

{

try

{Aa(1);

}

catch(intn)

{cout<<"dansf:"<<n<<"\n";

throw;

}

cout<<"suitef\n";

}

dansconstructeurA:1

dansf:1

dansmain:1

finmain

L’exceptionlevéeparlaconstructiondel’objeta(1)esttoutd’abordinterceptéeparlegestionnaire associé au bloc try du constructeur, d’où le premier message. Maisl’instruction throw qu’il contient demande de transmettre l’exception au bloc try

englobant, à savoir ici celui figurant dans la fonction f d’où le secondmessage. Làencore,uneinstructionthrowretransmetl’exceptionauniveausupérieur,àsavoirlebloc

340

Page 342: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

trydemain.

341

Page 343: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice140

ÉnoncéÉcrireuneclassepoint(àdeuxcoordonnéesentières)quidisposed’unconstructeuràdeuxarguments levantuneexceptionlorsquelesdeuxcomposantessontégales.Deplus,l’appeld’unconstructeursansargumentouàunseulargumentdevraégalementleverun autre typed’exception. Ici, on limitera les fonctionsmembrede point auxseulsconstructeurs.

Écrireunprogrammed’utilisationdelaclassepoint,interceptantconvenablementlesexceptionsprévues,enmentionnantlacause.

Il est préférable d’associer un type classe à chacune des deux sortes d’exceptionsprévues. Nous choisirons er_compos pour l’exception déclenchée en cas d’égalité descomposanteseter_constrpourcelledéclenchéeencasd’appeld’unconstructeurà0ou1argument ; la distinction entre les deux se fera par une valeur entière transmise auconstructeur et conservée ici dans un champ public. Voici ce que pourrait être ladéfinitiondenotreclasse:

classer_compos

{};

classer_constr

{public:

intnum;

er_constr(intn){num=n;}

};

classpoint

{intx,y;

public:

point(intabs,intord)

{if(abs==ord)thrownewer_compos;

x=abs;y=ord;

}

point()

{thrownewer_constr(0);

}

point(intabs)

{thrownewer_constr(1);

}

};

Voiciunexempledeprogrammequisecontentedesignalerlesexceptionsinterceptéesen interrompant l’exécution. On notera que les trois constructions proposéesprovoquent effectivement une exception qui, au bout du compte, interrompt le

342

Page 344: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

programme.Autrementdit,pourpercevoir l’effetdelasecondeoudelatroisième,ilfaudraitlaplaceravantlesautres.

#include<iostream>

usingnamespacestd;

main()

{

try

{//.....

pointb(1,1);//afficherait:exceptioncreationpoint:

//composantesegales

point();//afficherait:exceptioncreationpoint:

//constructeur0arg

point(3);//afficherait:exceptioncreationpoint:

//constructeur1arg

//.....

}

catch(er_compose)

{cout<<"exceptioncreationpoint:composantesegales\n";

exit(-1);

}

catch(er_constrec)

{switch(ec.num)

{case1:cout<<"exceptioncreationpoint:constructeur0arg\n";

break;

case2:cout<<"exceptioncreationpoint:constructeur1arg\n";

break;

}

exit(-1);

}

}

343

Page 345: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice141

Énoncé1.Quelsrésultatsfourniraceprogramme:

#include<iostream>

usingnamespacestd;

main()

{voidf();

try

{f();

}

catch(intn)

{cout<<"exceptintdansmain:"<<n<<"\n";

}

catch(...)

{cout<<"exceptionautrequeintdansmain\n";

}

cout<<"finmain\n";

}

voidf()

{

try

{intn=1;thrown;

}

catch(intn)

{cout<<"exceptintdansf:"<<n<<"\n";

throw;

}

}

2.Mêmequestionsil’onremplacelafonctionfpar:voidf()

{

try

{floatx=2.5;throwx;

}

catch(intn)

{cout<<"exceptintdansf:"<<n<<"\n";

throw;

}

}

1.exceptintdansf:1

exceptintdansmain:1

finmain

L’exception de type int levée dans f est traitée par le gestionnaire catch(int)

correspondant.Elle est retransmiseàunblocenglobantpar throw, donc traitée par le

344

Page 346: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

gestionnairecatch(int)dubloctrydumain.

2.exceptionautrequeintdansmain

finmain

Cettefois,aucungestionnaireapproprién’existepourtraiter l’exceptiondetypefloatlevée dans la fonction f. On recherche un gestionnaire approprié dans un bloc tryenglobant,cequiconduiticiàcatch(...).

345

Page 347: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre20Exercicesdesynthèse

346

Page 348: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice142

ÉnoncéRéaliser une classe nommée set_int permettant de manipuler des ensembles denombresentiers.Lenombremaximald’entiersquepourracontenirl’ensembleseraprécisé au constructeur qui allouera dynamiquement l’espace nécessaire. Onprévoiralesopérateurssuivants(edésigneunélémentdetypeset_intetnunentier:

•<<,telquee<<najoutel’élémentnàl'ensembleeþ;

•%,telquen%evale1sinappartientàeet0sinonþ;

•<<,telqueflot<<eenvoielecontenudel’ensembleesurleflotindiqué,souslaforme:

[entier1,entier2,...entiern]

Lafonctionmembrecardinalfourniralenombred’élémentsdel’ensemble.Enfin,ons’arrangera pour que l’affectation ou la transmission par valeur d’objets de typeset_intneposeaucunproblème(onaccepteraladuplicationcomplèted’objets).

N.B. Le chapitre 21 vous montrera comment résoudre cet exercice à l’aide descomposantsstandardintroduitsparlanorme,qu’ilnefautpaschercheràutiliserici.

Naturellement,notreclassecomportera,enmembresdonnée,lenombremaximal(nmax)d’éléments de l’ensemble, le nombre courant d’éléments (nelem) et un pointeur surl’emplacementcontenantlesvaleursdel’ensemble.

Comme notre classe comporte une partie dynamique, il est nécessaire, pour quel’affectationet la transmissionparvaleur sedéroulent convenablement,de surdéfinirl’opérateur d’affectation et demunir notre classe d’un constructeur par recopie. Lesdeuxfonctionsmembre(operator=etset_int )devrontprévoirune«copieprofonde»desobjets.Nousutiliseronspourcelauneméthodequenousavonsdéjàrencontréeetqui consiste à considérer que deux objets différents disposent systématiquement dedeuxpartiesdynamiquesdifférentes,mêmesiellespossèdentlemêmecontenu.

L’opérateur % doit être surdéfini obligatoirement sous la forme d’une fonction amie,puisquesonpremieropéranden’estpasde typeclasse.L’opérateurdesortiedansunflot doit, lui aussi, être surdéfini sous la forme d’une fonction amie, mais pour une

347

Page 349: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

raisondifférente:sonpremierargumentestdetypeostream.

Voiciladéclarationdenotreclasseset_int:/*fichierSETINT.H:déclarationdelaclasseset_int*/

#include<iostream>

usingnamespacestd;

classset_int

{int*adval;//adressedutableaudesvaleurs

intnmax;//nombremaxid'éléments

intnelem;//nombrecourantd'éléments

public:

set_int(int=20);//constructeur

set_int(set_int&);//constructeurparrecopie

//voirremarque1ci-après

set_int&operator=(set_int&);//opérateurd'affectation

//voirremarque2ci-après

~set_int();//destructeur

intcardinal();//cardinaldel'ensemble

set_int&operator<<(int);//ajoutd'unélément

friendintoperator%(int,set_int&);//appartenanced'unélément

//voirremarque3ci-après

//envoiensembledansunflot,voirremarque4

friendostream&operator<<(ostream&,set_int&);

};

Voici ce que pourrait être la définition de notre classe (les points délicats sontcommentésauseinmêmedesinstructions):

#include"setint.h"

#include<iostream>

usingnamespacestd;

/***************constructeur********************/

set_int::set_int(intdim)

{adval=newint[nmax=dim];//allocationtableaudevaleurs

nelem=0;

}

/******************destructeur******************/

set_int::~set_int()

{deleteadval;//libérationtableaudevaleurs

}

/**********constructeurparrecopie*************/

set_int::set_int(set_int&e)

{adval=newint[nmax=e.nmax];//allocationnouveautableau

nelem=e.nelem;

inti;

for(i=0;i<nelem;i++)//copieancientableaudansnouveau

adval[i]=e.adval[i];

}

/************opérateurd'affectation************/

set_int&set_int::operator=(set_int&e)//commentairesfaitpourb=a

{if(this!=&e)//onnefaitrienpoura=a

{deleteadval;//libérationpartiedynamiquedeb

adval=newint[nmax=e.nmax];//allocationnouvelensemblepoura

nelem=e.nelem;//danslequelonrecopie

inti;//entièrementl'ensembleb

for(i=0;i<nelem;i++)//avecsapartiedynamique

adval[i]=e.adval[i];

}

return*this;

}

/************fonctionmembrecardinal***********/

intset_int::cardinal()

{returnnelem;

348

Page 350: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

/************opérateurd'ajout<<***************/

set_int&set_int::operator<<(intnb)

{//onexaminesinbappartientdéjààl'ensemble

//enutilisantl'opérateur%

//s'iln'yappartientpas,ets'ilyaencoredelaplace

//onl'ajouteàl'ensemble

if(!(nb%*this)&&nelem<nmax)adval[nelem++]=nb;

return(*this);

}

/***********opérateurd'appartenance%**********/

intoperator%(intnb,set_int&e)

{inti=0;

//onexaminesinbappartientdéjààl'ensemble

//(danscecasivaudraneleenfindeboucle)

while((i<e.nelem)&&(e.adval[i]!=nb))i++;

return(i<e.nelem);

}

/******opérateur<<poursortiesurunflot*****/

ostream&operator<<(ostream&sortie,set_int&e)

{sortie<<"[";

inti;

for(i=0;i<e.nelem;i++)

sortie<<e.adval[i]<<"";

sortie<<"]";

returnsortie;

}

1. On pourrait ajouter le qualificatif const au constructeur par recopie, ce quiautoriserait l’initialisationd’unobjetparunobjet constant.Mais compte tenudespossibilitésd’utilisationdel’autreconstructeurdansdesconversionsimplicites,onautoriseraitdumêmecoup l’initialisationparunentierouun flottant, cequin’estguèresatisfaisant;onpourraitcependantinterdiredetellespossibilitésenutilisantlemot-cléexplicitdansladéclarationduconstructeur.

2.Latransmissiondelavaleurderetourdel’opérateurd’affectationn’estutilequesil’on souhaite permettre les affectations multiples. Il n’est pas indispensable detransmettre l’argument et la valeur de retour par référence, mais cela évite lesrecopies. On pourrait ici déclarer constant l’unique argument, ce qui autoriseraitl’utilisationd’unobjetconstantensecondopérandedel’affectation(moyennantunerecopie). Mais, du même coup, compte tenu des possibilités de conversionsimplicites,onautoriseraitégalementl’utilisationd’unentieroud’unflottant,cequin’est pas nécessairement souhaité ; là encore, ces possibilités pourraient êtreinterdites,moyennantl’utilisationappropriéedumot-cléexplicit.

3. On pourrait ajouter le qualificatif const à l’opérateur %, ce qui permettrait detravaillersurunensembleconstant;néanmoins,danscecas,ilyauraittransmissiond’unecopiedecetensembleàlafonction,cequin'estplustrèsefficace!

349

Page 351: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

4.Laremarqueprécédentes’appliqueàl’opérateur<<.

5.Notezqu’iciiln’estpaspossibled’agrandirl’ensembleau-delàdelalimitequiluiaétéimpartielorsdesaconstruction.Ilseraitassezfacilederemédieràcettelacuneenmodifiantsensiblementlafonctiond’ajoutd’unélément(operator<<).Ilsuffirait,en effet, qu’elle prévoie, lorsque la limite est atteinte, d’allouer un nouvelemplacement dynamique, par exemple d’une taille double de l’emplacementexistant, d’y recopier l’actuel contenu et de libérer l’ancien emplacement (enactualisantconvenablementlesmembresdonnéedel’objet).

Voici un exemple de programme utilisant la classe set_int, accompagné du résultatfourniparsonexécution:

/*************testdelaclasseset_int*********/

#include"setint.h"

#include<iostream>

usingnamespacestd;

main()

{voidfct(set_int);

voidfctref(set_int&);

set_intens;

cout<<"donnez10entiers\n";

inti,n;

for(i=0;i<10;i++)

{cin>>n;

ens<<n;

}

cout<<"ilya:"<<ens.cardinal()<<"entiersdifférents\n";

cout<<"quiformentl\'ensemble:"<<ens<<"\n";

fct(ens);

cout<<"auretourdefct,ilyena"<<ens.cardinal()<<"\n";

cout<<"quiformentl\'ensemble:"<<ens<<"\n";

fctref(ens);

cout<<"auretourdefctref,ilyena"<<ens.cardinal()<<"\n";

cout<<"quiformentl\'ensemble:"<<ens<<"\n";

cout<<"appartenancede-1:"<<-1%ens<<"\n";

cout<<"appartenancede500:"<<500%ens<<"\n";

set_intensa,ensb;

ensa=ensb=ens;

cout<<"ensemblea:"<<ensa<<"\n";

cout<<"ensembleb:"<<ensb<<"\n";

}

voidfct(set_inte)

{cout<<"ensemblereçuparfct:"<<e<<"\n";

e<<-1<<-2<<-3;

}

voidfctref(set_int&e)

{cout<<"ensemblereçuparfctref:"<<e<<"\n";

e<<-1<<-2<<-3;

}

donnez10entiers

3531851773

ilya:5entiersdifférents

quiformentl'ensemble:[35187]

ensemblereçuparfct:[35187]

350

Page 352: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

auretourdefct,ilyena5

quiformentl'ensemble:[35187]

ensemblereçuparfctref:[35187]

auretourdefctref,ilyena8

quiformentl'ensemble:[35187-1-2-3]

appartenancede-1:1

appartenancede500:0

ensemblea:[35187-1-2-3]

ensembleb:[35187-1-2-3]

351

Page 353: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice143

ÉnoncéCréeruneclassevectpermettantdemanipulerdes«vecteursdynamiques»d’entiers,c’est-à-diredestableauxd’entiersdontladimensionpeutêtredéfinieaumomentdeleurcréation(unetelleclasseadéjàétépartiellementréaliséedansl’exercice39).Cetteclassedevradisposerdesopérateurssuivants:

•[] pour l’accès à une des composantes du vecteur, et cela aussi bien au seind’uneexpressionqu’àgauched’uneaffectation(maiscettedernièresituationnedevrapasêtreautoriséesurdes«vecteursconstants»);

•==,telquesiv1etv2sontdeuxobjetsdetypevect,v1==v2prennelavaleur1siv1etv2sontdemêmedimensionetontlesmêmescomposantesetlavaleur0danslecascontraire;

•<<,telqueflot<<venvoielevecteurvsurleflotindiqué,souslaforme:<entier1,entier2,...,entiern>

Deplus,ons’arrangerapourquel’affectationetlatransmissionparvaleurd’objetsde type vect ne pose aucun problème ; pour ce faire, on acceptera de dupliquercomplètementlesobjetsconcernés.

N.B. Le chapitre 21 vous montrera comment résoudre cet exercice à l’aide descomposantsstandardintroduitsparlanorme,qu’ilnefautpaschercheràutiliserici.

Rappelons que lorsqu’on définit des objets constants, il n’est pas possible de leurappliquerunefonctionmembrepublique,saufsicettedernièreaétédéclaréeaveclequalificatifconst (auquel cas une telle fonctionpeut indifféremment être utilisée avecdesobjetsconstantsounonconstants).Pourobtenirl’effetdemandédel’opérateur[],lorsqu’il est appliqué à un vecteur constant, il est nécessaire d’en prévoir deuxdéfinitionsdontl’unes’appliqueauxvecteursconstants;pouréviterqu’onnepuisse,danscecas,l’utiliseràgauched’uneaffectation,ilestnécessairequ’ellerenvoiesonrésultat par valeur (et non par adresse comme le fera la fonction applicable auxvecteursnonconstants).

Voiciladéclarationdenotreclasse:#include<iostream>

352

Page 354: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

usingnamespacestd;

classvect

{intnelem;//nombredecomposantesduvecteur

int*adr;//pointeursurpartiedynamique

public:

vect(intn=1);//constructeur"usuel"

vect(vect&v);//constructeurparrecopie,

//voirremarque1ci-après

~vect();//destructeur

friendostream&operator<<(ostream&,vect&);//sortiesurunflot

vect&operator=(vect&v);//surdéfinitionopérateuraffectation

//voirremarque2ci-après

int&operator[](inti);//surdef[]pourvectnonconstants

intoperator[](inti)const;//surdef[]pourvectconstants

};

1. On pourrait ajouter le qualificatif const au constructeur par recopie, ce quiautoriserait l’initialisationd’unvecteurparunvecteurconstant.Maiscompte tenudes possibilités de l’autre constructeur dans des conversions implicites, onautoriseraitdumêmecoup l’initialisationparunentierouun flottant, cequin’estguèresatisfaisant;onpourraitcependantinterdiredetellespossibilitésenutilisantlemot-cléexplicitdansladéclarationduconstructeur.

2.Latransmissiondelavaleurderetourdel’opérateurd’affectationn’estutilequesil’on souhaite permettre les affectations multiples. Il n’est pas indispensable detransmettre l’argument et la valeur de retour par référence, mais cela évite lesrecopies. On pourrait ici déclarer constant l’unique argument, ce qui autoriseraitl’utilisationd’unobjetconstantensecondopérandedel’affectation(moyennantunerecopie). Mais, du même coup, compte tenu des possibilités de conversionsimplicites,onautoriseraitégalementl’utilisationd’unentieroud’unflottant,cequin’est pas nécessairement souhaité ; là encore, ces possibilités pourraient êtreinterdites,moyennantl’utilisationappropriéedumot-cléexplicit.

Voiciladéfinitiondesdifférentesfonctions:#include"vect.h"

#include<iostream>

usingnamespacestd;

vect::vect(intn)//constructeur"usuel"

{adr=newint[nelem=n];

}

vect::vect(vect&v)//constructeurparrecopie

{adr=newint[nelem=v.nelem];

inti;

for(i=0;i<nelem;i++)

adr[i]=v.adr[i];

}

vect::~vect()//destructeur

{deleteadr;

}

vect&vect::operator=(vect&v)//surdéfinitionopérateuraffectation

353

Page 355: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

{if(this!=&v)//onnefaitrienpoura=a

{deleteadr;

adr=newint[nelem=v.nelem];

inti;

for(i=0;i<nelem;i++)

adr[i]=v.adr[i];

}

return*this;

}

int&vect::operator[](inti)//surdéfinitionopérateur[]

{returnadr[i];

}

intvect::operator[](inti)const//surdéfinitionopérateur[]pourcst

{returnadr[i];

}

ostream&operator<<(ostream&sortie,vect&v)

{sortie<<"<";

inti;

for(i=0;i<v.nelem;i++)sortie<<v.adr[i]<<"";

sortie<<">";

returnsortie;

}

À titre indicatif, voici un petit programme utilisant la classe vect, accompagné durésultatfourniparsonexécution:

#include"vect.h"

#include<iostream>

usingnamespacestd;

main()

{inti;

vectv1(5),v2(10);

for(i=0;i<5;i++)v1[i]=i;

cout<<"v1="<<v1<<"\n";

for(i=0;i<10;i++)v2[i]=i*i;

cout<<"v2="<<v2<<"\n";

v1=v2;

cout<<"v1="<<v1<<"\n";

vectv3=v1;

cout<<"v3="<<v3<<"\n";

vectv4=v2;

cout<<"v4="<<v4<<"\n";

//constvectw(3);w[2]=5;//conduitbienàerreurcompilation

}

v1=<01234>

v2=<0149162536496481>

v1=<0149162536496481>

v3=<0149162536496481>

v4=<0149162536496481>

354

Page 356: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice144

ÉnoncéRéaliseruneclassenommée bit_array permettantdemanipulerdes tableauxdebits(autrementdit,destableauxdanslesquelschaqueélémentnepeutprendrequel’unedesdeuxvaleurs0ou1).Latailled’untableau(c’est-à-direlenombredebits)seradéfinielorsdesacréation(parunargumentpasséàsonconstructeur).Onprévoiralesopérateurssuivants:

•+=,telquet+=nmetteà1lebitderangndutableaut;

•-=,telquet-=nmetteà0lebitderangndutableaut;

•[],telquel'expressiont[i]fournisselavaleurdubitderangidutableaut(onne prévoira pas, ici, de pouvoir employer cet opérateur à gauche d'uneaffectation,commedanst[i]=...);

•++,telquet++metteà1touslesbitsdet;

•--,telquet--metteà0touslesbitsdet;

•<<,telqueflot<<tenvoielecontenudetsurleflotindiqué,souslaforme:<*bit1,bit2,...bitn*>

On fera en sorte que l’affectation et la transmission par valeur d’objets du typebit_arrayneposeaucunproblème.

N.B. Le chapitre 21 vous montrera comment résoudre cet exercice à l’aide descomposantsstandardintroduitsparlanorme,qu’ilnefautpaschercheràutiliserici.

Sil’onchercheàminimiserl’emplacementmémoireutilisépourlesobjetsdetypebit_array, il est nécessaire den’employer qu’un seul bit pour représenter un« élément »d’un tableau.Ces bits devront donc être regroupés, par exemple à raison de CHAR_BIT(définidanslimits.h)bitsparcaractère.

Manifestement, il faut prévoir que l’emplacement destiné à ces différents bits soitallouédynamiquementenfonctiondelavaleurfournieauconstructeur:pournbits,ilfaudran/CHAR_BIT+1caractères.

En membres donnée, il nous suffit de disposer d’un pointeur sur l’emplacement

355

Page 357: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

dynamique en question, ainsi que du nombre de bits du tableau. Pour simplifiercertainesdesfonctionsmembre,nousprévoironségalementdeconserverlenombredecaractèrescorrespondant.

Lesopérateurs+=,-=,++et--peuventêtredéfinis indifféremmentsous la formed’unefonctionmembreoud’unefonctionamie.Ici,nousavonschoisidesfonctionsmembre.L’énoncé ne précise rien quant au résultat fourni par ces 4 opérateurs. En fait, onpourrait prévoir qu’ils restituent le tableau après qu’ils y ont effectué l’opérationvoulue,maisenpratique,celasembledepeud’intérêt.Ici,nousavonsdoncsimplementprévuquecesopérateursnefourniraientaucunrésultat.

Pourque[]nesoitpasutilisabledansuneaffectationdelaformet[i]=...,ilsuffitdeprévoir qu’il fournisse son résultat par valeur (et non par référence comme on agénéralementl’habitudedelefaire).

Naturellement,iciencore,l’énoncénousimposedesurdéfinirl’opérateurd’affectationetdeprévoirunconstructeurparrecopie.

Voicicequepourraitêtreladéclarationdenotreclassebit_array:/*fichierbitarray.h:déclarationdelaclassebit_array*/

#include<iostream>

usingnamespacestd;

classbit_array

{intnbits;//nombrecourantdebitsdutableau

intncar;//nombredecaractèresnécessaires(redondant)

char*adb;//adressedel'emplacementcontenantlesbits

public:

bit_array(int=16);//constructeurusuel

bit_array(bit_array&);//constructeurparrecopie,

//voirremarque1ci-après

~bit_array();//destructeur

//lesopérateursbinaires

bit_array&operator=(bit_array&);//affectation,

//voirremarque2ci-après

intoperator[](int);//valeurd'unbit

voidoperator+=(int);//activationd'unbit

voidoperator-=(int);//désactivationd'unbit

//envoisurflot

friendostream&operator<<(ostream&,bit_array&);

//lesopérateursunaires

voidoperator++();//miseà1

voidoperator--();//miseà0

voidoperator~();//complémentà1

};

1. On pourrait ajouter le qualificatif const au constructeur par recopie, ce quiautoriserait l’initialisationd’unobjet bit_array par un objet constant.Mais comptetenu des possibilités de conversions implicites, on autoriserait du même coupl’initialisation par un entier ou par un flottant, ce qui n’est guère satisfaisant ; on

356

Page 358: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

pourraitcependantinterdiredetellespossibilitésenutilisantlemot-cléexplicitdansleconstructeuràunargumentdetypeint.

2.Laprésenced’unevaleurderetourdansl’opérateurd’affectationn’estutilequepourpermettre les affectations multiples. La transmission par référence de l’uniqueargumentn'estpasobligatoire.Onpourraitajouterlequalificatifconstpourautoriserl’affectation d’un objet constant (moyennant alors une recopie). Mais, du mêmecoup, compte tenu des possibilités de conversions implicites, on autoriseraitégalement l’utilisationd’unentieroud’unflottant,cequin’estpasnécessairementsatisfaisant ; là encore, ces possibilités pourraient être interdites, moyennantl’utilisationappropriéedumot-cléexplicit.

Voiciladéfinitiondesdifférentesfonctions.#include"bitarray.h"

#include<climits>

#include<iostream>

usingnamespacestd;

bit_array::bit_array(intnb)

{nbits=nb;

ncar=nbits/CHAR_BIT+1;

adb=newchar[ncar];

inti;

for(i=0;i<ncar;i++)adb[i]=0;//raz

}

bit_array::bit_array(bit_array&t)

{nbits=t.nbits;ncar=t.ncar;

adb=newchar[ncar];

inti;

for(i=0;i<ncar;i++)adb[i]=t.adb[i];

}

bit_array::~bit_array()

{deleteadb;

}

bit_array&bit_array::operator=(bit_array&t)

{if(this!=&t)//onnefaitrienpourt=t

{deleteadb;

nbits=t.nbits;ncar=t.ncar;

adb=newchar[ncar];

inti;

for(i=0;i<ncar;i++)

adb[i]=t.adb[i];

}

return*this;

}

intbit_array::operator[](inti)

{//lebitderangis'obtientenconsidérantlebit

//derangi%CHAR_BITducaractèrederangi/CHAR_BIT

intcarpos=i/CHAR_BIT;

intbitpos=i%CHAR_BIT;

return(adb[carpos]>>CHAR_BIT-bitpos-1)&0x01;

}

voidbit_array::operator+=(inti)

{intcarpos=i/CHAR_BIT;

if(carpos<0||carpos>=ncar)return;//protection

intbitpos=i%CHAR_BIT;

adb[carpos]|=(1<<(CHAR_BIT-bitpos-1));

357

Page 359: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

voidbit_array::operator-=(inti)

{intcarpos=i/CHAR_BIT;

if(carpos<0||carpos>=ncar)return;//protection

intbitpos=i%CHAR_BIT;

adb[carpos]&=~(1<<CHAR_BIT-bitpos-1);

}

ostream&operator<<(ostream&sortie,bit_array&t)

{sortie<<"<*";

inti;

for(i=0;i<t.nbits;i++)

sortie<<t[i]<<"";

sortie<<"*>";

returnsortie;

}

voidbit_array::operator++()

{inti;

for(i=0;i<ncar;i++)adb[i]=0xFFFF;

}

voidbit_array::operator--()

{inti;

for(i=0;i<ncar;i++)adb[i]=0;

}

voidbit_array::operator~()

{inti;

for(i=0;i<ncar;i++)adb[i]=~adb[i];

}

Voiciunprogrammed’essaide la classe bit_array, accompagné du résultat fourni parsonexécution:

/*programmed'essaidelaclassebit_array*/

main()

{bit_arrayt1(34);

cout<<"t1="<<t1<<"\n";

t1+=3;t1+=0;t1+=8;t1+=15;t1+=33;

cout<<"t1="<<t1<<"\n";

t1--;

cout<<"t1="<<t1<<"\n";

t1++;

cout<<"t1="<<t1<<"\n";

t1-=0;t1-=3;t1-=8;t1-=15;t1-=33;

cout<<"t1="<<t1<<"\n";

cout<<"t1="<<t1<<"\n";

bit_arrayt2(11),t3(17);

cout<<"t2="<<t2<<"\n";

t2=t3=t1;

cout<<"t3="<<t3<<"\n";

}

t1=<*00000000000000000000000000000000

00*>

t1=<*10010000100000010000000000000000

01*>

t1=<*00000000000000000000000000000000

00*>

t1=<*11111111111111111111111111111111

11*>

t1=<*01101111011111101111111111111111

10*>

t1=<*01101111011111101111111111111111

10*>

358

Page 360: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

t2=<*00000000000*>

t3=<*01101111011111101111111111111111

10*>

359

Page 361: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice145

ÉnoncéLacapacitédesnombresentiersest limitéepar la tailledu type longint.Créer uneclasse big_int permettant demanipuler des nombres entiers de valeur absolumentquelconque.

Pour ne pas alourdir l’exercice, on se limitera à des nombres sans signe et àl’opération d’addition ; on s’arrangera toutefois pour que des expressions mixtes(c’est-à-diremélangeantdesobjetsdetypelong_intavecdesentiersusuels)aientunsens.

Ondéfiniral’opérateur<<pourqu’ilpermetted’envoyerunobjetdetypebig_int surun flot. Parmi les différents constructeurs, on en prévoira un avec un argument detypechaînedecaractères,correspondantauxchiffresd’un«grandentier».

On fera en sorte que l’affectation et la transmission par valeur d’objets de typebig_intneposentaucunproblème.

Pour représenterun«grandentier», ladémarche laplusnaturelle (maispas lapluséconomiqueenplacemémoire!)consisteàconserverlenombresousformedécimale,àchaquechiffreétantassociéuncaractère.Pourcefaire,onpeutchoisirde«coder»un telchiffrepar lecaractèrecorrespondant(‘0’pour0,‘1’pour1…);onpeutaussichoisir de placer une valeur égale au chiffre lui-même (0 pour 0, 1 pour 1…). Ladernièresolutionobligeàeffecteurun« transcodage» lorsquel’ondoitpasserdelaforme chaîne de caractères à la forme big_int (dans le constructeur correspondant,notamment)ou,inversement,lorsqu’ondoitpasserdelaformebig_intàlaformesuitedecaractères(pourl’affichage).Enrevanche,ellesimplifiequelquepeul’algorithmed’addition,etc’estellequenousavonschoisie.

L’emplacementpermettantdeconserverungrandentierseraallouédynamiquement;sataille sera, naturellement, adaptée à la valeur du nombre qui s’y trouvera. Onconserveraégalement lenombrecourantdechiffresde l’entier ;onpourrait,en touterigueur, s’en passer mais nous verrons que sa présence simplifie quelque peu laprogrammation. En ce qui concerne l’ordre de rangement des chiffres au sein del’emplacementcorrespondant,ilyamanifestementdeuxpossibilités.Chacunepossèdedesavantagesetdesinconvénients ;nousavonsicichoisiderangerleschiffresdans

360

Page 362: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

l’ordreinversedeceluioùonlesécrit(unités,dizaines,centaines...).

Pourpouvoiraccepterlesexpressionsmixtes,ondisposedeplusieurssolutions:

soitsurdéfinirl’opérateur+pourtouslescaspossibles;

soit surdéfinir + uniquement lorsqu’il porte sur des grands entiers et prévoir unconstructeur recevant un argument de type unsigned long ; il permettra ainsi laconversionenbig_intden'importequeltypenumérique.

C’est la seconde solution que nous avons adoptée. Notez toutefois que, si elle a lemérite d’être la plus simple à programmer, elle n’est pas la plus efficace en tempsd’exécution.

Parailleurs,pourquelesconversionsenvisagéess’appliquentaupremieropérandedel’addition,ilestnécessairedesurdéfinirl’opérateur+commeunefonctionamie.

Voiciladéclarationdenotreclassebig_int(laprésenced’unconstructeurprivéàdeuxargumentsentiersserajustifiéeunpeuplusloin):

/*fichierbigint.h:déclarationdelaclassebig_int*/

#defineNCHIFMAX32//nombremaxidechiffresd'unentier(dépendde

//l'implémentation)

#include<iostream>

usingnamespacestd;

classbig_int

{intnchif;//nombredechiffres

char*adchif;//adresseemplacementcontenantleschiffres

big_int(int,int);//constructeurprivé(àusageinterne)

public:

big_int(unsignedlong=0);//constructeuràpartird'unnombreusuel

big_int(char*);//constructeuràpartird'unechaîne

big_int(big_int&);//constructeurparrecopie,voirremarque1

big_int&operator=(constbig_int&);//affectation,

//voirremarque2

friendbig_intoperator+(constbig_int&,constbig_int&);

//opérateur+,

//voirremarque3

friendostream&operator<<(ostream&,big_int&);//opérateur<<

};

1.Onpourraitajouterlequalificatifconstauconstructeurparrecopie,cequiauraitpoureffet d’autoriser l’initialisation d’un grand entier par un grand entier constant ouencore(comptetenudespossibilitésdeconversionimplicitedel’opérande)parunentier,voireunflottant(parconversionenentier).

2. La transmission par référence du résultat de l’opérateur d’affectation autorise lesaffectationsmultiples.Latransmissionparréférencedel’uniqueargumentn’estpasobligatoire. On a ajouté le qualificatif const pour autoriser l’affectation d’uneexpression (notamment la somme de deux grands entiers) ; compte tenu des

361

Page 363: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

possibilités de conversions implicites, cela autorise du même coup l’affectationd’unentier,d’unflottant(parconversionenentier)oud’unpointeurdetypechar*(encore faut-il, comme ici, ne pas avoir utilisé le mot-clé explicit dans lesconstructeurscorrespondants).

3. Pour les arguments de l’opérateur +, le qualificatif const est indispensable si l’onsouhaitebénéficierdesconversionsimplicitesdesopérandes,notammentd’unentierenungrandentier.Ducoup,comptetenudespossibilitésdeconversionsimplicitesdes opérandes, on autorise également l’utilisation de flottants ou de pointeurs detypechar*.

L’opérateur + commence par créer un emplacement temporaire pouvant recevoir unnombrecomportantunchiffredeplusqueleplusgranddesesdeuxopérandes(onnesaitpasencorecombiendechiffrescomporteraexactementlerésultat).Onycalculelasommesuivantunalgorithmecalquésurleprocessusmanueld’addition.

Puisoncréeunobjetdetypebig_intenutilisantunconstructeurparticulier:big_int(int,int). En fait, nous avons besoin d’un constructeur créant un big_int comportant unnombre de chiffres donné, ce dont nous ne disposons pas dans les constructeurspublics.Deplus,nousnepouvonspasutiliserunconstructeurdelaformebig_int(int)car,alors,lesadditionsmixtesfaisantintervenirdesentierschercheraientàl’employerpoureffectueruneconversion!C’estpourquoinousavonsprévuunconstructeuràdeuxarguments,lesecondétantfictif;deplus,nousl’avonsrenduprivé,cariln’anullementbesoind’êtreaccessibleàunutilisateurdelaclasse.

Voiciladéfinitiondesfonctionsdelaclassebig_int:/*définitiondesfonctionsdelaclassebig_int*/

#include<string.h>

#include"bigint.h"

#include<iostream>

usingnamespacestd;

big_int::big_int(intn,intp)//l'argumentpestfictif

{nchif=n;

adchif=newchar[nchif];

}

big_int::big_int(char*ch)

{

nchif=strlen(ch);

adchif=newchar[nchif];

inti;charc;

for(i=0;i<nchif;i++)

{c=ch[i]-'0';

if(c<0||c>9)c=0;//précaution

adchif[nchif-i-1]=c;//attentionàl'ordredeschiffres!

}

}

big_int::big_int(unsignedlongn)

{//oncréelegrandentiercorrespondantdansunemplacementtemporaire

362

Page 364: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

char*adtemp=newchar[NCHIFMAX];

inti=0;

while(n)

{adtemp[i++]=n%10;

n/=10;

}

//iciicontientlenombreexactdechiffres

nchif=i;

adchif=newchar[nchif];

for(i=0;i<nchif;i++)

adchif[i]=adtemp[i];

//onlibèrel'emplacementtemporaire

deleteadtemp;

}

big_int::big_int(big_int&n)

{nchif=n.nchif;

adchif=newchar[nchif];

inti;

for(i=0;i<nchif;i++)

adchif[i]=n.adchif[i];

}

big_int&big_int::operator=(constbig_int&n)

{if(this!=&n)

{deleteadchif;

nchif=n.nchif;

adchif=newchar[nchif];

inti;

for(i=0;i<nchif;i++)

adchif[i]=n.adchif[i];

}

return*this;

}

big_intoperator+(constbig_int&n,constbig_int&p)//voirremarque

{intnchifmax=(n.nchif>p.nchif)?n.nchif:p.nchif;

intncar=nchifmax+1;

//préparationdurésultatdanszonetemporairedetaillencar

char*adtemp=newchar[ncar];

inti,s,chif1,chif2;

intret=0;

for(i=0;i<nchifmax;i++)

{chif1=(i<n.nchif)?n.adchif[i]:0;

chif2=(i<p.nchif)?p.adchif[i]:0;

s=chif1+chif2+ret;

if(s>=10){s-=10;

ret=1;

}

elseret=0;

adtemp[i]=s;

}

if(ret==1)adtemp[ncar-1]=1;

elsencar--;

//constructiond'unobjetdetypebig_intoùl'onrecopielerésultat

big_intres(ncar,0);//secondargumentfictif

res.nchif=ncar;

for(i=0;i<ncar;i++)

res.adchif[i]=adtemp[i];

deleteadtemp;

returnres;

}

ostream&operator<<(ostream&sortie,big_int&n)

{inti;

for(i=n.nchif-1;i>=0;i--)//attentionàl'ordre!

sortie<<(int)n.adchif[i];

363

Page 365: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

returnsortie;

}

Certainscompilateursimposentl’attributconstpourlesargumentsdeoperator+.

Voici un petit programme d’utilisation de la classe big_int, accompagné du résultatfourniparsonexécution:

/*programmed'essai*/

#include"bigint.h"

#include<iostream>

usingnamespacestd;

main()

{big_intn1(12);big_intn2(35);big_intn3;

n3=n1+n2;

cout<<n1<<"+"<<n2<<"="<<n3<<"\n";

big_intn4("1234567890123456789"),n5("9876543210987654321"),n6;

n6=n4+n5;

cout<<n4<<"+"<<n5<<"="<<n6<<"\n";

cout<<n6<<"+"<<n1<<"="<<n6+n1<<"\n";

n2=n4+5;//seraitrejetéesioperator=n'avaitpasargumentconst

cout<<n4<<"+5="<<n2<<"\n";

//iciuneexpressioncommen1+"123"seraitcorrecteetdetypebig_int

//iciuneexpressioncommen2+5.69seraitcorrecteetdetypebig_int

}

12+35=47

1234567890123456789+9876543210987654321=11111111101111111110

11111111101111111110+12=11111111101111111122

1234567890123456789+5=1234567890123456794

364

Page 366: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice146

ÉnoncéCréerunpatrondeclassesnomméstack_gene,permettantdemanipulerdespilesdontles éléments sont de type quelconque. Ces derniers seront conservés dans unemplacementallouédynamiquementetdontladimensionserafournieauconstructeur(il ne s’agira donc pas d’un paramètre expression du patron). La classe devracomporterlesopérateurssuivants:

•<<,telquep<<najoutel'élémentnàlapilep(silapileestpleine,ilnesepasserarien);

•>>,telquep>>nplacedansnlavaleurduhautdelapilep,enlasupprimantdelapile(silapileestvide,ilnesepasserarien);

•++,telque++pvale1silapilepestpleineet0danslecascontraire;

•--,telque--pvale1silapilepestvideet0danslecascontraire;

•<<,telque,flotétantunflotdesortie,flot<<paffichelecontenudelapilepsurleflotsouslaforme://valeur_1valeur_2...valeur_n//.

On supposera que les objets de type stack_gene ne seront jamais soumis à destransmissionsparvaleurouàdesaffectations;onnechercheradoncpasàsurdéfinirleconstructeurparrecopieoul’opérateurd’affectation.

Enfait,onpeuts’inspirerdecequiaétéfaitdansl’exercice93pourréaliserunepiled’entiersenfaisantensortequeintsoitremplacéparunparamètredetype.

Voicicequepourraitêtreladéfinitiondenotrepatrondeclasses:#include<stdlib.h>

#include<iostream>//voirN.B.duparagrapheNouvellespossibilités

//d'entrées-sortiesduchapitre2

usingnamespacestd;

template<classT>classstack_gene

{intnmax;//nombremaximumdelavaleurdelapile

intnelem;//nombrecourantdevaleursdelapile

T*adv;//pointeursurlesvaleurs

public:

stack_gene(int=20);//constructeur

~stack_gene();//destructeur

stack_gene&operator<<(T);//opérateurd'empilage

stack_gene&operator>>(T&);//opérateurdedépilage

365

Page 367: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

//(attentionT&)

intoperator++();//opérateurdetestpilepleine

intoperator--();//opérateurdetestpilevide

//opérateur<<pourflotdesortie

friendostream&operator<<(ostream&,stack_gene<T>&);

};

template<classT>stack_gene<T>::stack_gene(intn)

{nmax=n;

adv=newT[nmax];

nelem=0;

}

template<classT>stack_gene<T>::~stack_gene()

{deleteadv;

}

template<classT>stack_gene<T>&stack_gene<T>::operator<<(Tn)

{if(nelem<nmax)adv[nelem++]=n;

return(*this);

}

template<classT>stack_gene<T>&stack_gene<T>::operator>>(T&n)

{if(nelem>0)n=adv[--nelem];

return(*this);

}

template<classT>intstack_gene<T>::operator++()

{return(nelem==nmax);

}

template<classT>intstack_gene<T>::operator--()

{return(nelem==0);

}

template<classT>ostream&operator<<(ostream&sortie,stack_gene<T>&p)

{sortie<<"//";

inti;

for(i=0;i<p.nelem;i++)sortie<<p.adv[i]<<"";

sortie<<"//";

returnsortie;

}

Àtitreindicatif,voiciunpetitprogrammed’utilisationdenotrepatrondeclasses(dontonsupposequeladéfinitionfiguredansstackg.h).Ilestaccompagnédurésultatfourniparsonexécution.

/************programmed'essaidestack_gene*********/

#include"stackg.h"

#include<iostream>//voirN.B.duparagrapheNouvellespossibilités

//d'entrées-sortiesduchapitre2

usingnamespacestd;

main()

{stack_gene<int>pi(20);//pilede20entiersmaxi

cout<<"pipleine:"<<++pi<<"vide:"<<--pi<<"\n";

pi<<2<<3<<12;

cout<<"pi="<<pi<<"\n";

stack_gene<float>pf(10);//pilede10flottantsmaxi

pf<<3.5<<4.25<<2;//2seraconvertienfloat

cout<<"pf="<<pf<<"\n";

floatx;pf>>x;

cout<<"hautdelapilepf="<<x;

cout<<"pf="<<pf<<"\n";

}

pipleine:0vide:1

pi=//2312//

366

Page 368: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

pf=//3.54.252//

hautdelapilepf=2pf=//3.54.25//

367

Page 369: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice147

ÉnoncéEns’inspirantdel’exercice143,créerunpatrondeclassespermettantdemanipulerdesvecteursdynamiquesdontlesélémentssontdetypequelconque.

Voiciladéclarationdenotrepatron:#include<iostream>//voirN.B.duparagrapheNouvellespossibilités

//d'entrées-sortiesduchapitre2

usingnamespacestd;

template<classT>classvect

{intnelem;//nombredecomposantesduvecteur

T*adr;//pointeursurpartiedynamique

public:

vect(intn=1);//constructeur"usuel"

vect(vect&v);//constructeurparrecopie

~vect();//destructeur

friendostream&operator<<(ostream&,vect<T>&);

vect<T>operator=(vect<T>&v);//surdéfinitionopérateuraffectation

T&operator[](inti);//surdef[]pourvectnonconstants

Toperator[](inti)const;//surdef[]pourvectconstants

};

Voiciladéfinitiondesdifférentesfonctionsmembre:#include"vectgen.h"

#include<iostream>

usingnamespacestd;

template<classT>vect<T>::vect(intn)//constructeur"usuel"

{adr=newT[nelem=n];

}

template<classT>vect<T>::vect(vect<T>&v)//constructeurparrecopie

{adr=newT[nelem=v.nelem];

inti;

for(i=0;i<nelem;i++)

adr[i]=v.adr[i];

}

template<classT>vect<T>::~vect()

{deleteadr;

}

template<classT>vect<T>vect<T>::operator=(vect<T>&v)

{if(this!=&v)//onnefaitrienpoura=a

{deleteadr;

adr=newT[nelem=v.nelem];

inti;

for(i=0;i<nelem;i++)

adr[i]=v.adr[i];

}

return*this;

}

template<classT>T&vect<T>::operator[](inti)

{returnadr[i];

368

Page 370: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

template<classT>Tvect<T>::operator[](inti)const

{returnadr[i];

}

template<classT>ostream&operator<<(ostream&sortie,vect<T>&v)

{sortie<<"<";

inti;

for(i=0;i<v.nelem;i++)sortie<<v.adr[i]<<"";

sortie<<">";

returnsortie;

}

Àtitreindicatif,voiciunpetitprogrammeutilisantnotrepatron,accompagnédurésultatfourniparsonexécution:

#include"vectgen.h"

#include<iostream>

usingnamespacestd;

main()

{inti;

vect<int>v1(5);vect<int>v2(10);

for(i=0;i<5;i++)v1[i]=i;

cout<<"v1="<<v1<<"\n";

for(i=0;i<10;i++)v2[i]=i*i;

cout<<"v2="<<v2<<"\n";

v1=v2;

cout<<"v1="<<v1<<"\n";

vect<int>v3=v1;

//vect<double>v3=v1;//seraitrejeté

cout<<"v3="<<v3<<"\n";

//constvect<float>w(3);w[2]=5;//conduitbienàerreurcompilation

//vect<float>v4(5);v4=v1;//conduitbienàerreurcompilation

}

v1=<01234>

v2=<0149162536496481>

v1=<0149162536496481>

v3=<0149162536496481>

369

Page 371: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Chapitre21Lescomposantsstandard

LesexercicesdesprécédentschapitresontétévolontairementrésolussansrecourirauxcomposantsstandardintroduitsparlanormedeC++.Manifestement,l’existencedecescomposants influe sur certains des exercices, soit en rendant leur solution quasimenttriviale,soitenlasimplifiantnotablement.C’estcequenousproposonsd’examinerici.Pour faciliter les choses, nous avons systématiquement reproduit le texte intégral del’énoncécorrespondant,avecsonanciennenumérotation.

Notezque, contrairement auxautres chapitres, celui-cin’apas étédotéd’un résumé.D’une part, l’ampleur de la bibliothèque de composants standard l’aurait rendurelativementvolumineux(onentrouverauneétudedétailléedansl’undenosouvragesconsacrésaulangageC++etpubliéségalementauxÉditionsEyrolles).D’autrepart,ilneconstituepasàproprementparlerunensemblecompletd’exercicessurlesujet.

370

Page 372: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice148(67revisité)

AncienénoncéRéaliser une classe nommée set_char permettant de manipuler des ensembles decaractères.Ondevrapouvoirréalisersuruntelensemblelesopérationsclassiquessuivantes : lui ajouter un nouvel élément, connaître son « cardinal » (nombred’éléments),savoirsiuncaractèredonnéluiappartient.

Ici,onn’effectueraaucuneallocationdynamiqued’emplacementsmémoire.Ilfaudradoncprévoir,enmembredonnée,untableaudetaillefixe.

Écrire,enoutre,unprogramme(main)utilisant laclasse set_char pourdéterminer lenombredecaractèresdifférentscontenusdansunmotluendonnée.

CommentairesLeconteneurset<char>peutjouerlerôledelaclassedemandéeset_char,àconditiondesupprimer de l’énoncé la contrainte relative à l’absence d’allocation dynamique. Eneffet,ellen’aplusderaisond’être,lesfonctionsmembredelaclasseset<char>allouantautomatiquementlaplacenécessaireaufuretàmesuredesbesoins.

Voici ce que pourrait devenir le programme de test fourni précédemment dans lechapitre 3. On notera que la fonction membre size fournit le nombre d’éléments del’ensemble, tandisque la fonctionmembreinsertpermet toutnaturellement l’insertiond’un élément.Quant à la fonction count, elle fournit le nombre d’éléments de valeurdonnéefigurantdansl’ensemble;sonrésultatestdonctoujourssoit0,soit1.

#include<iostream>

#include<set>//pourlaclasseset

usingnamespacestd;

main()

{set<char>ens;//voirremarque1ci-après

charmot[81];

cout<<"donnezunmot:";

cin>>mot;

inti;

for(i=0;i<strlen(mot);i++)ens.insert(mot[i]);

cout<<"ilcontient"<<ens.size()<<"caracteresdifferents\n";

if(ens.count('e'))cout<<"lecaractereeestpresent\n";

elsecout<<"lecaractereen'estpaspresent\n";

}

donnezunmot:bonjour

ilcontient6caracteresdifferents

371

Page 373: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

lecaractereen'estpaspresent

1.Certainscompilateursimposentlaprésenced’unsecondparamètredetypeprécisantlarelationd’ordreutiliséepourordonnerl’ensemble.Danscecas,ilfaudraécrire:set<char,less<char>>ens.

2. Ilexisteunautreconteneur,multiset, semblableàset,dans lequelunemêmevaleurpeut apparaître plusieurs fois. Il ne correspond plus à la notion mathématiqued’ensemble. On notera que la fonctionmembre count, présente également dans ceconteneur,voitalorssonnomnettementplusjustifiéquedanslecasdeset.

372

Page 374: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice149(68revisité)

AncienénoncéModifierlaclasseset_charprécédente,demanièreàdisposerdecequel’onnommeun«itérateur»sur lesdifférentsélémentsdel’ensemble.Ils’agitd’unmécanismepermettant d’accéder séquentiellement aux différents éléments. On prévoira troisnouvellesfonctionsmembre:init,quiinitialiseleprocessusd’exploration;prochain,quifournitl’élémentsuivantlorsqu’ilexisteetexiste,quiprécises’ilexisteencoreunélémentnonexploré.

Oncompléteraalorsleprogrammed’utilisationprécédent,demanièrequ’ilaffichelesdifférentscaractèrescontenusdanslemotfourniendonnée.

CommentairesIci encore,onpeututiliser le composant standard set<char> qui disposed’un itérateurintégréset<char>::iterator.Bien entendu, il n’y a plus de raison d’imposer l’existencedes fonctions init,prochain et existe. Les fonctionsmembre begin et end fournissent lesvaleursinitialesetfinalesàutiliserpourexplorerl’ensembleàl’aided’untelitérateur.Onprendragardeau faitque end pointenonpas sur le dernier élémentdu conteneur,maisjusteaprès.L’avancementdel’itérateurs’obtientparl’opérateur++.

Voicicequepourraitdevenir leprogrammede test fourniprécédemment.Lanotation*iecorrespondàl’élémentdésignéparlavaleurcourantedel’itérateurie.

#include<iostream>

#include<string.h>

#include<set>

usingnamespacestd;

main()

{set<char>ens;//voirremarque1ci-après

charmot[81];

cout<<"donnezunmot:";

cin>>mot;

inti;

for(i=0;i<strlen(mot);i++)ens.insert(mot[i]);

cout<<"ilcontient"<<ens.size()<<"caracteresdifferents"

<<"quisont:\n";

set<char>::iteratorie;//iterateursurunensembledecaracteres

for(ie=ens.begin();ie!=ens.end();ie++)

cout<<*ie<<"";

}

donnezunmot:bonjour

373

Page 375: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

ilcontient6caracteresdifferentsquisont:

bjnoru

1.Certainscompilateursimposentlaprésenced’unsecondparamètredetypeprécisantlarelationd’ordreutiliséepourordonnerl’ensemble.Danscecas,ilfaudraécrire:set<char,less<char>>ens.

2. Il existe plusieurs sortes d’itérateurs. Les plus courants sont les itérateursbidirectionnelsquipeuventêtreincrémentés(++)oudécrémentés(--)d’unepositionàlafois,et lesitérateursàaccèsdirectquipermettentl’accèsdirectàunélémentquelconque. Tous les conteneurs disposent des fonctions membre begin et end

permettantleurparcoursparunitérateurbidirectionnel.

374

Page 376: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice150(77revisité)

Ancienénoncé

1. Réaliser une classe nommée set_int permettant de manipuler des ensembles denombres entiers. On devra pouvoir réaliser sur un tel ensemble les opérationsclassiques suivantes : lui ajouter un nouvel élément, connaître son cardinal(nombred’éléments),savoirsiunentierdonnéluiappartient.

Ici, on conservera les différents éléments de l’ensemble dans un tableau allouédynamiquement par le constructeur.Un argument (auquel on pourra prévoir unevaleurpardéfaut)luipréciseralenombremaximald’élémentsdel’ensemble.

2.Écrire,enoutre,unprogramme(main)utilisantlaclasseset_intpourdéterminerlenombred’entiersdifférentscontenusdansuntableaud’entierslusendonnées.

3. Que faudrait-il faire pour qu’un objet du type set_int puisse être transmis parvaleur, soit comme argument d’appel, soit comme valeur de retour d’unefonction?

CommentairesOnpeututiliserlecomposantset<int>,àconditiondenepluspréciserqu’onconservelesélémentsdansuntableaudynamique.Laquestion3n’aplusderaisond’êtrecarlaclassesetdisposed’unconstructeurparrecopie.

Voicicequedevientl’exempledeprogrammedemandédanslasecondequestion:#include<iostream>

#include<set>//pourlaclasseset

usingnamespacestd;

main()

{set<int>ens;//voirremarqueci-après

cout<<"donnez20entiers\n";

inti,n;

for(i=0;i<20;i++)

{cin>>n;

ens.insert(n);

}

cout<<"ilya:"<<ens.size()<<"entiersdifferents\n";

}

375

Page 377: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Certainscompilateursimposentlaprésenced’unsecondparamètredetypeprécisantlarelation d’ordre utilisée pour ordonner l’ensemble. Dans ce cas, il faudra écrire :set<char,less<char>>ens.

376

Page 378: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice151(78revisité)

N.B.Cetexercicen’estrappeléiciqueparcequ’ilestutileàl’énoncésuivant.

Ancienénoncé

Modifier l’implémentation de la classe précédente (avec son constructeur parrecopie)defaçonquel’ensembled’entierssoitmaintenantreprésentéparune listechaînée (chaqueentierest rangédansunestructurecomportantunchampdestinéàcontenir un nombre et un champ destiné à contenir un pointeur sur la structuresuivante).L’interfacedelaclasse(lapartiepubliquedesadéclaration)devraresterinchangée, ce qui signifie qu’un client de la classe continuera à l’employer de lamêmefaçon.

CommentairesIl s’agit ici d’une demande de modification d’implémentation d’une classe qui n’amanifestementaucunsensdanslecasd’uncomposantstandard.

377

Page 379: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice152(79revisité)

AncienénoncéModifierlaclasseset_intprécédente(implémentéesouslaformed’unelistechaînée,avec ou sans son constructeur par recopie) pour qu’elle dispose de ce que l’onnommeun« itérateur» sur lesdifférents élémentsde l’ensemble.Rappelonsqu’ils’agitd’unmécanismepermettantd’accéderséquentiellementauxdifférentsélémentsdel’ensemble.Onprévoiratroisnouvellesfonctionsmembre:init,pourinitialiserleprocessusd’itération ; prochain, pour fournir l’élément suivant lorsqu’il existe etexiste,pourtesters’ilexisteencoreunélémentnonexploré.

On complétera alors le programme d’utilisation précédent (en fait, celui del’exercice 26), de manière qu’il affiche les différents entiers contenus dans lesvaleursfourniesendonnée.

CommentairesIciencore,iln’yaaucuneraisondevouloirmodifierl’implémentationd’uncomposantstandard.Voici cequepourrait devenir l’exempledeprogrammed’utilisation si l’onutilisaitlecomposantset<int>etl’itérateurassociéset<int>::iterator:

#include<iostream>

#include<set>//pourlaclasseset

usingnamespacestd;

main()

{set<int>ens;

cout<<"donnez20entiers\n";

inti,n;

for(i=0;i<20;i++)

{cin>>n;

ens.insert(n);

}

cout<<"ilya:"<<ens.size()<<"entiersdifferents\n";

cout<<"Cesont:\n";

set<int>::iteratoris;

for(is=ens.begin();is!=ens.end();is++)

cout<<*is<<"";

}

378

Page 380: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice153(90revisité)

AncienénoncéDéfiniruneclassevectpermettantdereprésenterdes«vecteursdynamiques»,c’est-à-dire dont la dimension peut ne pas être connue lors de la compilation. Plusprécisément, on prévoira de déclarer de tels vecteurs par une instruction de laforme:

vectt(exp);

danslaquelleexpdésigneuneexpressionquelconque(detypeentier).

Ondéfinira,defaçonappropriée,l’opérateur[]demanièrequ’ilpermetted’accéderà des éléments d’un objet d’un type vect comme on le ferait avec un tableauclassique.

Onnechercherapasàrésoudrelesproblèmesposéséventuellementparl’affectationoulatransmissionparvaleurd’objetsdetypevect.Enrevanche,ons’arrangerapourqu’aucunrisquede«débordement»d’indicen’existe.

CommentairesLaclassevector<int> fait l’affaire,ycomprispour l’affectationou la transmissionparvaleur.Elledisposed’un itérateuràaccèsdirect (nommétoujoursiterator).Mais, deplus,l’opérateur[]yestsurdéfinidesortequ’ilfournituneécritureplusconcisepourl’accès direct à un élément. Si v est un objet de type vector<int>, la notation v[i] estéquivalenteà*(v.begin()+i).

Toutefois,cetopérateur[]n’estpasprotégécontrelesdébordementsd’indice.Onpeutrésoudreleproblèmeenutilisant,àsaplace,lafonctionmembreatquidéclencheuneexceptionstandardout_of_rangeencasdedébordementd’indice.Sil’onveutabsolumentseteniràl’interfaceimposéeparl’énoncé,onpeutégalementcréerartificiellementuneclasse vect dérivée de vector<int>, dans laquelle on définit l’opérateur [] de façonappropriée. Cette dernière démarche a le mérite d’offrir toute latitude quant autraitementàmettreenœuvreencasdedébordement:déclenchementd’uneexception,modificationautoritairedelavaleurdel’indicecommeonl’afaitdanslasolutiondel’exercice39,etc.

Voici un premier exemple où l’on se contente d’utiliser la classe vector<int> et son

379

Page 381: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

opérateur[]:#include<iostream>

#include<vector>//pourlaclassevector

usingnamespacestd;

main()

{vector<int>v(6);

inti;

for(i=0;i<6;i++)v[i]=i;

for(i=0;i<6;i++)cout<<v[i]<<"";

}

Voiciunsecondexemple(accompagnédurésultatfourniparsonexécution)quimontrecommentutiliser la fonction atde laclasse vector<int> et traitantde façonappropriéel’exceptionstandardout_of_range:

#include<iostream>

#include<vector>//pourlaclassevector

#include<stdexcept>//pourlaclasseexceptionout_of_range

usingnamespacestd;

main()

{try

{vector<int>v(6);

inti;

for(i=0;i<6;i++)v.at(i)=i;

for(i=0;i<8;i++)cout<<v.at(i)<<"";//iciondebordedev

}

catch(out_of_rangeoor)

{cout<<"exceptionoutofrange\n";

exit(-1);

}

}

012345exceptionoutofrange

Voiciun troisièmeexempledans lequeloncréeuneclassevectdérivéedevector<int>.Ici, ondéclencheune exception out_of_range en casd’indice incorrect.Notezqu’il estnécessairederedéfinirleconstructeuràunargumententierdevect,bienquesoncorpssoitvide,ceciafindetransmettreladimensionauconstructeurdelaclassedebase.Ilfaudraitd’ailleursfairedemêmepour toutconstructeurdevectavecargumentsqu’onsouhaiteraitpouvoirutiliser.

#include<iostream>//voirN.B.duparagrapheNouvellespossibilités

//d'entrées-sortiesduchapitre2

#include<vector>//pourlaclassevector

#include<stdexcept>//pourlaclasseexceptionout_of_range

usingnamespacestd;

classvect:publicvector<int>

{public:

vect(intdim):vector<int>(dim){}//indispensable

//surdéfinitiondel'opérateur[]

int&operator[](inti)

{//onpourraitaussichercheràmodifierautoritairementlavaleur

//deipar:

//if((i<0)||(i>=(*this).size()))i=0;

return(*this).at(i);

380

Page 382: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

}

};

main()

{try

{vectv(6);

inti;

for(i=0;i<6;i++)v[i]=i;

for(i=0;i<8;i++)cout<<v[i]<<"";//iciondebordedev

}

catch(out_of_rangeoor)

{cout<<"exceptionoutofrange\n";

exit(-1);

}

}

012345exceptionoutofrange

381

Page 383: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice154(91revisité)

AncienénoncéEns’inspirantdel’exerciceprécédent,onsouhaitecréeruneclasseint2dpermettantde représenterdes tableauxdynamiquesd’entiers àdeux indices, c’est-à-diredontlesdimensionspeuventnepasêtreconnueslorsdelacompilation.Plusprécisément,onprévoiradedéclarerdetelstableauxparunedéclarationdelaforme:

int2dt(exp1,exp2);

danslaquelleexp1etexp2désignentuneexpressionquelconque(detypeentier).

On surdéfinira l’opérateur (), demanière qu’il permette d’accéder à des élémentsd’unobjetd’untypeint2dcommeonleferaitavecuntableauclassique.

Làencore,onnechercherapasàrésoudrelesproblèmesposéséventuellementparl’affectation ou la transmission par valeur d’objets de type int2d. En revanche, ons’arrangerapourqu’iln’existeaucunrisquededébordementd’indice.

CommentairesOnpeut facilement généraliser ce qui a été fait dans l’exercice précédent.La classevector<vector<int> > fait l’affaire, y compris pour l’affectation ou la transmission parvaleur. Mais, là encore, l’opérateur [] n’est pas protégé contre les débordementsd’indice.Onpeutrésoudreleproblèmeenutilisant,àsaplace,lafonctionmembreatquidéclencheuneexceptionstandardout_of_rangeencasdedébordementd’indice.

Dans le programme source, il faudra absolument laisser un espace entre les deuxsymboles>,afind’évitertouteconfusionavecl’opérateur>>.

Voiciunpremierexemple(accompagnédurésultatfourniparsonexécution)oùl’onsecontented’utiliserlaclassevector<vector<int>>etsonopérateur[]:

#include<iostream>

#include<vector>

usingnamespacestd;

main()

{vector<vector<int>>t1(4);//vecteurde4vecteurs

382

Page 384: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

vector<int>v(3);//vecteurde3entiers,noninitialise

inti,j;

for(i=0;i<4;i++)

t1[i]=v;

for(i=0;i<4;i++)

for(j=0;j<3;j++)

t1[i][j]=i+j;

for(i=0;i<4;i++)

{for(j=0;j<3;j++)

cout<<t1[i][j]<<"";

cout<<"\n";

}

}

012

123

234

345

Onnoteraqu’onaquandmêmedûcréerunobjettemporairev,detypevector<int>,afind’initialiser les différents vecteurs de t1. Mais les choses restent néanmoins plussimplesquecequiaétéfaitauchapitre7,sansrecourirauxcomposantsstandard.

Voici un second exemple qui montre comment utiliser la fonction at des classesvector<int>etvector<vector<int>>ettraitantdefaçonappropriéel’exceptionout_of_range:

#include<iostream>

#include<vector>

usingnamespacestd;

main()

{try

{vector<vector<int>>t1(4);//vecteurde4vecteurs-espace

//entre>et>

vector<int>v(3);//vecteurde3entiers,noninitialisé

inti,j;

for(i=0;i<4;i++)

t1.at(i)=v;

for(i=0;i<4;i++)

for(j=0;j<3;j++)

(t1.at(i)).at(j)=i+j;

for(i=0;i<4;i++)

{for(j=0;j<3;j++)

cout<<(t1.at(i)).at(j)<<"";

cout<<"\n";

}

}

catch(out_of_rangeoor)

{cout<<"exceptionoutofrange\n";

exit(-1);

}

}

Onpeutaussi,àl’imagedecequel’onafaitdansl’exerciceprécédent,chercheràsetenir à l’interface imposée par l’énoncé. Dans ce cas, on crée artificiellement uneclasse int2d, dérivée de vector<vector<int>, classe de base dont on exploite lesfonctionnalitéscommelemontrel’exemplesuivant.Ici,nousavonsattribuédesvaleurs

383

Page 385: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

arbitrairesauxindicesencasdedébordement,commeauchapitre7.#include<iostream>

#include<vector>

usingnamespacestd;

/********déclarationdelaclasseint2d********/

classint2d:publicvector<vector<int>>

{intnlig;//nombrede"lignes"

intncol;//nombrede"colonnes"

public:

int2d(intnl,intnc);//constructeur

int&operator()(int,int);//accèsàunélément,parses2"indices"

};

/***********définitionduconstructeur**********/

int2d::int2d(intnl,intnc):vector<vector<int>>(nl)

{nlig=nl;ncol=nc;

vector<int>v(nc);

inti;

for(i=0;i<nl;i++)(*this)[i]=v;

}

/**********définitiondel'opérateur()*********/

int&int2d::operator()(inti,intj)

{if((i<0)||(i>=nlig))i=0;//protectionssurpremierindice

if((j<0)||(j>=ncol))j=0;//protectionssursecondindice

return(*this)[i][j];

}

main()

{int2dt1(4,3);

inti,j;

for(i=0;i<4;i++)

for(j=0;j<3;j++)

t1(i,j)=i+j;

for(i=0;i<4;i++)

{for(j=0;j<3;j++)

cout<<t1(i,j)<<"";

cout<<"\n";

}

}

Lacomparaisonentre cette solutionet celleduchapitre7montre que le gain obtenuavecl’utilisationdescomposantsstandardresteassezrelatif.Toutefois,ilfautvoirquedans une situation réelle, la solution du chapitre 7 nécessiterait la redéfinition del’affectationetduconstructeurparrecopiedeint2d.Celaneseraitpasnécessaire ici,les fonctionscorrespondantesde la classedebase faisant l’affairepuisque la classedérivéenecomporteaucunepartiedynamiquesupplémentaire.

384

Page 386: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice155(93revisité)

AncienénoncéRéaliser une classe nommée stack_int permettant de gérer une pile d’entiers. Cesderniers seront conservés dans un emplacement alloué dynamiquement ; sadimensionseradéterminéeparl’argumentfourniàsonconstructeur(onluiprévoiraunevaleurpardéfautde20).Cetteclassedevracomporter lesopérateurssuivants(noussupposonsquepestunobjetdetypestack_intetnunentier):

• <<, tel que p<<n ajoute l’entier n à la pile p (si la pile est pleine, rien ne sepasse);

•>>,telquep>>nplacedansnlavaleurduhautdelapilep,enlasupprimantdelapile(silapileestvide,lavaleurdenneserapasmodifiée);

•++,telquep++vale1silapilepestpleineet0danslecascontraire;

•--,telquep--vale1silapilepestvideet0danslecascontraire.

On prévoira que les opérateurs << et >> pourront être utilisés sous les formessuivantes(n1,n2etn3étantdesentiers):

p<<n1<<n2<<n3;p>>n1>>n2<<n3;

Onferaensortequ’ilsoitpossibledetransmettreunepileparvaleur.Enrevanche,l’affectation entre piles ne sera pas permise, et on s’arrangera pour que cettesituationaboutisseàunarrêtdel’exécution.

CommentairesSil’onnes’intéressequ’auxseulesfonctionnalités(ajout,extraction,suppression,testpilepleineoupilevide)delaclassequ’ondemandedecréer,ilexisteuncomposantquifaitl’affaire.Ils’agitdestack<int,vector<int>>.Ici,ons’attendraitplussimplementàstack<int>.Enfait, lepatronstackestnonpasunconteneuràpartentière,maiscequel’onappelleunadaptateurdeconteneur.Ils’agitd’unpatrondeclasses,fondésurunconteneur d’un type donné (ici vector<int>) qui enmodifie l’interface, à la fois en larestreignantetenl’adaptantàdesfonctionnalitésdonnées,àsavoirici:

testpilevide(fonctionempty);

accèsàl’informationsituéeausommentdelapile(fonctiontop) :cettefonctionne

385

Page 387: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

modifiepaslavaleurdusommet;

dépôtd’unevaleursurlapile(fonctionpush);

suppressionde lavaleursituéeausommetde lapile (fonctionpop) ;onnoteraquepourvéritablement«dépiler»unevaleur,ilfauteffectuerdeuxappels:toppuispop.

Cetadaptateur,vector<int>,peut sebaser sur vector,dequeoulist.Nous ne justifieronspas ici le choix de vector, dicté uniquement par des détails d’implémentation etd’efficacité.

Onnoteraquelanotiondepilepleinen’existeplus,àproprementparler,comptetenudel’aspectdynamiquedececomposant.Parailleurs,iln’yaplusderaisond’interdirel’affectation.

Voicicequedevientleprogrammed’essaidel’exercice42danscecas:#include<iostream>

#include<stack>

#include<vector>

usingnamespacestd;

main()

{

voidfct(stack<int,vector<int>>);

stack<int,vector<int>>pile;//ilfautunconteneurdebase,icivector

cout<<"vide:"<<pile.empty()<<"\n";//ici,onafficheunbooléen

pile.push(1);pile.push(2);pile.push(3);pile.push(4);

fct(pile);

intn,p;

n=pile.top();pile.pop();p="pile.top();pile.pop();//depile

//2valeurs

cout<<"hautdelapileauretourdefct:"<<n<<""<<p<<"\n";

stack<int,vector<int>>pileb;

pileb=pile;//icil'affectationfonctionne!!!

}

voidfct(stack<int,vector<int>>pl)

{

cout<<"hautdelapilerecueparfct:";

intn,p;

n=pl.top();pl.pop();p=pl.top();pl.pop();//ondepile2valeurs

cout<<n<<""<<p<<"\n";

pl.push(12);//onenajouteune

}

vide:1

hautdelapilerecueparfct:43

hautdelapileauretourdefct:43

386

Page 388: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice156(142revisité)

AncienénoncéRéaliser une classe nommée set_int permettant de manipuler des ensembles denombresentiers.Lenombremaximald’entiersquepourracontenirl’ensembleseraprécisé au constructeur qui allouera dynamiquement l’espace nécessaire. Onprévoiralesopérateurssuivants(edésigneunélémentdetypeset_intetnunentier:

•<<,telquee<<najoutel’élémentnàl’ensemblee;

•%,telquen%evale1sinappartientàeet0sinon;

•<<,telqueflot<<eenvoielecontenudel’ensembleesurleflotindiqué,souslaforme:

[entier1,entier2,...entiern]

Lafonctionmembrecardinalfourniralenombred’élémentsdel’ensemble.Enfin,ons’arrangera pour que l’affectation ou la transmission par valeur d’objets de typeset_intneposeaucunproblème(onaccepteraladuplicationcomplèted’objets).

CommentairesSil’onnes’intéressequ’auxseulesfonctionnalitésdelaclasse,endehorsdelasortiesurunflot,celles-cisontfourniesintégralementparlecomposantstandardset<int>.Encequiconcernelasortiesurunflot,ondisposedeplusieursdémarches.Onpeutbiensûr la programmer au fur et à mesure des besoins, en écrivant à chaque fois lesquelquesinstructionsdeparcoursdel’ensemble:

set<int>ens;

set<int>::iteratosie;

.....

for(ie=ens.begin();ie!=ens.end();ie++)cout<<*ie<<"";

On peut aussi en faire une fonction ordinaire, comme dans cet exemple, analogue àl’exempled’utilisationdel’exerciceduchapitre16:

#include<iostream>

#include<set>

usingnamespacestd;

voidaffiche(set<int>);

main()

{voidfct(set<int>);

voidfctref(set<int>&);

387

Page 389: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

set<int>ens;

cout<<"donnez10entiers\n";

inti,n;

for(i=0;i<10;i++)

{cin>>n;

ens.insert(n);

}

cout<<"ilya:"<<ens.size()<<"entiersdifferents\n";

cout<<"quiformentl\'ensemble:";affiche(ens);

fct(ens);

cout<<"auretourdefct,ilyena"<<ens.size()<<"\n";

cout<<"quiformentl\'ensemble:";affiche(ens);

fctref(ens);

cout<<"auretourdefctref,ilyena"<<ens.size()<<"\n";

cout<<"quiformentl\'ensemble:";affiche(ens);

cout<<"appartenancede-1:"<<ens.count(-1)<<"\n";

cout<<"appartenancede500:"<<ens.count(500)<<"\n";

set<int>ensa,ensb;

ensa=ensb=ens;

cout<<"ensemblea:";affiche(ensa);

cout<<"ensembleb:";affiche(ensb);

}

voidfct(set<int>e)

{cout<<"ensemblereçuparfct:";affiche(e);

e.insert(-1);e.insert(-2);e.insert(-3);

}

voidfctref(set<int>&e)

{cout<<"ensemblerecuparfctref:";affiche(e);

e.insert(-1);e.insert(-2);e.insert(-3);

}

voidaffiche(set<int>e)

{set<int>::iteratorie;

cout<<"[";

for(ie=e.begin();ie!=e.end();ie++)

cout<<*ie<<"";

cout<<"]\n";

}

donnez10entiers

3531851773

ilya:5entiersdifferents

quiformentl'ensemble:[13578]

ensemblerecuparfct:[13578]

auretourdefct,ilyena5

quiformentl'ensemble:[13578]

ensemblerecuparfctref:[13578]

auretourdefctref,ilyena8

quiformentl'ensemble:[-3-2-113578]

appartenancede-1:1

appartenancede500:0

ensemblea:[-3-2-113578]

ensembleb:[-3-2-113578]

On peut également créer artificiellement une classe set_int, dérivée de set<int>, danslaquelle on surdéfinit l’opérateur <<, comme dans cet exemple qui fournit lesmêmesrésultatsqueleprécédent:

#include<iostream>

#include<set>

usingnamespacestd;

388

Page 390: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

/*************déclarationdelaclasseset_int*********/

classset_int:publicset<int>

{public:

//envoiensembledansunflot

friendostream&operator<<(ostream&,set_int&);

};

/*************définitiondelaclasseset_int*********/

ostream&operator<<(ostream&sortie,set_int&e)//voirremarque1

{sortie<<"[";

set<int>::iteratorie;

for(ie=e.begin();ie!=e.end();ie++)

sortie<<*ie<<"";

sortie<<"]";

returnsortie;

}

/*************testdelaclasseset_int*********/

main()

{

voidfct(set_int);

voidfctref(set_int&);

set_intens;

cout<<"donnez10entiers\n";

inti,n;

for(i=0;i<10;i++)

{cin>>n;

ens.insert(n);

}

cout<<"ilya:"<<ens.size()<<"entiersdifferents\n";

cout<<"quiformentl\'ensemble:"<<ens<<"\n";

fct(ens);

cout<<"auretourdefct,ilyena"<<ens.size()<<"\n";

cout<<"quiformentl\'ensemble:"<<ens<<"\n";

fctref(ens);

cout<<"auretourdefctref,ilyena"<<ens.size()<<"\n";

cout<<"quiformentl\'ensemble:"<<ens<<"\n";

cout<<"appartenancede-1:"<<ens.count(-1)<<"\n";

cout<<"appartenancede500:"<<ens.count(500)<<"\n";

set_intensa,ensb;

ensa=ensb=ens;

cout<<"ensemblea:"<<ensa<<"\n";

cout<<"ensembleb:"<<ensb<<"\n";

}

voidfct(set_inte)

{cout<<"ensemblereçuparfct:"<<e<<"\n";

e.insert(-1);e.insert(-2);e.insert(-3);

}

voidfctref(set_int&e)

{cout<<"ensemblereçuparfctref:"<<e<<"\n";

e.insert(-1);e.insert(-2);e.insert(-3);

}

1.Certainsenvironnements imposentque l’onmentionne l’espacedenomstd dans lasurdéfinitiondel’opérateur<<enécrivantstd::operator<<.

2.Ici,iln’estpasnécessairederedéfinirl’affectationouleconstructeurparrecopie.En effet, compte tenu des règles relatives à l’héritage, les fonctions par défautappellentbienlesfonctionsvouluesdanslaclassedebase;celasuffiticipuisquelaclassedérivéen’introduitaucunepartiedynamiquesupplémentaire.

389

Page 391: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

390

Page 392: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice157(143revisité)

AncienénoncéCréeruneclassevectpermettantdemanipulerdes«vecteursdynamiques»d’entiers,c’est-à-diredestableauxd’entiersdontladimensionpeutêtredéfinieaumomentdeleurcréation(unetelleclasseadéjàétépartiellementréaliséedansl’exercice80).Cetteclassedevradisposerdesopérateurssuivants:

•[] pour l’accès à une des composantes du vecteur, et cela aussi bien au seind’uneexpressionqu’àgauched’uneaffectation(maiscettedernièresituationnedevrapasêtreautoriséesurdes«vecteursconstants»);

•==,telquesiv1etv2sontdeuxobjetsdetypevect,v1==v2prennelavaleur1siv1etv2sontdemêmedimensionetontlesmêmescomposantesetlavaleur0danslecascontraire;

•<<,telqueflot<<venvoielevecteurvsurleflotindiqué,souslaforme:<entier1,entier2,...,entiern>

Deplus,ons’arrangerapourquel’affectationetlatransmissionparvaleurd’objetsde type vect ne pose aucun problème ; pour ce faire, on acceptera de dupliquercomplètementlesobjetsconcernés.

CommentairesEn dehors de la sortie sur un flot, le composant vector<int> répond parfaitement à laquestion(avec,ici,lesmêmesnomsd’opérateurs[]et==).Encequiconcernelasortiesurunflot,ondisposedeplusieursdémarches,commedansl’exerciceprécédent.Onpeutbiensûrlaprogrammeraufuretàmesuredesbesoins,enécrivantàchaquefoislesquelquesinstructionsdeparcoursdel’ensemble:

vector<int>v;

vector<int>::iteratosiv;

.....

for(iv=v.begin();iv!=v.end();iv++)cout<<*iv<<"";

On peut aussi en faire une fonction ordinaire, comme dans cet exemple, analogue àl’exempled’utilisationdel’exerciceduchapitre16:

#include<iostream>

#include<vector>

usingnamespacestd;

391

Page 393: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

main()

{voidaffiche(vector<int>);

inti;

vector<int>v1(5),v2(10);

for(i=0;i<5;i++)v1[i]=i;

cout<<"v1=";affiche(v1);

for(i=0;i<10;i++)v2[i]=i*i;

cout<<"v2=";affiche(v2);

v1=v2;

cout<<"v1=";affiche(v1);

vector<int>v3=v1;

cout<<"v3=";affiche(v3);

vector<int>v4=v2;

cout<<"v4=";affiche(v4);

//constvector<int>w(3);w[2]=5;//conduitbienàerreurcompilation

}

voidaffiche(vector<int>v)

{vector<int>::iteratoriv;

for(iv=v.begin();iv!=v.end();iv++)

cout<<*iv<<"";

cout<<"\n";

}

v1=01234

v2=0149162536496481

v1=0149162536496481

v3=0149162536496481

v4=0149162536496481

On peut également créer artificiellement une classe vect, dérivée de vector<int>, danslaquelle on surdéfinit l’opérateur <<, commedans cet exemplequi fournit lesmêmesrésultatsqueleprécédent:

#include<iostream>

#include<vector>

usingnamespacestd;

classvect:publicvector<int>

{public:

vect(intn):vector<int>(n){}//indispensable!!!!!!!

friendostream&operator<<(ostream&,vect&);

};

ostream&operator<<(ostream&sortie,vect&v)//voirremarqueci-après

{sortie<<"<";

vector<int>::iteratoriv;

for(iv=v.begin();iv!=v.end();iv++)sortie<<*iv<<"";

sortie<<">";

returnsortie;

}

main()

{inti;

vectv1(5),v2(10);

for(i=0;i<5;i++)v1[i]=i;

cout<<"v1="<<v1<<"\n";

for(i=0;i<10;i++)v2[i]=i*i;

cout<<"v2="<<v2<<"\n";

v1=v2;

cout<<"v1="<<v1<<"\n";

vectv3=v1;

cout<<"v3="<<v3<<"\n";

vectv4=v2;

cout<<"v4="<<v4<<"\n";

392

Page 394: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

//constvectw(3);w[2]=5;//conduitbienàerreurcompilation

}

Certains environnements imposent que l’on mentionne l’espace de nom std dans lasurdéfinitiondel’opérateur<<enécrivantstd::operator<<.

393

Page 395: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

Exercice158(94revisité)

AncienénoncéRéaliseruneclassenommée bit_array permettantdemanipulerdes tableauxdebits(autrementdit,destableauxdanslesquelschaqueélémentnepeutprendrequel’unedesdeuxvaleurs0ou1).Latailled’untableau(c’est-à-direlenombredebits)seradéfinielorsdesacréation(parunargumentpasséàsonconstructeur).Onprévoiralesopérateurssuivants:

•+=,telquet+=nmetteà1lebitderangndutableaut;

•-=,telquet-=nmetteà0lebitderangndutableaut;

•[],telquel’expressiont[i]fournisselavaleurdubitderangidutableaut(onne prévoira pas, ici, de pouvoir employer cet opérateur à gauche d’uneaffectation,commedanst[i]=...);

•++,telquet++metteà1touslesbitsdet;

•--,telquet--metteà0touslesbitsdet;

•<<,telqueflot<<tenvoielecontenudetsurleflotindiqué,souslaforme:<*bit1,bit2,...bitn*>

On fera en sorte que l’affectation et la transmission par valeur d’objets du typebit_arrayneposeaucunproblème.

CommentairesSil’onnes’intéressequ’auxseulesfonctionnalitésdelaclassequ’ondemanded’écrireetquel’onfaitabstractiondel’opérateurdesortiesurunflot, lecomposantstandardvector<bool> fera l’affaire. On notera cependant qu’alors, l’opérateur [] pourra êtreemployéàgauched’uneaffectation.

Si l’on tient absolument à ce que ces fonctionnalités soient mises en œuvre parl’intermédiaire des opérateurs proposés, on peut quand même s’appuyer sur lesfonctionnalités de la classe vector<bool> en créant une classe dérivée qu’on adapte defaçonappropriée.Voici cequepourrait être ladéfinitiond’une telle classe,nomméebit_array,accompagnéedumêmeexempled’utilisationquedansl’exercice94:

#include<iostream>

394

Page 396: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

#include<vector>

#include<limits.h>

usingnamespacestd;

/*déclarationdelaclassebit_array*/

classbit_array:publicvector<bool>

{public:

bit_array(int=16);

intoperator[](int)const;//valeurd'unbit

voidoperator+=(int);//activationd'unbit

voidoperator-=(int);//désactivationd'un//bit

//envoisurflot

friendostream&operator<<(ostream&,bit_array&);

//lesopérateursunaires

voidoperator++();//miseà1

voidoperator--();//miseà0

voidoperator~();//complémentà1

};

/*définitiondesfonctionsdelaclassebit_array*/

bit_array::bit_array(intnb):vector<bool>(nb){}

voidbit_array::operator+=(inti)

{(*this).vector<bool>::operator[](i)=true;

}//vector<bool>::operator[]pourforcerl'emploide[]declassedebase

voidbit_array::operator-=(inti)

{(*this).vector<bool>::operator[](i)=false;

}//vector<bool>::operator[]pourforcerl'emploide[]declassedebase

ostream&operator<<(ostream&sortie,bit_array&t)//voirremarque

{sortie<<"<*";

vector<bool>::iteratorie;

for(ie=t.begin();ie!=t.end();ie++)

sortie<<*ie<<"";

sortie<<"*>";

returnsortie;

}

voidbit_array::operator++()

{vector<bool>::iteratorie;

for(ie=(*this).begin();ie!=(*this).end();ie++)

*ie=true;

}

voidbit_array::operator--()

{vector<bool>::iteratorie;

for(ie=(*this).begin();ie!=(*this).end();ie++)

*ie=false;

}

voidbit_array::operator~()

{vector<bool>::iteratorie;

for(ie=(*this).begin();ie!=(*this).end();ie++)

*ie=!(*ie);

}

/*programmed'essaidelaclassebit_array*/

main()

{bit_arrayt1(34);

cout<<"t1="<<t1<<"\n";

t1+=3;t1+=0;t1+=8;t1+=15;t1+=33;

cout<<"t1="<<t1<<"\n";

t1--;

cout<<"t1="<<t1<<"\n";

t1++;

cout<<"t1="<<t1<<"\n";

t1-=0;t1-=3;t1-=8;t1-=15;t1-=33;

cout<<"t1="<<t1<<"\n";

cout<<"t1="<<t1<<"\n";

395

Page 397: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

bit_arrayt2(11),t3(17);

cout<<"t2="<<t2<<"\n";

t2=t3=t1;

cout<<"t3="<<t3<<"\n";

}

Certains environnements imposent que l’on mentionne l’espace de nom std dans lasurdéfinitiondel’opérateur<<enécrivantstd::operator<<.

La comparaison avec l’exercice correspondant du chapitre 16 montre que le gainobtenuavecl’utilisationdescomposantsstandardresteassezrelatif.L’essentielvientde ce qu’il n’est plus besoin ici de surdéfinir l’opérateur d’affectation et leconstructeurparrecopie.

396

Page 398: Exercices en langage C++: 150 exercices corrigés (Noire) (French …livre.fun/LIVREF/F6/F006068.pdf · 2018-06-07 · Complément idéal de Programmer en langage C++, du même auteur,

PoursuivretouteslesnouveautésnumériquesduGroupeEyrolles,retrouvez-noussurTwitteretFacebook

@ebookEyrolles

EbooksEyrolles

Etretrouveztouteslesnouveautéspapiersur

@Eyrolles

Eyrolles

397