Documente Academic
Documente Profesional
Documente Cultură
ThinkinginC++2ndedVolume2
ThinkingInC++
Volume2:PracticalProgramming
BruceEckel,President,MindView,Inc.
ChuckAllison,UtahValleyStateCollege
BookHomePage
AnnotatedSolutionGuide
ReportErrorsHere
Cover
Introduction
Part1:BuildingStableSystems
1:Exceptionhandling
2:Defensiveprogramming
Part2:TheStandardC++Library
3:Stringsindepth
4:Iostreams
5:Templatesindepth
6:Genericalgorithms
7:Genericcontainers
Part3:SpecialTopics
8:Runtimetypeidentification
9:Multipleinheritance
10:Designpatterns
11:Concurrency
Appendix
A:Recommendedreading
B:Etc
Index
Idliketocongratulatethebothofyouforaveryimpressivework!NotonlydidIfindyourbooktobean
enjoyableandrewardingreadIwasastoundedbytheaccuracybothintermsoftechnicalcorrectnessanduse
ofthelanguageIbelievethatyouhaveattainedalevelofcraftsmanshipthatissimplyoutstanding.
BjornKarlsson
EditorialBoard,C/C++UsersJournal
Thisbookisatremendousachievement.Youoweittoyourselftohaveacopyonyourshelf.
AlStevens
ContributingEditor,DoctorDobbsJournal
Eckelsbookistheonlyonetosoclearlyexplainhowtorethinkprogramconstructionforobjectorientation.That
thebookisalsoanexcellenttutorialontheinsandoutsofC++isanaddedbonus.
AndrewBinstock
Editor,UnixReview
BrucecontinuestoamazemewithhisinsightintoC++,andThinkinginC++ishisbestcollectionofideasyet.If
youwantclearanswerstodifficultquestionsaboutC++,buythisoutstandingbook.
GaryEntsminger
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
1/458
5/3/2015
ThinkinginC++2ndedVolume2
Author,TheTaoofObjects
ThinkinginC++patientlyandmethodicallyexplorestheissuesofwhenandhowtouseinlines,references,
operatoroverloading,inheritanceanddynamicobjects,aswellasadvancedtopicssuchastheproperuseof
templates,exceptionsandmultipleinheritance.TheentireeffortiswoveninafabricthatincludesEckelsown
philosophyofobjectandprogramdesign.AmustforeveryC++developersbookshelf,ThinkinginC++istheone
C++bookyoumusthaveifyouredoingseriousdevelopmentwithC++.
RichardHaleShaw
ContributingEditor,PCMagazine
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
2/458
5/3/2015
ThinkinginC++2ndedVolume2
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
3/458
5/3/2015
ThinkinginC++2ndedVolume2
CIPDATAAVAILABLE
VicePresidentandEditorialDirector,ECS:MarciaJ.Horton
Publisher:AlanR.Apt
AssociateEditor:ToniDianneHolm
EditorialAssistant:PatrickLindner
VicePresidentandDirectorofProductionandManufacturing,ESM:DavidW.Riccardi
ExecutiveManagingEditor:VinceOBrien
ManagingEditor:CamilleTrentacoste
ProductionEditor:IrwinZucker
DirectorofCreativeServices:PaulBelfanti
CreativeDirector:CaroleAnson
CoverandInteriorDesigner:DanielWillHarris
CoverIllustrations:TinaJensen
ManufacturingManager:TrudyPisciotti
ManufacturingBuyer:LisaMcDowell
MarketingManager:PamelaShaffer
2004MindView,Inc.
PublishedbyPearsonPrenticeHall
PearsonEducation,Inc.
UpperSaddleRiver,NJ07458
Allrightsreserved.Nopartofthisbookmaybereproducedinanyformorbyanymeans,withoutpermissioninwriting
fromthepublisher.
PearsonPrenticeHallisatrademarkofPearsonEducation,Inc.
Theauthorsandpublisherofthisbookhaveusedtheirbesteffortsinpreparingthisbook.Theseeffortsincludethe
development,research,andtestingofthetheoriesandprogramstodeterminetheireffectiveness.Theauthorsand
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
4/458
5/3/2015
ThinkinginC++2ndedVolume2
publishermakenowarrantyofanykind,expressedorimplied,withregardtotheseprogramsorthedocumentation
containedinthisbook.Theauthorsandpublishershallnotbeliableinanyeventforincidentalorconsequentialdamages
inconnectionwith,orarisingoutof,thefurnishing,performance,oruseoftheseprograms.
PrintedintheUnitedStatesofAmerica
10987654321
ISBN0130353132
PearsonEducationLtd.,London
PearsonEducationAustraliaPty.Ltd.,Sydney
PearsonEducationSingapore,Pte.Ltd.
PearsonEducationNorthAsiaLtd.,HongKong
PearsonEducationCanada,Inc.,Toronto
PearsonEducacindeMexico,S.A.deC.V.
PearsonEducationJapan,Tokyo
PearsonEducationMalaysia,Pte.Ltd.
PearsonEducation,Inc.,UpperSaddleRiver,NewJersey
Dedication
Toallthosewhohaveworkedtirelessly
todeveloptheC++language
Whatsinside
Introduction1
Goals..........................................1
Chapters.....................................2
Exercises.....................................5
Exercisesolutions...............5
Sourcecode.................................5
Compilers....................................7
Languagestandards.....................9
Seminars,CDROMs
&consulting.................................9
Errors.......................................10
Aboutthecover.........................10
Acknowledgements.....................10
I:BuildingStableSystems13
1:ExceptionHandling15
Traditionalerrorhandling.............16
Throwinganexception................18
Catchinganexception.................20
Thetryblock....................20
Exceptionhandlers...........20
Termination
andresumption................22
Exceptionmatching.....................23
Catchinganyexception.....25
Rethrowinganexception...26
Uncaughtexceptions.........26
Cleaningup................................28
Resourcemanagement.....30
Makingeverything
anobject.........................32
auto_ptr..........................35
Functionleveltryblocks...36
Standardexceptions...................38
Exceptionspecifications...............40
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
5/458
5/3/2015
ThinkinginC++2ndedVolume2
Betterexception
specifications?..................45
Exceptionspecifications
andinheritance................46
Whennottouse
exceptionspecifications.....47
Exceptionsafety.........................48
Programmingwithexceptions.......52
Whentoavoidexceptions..52
Typicalusesofexceptions.54
Overhead...................................58
Summary...................................60
Exercises...................................61
2:DefensiveProgramming63
Assertions.................................66
Asimpleunittestframework........70
Automatedtesting............71
TheTestSuiteFramework..75
Testsuites.......................79
Thetestframeworkcode...81
Debuggingtechniques.................87
Tracemacros...................87
Tracefile.........................88
Findingmemoryleaks.......90
Summary...................................96
Exercises...................................97
II:TheStandardC++Library101
3:StringsinDepth103
Whatsinastring?....................104
Creatingandinitializing
C++strings.............................106
Operatingonstrings.................109
Appending,inserting,and
concatenatingstrings......110
Replacingstring
characters......................112
Concatenationusingnonmember
overloadedoperators......117
Searchinginstrings..................117
Findinginreverse...........123
Findingfirst/lastof
asetofcharacters..........124
Removingcharacters
fromstrings...................126
Comparingstrings..........129
Stringsand
charactertraits...............134
Astringapplication...................140
Summary.................................145
Exercises.................................146
4:Iostreams151
Whyiostreams?........................151
Iostreamstotherescue............156
Insertersandextractors..156
Commonusage..............161
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
6/458
5/3/2015
ThinkinginC++2ndedVolume2
Lineorientedinput.........164
Handlingstreamerrors..............165
Fileiostreams...........................168
AFileProcessing
Example........................169
Openmodes...................171
Iostreambuffering....................173
Seekinginiostreams.................175
Stringiostreams.......................179
Inputstringstreams........180
Outputstringstreams......182
Outputstreamformatting..........186
Formatflags...................186
Formatfields..................188
Width,fill,andprecision..190
Anexhaustiveexample...191
Manipulators.............................194
Manipulatorswith
arguments.....................196
Creatingmanipulators.....199
Effectors........................201
Iostreamexamples....................203
Maintainingclasslibrary
sourcecode...................204
Detectingcompilererrors208
Asimpledatalogger.......211
Internationalization...................216
WideStreams.................216
Locales..........................218
Summary.................................221
Exercises.................................222
5:TemplatesinDepth227
Templateparameters.................227
Nontype
templateparameters.......228
Defaulttemplate
arguments.....................230
Templatetemplate
parameters....................232
Thetypenamekeyword...238
Usingthetemplate
keywordasahint...........240
MemberTemplates.........242
Functiontemplateissues...........245
Typedeductionoffunction
templatearguments........245
Functiontemplate
overloading....................249
Takingtheaddress
ofagenerated
functiontemplate............251
Applyingafunction
toanSTLsequence.........255
Partialorderingof
functiontemplates..........259
Templatespecialization...............260
Explicitspecialization.......261
PartialSpecialization.......263
Apracticalexample........265
Preventingtemplate
codebloat......................268
Namelookupissues..................273
Namesintemplates........273
Templatesandfriends.....279
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
7/458
5/3/2015
ThinkinginC++2ndedVolume2
Templateprogrammingidioms....285
Traits.............................285
Policies..........................291
Thecuriouslyrecurring
templatepattern.............294
Templatemetaprogramming.......297
Compiletime
programming.................298
Expressiontemplates......308
Templatecompilationmodels......315
Theinclusionmodel........315
Explicitinstantiation........316
Theseparationmodel......319
Summary.................................320
Exercises.................................321
6:GenericAlgorithms325
Afirstlook...............................325
Predicates......................329
Streamiterators.............331
Algorithmcomplexity......333
Functionobjects.......................335
Classificationof
functionobjects..............336
Automaticcreationof
functionobjects..............338
Adaptablefunctionobjects341
Morefunction
objectexamples.............343
Functionpointeradaptors351
Writingyourown
functionobjectadaptors..358
AcatalogofSTLalgorithms.......362
Supporttoolsfor
examplecreation............365
Fillingandgenerating......368
Counting........................370
Manipulatingsequences...372
Searchingandreplacing..377
Comparingranges..........385
Removingelements........389
Sortingandoperations
onsortedranges............393
Heapoperations.............403
Applyinganoperationto
eachelementinarange..405
Numericalgorithms.........413
Generalutilities..............417
Creatingyourown
STLstylealgorithms.................419
Summary.................................420
Exercises.................................421
7:GenericContainers429
Containersanditerators............429
STLreference
documentation................431
Afirstlook...............................432
Containersofstrings.......438
Inheritingfrom
STLcontainers................440
Aplethoraofiterators...............442
Iteratorsin
reversiblecontainers.......445
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
8/458
5/3/2015
ThinkinginC++2ndedVolume2
Iteratorcategories..........446
Predefinediterators........448
Thebasicsequences:
vector,list,deque.....................454
Basicsequenceoperations454
vector............................457
deque............................465
Convertingbetween
sequences......................467
Checkedrandomaccess.470
list.................................471
Swappingsequences.......477
set..........................................479
Acompletely
reusabletokenizer..........482
stack.......................................487
queue......................................491
Priorityqueues.........................496
Holdingbits..............................506
bitset<n>.......................507
vector<bool>.................511
Associativecontainers...............513
Generatorsandfillers
forassociativecontainers518
Themagicofmaps.........521
Multimapsand
duplicatekeys................523
Multisets........................527
CombiningSTLcontainers..........530
Cleaningup
containersofpointers...............534
Creatingyourowncontainers.....536
STLextensions.........................538
NonSTLcontainers..................540
Summary.................................546
Exercises.................................546
III:SpecialTopics549
8:RuntimeTypeIdentification551
Runtimecasts..........................551
Thetypeidoperator..................557
Castingto
intermediatelevels.........560
voidpointers..................561
UsingRTTI
withtemplates................562
Multipleinheritance....................563
SensibleusesforRTTI...............564
Atrashrecycler..............565
Mechanismand
overheadofRTTI......................570
Summary.................................570
Exercises.................................571
9:MultipleInheritance573
Perspective...............................573
Interfaceinheritance..................575
Implementationinheritance........579
Duplicatesubobjects.................585
Virtualbaseclasses...................589
Namelookupissues..................599
AvoidingMI..............................603
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
9/458
5/3/2015
ThinkinginC++2ndedVolume2
Extendinganinterface...............603
Summary.................................608
Exercises.................................609
10:DesignPatterns613
Thepatternconcept..................613
Prefercomposition
toinheritance.................615
Classifyingpatterns...................615
Features,idioms,
patterns.........................616
SimplifyingIdioms.....................617
Messenger.....................617
CollectingParameter.......618
Singleton.................................619
VariationsonSingleton....621
Command:choosing
theoperation...........................626
Decouplingeventhandling
withCommand...............628
Objectdecoupling.....................631
Proxy:frontingfor
anotherobject................632
State:changing
objectbehavior..............634
Adapter...................................636
TemplateMethod.......................639
Strategy:choosingthe
algorithmatruntime..................640
ChainofResponsibility:
tryingasequenceofstrategies...642
Factories:encapsulating
objectcreation.........................645
Polymorphicfactories......647
Abstractfactories............651
Virtualconstructors.........654
Builder:creating
complexobjects.......................660
Observer..................................667
Theinnerclassidiom....671
Theobserverexample....674
Multipledispatching...................679
Multipledispatching
withVisitor.....................683
Summary.................................687
Exercises.................................688
11:Concurrency691
Motivation................................692
ConcurrencyinC++..................694
InstallingZThreads.........695
DefiningTasks..........................696
UsingThreads..........................698
Creatingresponsive
userinterfaces...............700
Simplifyingwith
Executors.......................702
Yielding..........................706
Sleeping........................707
Priority..........................709
Sharinglimitedresources...........711
Ensuringthe
existenceofobjects........711
Improperlyaccessing
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
10/458
5/3/2015
ThinkinginC++2ndedVolume2
resources.......................715
Controllingaccess...........719
Simplifiedcoding
withGuards....................721
Threadlocalstorage.......724
Terminatingtasks......................727
Preventingiostream
collision.........................727
Theornamentalgarden...728
Terminating
whenblocked.................733
Interruption....................735
Cooperationbetweenthreads.....741
Waitandsignal...............742
Producerconsumer
relationships...................747
Solvingthreadingproblems
withqueues....................750
Broadcast......................757
Deadlock..................................764
Summary.................................770
Exercises.................................773
A:RecommendedReading777
GeneralC++............................777
Brucesbooks.................777
Chucksbooks................779
IndepthC++..........................779
DesignPatterns........................781
B:Etc783
Index791
Introduction
InVolume1ofthisbook,youlearnedthefundamentalsofCandC++.Inthis
volume,welookatmoreadvancedfeatures,withaneyetowardsdeveloping
techniquesandideasthatproducerobustC++programs.
WeassumeyouarefamiliarwiththematerialpresentedinVolume1.
Goals
Ourgoalsinthisbookareto:
1.Presentthematerialasimplestepatatime,sothereadercaneasilydigesteachconceptbefore
movingon.
2.Teachpracticalprogrammingtechniquesthatyoucanuseonadaytodaybasis.
3.Giveyouwhatwethinkisimportantforyoutounderstandaboutthelanguage,ratherthan
everythingweknow.Webelievethereisaninformationimportancehierarchy,andtherearesome
factsthat95%ofprogrammerswillneverneedtoknow,butthatwouldjustconfusepeopleandadd
totheirperceptionofthecomplexityofthelanguage.TotakeanexamplefromC,ifyoumemorize
theoperatorprecedencetable(weneverdid)youcanwriteclevercode.Butifyoumustthinkabout
it,itwillconfusethereader/maintainerofthatcode.Soforgetaboutprecedenceanduse
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
11/458
5/3/2015
ThinkinginC++2ndedVolume2
parentheseswhenthingsarentclear.Thissameattitudewillbetakenwithsomeinformationinthe
C++language,whichismoreimportantforcompilerwritersthanforprogrammers.
4.Keepeachsectionfocusedenoughsothelecturetimeandthetimebetweenexerciseperiodsis
small.Notonlydoesthiskeeptheaudiencemindsmoreactiveandinvolvedduringahandson
seminar,butitgivesthereaderagreatersenseofaccomplishment.
5.WehaveendeavorednottouseanyparticularvendorsversionofC++.Wehavetestedthecodeon
alltheimplementationswecould(describedlaterinthisintroduction),andwhenoneimplementation
absolutelyrefusedtoworkbecauseitdoesntconformtotheC++Standard,weveflaggedthatfact
intheexample(youllseetheflagsinthesourcecode)toexcludeitfromthebuildprocess.
6.Automatethecompilingandtestingofthecodeinthebook.Wehavediscoveredthatcodethatisnt
compiledandtestedisprobablybroken,sointhisvolumeweveinstrumentedtheexampleswith
testcode.Inaddition,thecodethatyoucandownloadfromhttp://www.MindView.nethasbeen
extracteddirectlyfromthetextofthebookusingprogramsthatautomaticallycreatemakefilesto
compileandrunthetests.Thiswayweknowthatthecodeinthebookiscorrect.
Chapters
Hereisabriefdescriptionofthechapterscontainedinthisbook:
Part1:BuildingStableSystems
1.Exceptionhandling.Errorhandlinghasalwaysbeenaprobleminprogramming.Evenifyoudutifully
returnerrorinformationorsetaflag,thefunctioncallermaysimplyignoreit.Exceptionhandlingisa
primaryfeatureinC++thatsolvesthisproblembyallowingyoutothrowanobjectoutofyourfunction
whenacriticalerrorhappens.Youthrowdifferenttypesofobjectsfordifferenterrors,andthefunction
callercatchestheseobjectsinseparateerrorhandlingroutines.Ifyouthrowanexception,itcannotbe
ignored,soyoucanguaranteethatsomethingwillhappeninresponsetoyourerror.Thedecisiontouse
exceptionsaffectscodedesigninpositive,fundamentalways.
2.DefensiveProgramming.Manysoftwareproblemscanbeprevented.Toprogramdefensivelyisto
craftcodeinsuchawaythatbugsarefoundandfixedearlybeforetheycandamageinthefield.Using
assertionsisthesinglemostimportantwaytovalidateyourcodeduringdevelopment,whileatthesame
timeleavinganexecutabledocumentationtrailinyourcodethatrevealsyourthoughtswhileyouwrotethe
codeinthefirstplace.Rigorouslytestyourcodebeforeyouletoutofyourhands.Anautomatedunit
testingframeworkisanindispensabletoolforsuccessful,everydaysoftwaredevelopment.
Part2:TheStandardC++Library
3.StringsinDepth.Themostcommonprogrammingactivityistextprocessing.TheC++stringclass
relievestheprogrammerfrommemorymanagementissues,whileatthesametimedeliveringa
powerhouseoftextprocessingcapability.C++alsosupportstheuseofwidecharactersandlocalesfor
internationalizedapplications.
4.Iostreams.OneoftheoriginalC++librariestheonethatprovidestheessentialI/Ofacilityiscalled
iostreams.IostreamsisintendedtoreplaceCsstdio.hwithanI/Olibrarythatiseasiertouse,more
flexible,andextensibleyoucanadaptittoworkwithyournewclasses.Thischapterteachesyouhowto
makethebestuseoftheexistingiostreamlibraryforstandardI/O,fileI/O,andinmemoryformatting.
5.TemplatesinDepth.ThedistinguishingfeatureofmodernC++isthebroadpoweroftemplates.
Templatesdomorethanjustcreategenericcontainers.Theysupportdevelopmentofrobust,generic,high
performancelibraries.Thereisalottoknowabouttemplatestheyconstitute,asitwere,asublanguage
withintheC++language,andgivetheprogrammeranimpressivedegreeofcontroloverthecompilation
process.ItisnotanoverstatementtosaythattemplateshaverevolutionizedC++programming.
6.GenericAlgorithms.Algorithmsareatthecoreofcomputing,andC++,throughitstemplatefacility,
supportsanimpressiveentourageofpowerful,efficient,andeasytousegenericalgorithms.Thestandard
algorithmsarealsocustomizablethroughfunctionobjects.Thischapterlooksateveryalgorithminthe
library.(Chapters6and7coverthatportionoftheStandardC++librarycommonlyknownastheStandard
TemplateLibrary,orSTL.)
7.GenericContainers&Iterators.C++supportsallthecommondatastructuresinatypesafe
manner.Youneverneedtoworryaboutwhatsuchacontainerholds.Thehomogeneityofitsobjectsis
guaranteed.Separatingthetraversingofacontainerfromthecontaineritself,anotheraccomplishmentof
templates,ismadepossiblethroughiterators.Thisingeniousarrangementallowsaflexibleapplicationof
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
12/458
5/3/2015
ThinkinginC++2ndedVolume2
algorithmstocontainersusingthesimplestofdesigns.
Part3:SpecialTopics
8.Runtimetypeidentification.Runtimetypeidentification(RTTI)findstheexacttypeofanobjectwhen
youonlyhaveapointerorreferencetothebasetype.Normally,youllwanttointentionallyignorethe
exacttypeofanobjectandletthevirtualfunctionmechanismimplementthecorrectbehaviorforthat
type.Butoccasionally(likewhenwritingsoftwaretoolssuchasdebuggers)itishelpfultoknowtheexact
typeofanobjectwiththisinformation,youcanoftenperformaspecialcaseoperationmoreefficiently.
ThischapterexplainswhatRTTIisforandhowtouseit.
9.Multipleinheritance.Thissoundssimpleatfirst:Anewclassisinheritedfrommorethanoneexisting
class.However,youcanendupwithambiguitiesandmultiplecopiesofbaseclassobjects.Thatproblemis
solvedwithvirtualbaseclasses,butthebiggerissueremains:Whendoyouuseit?Multipleinheritanceis
onlyessentialwhenyouneedtomanipulateanobjectthroughmorethanonecommonbaseclass.This
chapterexplainsthesyntaxformultipleinheritanceandshowsalternativeapproachesinparticular,how
templatessolveonetypicalproblem.Usingmultipleinheritancetorepairadamagedclassinterfaceis
demonstratedasavaluableuseofthisfeature.
10.DesignPatterns.Themostrevolutionaryadvanceinprogrammingsinceobjectsistheintroductionof
designpatterns.Adesignpatternisalanguageindependentcodificationofasolutiontoacommon
programmingproblem,expressedinsuchawaythatitcanapplytomanycontexts.Patternssuchas
Singleton,FactoryMethod,andVisitornowfindtheirwayintodailydiscussionsaroundthekeyboard.This
chaptershowshowtoimplementandusesomeofthemoreusefuldesignpatternsinC++.
11.ConcurrentProgramming.Peoplehavecometoexpectresponsiveuserinterfacesthat(seemto)
processmultipletaskssimultaneously.Modernoperatingsystemsallowprocessestohavemultiplethreads
thatsharetheprocessaddressspace.Multithreadedprogrammingrequiresadifferentmindset,however,
andcomeswithitsownsetofdifficulties.Thischapterusesafreelyavailablelibrary(theZThreadlibrary
byEricCrahenofIBM)toshowhowtoeffectivelymanagemultithreadedapplicationsinC++.
Exercises
Wehavediscoveredthatsimpleexercisesareexceptionallyusefulduringaseminartocompletea
studentsunderstanding.Youllfindasetattheendofeachchapter.
Thesearefairlysimple,sotheycanbefinishedinareasonableamountoftimeinaclassroomsituation
whiletheinstructorobserves,makingsureallthestudentsareabsorbingthematerial.Someexercisesare
abitmorechallengingtokeepadvancedstudentsentertained.Theyrealldesignedtobesolvedinashort
timeandareonlytheretotestandpolishyourknowledgeratherthanpresentmajorchallenges
(presumably,youllfindthoseonyourownormorelikelytheyllfindyou).
Exercisesolutions
SolutionstoexercisescanbefoundintheelectronicdocumentTheC++AnnotatedSolutionGuide,Volume
2,availableforanominalfeefromhttp://www.MindView.net.
Sourcecode
Thesourcecodeforthisbookiscopyrightedfreeware,distributedviathewebsite
http://www.MindView.net.Thecopyrightpreventsyoufromrepublishingthecodeinprintmediawithout
permission.
Inthestartingdirectorywhereyouunpackthecodeyouwillfindthefollowingcopyrightnotice:
//:!:CopyRight.txt
(c)19952004MindView,Inc.Allrightsreserved.
Sourcecodefilefromthebook
"ThinkinginC++,2ndEdition,Volume2."
Thefollowingpermissionsaregrantedrespectingthe
computersourcecode,whichiscontainedinthisfile:
Permissionisgrantedtoclassroomeducatorstousethis
fileaspartofinstructionalmaterialspreparedfor
classespersonallytaughtorsupervisedbytheeducatorwho
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
13/458
5/3/2015
ThinkinginC++2ndedVolume2
usesthispermission,providedthat(a)thebook"Thinking
inC++"iscitedastheoriginoneachpageorslidethat
containsanypartofthisfile,and(b)thatyoumaynot
removetheabovecopyrightlegendnorthisnotice.This
permissionextendstohandouts,slidesandother
presentationmaterials.
Forpurposesthatdonotincludethepublicationor
presentationofeducationalorinstructionalmaterials,
permissionalsoisgrantedtocomputerprogramdesigners
andprogrammers,andtotheiremployersandcustomers,(a)
touseandmodifythisfileforthepurposeofcreating
executablecomputersoftware,and(b)todistribute
resultingcomputerprogramsinbinaryformonly,provided
that(c)youmaynotremovetheabovecopyrightlegendnor
thisnoticefromretainedsourcecodecopiesofthisfile,
and(d)eachcopydistributedinbinaryformhasembedded
withinittheabovecopyrightnotice.
Apartfromthepermissionsgrantedabove,thesole
authorizeddistributionpointforadditionalcopiesofthis
fileishttp://www.MindView.net(andofficialmirrorsites)
whereitisavailable,subjecttothepermissionsand
restrictionssetforthherein.
Thefollowingareclarificationsofthelimitedpermissions
grantedabove:
1.Youmaynotpublishordistributeoriginalsor
modifiedversionsofthesourcecodetothesoftwareother
thaninclassroomsituationsdescribedabove.
2.Youmaynotusethesoftwarefileorportions
thereofinprintedmediawithouttheexpresspermissionof
thecopyrightowner.
Thecopyrightownerandauthororauthorsmakeno
representationaboutthesuitabilityofthissoftwarefor
anypurpose.Itisprovided"asis,"andallexpress,
implied,andstatutorywarrantiesandconditionsofany
kindincludinganywarrantiesandconditionsof
merchantability,satisfactoryquality,security,fitness
foraparticularpurposeandnoninfringement,are
disclaimed.Theentireriskastothequalityand
performanceofthesoftwareiswithyou.
Innoeventwilltheauthorsorthepublisherbeliablefor
anylostrevenue,savings,ordata,orfordirect,
indirect,special,consequential,incidental,exemplaryor
punitivedamages,howevercausedandregardlessofany
relatedtheoryofliability,arisingoutofthislicense
and/ortheuseoforinabilitytousethissoftware,even
ifthevendorsand/orthepublisherhavebeenadvisedof
thepossibilityofsuchdamages.Shouldthesoftwareprove
defective,youassumethecostofallnecessaryservicing,
repair,orcorrection.
Ifyouthinkyouhaveacorrectionforanerrorinthe
software,pleasesubmitthecorrectiontowww.MindView.net.
(Pleaseusethesameprocessfornoncodeerrorsfoundin
thebook.)
Ifyouhaveaneedforpermissionsnotgrantedabove,
pleaseinquireofMindView,Inc.,atwww.MindView.netor
sendarequestbyemailtoBruce@EckelObjects.com.
///:~
Youmayusethecodeinyourprojectsandintheclassroomaslongasthecopyrightnoticeisretained.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
14/458
5/3/2015
ThinkinginC++2ndedVolume2
Compilers
Yourcompilermaynotsupportallthefeaturesdiscussedinthisbook,especiallyifyoudonthavethe
newestversionofyourcompiler.ImplementingalanguagelikeC++isaHerculeantask,andyoucan
expectthatthefeatureswillappearinpiecesratherthanallatonce.Butifyouattemptoneofthe
examplesinthebookandgetalotoferrorsfromthecompiler,itsnotnecessarilyabuginthecodeorthe
compileritmaysimplynotbeimplementedinyourparticularcompileryet.
Weusedanumberofcompilerstotestthecodeinthisbook,inanattempttoensurethatourcode
conformstotheC++Standardandwillworkwithasmanycompilersaspossible.Unfortunately,notall
compilersconformtotheC++Standard,andsowehaveawayofexcludingcertainfilesfrombuildingwith
thosecompilers.Theseexclusionsarereflectedinthemakefilesautomaticallycreatedforthepackageof
codeforthisbookthatyoucandownloadfromwww.MindView.net.Youcanseetheexclusiontags
embeddedinthecommentsatthebeginningofeachlisting,soyouwillknowwhethertoexpecta
particularcompilertoworkonthatcode(inafewcases,thecompilerwillactuallycompilethecodebut
theexecutionbehavioriswrong,andweexcludethoseaswell).
Herearethetagsandthecompilersthattheyexcludefromthebuild:
{dmc}WalterBrightsDigitalMarscompilerforWindows,freelydownloadableat
www.DigitalMars.com.Thiscompilerisveryconformantandsoyouwillseealmostnoneofthese
tagsthroughoutthebook.
{g++}ThefreeGnuC++3.3.1,whichcomespreinstalledinmostLinuxpackagesandMacintosh
OSX.ItisalsopartofCygwinforWindows(seebelow).Itisavailableformostotherplatforms
fromgcc.gnu.org.
{msc}MicrosoftVersion7withVisualC++.NET(onlycomeswithVisualStudio.NETnotfreely
downloadable).
{bor}BorlandC++Version6(notthefreedownloadthisoneismoreuptodate).
{edg}EdisonDesignGroup(EDG)C++.Thisisthebenchmarkcompilerforstandards
conformance.Thistagoccursonlybecauseoflibraryissues,andbecausewewereusinga
complimentarycopyoftheEDGfrontendwithacomplimentarylibraryimplementationfrom
Dinkumware,Ltd.Nocompileerrorsoccurredbecauseofthecompileralone.
{mwcc}MetrowerksCodeWarriorforMacintoshOSX.NotethatOSXcomeswithGnuC++pre
installed,aswell.
Ifyoudownloadandunpackthecodepackageforthisbookfromwww.MindView.net,youllfindthe
makefilestobuildthecodefortheabovecompilers.WeusedthefreelyavailableGNUmake,which
comeswithLinux,Cygwin(afreeUnixshellthatrunsontopofWindowsseewww.Cygwin.com),orcanbe
installedonyourplatformseewww.gnu.org/software/make.(Othermakesmayormaynotworkwith
thesefiles,butarenotsupported.)Onceyouinstallmake,ifyoutypemakeatthecommandlineyoull
getinstructionsonhowtobuildthebookscodefortheabovecompilers.
Notethattheplacementofthesetagsonthefilesinthisbookindicatesthestateoftheparticularversion
ofthecompileratthetimewetriedit.Itspossibleandlikelythatthecompilervendorhasimprovedthe
compilersincethepublicationofthisbook.Itsalsopossiblethatwhilebuildingthebookwithsomany
compilers,wemayhavemisconfiguredaparticularcompilerthatwouldotherwisehavecompiledthecode
correctly.Thus,youshouldtrythecodeyourselfonyourcompiler,andalsocheckthecodedownloaded
fromwww.MindView.nettoseewhatiscurrent.
Languagestandards
Throughoutthisbook,whenreferringtoconformancetotheANSI/ISOCstandard,wewillbereferringto
the1989standard,andwillgenerallyjustsayC.OnlyifitisnecessarytodistinguishbetweenStandard
1989Candolder,preStandardversionsofCwillwemakethedistinction.WedonotreferenceC99inthis
book.
TheANSI/ISOC++CommitteelongagofinishedworkingonthefirstC++Standard,commonlyknownas
C++98.WewillusethetermStandardC++torefertothisstandardizedlanguage.Ifwesimplyreferto
C++,assumewemeanStandardC++.TheC++StandardsCommitteecontinuestoaddressissues
importanttotheC++communitythatwillbecomeC++0x,afutureC++Standardnotlikelytobeavailable
formanyyears.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
15/458
5/3/2015
ThinkinginC++2ndedVolume2
Seminars,CDROMs&consulting
BruceEckelscompany,MindView,Inc.,providespublichandsontrainingseminarsbasedonthematerial
inthisbook,andalsoforadvancedtopics.Selectedmaterialfromeachchapterrepresentsalesson,which
isfollowedbyamonitoredexerciseperiodsoeachstudentreceivespersonalattention.Wealsoprovide
onsitetraining,consulting,mentoring,anddesign&codewalkthroughs.Informationandsignupformsfor
upcomingseminarsandothercontactinformationisfoundathttp://www.MindView.net.
Errors
Nomatterhowmanytrickswritersusetodetecterrors,somealwayscreepinandtheseoftenleapoffthe
pageforafreshreader.Ifyoudiscoveranythingyoubelievetobeanerror,pleaseusethefeedback
systembuiltintotheelectronicversionofthisbook,whichyouwillfindathttp://www.MindView.net.
Yourhelpisappreciated.
Aboutthecover
ThecoverartworkwaspaintedbyLarryOBrienswife,TinaJensen(yes,theLarryOBrienwhowasthe
editorofSoftwareDevelopmentMagazineforsomanyyears).Notonlyarethepicturesbeautiful,theyare
alsoexcellentsuggestionsofpolymorphism.TheideaforusingtheseimagescamefromDanielWillHarris,
thecoverdesigner(www.WillHarris.com),workingwithBruce.
Acknowledgements
Volume2ofthisbooklanguishedinahalfcompletedstateforalongtimewhileBrucegotdistractedwith
otherthings,notablyJava,DesignPatternsandespeciallyPython(seewww.Python.org).IfChuckhadnt
beenwilling(foolishly,hehassometimesthought)tofinishtheotherhalfandbringthingsuptodate,this
bookalmostcertainlywouldnthavehappened.TherearentthatmanypeoplewhomBrucewouldhavefelt
comfortableentrustingthisbookto.Chuckspenchantforprecision,correctnessandclearexplanationis
whathasmadethisbookasgoodasitis.
JamieKingactedasaninternunderChucksdirectionduringthecompletionofthisbook.Hewasan
essentialpartofmakingsurethebookgotfinished,notonlybyprovidingfeedbackforChuck,but
especiallybecauseofhisrelentlessquestioningandpickingofeverysinglepossiblenitthathedidnt
completelyunderstand.Ifyourquestionsareansweredbythisbook,itsprobablybecauseJamieasked
themfirst.Jamiealsoenhancedanumberofthesampleprogramsandcreatedmanyoftheexercisesat
theendofeachchapter.ScottBaker,anotherofChucksinternsfundedbyMindView,Inc.,helpedwiththe
exercisesforChapter3.
EricCrahenofIBMwasinstrumentalinthecompletionofChapter11(Concurrency).Whenwewerelooking
forathreadspackage,wesoughtoutonethatwasintuitiveandeasytouse,whilebeingsufficientlyrobust
todothejob.WithEricwegotthatandthensomehewasextremelycooperativeandhasusedour
feedbacktoenhancehislibrary,whilewehavebenefitedfromhisinsightsaswell.
WearegratefultoPeteBeckerforbeingourtechnicaleditor.Fewpeopleareasarticulateand
discriminatingasPete,nottomentionasexpertinC++andsoftwaredevelopmentingeneral.Wealso
thankBjornKarlssonforhisgraciousandtimelytechnicalassistanceashereviewedtheentiremanuscript
withshortnotice.
WalterBrightmadeHerculeaneffortstomakesurethathisDigitalMarsC++compilerwouldcompilethe
examplesinthisbook.Hemakesthecompileravailableforfreedownloadsathttp://www.DigitalMars.com.
Thanks,Walter!
Theideasandunderstandinginthisbookhavecomefrommanyothersources,aswell:friendslikeAndrea
Provaglio,DanSaks,ScottMeyers,CharlesPetzold,andMichaelWilkpioneersofthelanguagelikeBjarne
Stroustrup,AndrewKoenig,andRobMurraymembersoftheC++StandardsCommitteelikeNathan
Myers(whowasparticularlyhelpfulandgenerouswithhisinsights),HerbSutter,PJPlauger,Kevlin
Henney,DavidAbrahams,TomPlum,RegCharney,TomPenello,SamDruker,UweSteinmueller,John
Spicer,SteveAdamczyk,andDaveedVandevoordepeoplewhohavespokenintheC++trackatthe
SoftwareDevelopmentConference(whichBrucecreatedanddeveloped,andChuckspokein)Colleagues
ofChucklikeMichaelSeaver,HustonFranklin,DavidWagstaff,andoftenstudentsinseminars,whoask
thequestionsweneedtoheartomakethematerialclearer.
Thebookdesign,typefaceselection,coverdesign,andcoverphotowerecreatedbyBrucesfriendDaniel
WillHarris,notedauthoranddesigner,whousedtoplaywithrubonlettersinjuniorhighschoolwhilehe
awaitedtheinventionofcomputersanddesktoppublishing.However,weproducedthecameraready
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
16/458
5/3/2015
ThinkinginC++2ndedVolume2
pagesourselves,sothetypesettingerrorsareours.MicrosoftWordXPwasusedtowritethebookandto
createcamerareadypages.ThebodytypefaceisVerdanaandtheheadlinesareinVerdana.Thecodetype
faceisCourierNew.
WealsowishtothankthegenerousprofessionalsattheEdisonDesignGroupandDinkumware,Ltd.,for
givinguscomplimentarycopiesoftheircompilerandlibrary(respectively).Withouttheirexpert
assistance,graciouslygiven,someoftheexamplesinthisbookcouldnothavebeentested.Wealsowish
tothankHowardHinnantandthefolksatMetrowerksforacopyoftheircompiler,andSandySmithand
thefolksatSlickEditforkeepingChucksuppliedwithaworldclasseditingenvironmentforsomanyyears.
GregComeaualsoprovidedacopyofhissuccessfulEDGbasedcompiler,ComeauC++.
Aspecialthankstoallourteachers,andallourstudents(whoareourteachersaswell).
EvanCofsky(Evan@TheUnixMan.com)providedallsortsofassistanceontheserveraswellas
developmentofprogramsinhisnowfavoritelanguage,Python.SharlynnCobaughandPaulaSteuerwere
instrumentalassistants,preventingBrucefrombeingwashedawayinafloodofprojects.
BrucessweetieDawnMcGeeprovidedmuchappreciatedinspirationandenthusiasmduringthisproject.
Thesupportingcastoffriendsincludes,butisnotlimitedto:MarkWestern,GenKiyooka,Kraig
Brockschmidt,ZackUrlocker,AndrewBinstock,NeilRubenking,SteveSinofsky,JDHildebrandt,Brian
McElhinney,BrinkleyBarr,BillGatesatMidnightEngineeringMagazine,LarryConstantine&Lucy
Lockwood,TomKeffer,GregPerry,DanPutterman,ChristiWestphal,GeneWang,DaveMayer,David
Intersimone,ClaireSawyers,TheItalians(AndreaProvaglio,LauraFallai,MarcoCantu,Corrado,Ilsaand
ChristinaGiustozzi),Chris&LauraStrand,TheAlmquists,BradJerbic,JohnKruth&MarilynCvitanic,Holly
Payne(yes,thefamousnovelist!),MarkMabry,TheRobbinsFamilies,TheMoelterFamilies(&the
McMillans),TheWilks,DaveStoner,LaurieAdams,TheCranstons,LarryFogg,Mike&KarenSequeira,
GaryEntsminger&AllisonBrody,ChesterAndersen,JoeLordi,Dave&BrendaBartlett,TheRentschlers,
TheSudeks,Lynn&Todd,andtheirfamilies.Andofcourse,Mom&Dad,Sandy,James&Natalie,Kim&
Jared,Isaac,andAbbi.
Part1:BuildingStableSystems
Softwareengineersspendaboutasmuchtimevalidatingcodeastheydocreating
it.Qualityisorshouldbethegoalofeveryprogrammer,andonecangoalong
waytowardsthatgoalbyeliminatingproblemsbeforetheyhappen.Inaddition,
softwaresystemsshouldberobustenoughtobehavereasonablyinthepresence
ofunforeseenenvironmentalproblems.
ExceptionswereintroducedintoC++tosupportsophisticatederrorhandlingwithoutclutteringcodewithan
inordinateamountoferrorhandlinglogic.Chapter1showshowproperuseofexceptionscanmakefor
wellbehavedsoftware,andalsointroducesthedesignprinciplesthatunderlieexceptionsafecode.In
Chapter2wecoverunittestinganddebuggingtechniquesintendedtomaximizecodequalitylongbefore
itsreleased.Theuseofassertionstoexpressandenforceprograminvariantsisasuresignofan
experiencedsoftwareengineer.Wealsointroduceasimpleframeworktosupportunittesting.
1:ExceptionHandling
Improvingerrorrecoveryisoneofthemostpowerfulwaysyoucanincreasethe
robustnessofyourcode.
Unfortunately,itsalmostacceptedpracticetoignoreerrorconditions,asifwereinastateofdenialabout
errors.Onereason,nodoubt,isthetediousnessandcodebloatofcheckingformanyerrors.Forexample,
printf()returnsthenumberofcharactersthatweresuccessfullyprinted,butvirtuallynoonechecksthis
value.Theproliferationofcodealonewouldbedisgusting,nottomentionthedifficultyitwouldaddin
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
17/458
5/3/2015
ThinkinginC++2ndedVolume2
readingthecode.
TheproblemwithCsapproachtoerrorhandlingcouldbethoughtofascouplingtheuserofafunction
musttietheerrorhandlingcodesocloselytothatfunctionthatitbecomestooungainlyandawkwardto
use.
OneofthemajorfeaturesinC++isexceptionhandling,whichisabetterwayofthinkingaboutand
handlingerrors.Withexceptionhandling:
1.Errorhandlingcodeisnotnearlysotedioustowrite,anditdoesntbecomemixedupwithyour
normalcode.Youwritethecodeyouwanttohappenlaterinaseparatesectionyouwritethe
codetocopewiththeproblems.Ifyoumakemultiplecallstoafunction,youhandletheerrorsfrom
thatfunctiononce,inoneplace.
2.Errorscannotbeignored.Ifafunctionneedstosendanerrormessagetothecallerofthatfunction,
itthrowsanobjectrepresentingthaterroroutofthefunction.Ifthecallerdoesntcatchthe
errorandhandleit,itgoestothenextenclosingdynamicscope,andsoonuntiltheerroriseither
caughtortheprogramterminatesbecausetherewasnohandlertocatchthattypeofexception.
ThischapterexaminesCsapproachtoerrorhandling(suchasitis),discusseswhyitdidnotworkwellfor
C,andexplainswhyitwontworkatallforC++.Thischapteralsocoverstry,throw,andcatch,theC++
keywordsthatsupportexceptionhandling.
Traditionalerrorhandling
Inmostoftheexamplesinthesevolumes,weuseassert()asitwasintended:fordebuggingduring
developmentwithcodethatcanbedisabledwith#defineNDEBUGfortheshippingproduct.Runtime
errorcheckingusestherequire.hfunctions(assure()andrequire())developedinChapter9inVolume
1andrepeatedhereinAppendixB.Thesefunctionsareaconvenientwaytosay,Theresaproblemhere
youllprobablywanttohandlewithsomemoresophisticatedcode,butyoudontneedtobedistractedbyit
inthisexample.Therequire.hfunctionsmightbeenoughforsmallprograms,butforcomplicated
productsyoullwanttowritemoresophisticatederrorhandlingcode.
Errorhandlingisquitestraightforwardwhenyouknowexactlywhattodo,becauseyouhaveallthe
necessaryinformationinthatcontext.Youcanjusthandletheerroratthatpoint.
Theproblemoccurswhenyoudonthaveenoughinformationinthatcontext,andyouneedtopassthe
errorinformationintoadifferentcontextwherethatinformationdoesexist.InC,youcanhandlethis
situationusingthreeapproaches:
1.Returnerrorinformationfromthefunctionor,ifthereturnvaluecannotbeusedthisway,seta
globalerrorconditionflag.(StandardCprovideserrnoandperror()tosupportthis.)Asmentioned
earlier,theprogrammerislikelytoignoretheerrorinformationbecausetediousandobfuscating
errorcheckingmustoccurwitheachfunctioncall.Inaddition,returningfromafunctionthathitsan
exceptionalconditionmightnotmakesense.
2.UsethelittleknownStandardClibrarysignalhandlingsystem,implementedwiththesignal()
function(todeterminewhathappenswhentheeventoccurs)andraise()(togenerateanevent).
Again,thisapproachinvolveshighcouplingbecauseitrequirestheuserofanylibrarythatgenerates
signalstounderstandandinstalltheappropriatesignalhandlingmechanism.Inlargeprojectsthe
signalnumbersfromdifferentlibrariesmightclash.
3.UsethenonlocalgotofunctionsintheStandardClibrary:setjmp()andlongjmp().With
setjmp()yousaveaknowngoodstateintheprogram,andifyougetintotrouble,longjmp()will
restorethatstate.Again,thereishighcouplingbetweentheplacewherethestateisstoredandthe
placewheretheerroroccurs.
WhenconsideringerrorhandlingschemeswithC++,theresanadditionalcriticalproblem:TheC
techniquesofsignalsandsetjmp()/longjmp()donotcalldestructors,soobjectsarentproperlycleaned
up.(Infact,iflongjmp()jumpspasttheendofascopewheredestructorsshouldbecalled,thebehavior
oftheprogramisundefined.)Thismakesitvirtuallyimpossibletoeffectivelyrecoverfromanexceptional
conditionbecauseyoullalwaysleaveobjectsbehindthathaventbeencleanedupandthatcannolonger
beaccessed.Thefollowingexampledemonstratesthiswithsetjmp/longjmp:
//:C01:Nonlocal.cpp
//setjmp()&longjmp().
#include<iostream>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
18/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<csetjmp>
usingnamespacestd
classRainbow{
public:
Rainbow(){cout<<"Rainbow()"<<endl}
~Rainbow(){cout<<"~Rainbow()"<<endl}
}
jmp_bufkansas
voidoz(){
Rainbowrb
for(inti=0i<3i++)
cout<<"there'snoplacelikehome"<<endl
longjmp(kansas,47)
}
intmain(){
if(setjmp(kansas)==0){
cout<<"tornado,witch,munchkins..."<<endl
oz()
}else{
cout<<"AuntieEm!"
<<"Ihadthestrangestdream..."
<<endl
}
}///:~
Thesetjmp()functionisoddbecauseifyoucallitdirectly,itstoresalltherelevantinformationaboutthe
currentprocessorstate(suchasthecontentsoftheinstructionpointerandruntimestackpointer)inthe
jmp_bufandreturnszero.Inthiscaseitbehaveslikeanordinaryfunction.However,ifyoucall
longjmp()usingthesamejmp_buf,itsasifyourereturningfromsetjmp()againyoupoprightout
thebackendofthesetjmp().Thistime,thevaluereturnedisthesecondargumenttolongjmp(),so
youcandetectthatyoureactuallycomingbackfromalongjmp().Youcanimaginethatwithmany
differentjmp_bufs,youcouldpoparoundtomanydifferentplacesintheprogram.Thedifferencebetween
alocalgoto(withalabel)andthisnonlocalgotoisthatyoucanreturntoanypredeterminedlocation
higherupintheruntimestackwithsetjmp()/longjmp()(whereveryouveplacedacalltosetjmp()).
TheprobleminC++isthatlongjmp()doesntrespectobjectsinparticularitdoesntcalldestructors
whenitjumpsoutofascope.[1]Destructorcallsareessential,sothisapproachwontworkwithC++.In
fact,theC++Standardstatesthatbranchingintoascopewithgoto(effectivelybypassingconstructor
calls),orbranchingoutofascopewithlongjmp()whereanobjectonthestackhasadestructor,
constitutesundefinedbehavior.
Throwinganexception
Ifyouencounteranexceptionalsituationinyourcodethatis,ifyoudonthaveenoughinformationinthe
currentcontexttodecidewhattodoyoucansendinformationabouttheerrorintoalargercontextby
creatinganobjectthatcontainsthatinformationandthrowingitoutofyourcurrentcontext.Thisiscalled
throwinganexception.Hereswhatitlookslike:
//:C01:MyError.cpp{RunByHand}
classMyError{
constchar*constdata
public:
MyError(constchar*constmsg=0):data(msg){}
}
voidf(){
//Herewe"throw"anexceptionobject:
throwMyError("somethingbadhappened")
}
intmain(){
//Asyoullseeshortly,wellwanta"tryblock"here:
f()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
19/458
5/3/2015
ThinkinginC++2ndedVolume2
}///:~
MyErrorisanordinaryclass,whichinthiscasetakesachar*asaconstructorargument.Youcanuse
anytypewhenyouthrow(includingbuiltintypes),butusuallyyoullcreatespecialclassesforthrowing
exceptions.
Thekeywordthrowcausesanumberofrelativelymagicalthingstohappen.First,itcreatesacopyofthe
objectyourethrowingand,ineffect,returnsitfromthefunctioncontainingthethrowexpression,even
thoughthatobjecttypeisntnormallywhatthefunctionisdesignedtoreturn.Anaivewaytothinkabout
exceptionhandlingisasanalternatereturnmechanism(althoughyoullfindyoucangetintotroubleifyou
takethatanalogytoofar).Youcanalsoexitfromordinaryscopesbythrowinganexception.Inanycase,a
valueisreturned,andthefunctionorscopeexits.
Anysimilaritytoareturnstatementendstherebecausewhereyoureturnissomeplacecompletely
differentfromwhereanormalfunctioncallreturns.(Youendupinanappropriatepartofthecodecalled
anexceptionhandlerthatmightbefarremovedfromwheretheexceptionwasthrown.)Inaddition,any
localobjectscreatedbythetimetheexceptionoccursaredestroyed.Thisautomaticcleanupoflocal
objectsisoftencalledstackunwinding.
Inaddition,youcanthrowasmanydifferenttypesofobjectsasyouwant.Typically,youllthrowa
differenttypeforeachcategoryoferror.Theideaistostoretheinformationintheobjectandinthename
ofitsclasssothatsomeoneinacallingcontextcanfigureoutwhattodowithyourexception.
Catchinganexception
Asmentionedearlier,oneoftheadvantagesofC++exceptionhandlingisthatyoucanconcentrateonthe
problemyouretryingtosolveinoneplace,andthendealwiththeerrorsfromthatcodeinanotherplace.
Thetryblock
Ifyoureinsideafunctionandyouthrowanexception(oracalledfunctionthrowsanexception),the
functionexitsbecauseofthethrownexception.Ifyoudontwantathrowtoleaveafunction,youcanset
upaspecialblockwithinthefunctionwhereyoutrytosolveyouractualprogrammingproblem(and
potentiallygenerateexceptions).Thisblockiscalledthetryblockbecauseyoutryyourvariousfunction
callsthere.Thetryblockisanordinaryscope,precededbythekeywordtry:
try{
//Codethatmaygenerateexceptions
}
Ifyoucheckforerrorsbycarefullyexaminingthereturncodesfromthefunctionsyouuse,youneedto
surroundeveryfunctioncallwithsetupandtestcode,evenifyoucallthesamefunctionseveraltimes.
Withexceptionhandling,youputeverythinginatryblockandhandleexceptionsafterthetryblock.Thus,
yourcodeisaloteasiertowriteandtoreadbecausethegoalofthecodeisnotconfusedwiththeerror
handling.
Exceptionhandlers
Ofcourse,thethrownexceptionmustendupsomeplace.Thisplaceistheexceptionhandler,andyouneed
oneexceptionhandlerforeveryexceptiontypeyouwanttocatch.However,polymorphismalsoworksfor
exceptions,sooneexceptionhandlercanworkwithanexceptiontypeandclassesderivedfromthattype.
Exceptionhandlersimmediatelyfollowthetryblockandaredenotedbythekeywordcatch:
try{
//Codethatmaygenerateexceptions
}catch(type1id1){
//Handleexceptionsoftype1
}catch(type2id2){
//Handleexceptionsoftype2
}catch(type3id3)
//Etc...
}catch(typeNidN)
//HandleexceptionsoftypeN
}
//Normalexecutionresumeshere...
Thesyntaxofacatchclauseresemblesfunctionsthattakeasingleargument.Theidentifier(id1,id2,and
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
20/458
5/3/2015
ThinkinginC++2ndedVolume2
soon)canbeusedinsidethehandler,justlikeafunctionargument,althoughyoucanomittheidentifierif
itsnotneededinthehandler.Theexceptiontypeusuallygivesyouenoughinformationtodealwithit.
Thehandlersmustappeardirectlyafterthetryblock.Ifanexceptionisthrown,theexceptionhandling
mechanismgoeshuntingforthefirsthandlerwithanargumentthatmatchesthetypeoftheexception.It
thenentersthatcatchclause,andtheexceptionisconsideredhandled.(Thesearchforhandlersstops
oncethecatchclauseisfound.)Onlythematchingcatchclauseexecutescontrolthenresumesafterthe
lasthandlerassociatedwiththattryblock.
Noticethat,withinthetryblock,anumberofdifferentfunctioncallsmightgeneratethesametypeof
exception,butyouneedonlyonehandler.
Toillustratetryandcatch,thefollowingvariationofNonlocal.cppreplacesthecalltosetjmp()witha
tryblockandreplacesthecalltolongjmp()withathrowstatement:
//:C01:Nonlocal2.cpp
//Illustratesexceptions.
#include<iostream>
usingnamespacestd
classRainbow{
public:
Rainbow(){cout<<"Rainbow()"<<endl}
~Rainbow(){cout<<"~Rainbow()"<<endl}
}
voidoz(){
Rainbowrb
for(inti=0i<3i++)
cout<<"there'snoplacelikehome"<<endl
throw47
}
intmain(){
try{
cout<<"tornado,witch,munchkins..."<<endl
oz()
}catch(int){
cout<<"AuntieEm!Ihadthestrangestdream..."
<<endl
}
}///:~
Whenthethrowstatementinoz()executes,programcontrolbacktracksuntilitfindsthecatchclause
thattakesanintparameter.Executionresumeswiththebodyofthatcatchclause.Themostimportant
differencebetweenthisprogramandNonlocal.cppisthatthedestructorfortheobjectrbiscalledwhen
thethrowstatementcausesexecutiontoleavethefunctionoz().
Terminationandresumption
Therearetwobasicmodelsinexceptionhandlingtheory:terminationandresumption.Intermination
(whichiswhatC++supports),youassumetheerrorissocriticalthattheresnowaytoautomatically
resumeexecutionatthepointwheretheexceptionoccurred.Inotherwords,whoeverthrewtheexception
decidedtherewasnowaytosalvagethesituation,andtheydontwanttocomeback.
Thealternativeerrorhandlingmodeliscalledresumption,firstintroducedwiththePL/Ilanguageinthe
1960s.[2]Usingresumptionsemanticsmeansthattheexceptionhandlerisexpectedtodosomethingto
rectifythesituation,andthenthefaultingcodeisautomaticallyretried,presumingsuccessthesecond
time.IfyouwantresumptioninC++,youmustexplicitlytransferexecutionbacktothecodewherethe
erroroccurred,usuallybyrepeatingthefunctioncallthatsentyouthereinthefirstplace.Itisnotunusual
toplaceyourtryblockinsideawhileloopthatkeepsreenteringthetryblockuntiltheresultis
satisfactory.
Historically,programmersusingoperatingsystemsthatsupportedresumptiveexceptionhandling
eventuallyendedupusingterminationlikecodeandskippingresumption.Althoughresumptionsounds
attractiveatfirst,itseemsitisntquitesousefulinpractice.Onereasonmaybethedistancethatcan
occurbetweentheexceptionanditshandler.Itisonethingtoterminatetoahandlerthatsfaraway,but
tojumptothathandlerandthenbackagainmaybetooconceptuallydifficultforlargesystemswherethe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
21/458
5/3/2015
ThinkinginC++2ndedVolume2
exceptionisgeneratedfrommanypoints.
Exceptionmatching
Whenanexceptionisthrown,theexceptionhandlingsystemlooksthroughthenearesthandlersinthe
ordertheyappearinthesourcecode.Whenitfindsamatch,theexceptionisconsideredhandledandno
furthersearchingoccurs.
Matchinganexceptiondoesntrequireaperfectcorrelationbetweentheexceptionanditshandler.An
objectorreferencetoaderivedclassobjectwillmatchahandlerforthebaseclass.(However,ifthe
handlerisforanobjectratherthanareference,theexceptionobjectisslicedtruncatedtothebasetype
asitispassedtothehandler.Thisdoesnodamage,butlosesallthederivedtypeinformation.)Forthis
reason,aswellastoavoidmakingyetanothercopyoftheexceptionobject,itisalwaysbettertocatchan
exceptionbyreferenceinsteadofbyvalue.[3]Ifapointeristhrown,theusualstandardpointerconversions
areusedtomatchtheexception.However,noautomatictypeconversionsareusedtoconvertfromone
exceptiontypetoanotherintheprocessofmatching.Forexample:
//:C01:Autoexcp.cpp
//Nomatchingconversions.
#include<iostream>
usingnamespacestd
classExcept1{}
classExcept2{
public:
Except2(constExcept1&){}
}
voidf(){throwExcept1()}
intmain(){
try{f()
}catch(Except2&){
cout<<"insidecatch(Except2)"<<endl
}catch(Except1&){
cout<<"insidecatch(Except1)"<<endl
}
}///:~
EventhoughyoumightthinkthefirsthandlercouldbematchedbyconvertinganExcept1objectintoan
Except2usingtheconvertingconstructor,thesystemwillnotperformsuchaconversionduringexception
handling,andyoullendupattheExcept1handler.
Thefollowingexampleshowshowabaseclasshandlercancatchaderivedclassexception:
//:C01:Basexcpt.cpp
//Exceptionhierarchies.
#include<iostream>
usingnamespacestd
classX{
public:
classTrouble{}
classSmall:publicTrouble{}
classBig:publicTrouble{}
voidf(){throwBig()}
}
intmain(){
Xx
try{
x.f()
}catch(X::Trouble&){
cout<<"caughtTrouble"<<endl
//Hiddenbyprevioushandler:
}catch(X::Small&){
cout<<"caughtSmallTrouble"<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
22/458
5/3/2015
ThinkinginC++2ndedVolume2
}catch(X::Big&){
cout<<"caughtBigTrouble"<<endl
}
}///:~
Here,theexceptionhandlingmechanismwillalwaysmatchaTroubleobject,oranythingthatisa
Trouble(throughpublicinheritance),[4]tothefirsthandler.Thatmeansthesecondandthirdhandlersare
nevercalledbecausethefirstonecapturesthemall.Itmakesmoresensetocatchthederivedtypesfirst
andputthebasetypeattheendtocatchanythinglessspecific.
Noticethattheseexamplescatchexceptionsbyreference,althoughfortheseclassesitisntimportant
becausetherearenoadditionalmembersinthederivedclasses,andtherearenoargumentidentifiersin
thehandlersanyway.Youllusuallywanttousereferenceargumentsratherthanvalueargumentsinyour
handlerstoavoidslicingoffinformation.
Catchinganyexception
Sometimesyouwanttocreateahandlerthatcatchesanytypeofexception.Youdothisusingtheellipsis
intheargumentlist:
catch(...){
cout<<"anexceptionwasthrown"<<endl
}
Becauseanellipsiscatchesanyexception,youllwanttoputitattheendofyourlistofhandlerstoavoid
preemptinganythatfollowit.
Theellipsisgivesyounopossibilitytohaveanargument,soyoucantknowanythingabouttheexception
oritstype.Itsacatchall.Suchacatchclauseisoftenusedtocleanupsomeresourcesandthen
rethrowtheexception.
Rethrowinganexception
Youusuallywanttorethrowanexceptionwhenyouhavesomeresourcethatneedstobereleased,suchas
anetworkconnectionorheapmemorythatneedstobedeallocated.(SeethesectionResource
Managementlaterinthischapterformoredetail).Ifanexceptionoccurs,youdontnecessarilycarewhat
errorcausedtheexceptionyoujustwanttoclosetheconnectionyouopenedpreviously.Afterthat,youll
wanttoletsomeothercontextclosertotheuser(thatis,higherupinthecallchain)handletheexception.
Inthiscasetheellipsisspecificationisjustwhatyouwant.Youwanttocatchanyexception,cleanupyour
resource,andthenrethrowtheexceptionforhandlingelsewhere.Yourethrowanexceptionbyusingthrow
withnoargumentinsideahandler:
catch(...){
cout<<"anexceptionwasthrown"<<endl
//Deallocateyourresourcehere,andthenrethrow
throw
}
Anyfurthercatchclausesforthesametryblockarestillignoredthethrowcausestheexceptiontogo
totheexceptionhandlersinthenexthighercontext.Inaddition,everythingabouttheexceptionobjectis
preserved,sothehandleratthehighercontextthatcatchesthespecificexceptiontypecanextractany
informationtheobjectmaycontain.
Uncaughtexceptions
Asweexplainedinthebeginningofthischapter,exceptionhandlingisconsideredbetterthanthe
traditionalreturnanerrorcodetechniquebecauseexceptionscantbeignored,andbecausetheerror
handlinglogicisseparatedfromtheproblemathand.Ifnoneoftheexceptionhandlersfollowinga
particulartryblockmatchesanexception,thatexceptionmovestothenexthighercontext,thatis,the
functionortryblocksurroundingthetryblockthatdidnotcatchtheexception.(Thelocationofthistry
blockisnotalwaysobviousatfirstglance,sinceitshigherupinthecallchain.)Thisprocesscontinues
until,atsomelevel,ahandlermatchestheexception.Atthatpoint,theexceptionisconsideredcaught,
andnofurthersearchingoccurs.
Theterminate()function
Ifnohandleratanylevelcatchestheexception,thespeciallibraryfunctionterminate()(declaredinthe
<exception>header)isautomaticallycalled.Bydefault,terminate()callstheStandardClibrary
functionabort(),whichabruptlyexitstheprogram.OnUnixsystems,abort()alsocausesacoredump.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
23/458
5/3/2015
ThinkinginC++2ndedVolume2
Whenabort()iscalled,nocallstonormalprogramterminationfunctionsoccur,whichmeansthat
destructorsforglobalandstaticobjectsdonotexecute.Theterminate()functionalsoexecutesifa
destructorforalocalobjectthrowsanexceptionwhilethestackisunwinding(interruptingtheexception
thatwasinprogress)orifaglobalorstaticobjectsconstructorordestructorthrowsanexception.(In
general,donotallowadestructortothrowanexception.)
Theset_terminate()function
Youcaninstallyourownterminate()functionusingthestandardset_terminate()function,which
returnsapointertotheterminate()functionyouarereplacing(whichwillbethedefaultlibraryversion
thefirsttimeyoucallit),soyoucanrestoreitlaterifyouwant.Yourcustomterminate()musttakeno
argumentsandhaveavoidreturnvalue.Inaddition,anyterminate()handleryouinstallmustnotreturn
orthrowanexception,butinsteadmustexecutesomesortofprogramterminationlogic.Ifterminate()
iscalled,theproblemisunrecoverable.
Thefollowingexampleshowstheuseofset_terminate().Here,thereturnvalueissavedandrestored
sothattheterminate()functioncanbeusedtohelpisolatethesectionofcodewheretheuncaught
exceptionoccurs:
//:C01:Terminator.cpp
//Useofset_terminate().Alsoshowsuncaughtexceptions.
#include<exception>
#include<iostream>
usingnamespacestd
voidterminator(){
cout<<"I'llbeback!"<<endl
exit(0)
}
void(*old_terminate)()=set_terminate(terminator)
classBotch{
public:
classFruit{}
voidf(){
cout<<"Botch::f()"<<endl
throwFruit()
}
~Botch(){throw'c'}
}
intmain(){
try{
Botchb
b.f()
}catch(...){
cout<<"insidecatch(...)"<<endl
}
}///:~
Thedefinitionofold_terminatelooksabitconfusingatfirst:itnotonlycreatesapointertoafunction,
butitinitializesthatpointertothereturnvalueofset_terminate().Eventhoughyoumightbefamiliar
withseeingasemicolonrightafterapointertofunctiondeclaration,hereitsjustanotherkindofvariable
andcanbeinitializedwhenitisdefined.
TheclassBotchnotonlythrowsanexceptioninsidef(),butalsoinitsdestructor.Thiscausesacallto
terminate(),asyoucanseeinmain().Eventhoughtheexceptionhandlersayscatch(...),whichwould
seemtocatcheverythingandleavenocauseforterminate()tobecalled,terminate()iscalled
anyway.Intheprocessofcleaninguptheobjectsonthestacktohandleoneexception,theBotch
destructoriscalled,andthatgeneratesasecondexception,forcingacalltoterminate().Thus,a
destructorthatthrowsanexceptionorcausesonetobethrownisusuallyasignofpoordesignorsloppy
coding.
Cleaningup
Partofthemagicofexceptionhandlingisthatyoucanpopfromnormalprogramflowintotheappropriate
exceptionhandler.Doingsowouldntbeuseful,however,ifthingswerentcleanedupproperlyasthe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
24/458
5/3/2015
ThinkinginC++2ndedVolume2
exceptionwasthrown.C++exceptionhandlingguaranteesthatasyouleaveascope,allobjectsinthat
scopewhoseconstructorshavebeencompletedwillhavetheirdestructorscalled.
Heresanexamplethatdemonstratesthatconstructorsthatarentcompleteddonthavetheassociated
destructorscalled.Italsoshowswhathappenswhenanexceptionisthrowninthemiddleofthecreationof
anarrayofobjects:
//:C01:Cleanup.cpp
//Exceptionscleanupcompleteobjectsonly.
#include<iostream>
usingnamespacestd
classTrace{
staticintcounter
intobjid
public:
Trace(){
objid=counter++
cout<<"constructingTrace#"<<objid<<endl
if(objid==3)throw3
}
~Trace(){
cout<<"destructingTrace#"<<objid<<endl
}
}
intTrace::counter=0
intmain(){
try{
Tracen1
//Throwsexception:
Tracearray[5]
Tracen2//Won'tgethere.
}catch(inti){
cout<<"caught"<<i<<endl
}
}///:~
TheclassTracekeepstrackofobjectssothatyoucantraceprogramprogress.Itkeepsacountofthe
numberofobjectscreatedwithastaticdatamembercounterandtracksthenumberoftheparticular
objectwithobjid.
Themainprogramcreatesasingleobject,n1(objid0),andthenattemptstocreateanarrayoffive
Traceobjects,butanexceptionisthrownbeforethefourthobject(#3)isfullycreated.Theobjectn2is
nevercreated.Youcanseetheresultsintheoutputoftheprogram:
constructingTrace#0
constructingTrace#1
constructingTrace#2
constructingTrace#3
destructingTrace#2
destructingTrace#1
destructingTrace#0
caught3
Threearrayelementsaresuccessfullycreated,butinthemiddleoftheconstructorforthefourthelement,
anexceptionisthrown.Becausethefourthconstructioninmain()(forarray[2])nevercompletes,only
thedestructorsforobjectsarray[1]andarray[0]arecalled.Finally,objectn1isdestroyed,butnot
objectn2,becauseitwasnevercreated.
Resourcemanagement
Whenwritingcodewithexceptions,itsparticularlyimportantthatyoualwaysask,Ifanexceptionoccurs,
willmyresourcesbeproperlycleanedup?Mostofthetimeyourefairlysafe,butinconstructorstheresa
particularproblem:ifanexceptionisthrownbeforeaconstructoriscompleted,theassociateddestructor
willnotbecalledforthatobject.Thus,youmustbeespeciallydiligentwhilewritingyourconstructor.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
25/458
5/3/2015
ThinkinginC++2ndedVolume2
Thedifficultyisinallocatingresourcesinconstructors.Ifanexceptionoccursintheconstructor,the
destructordoesntgetachancetodeallocatetheresource.Thisproblemoccursmostoftenwithnaked
pointers.Forexample:
//:C01:Rawp.cpp
//Nakedpointers.
#include<iostream>
#include<cstddef>
usingnamespacestd
classCat{
public:
Cat(){cout<<"Cat()"<<endl}
~Cat(){cout<<"~Cat()"<<endl}
}
classDog{
public:
void*operatornew(size_tsz){
cout<<"allocatingaDog"<<endl
throw47
}
voidoperatordelete(void*p){
cout<<"deallocatingaDog"<<endl
::operatordelete(p)
}
}
classUseResources{
Cat*bp
Dog*op
public:
UseResources(intcount=1){
cout<<"UseResources()"<<endl
bp=newCat[count]
op=newDog
}
~UseResources(){
cout<<"~UseResources()"<<endl
delete[]bp//Arraydelete
deleteop
}
}
intmain(){
try{
UseResourcesur(3)
}catch(int){
cout<<"insidehandler"<<endl
}
}///:~
Theoutputis
UseResources()
Cat()
Cat()
Cat()
allocatingaDog
insidehandler
TheUseResourcesconstructorisentered,andtheCatconstructorissuccessfullycompletedforthethree
arrayobjects.However,insideDog::operatornew(),anexceptionisthrown(tosimulateanoutof
memoryerror).Suddenly,youendupinsidethehandler,withouttheUseResourcesdestructorbeing
called.ThisiscorrectbecausetheUseResourcesconstructorwasunabletofinish,butitalsomeansthe
Catobjectsthatweresuccessfullycreatedontheheapwereneverdestroyed.
Makingeverythinganobject
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
26/458
5/3/2015
ThinkinginC++2ndedVolume2
Topreventsuchresourceleaks,youmustguardagainsttheserawresourceallocationsinoneoftwo
ways:
Youcancatchexceptionsinsidetheconstructorandthenreleasetheresource.
Youcanplacetheallocationsinsideanobjectsconstructor,andyoucanplacethedeallocations
insideanobjectsdestructor.
Usingthelatterapproach,eachallocationbecomesatomic,byvirtueofbeingpartofthelifetimeofalocal
object,andifitfails,theotherresourceallocationobjectsareproperlycleanedupduringstackunwinding.
ThistechniqueiscalledResourceAcquisitionIsInitialization(RAIIforshort)becauseitequatesresource
controlwithobjectlifetime.Usingtemplatesisanexcellentwaytomodifythepreviousexampletoachieve
this:
//:C01:Wrapped.cpp
//Safe,atomicpointers.
#include<iostream>
#include<cstddef>
usingnamespacestd
//Simplified.Yoursmayhaveotherarguments.
template<classT,intsz=1>classPWrap{
T*ptr
public:
classRangeError{}//Exceptionclass
PWrap(){
ptr=newT[sz]
cout<<"PWrapconstructor"<<endl
}
~PWrap(){
delete[]ptr
cout<<"PWrapdestructor"<<endl
}
T&operator[](inti)throw(RangeError){
if(i>=0&&i<sz)returnptr[i]
throwRangeError()
}
}
classCat{
public:
Cat(){cout<<"Cat()"<<endl}
~Cat(){cout<<"~Cat()"<<endl}
voidg(){}
}
classDog{
public:
void*operatornew[](size_t){
cout<<"AllocatingaDog"<<endl
throw47
}
voidoperatordelete[](void*p){
cout<<"DeallocatingaDog"<<endl
::operatordelete[](p)
}
}
classUseResources{
PWrap<Cat,3>cats
PWrap<Dog>dog
public:
UseResources(){cout<<"UseResources()"<<endl}
~UseResources(){cout<<"~UseResources()"<<endl}
voidf(){cats[1].g()}
}
intmain(){
try{
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
27/458
5/3/2015
ThinkinginC++2ndedVolume2
UseResourcesur
}catch(int){
cout<<"insidehandler"<<endl
}catch(...){
cout<<"insidecatch(...)"<<endl
}
}///:~
Thedifferenceistheuseofthetemplatetowrapthepointersandmakethemintoobjects.The
constructorsfortheseobjectsarecalledbeforethebodyoftheUseResourcesconstructor,andanyof
theseconstructorsthatcompletebeforeanexceptionisthrownwillhavetheirassociateddestructorscalled
duringstackunwinding.
ThePWraptemplateshowsamoretypicaluseofexceptionsthanyouveseensofar:Anestedclasscalled
RangeErroriscreatedtouseinoperator[]ifitsargumentisoutofrange.Becauseoperator[]returns
areference,itcannotreturnzero.(Therearenonullreferences.)Thisisatrueexceptionalconditionyou
dontknowwhattodointhecurrentcontextandyoucantreturnanimprobablevalue.Inthisexample,
RangeError[5]issimpleandassumesallthenecessaryinformationisintheclassname,butyoumight
alsowanttoaddamemberthatcontainsthevalueoftheindex,ifthatisuseful.
Nowtheoutputis
Cat()
Cat()
Cat()
PWrapconstructor
allocatingaDog
~Cat()
~Cat()
~Cat()
PWrapdestructor
insidehandler
Again,thestorageallocationforDogthrowsanexception,butthistimethearrayofCatobjectsis
properlycleanedup,sothereisnomemoryleak.
auto_ptr
SincedynamicmemoryisthemostfrequentresourceusedinatypicalC++program,thestandard
providesanRAIIwrapperforpointerstoheapmemorythatautomaticallyfreesthememory.Theauto_ptr
classtemplate,definedinthe<memory>header,hasaconstructorthattakesapointertoitsgeneric
type(whateveryouuseinyourcode).Theauto_ptrclasstemplatealsooverloadsthepointeroperators*
and>toforwardtheseoperationstotheoriginalpointertheauto_ptrobjectisholding.Soyoucanuse
theauto_ptrobjectasifitwerearawpointer.Hereshowitworks:
//:C01:Auto_ptr.cpp
//IllustratestheRAIInatureofauto_ptr.
#include<memory>
#include<iostream>
#include<cstddef>
usingnamespacestd
classTraceHeap{
inti
public:
staticvoid*operatornew(size_tsiz){
void*p=::operatornew(siz)
cout<<"AllocatingTraceHeapobjectontheheap"
<<"ataddress"<<p<<endl
returnp
}
staticvoidoperatordelete(void*p){
cout<<"DeletingTraceHeapobjectataddress"
<<p<<endl
::operatordelete(p)
}
TraceHeap(inti):i(i){}
intgetVal()const{returni}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
28/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
auto_ptr<TraceHeap>pMyObject(newTraceHeap(5))
cout<<pMyObject>getVal()<<endl//Prints5
}///:~
TheTraceHeapclassoverloadstheoperatornewandoperatordeletesoyoucanseeexactlywhats
happening.Noticethat,likeanyotherclasstemplate,youspecifythetypeyouregoingtouseina
templateparameter.YoudontsayTraceHeap*,howeverauto_ptralreadyknowsthatitwillbestoring
apointertoyourtype.Thesecondlineofmain()verifiesthatauto_ptrsoperator>()functionapplies
theindirectiontotheoriginal,underlyingpointer.Mostimportant,eventhoughwedidntexplicitlydelete
theoriginalpointer,pMyObjectsdestructordeletestheoriginalpointerduringstackunwinding,asthe
followingoutputverifies:
AllocatingTraceHeapobjectontheheapataddress8930040
5
DeletingTraceHeapobjectataddress8930040
Theauto_ptrclasstemplateisalsohandyforpointerdatamembers.Sinceclassobjectscontainedby
valuearealwaysdestructed,auto_ptrmembersalwaysdeletetherawpointertheywrapwhenthe
containingobjectisdestructed.[6]
Functionleveltryblocks
Sinceconstructorscanroutinelythrowexceptions,youmightwanttohandleexceptionsthatoccurwhenan
objectsmemberorbasesubobjectsareinitialized.Todothis,youcanplacetheinitializationofsuch
subobjectsinafunctionleveltryblock.Inadeparturefromtheusualsyntax,thetryblockforconstructor
initializersistheconstructorbody,andtheassociatedcatchblockfollowsthebodyoftheconstructor,as
inthefollowingexample:
//:C01:InitExcept.cpp{bor}
//Handlesexceptionsfromsubobjects.
#include<iostream>
usingnamespacestd
classBase{
inti
public:
classBaseExcept{}
Base(inti):i(i){throwBaseExcept()}
}
classDerived:publicBase{
public:
classDerivedExcept{
constchar*msg
public:
DerivedExcept(constchar*msg):msg(msg){}
constchar*what()const{returnmsg}
}
Derived(intj)try:Base(j){
//Constructorbody
cout<<"Thiswon'tprint"<<endl
}catch(BaseExcept&){
throwDerivedExcept("Basesubobjectthrew")
}
}
intmain(){
try{
Derivedd(3)
}catch(Derived::DerivedExcept&d){
cout<<d.what()<<endl//"Basesubobjectthrew"
}
}///:~
NoticethattheinitializerlistintheconstructorforDerivedgoesafterthetrykeywordbutbeforethe
constructorbody.Ifanexceptiondoesoccur,thecontainedobjectisnotconstructed,soitmakesnosense
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
29/458
5/3/2015
ThinkinginC++2ndedVolume2
toreturntothecodethatcreatedit.Forthisreason,theonlysensiblethingtodoistothrowanexception
inthefunctionlevelcatchclause.
Althoughitisnotterriblyuseful,C++alsoallowsfunctionleveltryblocksforanyfunction,asthe
followingexampleillustrates:
//:C01:FunctionTryBlock.cpp{bor}
//Functionleveltryblocks.
//{RunByHand}(Dontrunautomaticallybythemakefile)
#include<iostream>
usingnamespacestd
intmain()try{
throw"main"
}catch(constchar*msg){
cout<<msg<<endl
return1
}///:~
Inthiscase,thecatchblockcanreturninthesamemannerthatthefunctionbodynormallyreturns.Using
thistypeoffunctionleveltryblockisntmuchdifferentfrominsertingatrycatcharoundthecodeinside
ofthefunctionbody.
Standardexceptions
TheexceptionsusedwiththeStandardC++libraryarealsoavailableforyouruse.Generallyitseasier
andfastertostartwithastandardexceptionclassthantotrytodefineyourown.Ifthestandardclass
doesntdoexactlywhatyouneed,youcanderivefromit.
Allstandardexceptionclassesderiveultimatelyfromtheclassexception,definedintheheader
<exception>.Thetwomainderivedclassesarelogic_errorandruntime_error,whicharefoundin
<stdexcept>(whichitselfincludes<exception>).Theclasslogic_errorrepresentserrorsin
programminglogic,suchaspassinganinvalidargument.Runtimeerrorsarethosethatoccurastheresult
ofunforeseenforcessuchashardwarefailureormemoryexhaustion.Bothruntime_errorand
logic_errorprovideaconstructorthattakesastd::stringargumentsothatyoucanstoreamessagein
theexceptionobjectandextractitlaterwithexception::what(),asthefollowingprogramillustrates:
//:C01:StdExcept.cpp
//Derivesanexceptionclassfromstd::runtime_error.
#include<stdexcept>
#include<iostream>
usingnamespacestd
classMyError:publicruntime_error{
public:
MyError(conststring&msg=""):runtime_error(msg){}
}
intmain(){
try{
throwMyError("mymessage")
}catch(MyError&x){
cout<<x.what()<<endl
}
}///:~
Althoughtheruntime_errorconstructorinsertsthemessageintoitsstd::exceptionsubobject,
std::exceptiondoesnotprovideaconstructorthattakesastd::stringargument.Youllusuallywantto
deriveyourexceptionclassesfromeitherruntime_errororlogic_error(oroneoftheirderivatives),and
notfromstd::exception.
Thefollowingtablesdescribethestandardexceptionclasses:
exception
Thebaseclassforalltheexceptions
thrownbytheC++Standardlibrary.You
canaskwhat()andretrievetheoptional
stringwithwhichtheexceptionwas
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
30/458
5/3/2015
ThinkinginC++2ndedVolume2
initialized.
logic_error
Derivedfromexception.Reportsprogram
logicerrors,whichcouldpresumablybe
detectedbyinspection.
runtime_error
Derivedfromexception.Reportsruntime
errors,whichcanpresumablybedetected
onlywhentheprogramexecutes.
Theiostreamexceptionclassios::failureisalsoderivedfromexception,butithasnofurthersubclasses.
Youcanusetheclassesinbothofthefollowingtablesastheyare,oryoucanusethemasbaseclasses
fromwhichtoderiveyourownmorespecifictypesofexceptions.
Exceptionclassesderivedfromlogic_error
domain_error
Reportsviolationsofa
precondition.
invalid_argument
Indicatesaninvalidargumentto
thefunctionfromwhichitis
thrown.
length_error
Indicatesanattempttoproduce
anobjectwhoselengthisgreater
thanorequaltonpos(thelargest
representablevalueofcontexts
sizetype,usuallystd::size_t).
out_of_range
Reportsanoutofrangeargument.
bad_cast
Thrownforexecutinganinvalid
dynamic_castexpressionin
runtimetypeidentification(see
Chapter8).
bad_typeid
Reportsanullpointerpinan
expressiontypeid(*p).(Again,a
runtimetypeidentificationfeature
inChapter8).
Exceptionclassesderivedfromruntime_error
range_error
Reportsviolationofapostcondition.
overflow_error
Reportsanarithmeticoverflow.
bad_alloc
Reportsafailuretoallocatestorage.
Exceptionspecifications
Yourenotrequiredtoinformthepeopleusingyourfunctionwhatexceptionsyoumightthrow.However,
failuretodosocanbeconsidereduncivilizedbecauseitmeansthatuserscannotbesurewhatcodeto
writetocatchallpotentialexceptions.Iftheyhaveyoursourcecode,theycanhuntthroughandlookfor
throwstatements,butoftenalibrarydoesntcomewithsources.Gooddocumentationcanhelpalleviate
thisproblem,buthowmanysoftwareprojectsarewelldocumented?C++providessyntaxtotelltheuser
theexceptionsthatarethrownbythisfunction,sotheusercanhandlethem.Thisistheoptionalexception
specification,whichadornsafunctionsdeclaration,appearingaftertheargumentlist.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
31/458
5/3/2015
ThinkinginC++2ndedVolume2
Theexceptionspecificationreusesthekeywordthrow,followedbyaparenthesizedlistofallthetypesof
potentialexceptionsthatthefunctioncanthrow.Yourfunctiondeclarationmightlooklikethis:
voidf()throw(toobig,toosmall,divzero)
Asfarasexceptionsareconcerned,thetraditionalfunctiondeclaration
voidf()
meansthatanytypeofexceptioncanbethrownfromthefunction.Ifyousay
voidf()throw()
noexceptionswhatsoeverwillbethrownfromthefunction(soyoudbetterbesurethatnofunctions
fartherdowninthecallchainletanyexceptionspropagateup!).
Forgoodcodingpolicy,gooddocumentation,andeaseofuseforthefunctioncaller,considerusing
exceptionspecificationswhenyouwritefunctionsthatthrowexceptions.(Variationsonthisguidelineare
discussedlaterinthischapter.)
Theunexpected()function
Ifyourexceptionspecificationclaimsyouregoingtothrowacertainsetofexceptionsandthenyouthrow
somethingthatisntinthatset,whatsthepenalty?Thespecialfunctionunexpected()iscalledwhenyou
throwsomethingotherthanwhatappearsintheexceptionspecification.Shouldthisunfortunatesituation
occur,thedefaultunexpected()callstheterminate()functiondescribedearlierinthischapter.
Theset_unexpected()function
Liketerminate(),theunexpected()mechanisminstallsyourownfunctiontorespondtounexpected
exceptions.Youdosowithafunctioncalledset_unexpected(),which,likeset_terminate(),takesthe
addressofafunctionwithnoargumentsandvoidreturnvalue.Also,becauseitreturnsthepreviousvalue
oftheunexpected()pointer,youcansaveitandrestoreitlater.Touseset_unexpected(),includethe
headerfile<exception>.Heresanexamplethatshowsasimpleuseofthefeaturesdiscussedsofarin
thissection:
//:C01:Unexpected.cpp
//Exceptionspecifications&unexpected(),
//{msc}(Doesntterminateproperly)
#include<exception>
#include<iostream>
usingnamespacestd
classUp{}
classFit{}
voidg()
voidf(inti)throw(Up,Fit){
switch(i){
case1:throwUp()
case2:throwFit()
}
g()
}
//voidg(){}//Version1
voidg(){throw47}//Version2
voidmy_unexpected(){
cout<<"unexpectedexceptionthrown"<<endl
exit(0)
}
intmain(){
set_unexpected(my_unexpected)//(Ignoresreturnvalue)
for(inti=1i<=3i++)
try{
f(i)
}catch(Up){
cout<<"Upcaught"<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
32/458
5/3/2015
ThinkinginC++2ndedVolume2
}catch(Fit){
cout<<"Fitcaught"<<endl
}
}///:~
TheclassesUpandFitarecreatedsolelytothrowasexceptions.Oftenexceptionclasseswillbesmall,
buttheycancertainlyholdadditionalinformationsothatthehandlerscanqueryforit.
Thef()functionpromisesinitsexceptionspecificationtothrowonlyexceptionsoftypeUpandFit,and
fromlookingatthefunctiondefinition,thisseemsplausible.Versiononeofg(),calledbyf(),doesnt
throwanyexceptions,sothisistrue.Butifsomeonechangesg()sothatitthrowsadifferenttypeof
exception(likethesecondversioninthisexample,whichthrowsanint),theexceptionspecificationfor
f()isviolated.
Themy_unexpected()functionhasnoargumentsorreturnvalue,followingtheproperformforacustom
unexpected()function.Itsimplydisplaysamessagesothatyoucanseethatitwascalled,andthen
exitstheprogram(exit(0)isusedheresothatthebooksmakeprocessisnotaborted).Yournew
unexpected()functionshouldnothaveareturnstatement.
Inmain(),thetryblockiswithinaforloop,soallthepossibilitiesareexercised.Inthisway,youcan
achievesomethinglikeresumption.Nestthetryblockinsideafor,while,do,orifandcauseany
exceptionstoattempttorepairtheproblemthenattemptthetryblockagain.
OnlytheUpandFitexceptionsarecaughtbecausethosearetheonlyexceptionsthattheprogrammerof
f()saidwouldbethrown.Versiontwoofg()causesmy_unexpected()tobecalledbecausef()then
throwsanint.
Inthecalltoset_unexpected(),thereturnvalueisignored,butitcanalsobesavedinapointerto
functionandberestoredlater,aswedidintheset_terminate()exampleearlierinthischapter.
Atypicalunexpectedhandlerlogstheerrorandterminatestheprogrambycallingexit().Itcan,
however,throwanotherexception(orrethrowthesameexception)orcallabort().Ifitthrowsan
exceptionofatypeallowedbythefunctionwhosespecificationwasoriginallyviolated,thesearchresumes
atthecallofthefunctionwiththisexceptionspecification.(Thisbehaviorisuniquetounexpected().)
Iftheexceptionthrownfromyourunexpectedhandlerisnotallowedbytheoriginalfunctions
specification,oneofthefollowingoccurs:
1.Ifstd::bad_exception(definedin<exception>)wasinthefunctionsexceptionspecification,the
exceptionthrownfromtheunexpectedhandlerisreplacedwithastd::bad_exceptionobject,and
thesearchresumesfromthefunctionasbefore.
2.Iftheoriginalfunctionsspecificationdidnotincludestd::bad_exception,terminate()iscalled.
Thefollowingprogramillustratesthisbehavior:
//:C01:BadException.cpp{bor}
#include<exception>//Forstd::bad_exception
#include<iostream>
#include<cstdio>
usingnamespacestd
//Exceptionclasses:
classA{}
classB{}
//terminate()handler
voidmy_thandler(){
cout<<"terminatecalled"<<endl
exit(0)
}
//unexpected()handlers
voidmy_uhandler1(){throwA()}
voidmy_uhandler2(){throw}
//Ifweembedthisthrowstatementinforg,
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
33/458
5/3/2015
ThinkinginC++2ndedVolume2
//thecompilerdetectstheviolationandreports
//anerror,soweputitinitsownfunction.
voidt(){throwB()}
voidf()throw(A){t()}
voidg()throw(A,bad_exception){t()}
intmain(){
set_terminate(my_thandler)
set_unexpected(my_uhandler1)
try{
f()
}catch(A&){
cout<<"caughtanAfromf"<<endl
}
set_unexpected(my_uhandler2)
try{
g()
}catch(bad_exception&){
cout<<"caughtabad_exceptionfromg"<<endl
}
try{
f()
}catch(...){
cout<<"Thiswillneverprint"<<endl
}
}///:~
Themy_uhandler1()handlerthrowsanacceptableexception(A),soexecutionresumesatthefirst
catch,whichsucceeds.Themy_uhandler2()handlerdoesnotthrowavalidexception(B),butsinceg
specifiesbad_exception,theBexceptionisreplacedbyabad_exceptionobject,andthesecondcatch
alsosucceeds.Sincefdoesnotincludebad_exceptioninitsspecification,my_thandler()iscalledasa
terminatehandler.Herestheoutput:
caughtanAfromf
caughtabad_exceptionfromg
terminatecalled
Betterexceptionspecifications?
Youmayfeelthattheexistingexceptionspecificationrulesarentverysafe,andthat
voidf()
shouldmeanthatnoexceptionsarethrownfromthisfunction.Iftheprogrammerwantstothrowanytype
ofexception,youmightthinkheorsheshouldhavetosay
voidf()throw(...)//NotinC++
Thiswouldsurelybeanimprovementbecausefunctiondeclarationswouldbemoreexplicit.Unfortunately,
youcantalwaysknowbylookingatthecodeinafunctionwhetheranexceptionwillbethrownitcould
happenbecauseofamemoryallocation,forexample.Worse,existingfunctionswrittenbeforeexception
handlingwasintroducedintothelanguagemayfindthemselvesinadvertentlythrowingexceptionsbecause
ofthefunctionstheycall(whichmightbelinkedintonew,exceptionthrowingversions).Hence,the
uninformativesituationwhereby
voidf()
means,MaybeIllthrowanexception,maybeIwont.Thisambiguityisnecessarytoavoidhindering
codeevolution.Ifyouwanttospecifythatfthrowsnoexceptions,usetheemptylist,asin:
voidf()throw()
Exceptionspecificationsandinheritance
Eachpublicfunctioninaclassessentiallyformsacontractwiththeuserifyoupassitcertainarguments,
itwillperformcertainoperationsand/orreturnaresult.Thesamecontractmustholdtrueinderived
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
34/458
5/3/2015
ThinkinginC++2ndedVolume2
classesotherwisetheexpectedisarelationshipbetweenderivedandbaseclassesisviolated.Since
exceptionspecificationsarelogicallypartofafunctionsdeclaration,theytoomustremainconsistent
acrossaninheritancehierarchy.Forexample,ifamemberfunctioninabaseclasssaysitwillonlythrow
anexceptionoftypeA,anoverrideofthatfunctioninaderivedclassmustnotaddanyotherexception
typestothespecificationlistbecausethatwouldbreakanyprogramsthatadheretothebaseclass
interface.Youcan,however,specifyfewerexceptionsornoneatall,sincethatdoesntrequiretheuserto
doanythingdifferently.YoucanalsospecifyanythingthatisaAinplaceofAinthederivedfunctions
specification.Heresanexample.
//:C01:Covariance.cpp{xo}
//Shouldcausecompileerror.{mwcc}{msc}
#include<iostream>
usingnamespacestd
classBase{
public:
classBaseException{}
classDerivedException:publicBaseException{}
virtualvoidf()throw(DerivedException){
throwDerivedException()
}
virtualvoidg()throw(BaseException){
throwBaseException()
}
}
classDerived:publicBase{
public:
voidf()throw(BaseException){
throwBaseException()
}
virtualvoidg()throw(DerivedException){
throwDerivedException()
}
}///:~
AcompilershouldflagtheoverrideofDerived::f()withanerror(oratleastawarning)sinceitchanges
itsexceptionspecificationinawaythatviolatesthespecificationofBase::f().Thespecificationfor
Derived::g()isacceptablebecauseDerivedExceptionisaBaseException(nottheotherway
around).YoucanthinkofBase/DerivedandBaseException/DerivedExceptionasparallelclass
hierarchieswhenyouareinDerived,youcanreplacereferencestoBaseExceptioninexception
specificationsandreturnvalueswithDerivedException.Thisbehavioriscalledcovariance(sinceboth
setsofclassesvarydowntheirrespectivehierarchiestogether).(ReminderfromVolume1:parameter
typesarenotcovariantyouarenotallowedtochangethesignatureofanoverriddenvirtualfunction.)
Whennottouseexceptionspecifications
IfyouperusethefunctiondeclarationsthroughouttheStandardC++library,youllfindthatnotasingle
exceptionspecificationoccursanywhere!Althoughthismightseemstrange,thereisagoodreasonforthis
seemingincongruity:thelibraryconsistsmainlyoftemplates,andyouneverknowwhatagenerictypeor
functionmightdo.Forexample,supposeyouaredevelopingagenericstacktemplateandattempttoaffix
anexceptionspecificationtoyourpopfunction,likethis:
Tpop()throw(logic_error)
Sincetheonlyerroryouanticipateisastackunderflow,youmightthinkitssafetospecifyalogic_error
orsomeotherappropriateexceptiontype.ButtypeTscopyconstructorcouldthrowanexception.Then
unexpected()wouldbecalled,andyourprogramwouldterminate.Youcantmakeunsupportable
guarantees.Ifyoudontknowwhatexceptionsmightoccur,dontuseexceptionspecifications.Thatswhy
templateclasses,whichconstitutethemajorityoftheStandardC++library,donotuseexception
specificationstheyspecifytheexceptionstheyknowaboutindocumentationandleavetheresttoyou.
Exceptionspecificationsaremainlyfornontemplateclasses.
Exceptionsafety
InChapter7welltakeanindepthlookatthecontainersintheStandardC++library,includingthestack
container.Onethingyoullnoticeisthatthedeclarationofthepop()memberfunctionlookslikethis:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
35/458
5/3/2015
ThinkinginC++2ndedVolume2
voidpop()
Youmightthinkitstrangethatpop()doesntreturnavalue.Instead,itjustremovestheelementatthe
topofthestack.Toretrievethetopvalue,calltop()beforeyoucallpop().Thereisanimportantreason
forthisbehavior,andithastodowithexceptionsafety,acrucialconsiderationinlibrarydesign.Thereare
differentlevelsofexceptionsafety,butmostimportantly,andjustasthenameimplies,exceptionsafetyis
aboutcorrectsemanticsinthefaceofexceptions.
Supposeyouareimplementingastackwithadynamicarray(wellcallitdataandthecounterinteger
count),andyoutrytowritepop()sothatitreturnsavalue.Thecodeforsuchapop()mightlook
somethinglikethis:
template<classT>Tstack<T>::pop(){
if(count==0)
throwlogic_error("stackunderflow")
else
returndata[count]
}
Whathappensifthecopyconstructorthatiscalledforthereturnvalueinthelastlinethrowsanexception
whenthevalueisreturned?Thepoppedelementisnotreturnedbecauseoftheexception,andyetcount
hasalreadybeendecremented,sothetopelementyouwantedislostforever!Theproblemisthatthis
functionattemptstodotwothingsatonce:(1)returnavalue,and(2)changethestateofthestack.Itis
bettertoseparatethesetwoactionsintotwoseparatememberfunctions,whichisexactlywhatthe
standardstackclassdoes.(Inotherwords,followthedesignpracticeofcohesioneveryfunctionshould
doonethingwell.)Exceptionsafecodeleavesobjectsinaconsistentstateanddoesnotleakresources.
Youalsoneedtobecarefulwritingcustomassignmentoperators.InChapter12ofVolume1,yousawthat
operator=shouldadheretothefollowingpattern:
1.Makesureyourenotassigningtoself.Ifyouare,gotostep6.(Thisisstrictlyanoptimization.)
2.Allocatenewmemoryrequiredbypointerdatamembers.
3.Copydatafromtheoldmemorytothenew.
4.Deletetheoldmemory.
5.Updatetheobjectsstatebyassigningthenewheappointerstothepointerdatamembers.
6.Return*this.
Itsimportanttonotchangethestateofyourobjectuntilallthenewpieceshavebeensafelyallocatedand
initialized.Agoodtechniqueistomovesteps2and3intoaseparatefunction,oftencalledclone().The
followingexampledoesthisforaclassthathastwopointermembers,theStringandtheInts:
//:C01:SafeAssign.cpp
//AnExceptionsafeoperator=.
#include<iostream>
#include<new>//Forstd::bad_alloc
#include<cstring>
#include<cstddef>
usingnamespacestd
//Aclassthathastwopointermembersusingtheheap
classHasPointers{
//AHandleclasstoholdthedata
structMyData{
constchar*theString
constint*theInts
size_tnumInts
MyData(constchar*pString,constint*pInts,
size_tnInts)
:theString(pString),theInts(pInts),numInts(nInts){}
}*theData//Thehandle
//Cloneandcleanupfunctions:
staticMyData*clone(constchar*otherString,
constint*otherInts,size_tnInts){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
36/458
5/3/2015
ThinkinginC++2ndedVolume2
char*newChars=newchar[strlen(otherString)+1]
int*newInts
try{
newInts=newint[nInts]
}catch(bad_alloc&){
delete[]newChars
throw
}
try{
//Thisexampleusesbuiltintypes,soitwon't
//throw,butforclasstypesitcouldthrow,sowe
//useatryblockforillustration.(Thisisthe
//pointoftheexample!)
strcpy(newChars,otherString)
for(size_ti=0i<nInts++i)
newInts[i]=otherInts[i]
}catch(...){
delete[]newInts
delete[]newChars
throw
}
returnnewMyData(newChars,newInts,nInts)
}
staticMyData*clone(constMyData*otherData){
returnclone(otherData>theString,otherData>theInts,
otherData>numInts)
}
staticvoidcleanup(constMyData*theData){
delete[]theData>theString
delete[]theData>theInts
deletetheData
}
public:
HasPointers(constchar*someString,constint*someInts,
size_tnumInts){
theData=clone(someString,someInts,numInts)
}
HasPointers(constHasPointers&source){
theData=clone(source.theData)
}
HasPointers&operator=(constHasPointers&rhs){
if(this!=&rhs){
MyData*newData=clone(rhs.theData>theString,
rhs.theData>theInts,rhs.theData>numInts)
cleanup(theData)
theData=newData
}
return*this
}
~HasPointers(){cleanup(theData)}
friendostream&
operator<<(ostream&os,constHasPointers&obj){
os<<obj.theData>theString<<":"
for(size_ti=0i<obj.theData>numInts++i)
os<<obj.theData>theInts[i]<<''
returnos
}
}
intmain(){
intsomeNums[]={1,2,3,4}
size_tsomeCount=sizeofsomeNums/sizeofsomeNums[0]
intsomeMoreNums[]={5,6,7}
size_tsomeMoreCount=
sizeofsomeMoreNums/sizeofsomeMoreNums[0]
HasPointersh1("Hello",someNums,someCount)
HasPointersh2("Goodbye",someMoreNums,someMoreCount)
cout<<h1<<endl//Hello:1234
h1=h2
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
37/458
5/3/2015
ThinkinginC++2ndedVolume2
cout<<h1<<endl//Goodbye:567
}///:~
Forconvenience,HasPointersusestheMyDataclassasahandletothetwopointers.Wheneveritstime
toallocatemorememory,whetherduringconstructionorassignment,thefirstclonefunctionisultimately
calledtodothejob.Ifmemoryfailsforthefirstcalltothenewoperator,abad_allocexceptionis
thrownautomatically.Ifithappensonthesecondallocation(fortheInts),wemustcleanupthememory
fortheStringhencethefirsttryblockthatcatchesabad_allocexception.Thesecondtryblockisnt
crucialherebecausewerejustcopyingintsandpointers(sonoexceptionswilloccur),butwheneveryou
copyobjects,theirassignmentoperatorscanpossiblycauseanexception,soeverythingneedstobe
cleanedup.Inbothexceptionhandlers,noticethatwerethrowtheexception.Thatsbecausewerejust
managingresourcesheretheuserstillneedstoknowthatsomethingwentwrong,sowelettheexception
propagateupthedynamicchain.Softwarelibrariesthatdontsilentlyswallowexceptionsarecalled
exceptionneutral.Alwaysstrivetowritelibrariesthatarebothexceptionsafeandexceptionneutral.[7]
Ifyouinspectthepreviouscodeclosely,youllnoticethatnoneofthedeleteoperationswillthrowan
exception.Thiscodedependsonthatfact.Recallthatwhenyoucalldeleteonanobject,theobjects
destructoriscalled.Itturnsouttobepracticallyimpossibletodesignexceptionsafecodewithout
assumingthatdestructorsdontthrowexceptions.Dontletdestructorsthrowexceptions.(Weregoingto
remindyouaboutthisoncemorebeforethischapterisdone).[8]
Programmingwithexceptions
Formostprogrammers,especiallyCprogrammers,exceptionsarenotavailableintheirexistinglanguage
andrequiresomeadjustment.Hereareguidelinesforprogrammingwithexceptions.
Whentoavoidexceptions
Exceptionsarenttheanswertoallproblemsoverusecancausetrouble.Thefollowingsectionspointout
situationswhereexceptionsarenotwarranted.Thebestadvicefordecidingwhentouseexceptionsisto
throwexceptionsonlywhenafunctionfailstomeetitsspecification.
Notforasynchronousevents
TheStandardCsignal()systemandanysimilarsystemhandleasynchronousevents:eventsthathappen
outsidetheflowofaprogram,andthuseventstheprogramcannotanticipate.YoucannotuseC++
exceptionstohandleasynchronouseventsbecausetheexceptionanditshandlerareonthesamecall
stack.Thatis,exceptionsrelyonthedynamicchainoffunctioncallsontheprogramsruntimestack(they
havedynamicscope),whereasasynchronouseventsmustbehandledbycompletelyseparatecodethatis
notpartofthenormalprogramflow(typically,interruptserviceroutinesoreventloops).Dontthrow
exceptionsfrominterrupthandlers.
Thisisnottosaythatasynchronouseventscannotbeassociatedwithexceptions.Buttheinterrupthandler
shoulddoitsjobasquicklyaspossibleandthenreturn.Thetypicalwaytohandlethissituationistoseta
flagintheinterrupthandler,andcheckitsynchronouslyinthemainlinecode.
Notforbenignerrorconditions
Ifyouhaveenoughinformationtohandleanerror,itsnotanexception.Takecareofitinthecurrent
contextratherthanthrowinganexceptiontoalargercontext.
Also,C++exceptionsarenotthrownformachineleveleventssuchasdividebyzero.[9]Itsassumedthat
someothermechanism,suchastheoperatingsystemorhardware,dealswiththeseevents.Inthisway,
C++exceptionscanbereasonablyefficient,andtheiruseisisolatedtoprogramlevelexceptional
conditions.
Notforflowofcontrol
Anexceptionlookssomewhatlikeanalternatereturnmechanismandsomewhatlikeaswitchstatement,
soyoumightbetemptedtouseanexceptioninsteadoftheseordinarylanguagemechanisms.Thisisabad
idea,partlybecausetheexceptionhandlingsystemissignificantlylessefficientthannormalprogram
execution.Exceptionsarearareevent,sothenormalprogramshouldntpayforthem.Also,exceptions
fromanythingotherthanerrorconditionsarequiteconfusingtotheuserofyourclassorfunction.
Yourenotforcedtouseexceptions
Someprogramsarequitesimple(smallutilities,forexample).Youmightonlyneedtotakeinputand
performsomeprocessing.Intheseprograms,youmightattempttoallocatememoryandfail,trytoopena
fileandfail,andsoon.Itisacceptableintheseprogramstodisplayamessageandexittheprogram,
allowingthesystemtocleanupthemess,ratherthantoworkhardtocatchallexceptionsandrecoverall
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
38/458
5/3/2015
ThinkinginC++2ndedVolume2
theresourcesyourself.Basically,ifyoudontneedexceptions,yourenotforcedtousethem.
Newexceptions,oldcode
Anothersituationthatarisesisthemodificationofanexistingprogramthatdoesntuseexceptions.You
mightintroducealibrarythatdoesuseexceptionsandwonderifyouneedtomodifyallyourcode
throughouttheprogram.Assumingyouhaveanacceptableerrorhandlingschemealreadyinplace,the
moststraightforwardthingtodoissurroundthelargestblockthatusesthenewlibrary(thismightbeall
thecodeinmain())withatryblock,followedbyacatch(...)andbasicerrormessage).Youcanrefine
thistowhateverdegreenecessarybyaddingmorespecifichandlers,but,inanycase,thecodeyoumust
addcanbeminimal.Itsevenbettertoisolateyourexceptiongeneratingcodeinatryblockandwrite
handlerstoconverttheexceptionsintoyourexistingerrorhandlingscheme.
Itstrulyimportanttothinkaboutexceptionswhenyourecreatingalibraryforsomeoneelsetouse,
especiallyifyoucantknowhowtheyneedtorespondtocriticalerrorconditions(recalltheearlier
discussionsonexceptionsafetyandwhytherearenoexceptionspecificationsintheStandardC++
Library).
Typicalusesofexceptions
Douseexceptionstodothefollowing:
Fixtheproblemandretrythefunctionthatcausedtheexception.
Patchthingsupandcontinuewithoutretryingthefunction.
Dowhateveryoucaninthecurrentcontextandrethrowthesameexceptiontoahighercontext.
Dowhateveryoucaninthecurrentcontextandthrowadifferentexceptiontoahighercontext.
Terminatetheprogram.
Wrapfunctions(especiallyClibraryfunctions)thatuseordinaryerrorschemessotheyproduce
exceptionsinstead.
Simplify.Ifyourerrorhandlingschememakesthingsmorecomplicated,itispainfulandannoyingto
use.Exceptionscanbeusedtomakeerrorhandlingsimplerandmoreeffective.
Makeyourlibraryandprogramsafer.Thisisashortterminvestment(fordebugging)andalongterm
investment(forapplicationrobustness).
Whentouseexceptionspecifications
Theexceptionspecificationislikeafunctionprototype:ittellstheusertowriteexceptionhandlingcode
andwhatexceptionstohandle.Ittellsthecompilertheexceptionsthatmightcomeoutofthisfunctionso
thatitcandetectviolationsatruntime.
Youcantalwayslookatthecodeandanticipatewhichexceptionswillarisefromaparticularfunction.
Sometimes,thefunctionsitcallsproduceanunexpectedexception,andsometimesanoldfunctionthat
didntthrowanexceptionisreplacedwithanewonethatdoes,andyougetacalltounexpected().Any
timeyouuseexceptionspecificationsorcallfunctionsthatdo,considercreatingyourownunexpected()
functionthatlogsamessageandtheneitherthrowsanexceptionorabortstheprogram.
Asweexplainedearlier,youshouldavoidusingexceptionspecificationsintemplateclasses,sinceyou
cantanticipatewhattypesofexceptionsthetemplateparameterclassesmightthrow.
Startwithstandardexceptions
CheckouttheStandardC++libraryexceptionsbeforecreatingyourown.Ifastandardexceptiondoes
whatyouneed,chancesareitsaloteasierforyourusertounderstandandhandle.
Iftheexceptiontypeyouwantisntpartofthestandardlibrary,trytoinheritonefromanexisting
standardexception.Itsniceifyouruserscanalwayswritetheircodetoexpectthewhat()function
definedintheexception()classinterface.
Nestyourownexceptions
Ifyoucreateexceptionsforyourparticularclass,itsagoodideatonesttheexceptionclasseseither
insideyourclassorinsideanamespacecontainingyourclass,toprovideaclearmessagetothereader
thatthisexceptionisonlyforyourclass.Inaddition,itpreventspollutionoftheglobalnamespace.
YoucannestyourexceptionsevenifyourederivingthemfromC++Standardexceptions.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
39/458
5/3/2015
ThinkinginC++2ndedVolume2
Useexceptionhierarchies
Usingexceptionhierarchiesisavaluablewaytoclassifythetypesofcriticalerrorsthatmightbe
encounteredwithyourclassorlibrary.Thisgiveshelpfulinformationtousers,assiststheminorganizing
theircode,andgivesthemtheoptionofignoringallthespecifictypesofexceptionsandjustcatchingthe
baseclasstype.Also,anyexceptionsaddedlaterbyinheritingfromthesamebaseclasswillnotforceall
existingcodetoberewrittenthebaseclasshandlerwillcatchthenewexception.
TheStandardC++exceptionsareagoodexampleofanexceptionhierarchy.Buildyourexceptionsontop
ofitifyoucan.
Multipleinheritance(MI)
AsyoullreadinChapter9,theonlyessentialplaceforMIisifyouneedtoupcastanobjectpointertotwo
differentbaseclassesthatis,ifyouneedpolymorphicbehaviorwithbothofthosebaseclasses.Itturns
outthatexceptionhierarchiesareusefulplacesformultipleinheritancebecauseabaseclasshandlerfrom
anyoftherootsofthemultiplyinheritedexceptionclasscanhandletheexception.
Catchbyreference,notbyvalue
AsyousawinthesectionExceptionmatching,youshouldcatchexceptionsbyreferencefortworeasons:
Toavoidmakinganeedlesscopyoftheexceptionobjectwhenitispassedtothehandler.
Toavoidobjectslicingwhencatchingaderivedexceptionasabaseclassobject.
Althoughyoucanalsothrowandcatchpointers,bydoingsoyouintroducemorecouplingthethrowerand
thecatchermustagreeonhowtheexceptionobjectisallocatedandcleanedup.Thisisaproblembecause
theexceptionitselfmighthaveoccurredfromheapexhaustion.Ifyouthrowexceptionobjects,the
exceptionhandlingsystemtakescareofallstorage.
Throwexceptionsinconstructors
Becauseaconstructorhasnoreturnvalue,youvepreviouslyhadtwowaystoreportanerrorduring
construction:
Setanonlocalflagandhopetheuserchecksit.
Returnanincompletelycreatedobjectandhopetheuserchecksit.
ThisproblemisseriousbecauseCprogrammersexpectthatobjectcreationisalwayssuccessful,whichis
notunreasonableinCbecausethetypesaresoprimitive.Butcontinuingexecutionafterconstructionfails
inaC++programisaguaranteeddisaster,soconstructorsareoneofthemostimportantplacestothrow
exceptionsnowyouhaveasafe,effectivewaytohandleconstructorerrors.However,youmustalsopay
attentiontopointersinsideobjectsandthewaycleanupoccurswhenanexceptionisthrowninsidea
constructor.
Dontcauseexceptionsindestructors
Becausedestructorsarecalledintheprocessofthrowingotherexceptions,youllneverwanttothrowan
exceptioninadestructororcauseanotherexceptiontobethrownbysomeactionyouperforminthe
destructor.Ifthishappens,anewexceptioncanbethrownbeforethecatchclauseforanexisting
exceptionisreached,whichwillcauseacalltoterminate().
Ifyoucallanyfunctionsinsideadestructorthatcanthrowexceptions,thosecallsshouldbewithinatry
blockinthedestructor,andthedestructormusthandleallexceptionsitself.Nonemustescapefromthe
destructor.
Avoidnakedpointers
SeeWrapped.cppearlierinthischapter.Anakedpointerusuallymeansvulnerabilityintheconstructorif
resourcesareallocatedforthatpointer.Apointerdoesnthaveadestructor,sothoseresourcesarent
releasedifanexceptionisthrownintheconstructor.Useauto_ptrorothersmartpointertypes[10]for
pointersthatreferenceheapmemory.
Overhead
Whenanexceptionisthrown,theresconsiderableruntimeoverhead(butitsgoodoverhead,sinceobjects
arecleanedupautomatically!).Forthisreason,youneverwanttouseexceptionsaspartofyournormal
flowofcontrol,nomatterhowtemptingandcleveritmayseem.Exceptionsshouldoccuronlyrarely,so
theoverheadispiledontheexceptionandnotonthenormallyexecutingcode.Oneoftheimportantdesign
goalsforexceptionhandlingwasthatitcouldbeimplementedwithnoimpactonexecutionspeedwhenit
wasntusedthatis,aslongasyoudontthrowanexception,yourcoderunsasfastasitwouldwithout
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
40/458
5/3/2015
ThinkinginC++2ndedVolume2
exceptionhandling.Whetherthisistruedependsontheparticularcompilerimplementationyoureusing.
(Seethedescriptionofthezerocostmodellaterinthissection.)
Youcanthinkofathrowexpressionasacalltoaspecialsystemfunctionthattakestheexceptionobject
asanargumentandbacktracksupthechainofexecution.Forthistowork,extrainformationneedstobe
putonthestackbythecompiler,toaidinstackunwinding.Tounderstandthis,youneedtoknowaboutthe
runtimestack.
Wheneverafunctioniscalled,informationaboutthatfunctionispushedontotheruntimestackinan
activationrecordinstance(ARI),alsocalledastackframe.Atypicalstackframecontainstheaddressof
thecallingfunction(soexecutioncanreturntoit),apointertotheARIofthefunctionsstaticparent(the
scopethatlexicallycontainsthecalledfunction,sovariablesglobaltothefunctioncanbeaccessed),anda
pointertothefunctionthatcalledit(itsdynamicparent).Thepaththatlogicallyresultsfromrepetitively
followingthedynamicparentlinksisthedynamicchain,orcallchain,thatwevementionedpreviouslyin
thischapter.Thisishowexecutioncanbacktrackwhenanexceptionisthrown,anditisthemechanism
thatmakesitpossibleforcomponentsdevelopedwithoutknowledgeofoneanothertocommunicateerrors
atruntime.
Toenablestackunwindingforexceptionhandling,extraexceptionrelatedinformationabouteachfunction
needstobeavailableforeachstackframe.Thisinformationdescribeswhichdestructorsneedtobecalled
(sothatlocalobjectscanbecleanedup),indicateswhetherthecurrentfunctionhasatryblock,andlists
whichexceptionstheassociatedcatchclausescanhandle.Thereisspacepenaltyforthisextrainformation,
soprogramsthatsupportexceptionhandlingcanbesomewhatlargerthanthosethatdont.[11]Eventhe
compiletimesizeofprogramsusingexceptionhandlingisgreater,sincethelogicofhowtogeneratethe
expandedstackframesduringruntimemustbegeneratedbythecompiler.
Toillustratethis,wecompiledthefollowingprogrambothwithandwithoutexceptionhandlingsupportin
BorlandC++BuilderandMicrosoftVisualC++:[12]
//:C01:HasDestructor.cpp{O}
classHasDestructor{
public:
~HasDestructor(){}
}
voidg()//Forallweknow,gmaythrow.
voidf(){
HasDestructorh
g()
}///:~
Ifexceptionhandlingisenabled,thecompilermustkeepinformationabout~HasDestructor()available
atruntimeintheARIforf()(soitcandestroyhproperlyshouldg()throwanexception).Thefollowing
tablesummarizestheresultofthecompilationsintermsofthesizeofthecompiled(.obj)files(inbytes).
Compiler\Mode
WithException
Support
WithoutException
Support
Borland
616
234
Microsoft
1162
680
Donttakethepercentagedifferencesbetweenthetwomodestooseriously.Rememberthatexceptions
(should)typicallyconstituteasmallpartofaprogram,sothespaceoverheadtendstobemuchsmaller
(usuallybetween5and15percent).
Thisextrahousekeepingslowsdownexecution,butaclevercompilerimplementationavoidsthis.Since
informationaboutexceptionhandlingcodeandtheoffsetsoflocalobjectscanbecomputedonceat
compiletime,suchinformationcanbekeptinasingleplaceassociatedwitheachfunction,butnotineach
ARI.YouessentiallyremoveexceptionoverheadfromeachARIandthusavoidtheextratimetopushthem
ontothestack.Thisapproachiscalledthezerocostmodel[13]ofexceptionhandling,andtheoptimized
storagementionedearlierisknownastheshadowstack.[14]
Summary
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
41/458
5/3/2015
ThinkinginC++2ndedVolume2
Errorrecoveryisafundamentalconcernforeveryprogramyouwrite.ItsespeciallyimportantinC++
whencreatingprogramcomponentsforotherstouse.Tocreatearobustsystem,eachcomponentmustbe
robust.
ThegoalsforexceptionhandlinginC++aretosimplifythecreationoflarge,reliableprogramsusingless
codethancurrentlypossible,withmoreconfidencethatyourapplicationdoesnthaveanunhandlederror.
Thisisaccomplishedwithlittleornoperformancepenaltyandwithlowimpactonexistingcode.
Basicexceptionsarenotterriblydifficulttolearnbeginusingtheminyourprogramsassoonasyoucan.
Exceptionsareoneofthosefeaturesthatprovideimmediateandsignificantbenefitstoyourproject.
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.Writethreefunctions:onethatreturnsanerrorvaluetoindicateanerrorcondition,onethatsets
errno,andonethatusessignal().Writecodethatcallsthesefunctionsandrespondstotheerrors.
Nowwriteafourthfunctionthatthrowsanexception.Callthisfunctionandcatchtheexception.
Describethedifferencesbetweenthesefourapproaches,andwhyexceptionhandlingisan
improvement.
2.Createaclasswithmemberfunctionsthatthrowexceptions.Withinthisclass,makeanestedclass
touseasanexceptionobject.Ittakesasingleconstchar*asitsargumentthisrepresentsa
descriptionstring.Createamemberfunctionthatthrowsthisexception.(Statethisinthefunctions
exceptionspecification.)Writeatryblockthatcallsthisfunctionandacatchclausethathandles
theexceptionbydisplayingitsdescriptionstring.
3.RewritetheStashclassfromChapter13ofVolume1sothatitthrowsout_of_rangeexceptionsfor
operator[].
4.Writeagenericmain()thattakesallexceptionsandreportsthemaserrors.
5.Createaclasswithitsownoperatornew.Thisoperatorshouldallocatetenobjects,andonthe
eleventhobjectrunoutofmemoryandthrowanexception.Alsoaddastaticmemberfunction
thatreclaimsthismemory.Nowcreateamain()withatryblockandacatchclausethatcallsthe
memoryrestorationroutine.Puttheseinsideawhileloop,todemonstraterecoveringfroman
exceptionandcontinuingexecution.
6.Createadestructorthatthrowsanexception,andwritecodetoprovetoyourselfthatthisisabad
ideabyshowingthatifanewexceptionisthrownbeforethehandlerfortheexistingoneisreached,
terminate()iscalled.
7.Provetoyourselfthatallexceptionobjects(theonesthatarethrown)areproperlydestroyed.
8.Provetoyourselfthatifyoucreateanexceptionobjectontheheapandthrowthepointertothat
object,itwillnotbecleanedup.
9.Writeafunctionwithanexceptionspecificationthatcanthrowfourexceptiontypes:achar,anint,
abool,andyourownexceptionclass.Catcheachinmain()andverifythecatch.Deriveyour
exceptionclassfromastandardexception.Writethefunctioninsuchawaythatthesystem
recoversandtriestoexecuteitagain.
10.Modifyyoursolutiontothepreviousexercisetothrowadoublefromthefunction,violatingthe
exceptionspecification.Catchtheviolationwithyourownunexpectedhandlerthatdisplaysa
messageandexitstheprogramgracefully(meaningabort()isnotcalled).
11.WriteaGarageclassthathasaCarthatishavingtroubleswithitsMotor.Useafunctionleveltry
blockintheGarageclassconstructortocatchanexception(thrownfromtheMotorclass)whenits
Carobjectisinitialized.ThrowadifferentexceptionfromthebodyoftheGarageconstructors
handlerandcatchitinmain().
2:DefensiveProgramming
Writingperfectsoftwaremaybeanelusivegoalfordevelopers,butafew
defensivetechniques,routinelyapplied,cangoalongwaytowardimprovingthe
qualityofyourcode.
Althoughthecomplexityoftypicalproductionsoftwareguaranteesthattesterswillalwayshaveajob,we
hopeyoustillyearntoproducedefectfreesoftware.Objectorienteddesigntechniquesdomuchtocorral
thedifficultyoflargeprojects,buteventuallyyoumustwriteloopsandfunctions.Thesedetailsof
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
42/458
5/3/2015
ThinkinginC++2ndedVolume2
programminginthesmallbecomethebuildingblocksofthelargercomponentsneededforyourdesigns.
Ifyourloopsareoffbyoneoryourfunctionscalculatethecorrectvaluesonlymostofthetime,yourein
troublenomatterhowfancyyouroverallmethodology.Inthischapter,youllseepracticesthathelpcreate
robustcoderegardlessofthesizeofyourproject.
Yourcodeis,amongotherthings,anexpressionofyourattempttosolveaproblem.Itshouldbeclearto
thereader(includingyourself)exactlywhatyouwerethinkingwhenyoudesignedthatloop.Atcertain
pointsinyourprogram,youshouldbeabletomakeboldstatementsthatsomeconditionorotherholds.(If
youcant,youreallyhaventyetsolvedtheproblem.)Suchstatementsarecalledinvariants,sincethey
shouldinvariablybetrueatthepointwheretheyappearinthecodeifnot,eitheryourdesignisfaulty,or
yourcodedoesnotaccuratelyreflectyourdesign.
ConsideraprogramthatplaystheguessinggameofHiLo.Onepersonthinksofanumberbetween1and
100,andtheotherpersonguessesthenumber.(Wellletthecomputerdotheguessing.)Thepersonwho
holdsthenumbertellstheguesserwhethertheirguessishigh,loworcorrect.Thebeststrategyforthe
guesserisabinarysearch,whichchoosesthemidpointoftherangeofnumberswherethesoughtafter
numberresides.Thehighlowresponsetellstheguesserwhichhalfofthelistholdsthenumber,andthe
processrepeats,halvingthesizeoftheactivesearchrangeoneachiteration.Sohowdoyouwritealoop
todrivetherepetitionproperly?Itsnotsufficienttojustsay
boolguessed=false
while(!guessed){
...
}
becauseamalicioususermightresponddeceitfully,andyoucouldspendalldayguessing.What
assumption,howeversimple,areyoumakingeachtimeyouguess?Inotherwords,whatconditionshould
holdbydesignoneachloopiteration?
Thesimpleassumptionisthatthesecretnumberiswithinthecurrentactiverangeofunguessednumbers:
[1,100].Supposewelabeltheendpointsoftherangewiththevariableslowandhigh.Eachtimeyoupass
throughtheloopyouneedtomakesurethatifthenumberwasintherange[low,high]atthebeginning
oftheloop,youcalculatethenewrangesothatitstillcontainsthenumberattheendofthecurrentloop
iteration.
Thegoalistoexpresstheloopinvariantincodesothataviolationcanbedetectedatruntime.
Unfortunately,sincethecomputerdoesntknowthesecretnumber,youcantexpressthisconditiondirectly
incode,butyoucanatleastmakeacommenttothateffect:
while(!guessed){
//INVARIANT:thenumberisintherange[low,high]
...
}
Whathappenswhentheusersaysthataguessistoohighortoolowwhenitisnt?Thedeceptionwill
excludethesecretnumberfromthenewsubrange.Becauseoneliealwaysleadstoanother,eventually
yourrangewilldiminishtonothing(sinceyoushrinkitbyhalfeachtimeandthesecretnumberisntin
there).Wecanexpressthisconditioninthefollowingprogram:
//:C02:HiLo.cpp{RunByHand}
//PlaysthegameofHiLotoillustratealoopinvariant.
#include<cstdlib>
#include<iostream>
#include<string>
usingnamespacestd
intmain(){
cout<<"Thinkofanumberbetween1and100"<<endl
<<"Iwillmakeaguess"
<<"tellmeifI'm(H)ighor(L)ow"<<endl
intlow=1,high=100
boolguessed=false
while(!guessed){
//Invariant:thenumberisintherange[low,high]
if(low>high){//Invariantviolation
cout<<"Youcheated!Iquit"<<endl
returnEXIT_FAILURE
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
43/458
5/3/2015
ThinkinginC++2ndedVolume2
}
intguess=(low+high)/2
cout<<"Myguessis"<<guess<<"."
cout<<"(H)igh,(L)ow,or(E)qual?"
stringresponse
cin>>response
switch(toupper(response[0])){
case'H':
high=guess1
break
case'L':
low=guess+1
break
case'E':
guessed=true
break
default:
cout<<"Invalidresponse"<<endl
continue
}
}
cout<<"Igotit!"<<endl
returnEXIT_SUCCESS
}///:~
Theviolationoftheinvariantisdetectedwiththeconditionif(low>high),becauseiftheuseralways
tellsthetruth,wewillalwaysfindthesecretnumberbeforewerunoutofguesses.
WealsouseastandardCtechniqueforreportingprogramstatustothecallingcontextbyreturning
differentvaluesfrommain().Itisportabletousethestatementreturn0toindicatesuccess,butthere
isnoportablevaluetoindicatefailure.Forthisreasonweusethemacrodeclaredforthispurposein
<cstdlib>:EXIT_FAILURE.Forconsistency,wheneverweuseEXIT_FAILUREwealsouse
EXIT_SUCCESS,eventhoughthelatterisalwaysdefinedaszero.
Assertions
TheconditionintheHiLoprogramdependsonuserinput,soyoucantpreventaviolationoftheinvariant.
However,invariantsusuallydependonlyonthecodeyouwrite,sotheywillalwaysholdifyouve
implementedyourdesigncorrectly.Inthiscase,itisclearertomakeanassertion,whichisapositive
statementthatrevealsyourdesigndecisions.
Supposeyouareimplementingavectorofintegers:anexpandablearraythatgrowsondemand.The
functionthataddsanelementtothevectormustfirstverifythatthereisanopenslotintheunderlying
arraythatholdstheelementsotherwise,itneedstorequestmoreheapspaceandcopytheexisting
elementstothenewspacebeforeaddingthenewelement(anddeletingtheoldarray).Suchafunction
mightlooklikethefollowing:
voidMyVector::push_back(intx){
if(nextSlot==capacity)
grow()
assert(nextSlot<capacity)
data[nextSlot++]=x
}
Inthisexample,dataisadynamicarrayofintswithcapacityslotsandnextSlotslotsinuse.The
purposeofgrow()istoexpandthesizeofdatasothatthenewvalueofcapacityisstrictlygreaterthan
nextSlot.ProperbehaviorofMyVectordependsonthisdesigndecision,anditwillneverfailiftherestof
thesupportingcodeiscorrect.Weasserttheconditionwiththeassert()macro,whichisdefinedinthe
header<cassert>.
TheStandardClibraryassert()macroisbrief,tothepoint,andportable.Iftheconditioninits
parameterevaluatestononzero,executioncontinuesuninterruptedifitdoesnt,amessagecontainingthe
textoftheoffendingexpressionalongwithitssourcefilenameandlinenumberisprintedtothestandard
errorchannelandtheprogramaborts.Isthattoodrastic?Inpractice,itismuchmoredrastictolet
executioncontinuewhenabasicdesignassumptionhasfailed.Yourprogramneedstobefixed.
Ifallgoeswell,youwillthoroughlytestyourcodewithallassertionsintactbythetimethefinalproductis
deployed.(Wellsaymoreabouttestinglater.)Dependingonthenatureofyourapplication,themachine
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
44/458
5/3/2015
ThinkinginC++2ndedVolume2
cyclesneededtotestallassertionsatruntimemightbetoomuchofaperformancehitinthefield.Ifthats
thecase,youcanremovealltheassertioncodeautomaticallybydefiningthemacroNDEBUGand
rebuildingtheapplication.
Toseehowthisworks,notethatatypicalimplementationofassert()lookssomethinglikethis:
#ifdefNDEBUG
#defineassert(cond)((void)0)
#else
voidassertImpl(constchar*,constchar*,long)
#defineassert(cond)\
((cond)?(void)0:assertImpl(???))
#endif
WhenthemacroNDEBUGisdefined,thecodedecaystotheexpression(void)0,soallthatsleftinthe
compilationstreamisanessentiallyemptystatementasaresultofthesemicolonyouappendedtoeach
assert()invocation.IfNDEBUGisnotdefined,assert(cond)expandstoaconditionalstatementthat,
whencondiszero,callsacompilerdependentfunction(whichwenamedassertImpl())withastring
argumentrepresentingthetextofcond,alongwiththefilenameandlinenumberwheretheassertion
appeared.(Weused???asaplaceholderintheexample,butthestringmentionedisactuallycomputed
there,alongwiththefilenameandthelinenumberwherethemacrooccursinthatfile.Howthesevalues
areobtainedisimmaterialtoourdiscussion.)Ifyouwanttoturnassertionsonandoffatdifferentpointsin
yourprogram,youmustnotonly#defineor#undefNDEBUG,butyoumustalsoreinclude<cassert>.
MacrosareevaluatedasthepreprocessorencountersthemandthususewhateverNDEBUGstateapplies
atthepointofinclusion.ThemostcommonwaytodefineNDEBUGonceforanentireprogramisasa
compileroption,whetherthroughprojectsettingsinyourvisualenvironmentorviathecommandline,as
in:
myccDNDEBUGmyfile.cpp
MostcompilersusetheDflagtodefinemacronames.(Substitutethenameofyourcompilersexecutable
formyccabove.)Theadvantageofthisapproachisthatyoucanleaveyourassertionsinthesourcecode
asaninvaluablebitofdocumentation,andyetthereisnoruntimepenalty.Becausethecodeinan
assertiondisappearswhenNDEBUGisdefined,itisimportantthatyouneverdoworkinanassertion.Only
testconditionsthatdonotchangethestateofyourprogram.
WhetherusingNDEBUGforreleasedcodeisagoodidearemainsasubjectofdebate.TonyHoare,oneof
themostinfluentialcomputerscientistsofalltime,[15]hassuggestedthatturningoffruntimecheckssuch
asassertionsissimilartoasailingenthusiastwhowearsalifejacketwhiletrainingonlandandthen
discardsitwhenhegoestosea.[16]Ifanassertionfailsinproduction,youhaveaproblemmuchworse
thandegradationinperformance,sochoosewisely.
Notallconditionsshouldbeenforcedbyassertions.Usererrorsandruntimeresourcefailuresshouldbe
signaledbythrowingexceptions,asweexplainedindetailinChapter1.Itistemptingtouseassertionsfor
mosterrorconditionswhileroughingoutcode,withtheintenttoreplacemanyofthemlaterwithrobust
exceptionhandling.Likeanyothertemptation,usecaution,sinceyoumightforgettomakeallthe
necessarychangeslater.Remember:assertionsareintendedtoverifydesigndecisionsthatwillonlyfail
becauseoffaultyprogrammerlogic.Theidealistosolveallassertionviolationsduringdevelopment.Dont
useassertionsforconditionsthatarenttotallyinyourcontrol(forexample,conditionsthatdependonuser
input).Inparticular,youwouldntwanttouseassertionstovalidatefunctionargumentsthrowa
logic_errorinstead.
TheuseofassertionsasatooltoensureprogramcorrectnesswasformalizedbyBertrandMeyerinhis
DesignbyContractmethodology.[17]Everyfunctionhasanimplicitcontractwithclientsthat,givencertain
preconditions,guaranteescertainpostconditions.Inotherwords,thepreconditionsaretherequirements
forusingthefunction,suchassupplyingargumentswithincertainranges,andthepostconditionsarethe
resultsdeliveredbythefunction,eitherbyreturnvalueorbysideeffect.
Whenclientprogramsfailtogiveyouvalidinput,youmusttellthemtheyhavebrokenthecontract.Thisis
notthebesttimetoaborttheprogram(althoughyourejustifiedindoingsosincethecontractwas
violated),butanexceptioniscertainlyappropriate.ThisiswhytheStandardC++librarythrows
exceptionsderivedfromlogic_error,suchasout_of_range.[18]Iftherearefunctionsthatonlyyoucall,
however,suchasprivatefunctionsinaclassofyourowndesign,theassert()macroisappropriate,since
youhavetotalcontroloverthesituationandyoucertainlywanttodebugyourcodebeforeshipping.
Apostconditionfailureindicatesaprogramerror,anditisappropriatetouseassertionsforanyinvariant
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
45/458
5/3/2015
ThinkinginC++2ndedVolume2
atanytime,includingthepostconditiontestattheendofafunction.Thisappliesinparticulartoclass
memberfunctionsthatmaintainthestateofanobject.IntheMyVectorexampleearlier,forinstance,a
reasonableinvariantforallpublicmemberfunctionswouldbe:
assert(0<=nextSlot&&nextSlot<=capacity)
or,ifnextSlotisanunsignedinteger,simply
assert(nextSlot<=capacity)
Suchaninvariantiscalledaclassinvariantandcanreasonablybeenforcedbyanassertion.Subclasses
playtheroleofsubcontractortotheirbaseclassesbecausetheymustmaintaintheoriginalcontract
betweenthebaseclassanditsclients.Forthisreason,thepreconditionsinderivedclassesmustimposeno
extrarequirementsbeyondthoseinthebasecontract,andthepostconditionsmustdeliveratleastas
much.[19]
Validatingresultsreturnedtotheclient,however,isnothingmoreorlessthantesting,sousingpost
conditionassertionsinthiscasewouldbeduplicatingwork.Yes,itsgooddocumentation,butmorethan
onedeveloperhasbeenfooledintoimproperlyusingpostconditionassertionsasasubstituteforunit
testing.
Asimpleunittestframework
Writingsoftwareisallaboutmeetingrequirements.[20]Creatingtheserequirementsisdifficult,andthey
canchangefromdaytodayyoumightdiscoverataweeklyprojectmeetingthatwhatyoujustspentthe
weekdoingisnotexactlywhattheusersreallywant.
Peoplecannotarticulatesoftwarerequirementswithoutsamplinganevolving,workingsystem.Itsmuch
bettertospecifyalittle,designalittle,codealittle,andtestalittle.Then,afterevaluatingtheoutcome,
doitalloveragain.Theabilitytodevelopinsuchaniterativefashionisoneofthegreatadvancesofthe
objectorientedapproach,butitrequiresnimbleprogrammerswhocancraftresilientcode.Changeishard.
Anotherimpetusforchangecomesfromyou,theprogrammer.Thecraftspersoninyouwantstocontinually
improvethedesignofyourcode.Whatmaintenanceprogrammerhasntcursedtheaging,flagship
companyproductasaconvoluted,unmodifiablepatchworkofspaghetti?Managementsreluctancetolet
youtamperwithafunctioningsystemrobscodeoftheresilienceitneedstoendure.Ifitsnotbroken,
dontfixiteventuallygiveswayto,Wecantfixitrewriteit.Changeisnecessary.
Fortunately,ourindustryisgrowingaccustomedtothedisciplineofrefactoring,theartofinternally
restructuringcodetoimproveitsdesign,withoutchangingitsbehavior.[21]Suchimprovementsinclude
extractinganewfunctionfromanother,orinversely,combiningmemberfunctionsreplacingamember
functionwithanobjectparameterizingamemberfunctionorclassandreplacingconditionalswith
polymorphism.Refactoringhelpscodeevolve.
Whethertheforceforchangecomesfromusersorprogrammers,changestodaymaybreakwhatworked
yesterday.Weneedawaytobuildcodethatwithstandschangeandimprovesovertime.
ExtremeProgramming(XP)[22]isonlyoneofmanypracticesthatsupportaquickonyourfeetmotif.In
thissectionweexplorewhatwethinkisthekeytomakingflexible,incrementaldevelopmentsucceed:an
easytouseautomatedunittestframework.(Notethattesters,softwareprofessionalswhotestothers
codeforaliving,arestillindispensable.Here,wearemerelydescribingawaytohelpdeveloperswrite
bettercode.)
Developerswriteunitteststogaintheconfidencetosaythetwomostimportantthingsthatanydeveloper
cansay:
1.Iunderstandtherequirements.
2.Mycodemeetsthoserequirements(tothebestofmyknowledge).
Thereisnobetterwaytoensurethatyouknowwhatthecodeyoureabouttowriteshoulddothantowrite
theunittestsfirst.Thissimpleexercisehelpsfocusthemindonthetaskaheadandwilllikelyleadto
workingcodefasterthanjustjumpingintocoding.Or,toexpressitinXPterms:
Testing+programmingisfasterthanjustprogramming.
Writingtestsfirstalsoguardsyouagainstboundaryconditionsthatmightbreakyourcode,soyourcodeis
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
46/458
5/3/2015
ThinkinginC++2ndedVolume2
morerobust.
Whenyourcodepassesallyourtests,youknowthatifthesystemisntworking,yourcodeisprobablynot
theproblem.ThestatementAllmytestspassisapowerfulargument.
Automatedtesting
Sowhatdoesaunittestlooklike?Toooftendevelopersjustusesomewellbehavedinputtoproducesome
expectedoutput,whichtheyinspectvisually.Twodangersexistinthisapproach.First,programsdont
alwaysreceiveonlywellbehavedinput.Weallknowthatweshouldtesttheboundariesofprograminput,
butitshardtothinkaboutthiswhenyouretryingtojustgetthingsworking.Ifyouwritethetestfora
functionfirstbeforeyoustartcoding,youcanwearyourtesterhatandaskyourself,Whatcould
possiblymakethisbreak?Codeatestthatwillprovethefunctionyoullwriteisntbroken,andthenputon
yourdeveloperhatandmakeithappen.Youllwritebettercodethanifyouhadntwrittenthetestfirst.
Theseconddangeristhatinspectingoutputvisuallyistediousanderrorprone.Mostanysuchthinga
humancandoacomputercando,butwithouthumanerror.Itsbettertoformulatetestsascollectionsof
Booleanexpressionsandhaveatestprogramreportanyfailures.
Forexample,supposeyouneedtobuildaDateclassthathasthefollowingproperties:
Adatecanbeinitializedwithastring(YYYYMMDD),threeintegers(Y,M,D),ornothing(giving
todaysdate).
Adateobjectcanyielditsyear,month,anddayorastringoftheformYYYYMMDD.
Allrelationalcomparisonsareavailable,aswellascomputingthedurationbetweentwodates(in
years,months,anddays).
Datestobecomparedneedtobeabletospananarbitrarynumberofcenturies(forexample,
16002200).
Yourclasscanstorethreeintegersrepresentingtheyear,month,andday.(Justbesuretheyearisat
least16bitsinsizetosatisfythelastbulleteditem.)TheinterfaceforyourDateclassmightlooklikethis:
//:C02:Date1.h
//AfirstpassatDate.h.
#ifndefDATE1_H
#defineDATE1_H
#include<string>
classDate{
public:
//Astructtoholdelapsedtime:
structDuration{
intyears
intmonths
intdays
Duration(inty,intm,intd)
:years(y),months(m),days(d){}
}
Date()
Date(intyear,intmonth,intday)
Date(conststd::string&)
intgetYear()const
intgetMonth()const
intgetDay()const
std::stringtoString()const
friendbooloperator<(constDate&,constDate&)
friendbooloperator>(constDate&,constDate&)
friendbooloperator<=(constDate&,constDate&)
friendbooloperator>=(constDate&,constDate&)
friendbooloperator==(constDate&,constDate&)
friendbooloperator!=(constDate&,constDate&)
friendDurationduration(constDate&,constDate&)
}
#endif//DATE1_H///:~
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
47/458
5/3/2015
ThinkinginC++2ndedVolume2
Beforeyouimplementthisclass,youcansolidifyyourgraspoftherequirementsbywritingthebeginnings
ofatestprogram.Youmightcomeupwithsomethinglikethefollowing:
//:C02:SimpleDateTest.cpp
//{L}Date
#include<iostream>
#include"Date.h"//FromAppendixB
usingnamespacestd
//Testmachinery
intnPass=0,nFail=0
voidtest(boolt){if(t)nPass++elsenFail++}
intmain(){
Datemybday(1951,10,1)
test(mybday.getYear()==1951)
test(mybday.getMonth()==10)
test(mybday.getDay()==1)
cout<<"Passed:"<<nPass<<",Failed:"
<<nFail<<endl
}
/*Expectedoutput:
Passed:3,Failed:0
*////:~
Inthistrivialcase,thefunctiontest()maintainstheglobalvariablesnPassandnFail.Theonlyvisual
inspectionyoudoistoreadthefinalscore.Ifatestfailed,amoresophisticatedtest()displaysan
appropriatemessage.Theframeworkdescribedlaterinthischapterhassuchatestfunction,amongother
things.
YoucannowimplementenoughoftheDateclasstogettheseteststopass,andthenyoucanproceed
iterativelyuntilalltherequirementsaremet.Bywritingtestsfirst,youaremorelikelytothinkofcorner
casesthatmightbreakyourupcomingimplementation,andyouremorelikelytowritethecodecorrectly
thefirsttime.SuchanexercisemightproducethefollowingversionofatestfortheDateclass:
//:C02:SimpleDateTest2.cpp
//{L}Date
#include<iostream>
#include"Date.h"
usingnamespacestd
//Testmachinery
intnPass=0,nFail=0
voidtest(boolt){if(t)++nPasselse++nFail}
intmain(){
Datemybday(1951,10,1)
Datetoday
Datemyevebday("19510930")
//Testtheoperators
test(mybday<today)
test(mybday<=today)
test(mybday!=today)
test(mybday==mybday)
test(mybday>=mybday)
test(mybday<=mybday)
test(myevebday<mybday)
test(mybday>myevebday)
test(mybday>=myevebday)
test(mybday!=myevebday)
//Testthefunctions
test(mybday.getYear()==1951)
test(mybday.getMonth()==10)
test(mybday.getDay()==1)
test(myevebday.getYear()==1951)
test(myevebday.getMonth()==9)
test(myevebday.getDay()==30)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
48/458
5/3/2015
ThinkinginC++2ndedVolume2
test(mybday.toString()=="19511001")
test(myevebday.toString()=="19510930")
//Testduration
Dated2(2003,7,4)
Date::Durationdur=duration(mybday,d2)
test(dur.years==51)
test(dur.months==9)
test(dur.days==3)
//Reportresults:
cout<<"Passed:"<<nPass<<",Failed:"
<<nFail<<endl
}///:~
Thistestcanbemorefullydeveloped.Forexample,wehaventtestedthatlongdurationsarehandled
correctly.Wellstophere,butyougettheidea.ThefullimplementationfortheDateclassisavailablein
thefilesDate.handDate.cppintheappendix.[23]
TheTestSuiteFramework
SomeautomatedC++unittesttoolsareavailableontheWorldWideWebfordownload,suchasCppUnit.
[24]
Ourpurposehereisnotonlytopresentatestmechanismthatiseasytouse,butalsoeasyto
understandinternallyandevenmodifyifnecessary.So,inthespiritofDoTheSimplestThingThatCould
PossiblyWork,[25]wehavedevelopedtheTestSuiteFramework,anamespacenamedTestSuitethat
containstwokeyclasses:TestandSuite.
TheTestclassisanabstractbaseclassfromwhichyouderiveatestobject.Itkeepstrackofthenumber
ofpassesandfailuresanddisplaysthetextofanytestconditionthatfails.Yousimplytooverridethe
run()memberfunction,whichshouldinturncallthetest_()macroforeachBooleantestconditionyou
define.
TodefineatestfortheDateclassusingtheframework,youcaninheritfromTestasshowninthe
followingprogram:
//:C02:DateTest.h
#ifndefDATETEST_H
#defineDATETEST_H
#include"Date.h"
#include"../TestSuite/Test.h"
classDateTest:publicTestSuite::Test{
Datemybday
Datetoday
Datemyevebday
public:
DateTest():mybday(1951,10,1),myevebday("19510930"){}
voidrun(){
testOps()
testFunctions()
testDuration()
}
voidtestOps(){
test_(mybday<today)
test_(mybday<=today)
test_(mybday!=today)
test_(mybday==mybday)
test_(mybday>=mybday)
test_(mybday<=mybday)
test_(myevebday<mybday)
test_(mybday>myevebday)
test_(mybday>=myevebday)
test_(mybday!=myevebday)
}
voidtestFunctions(){
test_(mybday.getYear()==1951)
test_(mybday.getMonth()==10)
test_(mybday.getDay()==1)
test_(myevebday.getYear()==1951)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
49/458
5/3/2015
ThinkinginC++2ndedVolume2
test_(myevebday.getMonth()==9)
test_(myevebday.getDay()==30)
test_(mybday.toString()=="19511001")
test_(myevebday.toString()=="19510930")
}
voidtestDuration(){
Dated2(2003,7,4)
Date::Durationdur=duration(mybday,d2)
test_(dur.years==51)
test_(dur.months==9)
test_(dur.days==3)
}
}
#endif//DATETEST_H///:~
RunningthetestisasimplematterofinstantiatingaDateTestobjectandcallingitsrun()member
function:
//:C02:DateTest.cpp
//Automatedtesting(withaframework).
//{L}Date../TestSuite/Test
#include<iostream>
#include"DateTest.h"
usingnamespacestd
intmain(){
DateTesttest
test.run()
returntest.report()
}
/*Output:
Test"DateTest":
Passed:21,Failed:0
*////:~
TheTest::report()functiondisplaysthepreviousoutputandreturnsthenumberoffailures,soitis
suitabletouseasareturnvaluefrommain().
TheTestclassusesRTTI[26]togetthenameofyourclass(forexample,DateTest)forthereport.There
isalsoasetStream()memberfunctionifyouwantthetestresultssenttoafileinsteadoftothe
standardoutput(thedefault).YoullseetheTestclassimplementationlaterinthischapter.
Thetest_()macrocanextractthetextoftheBooleanconditionthatfails,alongwithitsfilenameandline
number.[27]Toseewhathappenswhenafailureoccurs,youcanintroduceanintentionalerrorinthecode,
forexamplebyreversingtheconditioninthefirstcalltotest_()inDateTest::testOps()intheprevious
examplecode.Theoutputindicatesexactlywhattestwasinerrorandwhereithappened:
DateTestfailure:(mybday>today),DateTest.h(line31)
Test"DateTest":
Passed:20Failed:1
Inadditiontotest_(),theframeworkincludesthefunctionssucceed_()andfail_(),forcaseswherea
Booleantestwontdo.Thesefunctionsapplywhentheclassyouretestingmightthrowexceptions.During
testing,createaninputsetthatwillcausetheexceptiontooccur.Ifitdoesnt,itsanerrorandyoucall
fail_()explicitlytodisplayamessageandupdatethefailurecount.Ifitdoesthrowtheexceptionas
expected,youcallsucceed_()toupdatethesuccesscount.
Toillustrate,supposewemodifythespecificationofthetwonondefaultDateconstructorstothrowa
DateErrorexception(atypenestedinsideDateandderivedfromstd::logic_error)iftheinput
parametersdonotrepresentavaliddate:
Date(conststring&s)throw(DateError)
Date(intyear,intmonth,intday)throw(DateError)
TheDateTest::run()memberfunctioncannowcallthefollowingfunctiontotesttheexceptionhandling:
voidtestExceptions(){
try{
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
50/458
5/3/2015
ThinkinginC++2ndedVolume2
Dated(0,0,0)//Invalid
fail_("InvaliddateundetectedinDateintctor")
}catch(Date::DateError&){
succeed_()
}
try{
Dated("")//Invalid
fail_("InvaliddateundetectedinDatestringctor")
}catch(Date::DateError&){
succeed_()
}
}
Inbothcases,ifanexceptionisnotthrown,itisanerror.Noticethatyoumustmanuallypassamessage
tofail_(),sincenoBooleanexpressionisbeingevaluated.
Testsuites
Realprojectsusuallycontainmanyclasses,soyouneedawaytogrouptestssothatyoucanjustpusha
singlebuttontotesttheentireproject.[28]TheSuiteclasscollectstestsintoafunctionalunit.YouaddTest
objectstoaSuitewiththeaddTest()memberfunction,oryoucanincludeanentireexistingsuitewith
addSuite().Toillustrate,thefollowingexamplecollectstheprogramsinChapter3thatusetheTest
classintoasinglesuite.NotethatthisfilewillappearintheChapter3subdirectory:
//:C03:StringSuite.cpp
//{L}../TestSuite/Test../TestSuite/Suite
//{L}TrimTest
//IllustratesatestsuiteforcodefromChapter3
#include<iostream>
#include"../TestSuite/Suite.h"
#include"StringStorage.h"
#include"Sieve.h"
#include"Find.h"
#include"Rparse.h"
#include"TrimTest.h"
#include"CompStr.h"
usingnamespacestd
usingnamespaceTestSuite
intmain(){
Suitesuite("StringTests")
suite.addTest(newStringStorageTest)
suite.addTest(newSieveTest)
suite.addTest(newFindTest)
suite.addTest(newRparseTest)
suite.addTest(newTrimTest)
suite.addTest(newCompStrTest)
suite.run()
longnFail=suite.report()
suite.free()
returnnFail
}
/*Output:
s1=62345
s2=12345
Suite"StringTests"
====================
Test"StringStorageTest":
Passed:2Failed:0
Test"SieveTest":
Passed:50Failed:0
Test"FindTest":
Passed:9Failed:0
Test"RparseTest":
Passed:8Failed:0
Test"TrimTest":
Passed:11Failed:0
Test"CompStrTest":
Passed:8Failed:0
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
51/458
5/3/2015
ThinkinginC++2ndedVolume2
*////:~
Fiveoftheabovetestsarecompletelycontainedinheaderfiles.TrimTestisnot,becauseitcontainsstatic
datathatmustbedefinedinanimplementationfile.Thetwofirsttwooutputlinesaretracelinesfromthe
StringStoragetest.Youmustgivethesuiteanameasaconstructorargument.TheSuite::run()
memberfunctioncallsTest::run()foreachofitscontainedtests.Muchthesamethinghappensfor
Suite::report(),exceptthatyoucansendtheindividualtestreportstoadifferentdestinationstreamthan
thatofthesuitereport.IfthetestpassedtoaddSuite()alreadyhasastreampointerassigned,itkeeps
it.Otherwise,itgetsitsstreamfromtheSuiteobject.(AswithTest,thereisanoptionalsecondargument
tothesuiteconstructorthatdefaultstostd::cout.)ThedestructorforSuitedoesnotautomaticallydelete
thecontainedTestpointersbecausetheydontneedtoresideontheheapthatsthejobof
Suite::free().
Thetestframeworkcode
ThetestframeworkcodeisinasubdirectorycalledTestSuiteinthecodedistributionavailableat
www.MindView.net.Touseit,includethesearchpathfortheTestSuitesubdirectoryinyourheader,link
theobjectfiles,andincludetheTestSuitesubdirectoryinthelibrarysearchpath.Hereistheheaderfor
Test.h:
//:TestSuite:Test.h
#ifndefTEST_H
#defineTEST_H
#include<string>
#include<iostream>
#include<cassert>
usingstd::string
usingstd::ostream
usingstd::cout
//fail_()hasanunderscoretopreventcollisionwith
//ios::fail().Forconsistency,test_()andsucceed_()
//alsohaveunderscores.
#definetest_(cond)\
do_test(cond,#cond,__FILE__,__LINE__)
#definefail_(str)\
do_fail(str,__FILE__,__LINE__)
namespaceTestSuite{
classTest{
ostream*osptr
longnPass
longnFail
//Disallowed:
Test(constTest&)
Test&operator=(constTest&)
protected:
voiddo_test(boolcond,conststring&lbl,
constchar*fname,longlineno)
voiddo_fail(conststring&lbl,
constchar*fname,longlineno)
public:
Test(ostream*osptr=&cout){
this>osptr=osptr
nPass=nFail=0
}
virtual~Test(){}
virtualvoidrun()=0
longgetNumPassed()const{returnnPass}
longgetNumFailed()const{returnnFail}
constostream*getStream()const{returnosptr}
voidsetStream(ostream*osptr){this>osptr=osptr}
voidsucceed_(){++nPass}
longreport()const
virtualvoidreset(){nPass=nFail=0}
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
52/458
5/3/2015
ThinkinginC++2ndedVolume2
}//namespaceTestSuite
#endif//TEST_H///:~
TherearethreevirtualfunctionsintheTestclass:
Avirtualdestructor
Thefunctionreset()
Thepurevirtualfunctionrun()
AsexplainedinVolume1,itisanerrortodeleteaderivedheapobjectthroughabasepointerunlessthe
baseclasshasavirtualdestructor.Anyclassintendedtobeabaseclass(usuallyevidencedbythe
presenceofatleastoneothervirtualfunction)shouldhaveavirtualdestructor.Thedefaultimplementation
oftheTest::reset()resetsthesuccessandfailurecounterstozero.Youmightwanttooverridethis
functiontoresetthestateofthedatainyourderivedtestobjectjustbesuretocallTest::reset()
explicitlyinyouroverridesothatthecountersarereset.TheTest::run()memberfunctionispurevirtual
sinceyouarerequiredtooverrideitinyourderivedclass.
Thetest_()andfail_()macroscanincludefilenameandlinenumberinformationavailablefromthe
preprocessor.Weoriginallyomittedthetrailingunderscoresinthenames,butthefail()macrothen
collidedwithios::fail(),causingcompilererrors.
HereistheimplementationoftheremainderoftheTestfunctions:
//:TestSuite:Test.cpp{O}
#include"Test.h"
#include<iostream>
#include<typeinfo>
usingnamespacestd
usingnamespaceTestSuite
voidTest::do_test(boolcond,conststd::string&lbl,
constchar*fname,longlineno){
if(!cond)
do_fail(lbl,fname,lineno)
else
succeed_()
}
voidTest::do_fail(conststd::string&lbl,
constchar*fname,longlineno){
++nFail
if(osptr){
*osptr<<typeid(*this).name()
<<"failure:("<<lbl<<"),"<<fname
<<"(line"<<lineno<<")"<<endl
}
}
longTest::report()const{
if(osptr){
*osptr<<"Test\""<<typeid(*this).name()
<<"\":\n\tPassed:"<<nPass
<<"\tFailed:"<<nFail
<<endl
}
returnnFail
}///:~
TheTestclasskeepstrackofthenumberofsuccessesandfailuresaswellasthestreamwhereyouwant
Test::report()todisplaytheresults.Thetest_()andfail_()macrosextractthecurrentfilenameand
linenumberinformationfromthepreprocessorandpassthefilenametodo_test()andthelinenumber
todo_fail(),whichdotheactualworkofdisplayingamessageandupdatingtheappropriatecounter.We
cantthinkofagoodreasontoallowcopyandassignmentoftestobjects,sowehavedisallowedthese
operationsbymakingtheirprototypesprivateandomittingtheirrespectivefunctionbodies.
HereistheheaderfileforSuite:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
53/458
5/3/2015
ThinkinginC++2ndedVolume2
//:TestSuite:Suite.h
#ifndefSUITE_H
#defineSUITE_H
#include<vector>
#include<stdexcept>
#include"../TestSuite/Test.h"
usingstd::vector
usingstd::logic_error
namespaceTestSuite{
classTestSuiteError:publiclogic_error{
public:
TestSuiteError(conststring&s="")
:logic_error(s){}
}
classSuite{
stringname
ostream*osptr
vector<Test*>tests
voidreset()
//Disallowedops:
Suite(constSuite&)
Suite&operator=(constSuite&)
public:
Suite(conststring&name,ostream*osptr=&cout)
:name(name){this>osptr=osptr}
stringgetName()const{returnname}
longgetNumPassed()const
longgetNumFailed()const
constostream*getStream()const{returnosptr}
voidsetStream(ostream*osptr){this>osptr=osptr}
voidaddTest(Test*t)throw(TestSuiteError)
voidaddSuite(constSuite&)
voidrun()//CallsTest::run()repeatedly
longreport()const
voidfree()//Deletestests
}
}//namespaceTestSuite
#endif//SUITE_H///:~
TheSuiteclassholdspointerstoitsTestobjectsinavector.Noticetheexceptionspecificationonthe
addTest()memberfunction.Whenyouaddatesttoasuite,Suite::addTest()verifiesthatthepointer
youpassisnotnullifitisnull,itthrowsaTestSuiteErrorexception.Sincethismakesitimpossibleto
addanullpointertoasuite,addSuite()assertsthisconditiononeachofitstests,asdotheother
functionsthattraversethevectoroftests(seethefollowingimplementation).Copyandassignmentare
disallowedastheyareintheTestclass.
//:TestSuite:Suite.cpp{O}
#include"Suite.h"
#include<iostream>
#include<cassert>
#include<cstddef>
usingnamespacestd
usingnamespaceTestSuite
voidSuite::addTest(Test*t)throw(TestSuiteError){
//Verifytestisvalidandhasastream:
if(t==0)
throwTestSuiteError("NulltestinSuite::addTest")
elseif(osptr&&!t>getStream())
t>setStream(osptr)
tests.push_back(t)
t>reset()
}
voidSuite::addSuite(constSuite&s){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
54/458
5/3/2015
ThinkinginC++2ndedVolume2
for(size_ti=0i<s.tests.size()++i){
assert(tests[i])
addTest(s.tests[i])
}
}
voidSuite::free(){
for(size_ti=0i<tests.size()++i){
deletetests[i]
tests[i]=0
}
}
voidSuite::run(){
reset()
for(size_ti=0i<tests.size()++i){
assert(tests[i])
tests[i]>run()
}
}
longSuite::report()const{
if(osptr){
longtotFail=0
*osptr<<"Suite\""<<name
<<"\"\n======="
size_ti
for(i=0i<name.size()++i)
*osptr<<'='
*osptr<<"="<<endl
for(i=0i<tests.size()++i){
assert(tests[i])
totFail+=tests[i]>report()
}
*osptr<<"======="
for(i=0i<name.size()++i)
*osptr<<'='
*osptr<<"="<<endl
returntotFail
}
else
returngetNumFailed()
}
longSuite::getNumPassed()const{
longtotPass=0
for(size_ti=0i<tests.size()++i){
assert(tests[i])
totPass+=tests[i]>getNumPassed()
}
returntotPass
}
longSuite::getNumFailed()const{
longtotFail=0
for(size_ti=0i<tests.size()++i){
assert(tests[i])
totFail+=tests[i]>getNumFailed()
}
returntotFail
}
voidSuite::reset(){
for(size_ti=0i<tests.size()++i){
assert(tests[i])
tests[i]>reset()
}
}///:~
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
55/458
5/3/2015
ThinkinginC++2ndedVolume2
WewillbeusingtheTestSuiteframeworkwhereveritappliesthroughouttherestofthisbook.
Debuggingtechniques
Thebestdebugginghabitistouseassertionsasexplainedinthebeginningofthischapterbydoingso
youllhelpfindlogicerrorsbeforetheycauserealtrouble.Thissectioncontainssomeothertipsand
techniquesthatmighthelpduringdebugging.
Tracemacros
Sometimesitsusefultoprintthecodeofeachstatementasitisexecuted,eithertocoutortoatracefile.
Heresapreprocessormacrotoaccomplishthis:
#defineTRACE(ARG)cout<<#ARG<<endlARG
Nowyoucangothroughandsurroundthestatementsyoutracewiththismacro.However,thiscan
introduceproblems.Forexample,ifyoutakethestatement:
for(inti=0i<100i++)
cout<<i<<endl
andputbothlinesinsideTRACE()macros,yougetthis:
TRACE(for(inti=0i<100i++))
TRACE(cout<<i<<endl)
whichexpandstothis:
cout<<"for(inti=0i<100i++)"<<endl
for(inti=0i<100i++)
cout<<"cout<<i<<endl"<<endl
cout<<i<<endl
whichisntexactlywhatyouwant.Thus,youmustusethistechniquecarefully.
ThefollowingisavariationontheTRACE()macro:
#defineD(a)cout<<#a"=["<<a<<"]"<<endl
Ifyouwanttodisplayanexpression,yousimplyputitinsideacalltoD().Theexpressionisdisplayed,
followedbyitsvalue(assumingtheresanoverloadedoperator<<fortheresulttype).Forexample,you
cansayD(a+b).Youcanusethismacroanytimeyouwanttocheckanintermediatevalue.
Thesetwomacrosrepresentthetwomostfundamentalthingsyoudowithadebugger:tracethroughthe
codeexecutionanddisplayvalues.Agooddebuggerisanexcellentproductivitytool,butsometimes
debuggersarenotavailable,oritsnotconvenienttousethem.Thesetechniquesalwayswork,regardless
ofthesituation.
Tracefile
DISCLAIMER:ThissectionandthenextcontaincodewhichisofficiallyunsanctionedbytheC++Standard.
Inparticular,weredefinecoutandnewviamacros,whichcancausesurprisingresultsifyourenot
careful.Ourexamplesworkonallthecompilersweuse,however,andprovideusefulinformation.Thisis
theonlyplaceinthisbookwherewewilldepartfromthesanctityofstandardcompliantcodingpractice.
Useatyourownrisk!Notethatinorderforthistowork,ausingdeclarationmustbeused,sothatcout
isntprefixedbyitsnamespace,i.e.std::coutwillnotwork.
Thefollowingcodeeasilycreatesatracefileandsendsalltheoutputthatwouldnormallygotocoutinto
thatfile.Allyoumustdois#defineTRACEONandincludetheheaderfile(ofcourse,itsfairlyeasyjustto
writethetwokeylinesrightintoyourfile):
//:C03:Trace.h
//Creatingatracefile.
#ifndefTRACE_H
#defineTRACE_H
#include<fstream>
#ifdefTRACEON
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
56/458
5/3/2015
ThinkinginC++2ndedVolume2
std::ofstreamTRACEFILE__("TRACE.OUT")
#definecoutTRACEFILE__
#endif
#endif//TRACE_H///:~
Heresasimpletestofthepreviousfile:
//:C03:Tracetst.cpp{bor}
#include<iostream>
#include<fstream>
#include"../require.h"
usingnamespacestd
#defineTRACEON
#include"Trace.h"
intmain(){
ifstreamf("Tracetst.cpp")
assure(f,"Tracetst.cpp")
cout<<f.rdbuf()//Dumpsfilecontentstofile
}///:~
BecausecouthasbeentextuallyturnedintosomethingelsebyTrace.h,allthecoutstatementsinyour
programnowsendinformationtothetracefile.Thisisaconvenientwayofcapturingyouroutputintoa
file,incaseyouroperatingsystemdoesntmakeoutputredirectioneasy.
Findingmemoryleaks
ThefollowingstraightforwarddebuggingtechniquesareexplainedinVolume1:
1.Forarrayboundschecking,usetheArraytemplateinC16:Array3.cppofVolume1forallarrays.
Youcanturnoffthecheckingandincreaseefficiencywhenyourereadytoship.(Althoughthis
doesntdealwiththecaseoftakingapointertoanarray.)
2.Checkfornonvirtualdestructorsinbaseclasses.
Trackingnew/deleteandmalloc/free
Commonproblemswithmemoryallocationincludemistakenlycallingdeleteformemorythatsnotonthe
freestore,deletingthefreestoremorethanonce,and,mostoften,forgettingtodeleteapointer.This
sectiondiscussesasystemthatcanhelpyoutrackdownthesekindsofproblems.
Asanadditionaldisclaimerbeyondthatoftheprecedingsection:becauseofthewayweoverloadnew,the
followingtechniquemaynotworkonallplatforms,andwillonlyworkforprogramsthatdonotcallthe
functionoperatornew()explicitly.Wehavebeenquitecarefulinthisbooktoonlypresentcodethatfully
conformstotheC++Standard,butinthisoneinstanceweremakinganexceptionforthefollowing
reasons:
1.Eventhoughitstechnicallyillegal,itworksonmanycompilers.[29]
2.Weillustratesomeusefulthinkingalongtheway.
Tousethememorycheckingsystem,yousimplyincludetheheaderfileMemCheck.h,linkthe
MemCheck.objfileintoyourapplicationtointerceptallthecallstonewanddelete,andcallthemacro
MEM_ON()(explainedlaterinthissection)toinitiatememorytracing.Atraceofallallocationsand
deallocationsisprintedtothestandardoutput(viastdout).Whenyouusethissystem,allcallstonew
storeinformationaboutthefileandlinewheretheywerecalled.Thisisaccomplishedbyusingthe
placementsyntaxforoperatornew.[30]Althoughyoutypicallyusetheplacementsyntaxwhenyouneedto
placeobjectsataspecificpointinmemory,itcanalsocreateanoperatornew()withanynumberof
arguments.Thisisusedinthefollowingexampletostoretheresultsofthe__FILE__and__LINE__
macroswhenevernewiscalled:
//:C02:MemCheck.h
#ifndefMEMCHECK_H
#defineMEMCHECK_H
#include<cstddef>//Forsize_t
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
57/458
5/3/2015
ThinkinginC++2ndedVolume2
//Usurpthenewoperator(bothscalarandarrayversions)
void*operatornew(std::size_t,constchar*,long)
void*operatornew[](std::size_t,constchar*,long)
#definenewnew(__FILE__,__LINE__)
externbooltraceFlag
#defineTRACE_ON()traceFlag=true
#defineTRACE_OFF()traceFlag=false
externboolactiveFlag
#defineMEM_ON()activeFlag=true
#defineMEM_OFF()activeFlag=false
#endif//MEMCHECK_H///:~
Itisimportanttoincludethisfileinanysourcefileinwhichyouwanttotrackfreestoreactivity,but
includeitlast(afteryourother#includedirectives).Mostheadersinthestandardlibraryaretemplates,
andsincemostcompilersusetheinclusionmodeloftemplatecompilation(meaningallsourcecodeisin
theheaders),themacrothatreplacesnewinMemCheck.hwouldusurpallinstancesofthenewoperator
inthelibrarysourcecode(andwouldlikelyresultincompileerrors).Besides,youareonlyinterestedin
trackingyourownmemoryerrors,notthelibrarys.
Inthefollowingfile,whichcontainsthememorytrackingimplementation,everythingisdonewithC
standardI/OratherthanwithC++iostreams.Itshouldntmakeadifference,sincewerenotinterfering
withiostreamsuseofthefreestore,butwhenwetriedit,somecompilerscomplained.Allcompilerswere
happywiththe<cstdio>version.
//:C02:MemCheck.cpp{O}
#include<cstdio>
#include<cstdlib>
#include<cassert>
#include<cstddef>
usingnamespacestd
#undefnew
//GlobalflagssetbymacrosinMemCheck.h
booltraceFlag=true
boolactiveFlag=false
namespace{
//Memorymapentrytype
structInfo{
void*ptr
constchar*file
longline
}
//Memorymapdata
constsize_tMAXPTRS=10000u
InfomemMap[MAXPTRS]
size_tnptrs=0
//Searchesthemapforanaddress
intfindPtr(void*p){
for(size_ti=0i<nptrs++i)
if(memMap[i].ptr==p)
returni
return1
}
voiddelPtr(void*p){
intpos=findPtr(p)
assert(pos>=0)
//Removepointerfrommap
for(size_ti=posi<nptrs1++i)
memMap[i]=memMap[i+1]
nptrs
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
58/458
5/3/2015
ThinkinginC++2ndedVolume2
//Dummytypeforstaticdestructor
structSentinel{
~Sentinel(){
if(nptrs>0){
printf("Leakedmemoryat:\n")
for(size_ti=0i<nptrs++i)
printf("\t%p(file:%s,line%ld)\n",
memMap[i].ptr,memMap[i].file,memMap[i].line)
}
else
printf("Nousermemoryleaks!\n")
}
}
//Staticdummyobject
Sentinels
}//Endanonymousnamespace
//Overloadscalarnew
void*
operatornew(size_tsiz,constchar*file,longline){
void*p=malloc(siz)
if(activeFlag){
if(nptrs==MAXPTRS){
printf("memorymaptoosmall(increaseMAXPTRS)\n")
exit(1)
}
memMap[nptrs].ptr=p
memMap[nptrs].file=file
memMap[nptrs].line=line
++nptrs
}
if(traceFlag){
printf("Allocated%ubytesataddress%p",siz,p)
printf("(file:%s,line:%ld)\n",file,line)
}
returnp
}
//Overloadarraynew
void*
operatornew[](size_tsiz,constchar*file,longline){
returnoperatornew(siz,file,line)
}
//Overridescalardelete
voidoperatordelete(void*p){
if(findPtr(p)>=0){
free(p)
assert(nptrs>0)
delPtr(p)
if(traceFlag)
printf("Deletedmemoryataddress%p\n",p)
}
elseif(!p&&activeFlag)
printf("Attempttodeleteunknownpointer:%p\n",p)
}
//Overridearraydelete
voidoperatordelete[](void*p){
operatordelete(p)
}///:~
TheBooleanflagstraceFlagandactiveFlagareglobal,sotheycanbemodifiedinyourcodebythe
macrosTRACE_ON(),TRACE_OFF(),MEM_ON(),andMEM_OFF().Ingeneral,encloseallthecode
inyourmain()withinaMEM_ON()MEM_OFF()pairsothatmemoryisalwaystracked.Tracing,which
echoestheactivityofthereplacementfunctionsforoperatornew()andoperatordelete(),isonby
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
59/458
5/3/2015
ThinkinginC++2ndedVolume2
default,butyoucanturnitoffwithTRACE_OFF().Inanycase,thefinalresultsarealwaysprinted(see
thetestrunslaterinthischapter).
TheMemCheckfacilitytracksmemorybykeepingalladdressesallocatedbyoperatornew()inan
arrayofInfostructures,whichalsoholdsthefilenameandlinenumberwherethecalltonewoccurred.
Topreventcollisionwithanynamesyouhaveplacedintheglobalnamespace,asmuchinformationas
possibleiskeptinsidetheanonymousnamespace.TheSentinelclassexistssolelytocallastaticobject
destructorastheprogramshutsdown.ThisdestructorinspectsmemMaptoseeifanypointersarewaiting
tobedeleted(indicatingamemoryleak).
Ouroperatornew()usesmalloc()togetmemory,andthenaddsthepointeranditsassociatedfile
informationtomemMap.Theoperatordelete()functionundoesallthatworkbycallingfree()and
decrementingnptrs,butfirstitcheckstoseeifthepointerinquestionisinthemapinthefirstplace.Ifit
isnt,eitheryouretryingtodeleteanaddressthatisntonthefreestore,oryouretryingtodeleteone
thatsalreadybeendeletedandremovedfromthemap.TheactiveFlagvariableisimportanthere
becausewedontwanttoprocessanydeallocationsfromanysystemshutdownactivity.Bycalling
MEM_OFF()attheendofyourcode,activeFlagwillbesettofalse,andsuchsubsequentcallsto
deletewillbeignored.(Thatsbadinarealprogram,butourpurposehereistofindyourleakswerenot
debuggingthelibrary.)Forsimplicity,weforwardallworkforarraynewanddeletetotheirscalar
counterparts.
ThefollowingisasimpletestusingtheMemCheckfacility:
//:C02:MemTest.cpp
//{L}MemCheck
//TestofMemChecksystem.
#include<iostream>
#include<vector>
#include<cstring>
#include"MemCheck.h"//Mustappearlast!
usingnamespacestd
classFoo{
char*s
public:
Foo(constchar*s){
this>s=newchar[strlen(s)+1]
strcpy(this>s,s)
}
~Foo(){delete[]s}
}
intmain(){
MEM_ON()
cout<<"hello"<<endl
int*p=newint
deletep
int*q=newint[3]
delete[]q
int*r
deleter
vector<int>v
v.push_back(1)
Foos("goodbye")
MEM_OFF()
}///:~
ThisexampleverifiesthatyoucanuseMemCheckinthepresenceofstreams,standardcontainers,and
classesthatallocatememoryinconstructors.Thepointerspandqareallocatedanddeallocatedwithout
anyproblem,butrisnotavalidheappointer,sotheoutputindicatestheerrorasanattempttodeletean
unknownpointer:
hello
Allocated4bytesataddress0xa010778(file:memtest.cpp,line:25)
Deletedmemoryataddress0xa010778
Allocated12bytesataddress0xa010778(file:memtest.cpp,line:27)
Deletedmemoryataddress0xa010778
Attempttodeleteunknownpointer:0x1
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
60/458
5/3/2015
ThinkinginC++2ndedVolume2
Allocated8bytesataddress0xa0108c0(file:memtest.cpp,line:14)
Deletedmemoryataddress0xa0108c0
Nousermemoryleaks!
BecauseofthecalltoMEM_OFF(),nosubsequentcallstooperatordelete()byvectororostream
areprocessed.Youstillmightgetsomecallstodeletefromreallocationsperformedbythecontainers.
IfyoucallTRACE_OFF()atthebeginningoftheprogram,theoutputis
hello
Attempttodeleteunknownpointer:0x1
Nousermemoryleaks!
Summary
Muchoftheheadacheofsoftwareengineeringcanbeavoidedbybeingdeliberateaboutwhatyouredoing.
Youveprobablybeenusingmentalassertionsasyouvecraftedyourloopsandfunctions,evenifyou
haventroutinelyusedtheassert()macro.Ifyoulluseassert(),youllfindlogicerrorssoonerandend
upwithmorereadablecodeaswell.Remembertoonlyuseassertionsforinvariants,though,andnotfor
runtimeerrorhandling.
Nothingwillgiveyoumorepeaceofmindthanthoroughlytestedcode.Ifitsbeenahassleforyouinthe
past,useanautomatedframework,suchastheonewevepresentedhere,tointegrateroutinetestinginto
yourdailywork.You(andyourusers!)willbegladyoudid.
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.WriteatestprogramusingtheTestSuiteFrameworkforthestandardvectorclassthatthoroughly
teststhefollowingmemberfunctionswithavectorofintegers:push_back()(appendsanelement
totheendofthevector),front()(returnsthefirstelementinthevector),back()(returnsthe
lastelementinthevector),pop_back()(removesthelastelementwithoutreturningit),at()
(returnstheelementinaspecifiedindexposition),andsize()(returnsthenumberofelements).Be
suretoverifythatvector::at()throwsastd::out_of_rangeexceptionifthesuppliedindexisout
ofrange.
2.SupposeyouareaskedtodevelopaclassnamedRationalthatsupportsrationalnumbers
(fractions).ThefractioninaRationalobjectshouldalwaysbestoredinlowestterms,anda
denominatorofzeroisanerror.HereisasampleinterfaceforsuchaRationalclass:
//:C02:Rational.h{xo}
#ifndefRATIONAL_H
#defineRATIONAL_H
#include<iosfwd>
classRational{
public:
Rational(intnumerator=0,intdenominator=1)
Rationaloperator()const
friendRationaloperator+(constRational&,
constRational&)
friendRationaloperator(constRational&,
constRational&)
friendRationaloperator*(constRational&,
constRational&)
friendRationaloperator/(constRational&,
constRational&)
friendstd::ostream&
operator<<(std::ostream&,constRational&)
friendstd::istream&
operator>>(std::istream&,Rational&)
Rational&operator+=(constRational&)
Rational&operator=(constRational&)
Rational&operator*=(constRational&)
Rational&operator/=(constRational&)
friendbooloperator<(constRational&,
constRational&)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
61/458
5/3/2015
ThinkinginC++2ndedVolume2
friendbooloperator>(constRational&,
constRational&)
friendbooloperator<=(constRational&,
constRational&)
friendbooloperator>=(constRational&,
constRational&)
friendbooloperator==(constRational&,
constRational&)
friendbooloperator!=(constRational&,
constRational&)
}
#endif//RATIONAL_H///:~
Writeacompletespecificationforthisclass,includingpreconditions,postconditions,andexception
specifications.
3.WriteatestusingtheTestSuiteframeworkthatthoroughlytestsallthespecificationsfromthe
previousexercise,includingtestingexceptions.
4.ImplementtheRationalclasssothatallthetestsfromthepreviousexercisepass.Useassertions
onlyforinvariants.
5.ThefileBuggedSearch.cppbelowcontainsabinarysearchfunctionthatsearchestherange[beg,
end)forwhat.Therearesomebugsinthealgorithm.Usethetracetechniquesfromthischapterto
debugthesearchfunction.
//:C02:BuggedSearch.cpp{xo}
//{L}../TestSuite/Test
#include<cstdlib>
#include<ctime>
#include<cassert>
#include<fstream>
#include"../TestSuite/Test.h"
usingnamespacestd
//Thisfunctionisonlyonewithbugs
int*binarySearch(int*beg,int*end,intwhat){
while(endbeg!=1){
if(*beg==what)returnbeg
intmid=(endbeg)/2
if(what<=beg[mid])end=beg+mid
elsebeg=beg+mid
}
return0
}
classBinarySearchTest:publicTestSuite::Test{
enum{SZ=10}
int*data
intmax//Tracklargestnumber
intcurrent//Currentnoncontainednumber
//UsedinnotContained()
//Findthenextnumbernotcontainedinthearray
intnotContained(){
while(data[current]+1==data[current+1])
++current
if(current>=SZ)returnmax+1
intretValue=data[current++]+1
returnretValue
}
voidsetData(){
data=newint[SZ]
assert(!max)
//Inputvalueswithincrementsofone.Leave
//outsomevaluesonbothoddandevenindexes.
for(inti=0i<SZ
rand()%2==0?max+=1:max+=2)
data[i++]=max
}
voidtestInBound(){
//Testlocationsbothoddandeven
//notcontainedandcontained
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
62/458
5/3/2015
ThinkinginC++2ndedVolume2
for(inti=SZi>=0)
test_(binarySearch(data,data+SZ,data[i]))
for(inti=notContained()i<max
i=notContained())
test_(!binarySearch(data,data+SZ,i))
}
voidtestOutBounds(){
//Testlowervalues
for(inti=data[0]i>data[0]100)
test_(!binarySearch(data,data+SZ,i))
//Testhighervalues
for(inti=data[SZ1]
++i<data[SZ1]+100)
test_(!binarySearch(data,data+SZ,i))
}
public:
BinarySearchTest(){max=current=0}
voidrun(){
setData()
testInBound()
testOutBounds()
delete[]data
}
}
intmain(){
srand(time(0))
BinarySearchTestt
t.run()
returnt.report()
}///:~
Part2:TheStandardC++Library
StandardC++notonlyincorporatesalltheStandardClibraries(withsmall
additionsandchangestosupporttypesafety),italsoaddslibrariesofitsown.
TheselibrariesarefarmorepowerfulthanthoseinStandardCtheleverageyou
getfromthemisanalogoustotheleverageyougetfromchangingfromCtoC++.
ThissectionofthebookgivesyouanindepthintroductiontokeyportionsoftheStandardC++library.
ThemostcompleteandalsothemostobscurereferencetothefulllibrariesistheStandarditself.Bjarne
StroustrupsTheC++ProgrammingLanguage,ThirdEdition(AddisonWesley,2000)remainsareliable
referenceforboththelanguageandthelibrary.ThemostcelebratedlibraryonlyreferenceisTheC++
StandardLibrary:ATutorialandReference,byNicolaiJosuttis(AddisonWesley,1999).Thegoalofthe
chaptersinthispartofthebookistoprovideyouwithanencyclopediaofdescriptionsandexamplesso
thatyoullhaveagoodstartingpointforsolvinganyproblemthatrequirestheuseoftheStandard
libraries.However,sometechniquesandtopicsarerarelyusedandarenotcoveredhere.Ifyoucantfind
itinthesechapters,reachfortheothertwobooksthisbookisnotintendedtoreplacethosebooksbut
rathertocomplementthem.Inparticular,wehopethataftergoingthroughthematerialinthefollowing
chaptersyoullhaveamucheasiertimeunderstandingthosebooks.
Youwillnoticethatthesechaptersdonotcontainexhaustivedocumentationdescribingeveryfunctionand
classintheStandardC++library.WeveleftthefulldescriptionstoothersinparticulartoP.J.Plaugers
DinkumwareC/C++LibraryReferenceathttp://www.dinkumware.com.Thisisanexcellentonlinesource
ofstandardlibrarydocumentationinHTMLformatthatyoucankeepresidentonyourcomputerandview
withaWebbrowserwheneveryouneedtolooksomethingup.Youcanviewthisonlineorpurchaseitfor
localviewing.ItcontainscompletereferencepagesfortheboththeCandC++libraries(soitsgoodto
useforallyourStandardC/C++programmingquestions).Electronicdocumentationiseffectivenotonly
becauseyoucanalwayshaveitwithyou,butalsobecauseyoucandoanelectronicsearch.
Whenyoureactivelyprogramming,theseresourcesshouldsatisfyyourreferenceneeds(andyoucanuse
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
63/458
5/3/2015
ThinkinginC++2ndedVolume2
themtolookupanythinginthischapterthatisntcleartoyou).AppendixAlistsadditionalreferences.
ThefirstchapterinthissectionintroducestheStandardC++stringclass,whichisapowerfultoolthat
simplifiesmostofthetextprocessingchoresyoumighthave.Chancesare,anythingyouvedoneto
characterstringswithlinesofcodeinCcanbedonewithamemberfunctioncallinthestringclass.
Chapter4coverstheiostreamslibrary,whichcontainsclassesforprocessinginputandoutputwithfiles,
stringtargets,andthesystemconsole.
AlthoughChapter5,TemplatesinDepth,isnotexplicitlyalibrarychapter,itisnecessarypreparationfor
thetwochaptersthatfollow.InChapter6weexaminethegenericalgorithmsofferedbytheStandardC++
library.Becausetheyareimplementedwithtemplates,thesealgorithmscanbeappliedtoanysequenceof
objects.Chapter7coversthestandardcontainersandtheirassociatediterators.Wecoveralgorithmsfirst
becausetheycanbefullyexploredbyusingonlyarraysandthevectorcontainer(whichwehavebeen
usingsinceearlyinVolume1).Itisalsonaturaltousethestandardalgorithmsinconnectionwith
containers,soitsgoodtobefamiliarwiththealgorithmsbeforestudyingthecontainers.
3:StringsinDepth
StringprocessingwithcharacterarraysisoneofthebiggesttimewastersinC.
Characterarraysrequiretheprogrammertokeeptrackofthedifferencebetween
staticquotedstringsandarrayscreatedonthestackandtheheap,andthefact
thatsometimesyourepassingaroundachar*andsometimesyoumustcopythe
wholearray.
Especiallybecausestringmanipulationissocommon,characterarraysareagreatsourceof
misunderstandingsandbugs.Despitethis,creatingstringclassesremainedacommonexercisefor
beginningC++programmersformanyyears.TheStandardC++librarystringclasssolvestheproblemof
characterarraymanipulationonceandforall,keepingtrackofmemoryevenduringassignmentsand
copyconstructions.Yousimplydontneedtothinkaboutit.
Thischapter[31]examinestheStandardC++stringclass,beginningwithalookatwhatconstitutesaC++
stringandhowtheC++versiondiffersfromatraditionalCcharacterarray.Youlllearnaboutoperations
andmanipulationsusingstringobjects,andyoullseehowC++stringsaccommodatevariationin
charactersetsandstringdataconversion.
Handlingtextisoneoftheoldestprogrammingapplications,soitsnotsurprisingthattheC++string
drawsheavilyontheideasandterminologythathavelongbeenusedinCandotherlanguages.Asyou
begintoacquaintyourselfwithC++strings,thisfactshouldbereassuring.Nomatterwhichprogramming
idiomyouchoose,therearethreecommonthingsyouwanttodowithastring:
Createormodifythesequenceofcharactersstoredinthestring.
Detectthepresenceorabsenceofelementswithinthestring.
Translatebetweenvariousschemesforrepresentingstringcharacters.
YoullseehoweachofthesejobsisaccomplishedusingC++stringobjects.
Whatsinastring?
InC,astringissimplyanarrayofcharactersthatalwaysincludesabinaryzero(oftencalledthenull
terminator)asitsfinalarrayelement.TherearesignificantdifferencesbetweenC++stringsandtheirC
progenitors.First,andmostimportant,C++stringshidethephysicalrepresentationofthesequenceof
characterstheycontain.Youdontneedtobeconcernedaboutarraydimensionsornullterminators.A
stringalsocontainscertainhousekeepinginformationaboutthesizeandstoragelocationofitsdata.
Specifically,aC++stringobjectknowsitsstartinglocationinmemory,itscontent,itslengthin
characters,andthelengthincharacterstowhichitcangrowbeforethestringobjectmustresizeits
internaldatabuffer.C++stringsthusgreatlyreducethelikelihoodofmakingthreeofthemostcommon
anddestructiveCprogrammingerrors:overwritingarraybounds,tryingtoaccessarraysthrough
uninitializedorincorrectlyvaluedpointers,andleavingpointersdanglingafteranarrayceasestooccupy
thestoragethatwasonceallocatedtoit.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
64/458
5/3/2015
ThinkinginC++2ndedVolume2
TheexactimplementationofmemorylayoutforthestringclassisnotdefinedbytheC++Standard.This
architectureisintendedtobeflexibleenoughtoallowdifferingimplementationsbycompilervendors,yet
guaranteepredictablebehaviorforusers.Inparticular,theexactconditionsunderwhichstorageis
allocatedtoholddataforastringobjectarenotdefined.Stringallocationruleswereformulatedtoallow
butnotrequireareferencecountedimplementation,butwhetherornottheimplementationusesreference
counting,thesemanticsmustbethesame.Toputthisabitdifferently,inC,everychararrayoccupiesa
uniquephysicalregionofmemory.InC++,individualstringobjectsmayormaynotoccupyunique
physicalregionsofmemory,butifreferencecountingavoidsstoringduplicatecopiesofdata,theindividual
objectsmustlookandactasthoughtheyexclusivelyownuniqueregionsofstorage.Forexample:
//:C03:StringStorage.h
#ifndefSTRINGSTORAGE_H
#defineSTRINGSTORAGE_H
#include<iostream>
#include<string>
#include"../TestSuite/Test.h"
usingstd::cout
usingstd::endl
usingstd::string
classStringStorageTest:publicTestSuite::Test{
public:
voidrun(){
strings1("12345")
//Thismaycopythefirsttothesecondor
//usereferencecountingtosimulateacopy:
strings2=s1
test_(s1==s2)
//Eitherway,thisstatementmustONLYmodifys1:
s1[0]='6'
cout<<"s1="<<s1<<endl//62345
cout<<"s2="<<s2<<endl//12345
test_(s1!=s2)
}
}
#endif//STRINGSTORAGE_H///:~
//:C03:StringStorage.cpp
//{L}../TestSuite/Test
#include"StringStorage.h"
intmain(){
StringStorageTestt
t.run()
returnt.report()
}///:~
Wesaythatanimplementationthatonlymakesuniquecopieswhenastringismodifiedusesacopyon
writestrategy.Thisapproachsavestimeandspacewhenstringsareusedonlyasvalueparametersorin
otherreadonlysituations.
Whetheralibraryimplementationusesreferencecountingornotshouldbetransparenttousersofthe
stringclass.Unfortunately,thisisnotalwaysthecase.Inmultithreadedprograms,itispractically
impossibletouseareferencecountingimplementationsafely.[32]
CreatingandinitializingC++strings
Creatingandinitializingstringsisastraightforwardpropositionandfairlyflexible.IntheSmallString.cpp
examplebelow,thefirststring,imBlank,isdeclaredbutcontainsnoinitialvalue.UnlikeaCchararray,
whichwouldcontainarandomandmeaninglessbitpatternuntilinitialization,imBlankdoescontain
meaningfulinformation.Thisstringobjectisinitializedtoholdnocharactersandcanproperlyreportits
zerolengthandabsenceofdataelementsusingclassmemberfunctions.
Thenextstring,heyMom,isinitializedbytheliteralargumentWherearemysocks?Thisformof
initializationusesaquotedcharacterarrayasaparametertothestringconstructor.Bycontrast,
standardReplyissimplyinitializedwithanassignment.Thelaststringofthegroup,useThisOneAgain,
isinitializedusinganexistingC++stringobject.Putanotherway,thisexampleillustratesthatstring
objectsletyoudothefollowing:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
65/458
5/3/2015
ThinkinginC++2ndedVolume2
Createanemptystringanddeferinitializingitwithcharacterdata.
Initializeastringbypassingaliteral,quotedcharacterarrayasanargumenttotheconstructor.
Initializeastringusingtheequalsign(=).
Useonestringtoinitializeanother.
//:C03:SmallString.cpp
#include<string>
usingnamespacestd
intmain(){
stringimBlank
stringheyMom("Wherearemysocks?")
stringstandardReply="Beamedintodeep"
"spaceonwideangledispersion?"
stringuseThisOneAgain(standardReply)
}///:~
Thesearethesimplestformsofstringinitialization,butvariationsoffermoreflexibilityandcontrol.You
candothefollowing:
UseaportionofeitheraCchararrayoraC++string.
Combinedifferentsourcesofinitializationdatausingoperator+.
Usethestringobjectssubstr()memberfunctiontocreateasubstring.
Heresaprogramthatillustratesthesefeatures:
//:C03:SmallString2.cpp
#include<string>
#include<iostream>
usingnamespacestd
intmain(){
strings1("Whatisthesoundofoneclamnapping?")
strings2("Anythingworthdoingisworthoverdoing.")
strings3("IsawElvisinaUFO")
//Copythefirst8chars:
strings4(s1,0,8)
cout<<s4<<endl
//Copy6charsfromthemiddleofthesource:
strings5(s2,15,6)
cout<<s5<<endl
//Copyfrommiddletoend:
strings6(s3,6,15)
cout<<s6<<endl
//Copymanydifferentthings:
stringquoteMe=s4+"that"+
//substr()copies10charsatelement20
s1.substr(20,10)+s5+
//substr()copiesuptoeither100char
//oreosstartingatelement5
"with"+s3.substr(5,100)+
//OKtocopyasinglecharthisway
s1.substr(37,1)
cout<<quoteMe<<endl
}///:~
Thestringmemberfunctionsubstr()takesastartingpositionasitsfirstargumentandthenumberof
characterstoselectasthesecondargument.Bothargumentshavedefaultvalues.Ifyousaysubstr()
withanemptyargumentlist,youproduceacopyoftheentirestring,sothisisaconvenientwayto
duplicateastring.
Herestheoutputfromtheprogram:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
66/458
5/3/2015
ThinkinginC++2ndedVolume2
Whatis
doing
ElvisinaUFO
WhatisthatoneclamdoingwithElvisinaUFO?
Noticethefinallineoftheexample.C++allowsstringinitializationtechniquestobemixedinasingle
statement,aflexibleandconvenientfeature.Alsonoticethatthelastinitializercopiesjustonecharacter
fromthesourcestring.
Anotherslightlymoresubtleinitializationtechniqueinvolvestheuseofthestringiterators
string::begin()andstring::end().Thistechniquetreatsastringlikeacontainerobject(whichyouve
seenprimarilyintheformofvectorsofaryoullseemanymorecontainersinChapter7),whichuses
iteratorstoindicatethestartandendofasequenceofcharacters.Inthiswayyoucanhandastring
constructortwoiterators,anditcopiesfromonetotheotherintothenewstring:
//:C03:StringIterators.cpp
#include<string>
#include<iostream>
#include<cassert>
usingnamespacestd
intmain(){
stringsource("xxx")
strings(source.begin(),source.end())
assert(s==source)
}///:~
Theiteratorsarenotrestrictedtobegin()andend()youcanincrement,decrement,andaddinteger
offsetstothem,allowingyoutoextractasubsetofcharactersfromthesourcestring.
C++stringsmaynotbeinitializedwithsinglecharactersorwithASCIIorotherintegervalues.Youcan
initializeastringwithanumberofcopiesofasinglecharacter,however:
//:C03:UhOh.cpp
#include<string>
#include<cassert>
usingnamespacestd
intmain(){
//Error:nosinglecharinits
//!stringnothingDoing1('a')
//Error:nointegerinits
//!stringnothingDoing2(0x37)
//Thefollowingislegal:
stringokay(5,'a')
assert(okay==string("aaaaa"))
}///:~
Thefirstargumentindicatesthenumberofcopiesofthesecondargumenttoplaceinthestring.The
secondargumentcanonlybeasinglechar,notachararray.
Operatingonstrings
IfyouveprogrammedinC,youareaccustomedtothefamilyoffunctionsthatwrite,search,modify,and
copychararrays.TherearetwounfortunateaspectsoftheStandardClibraryfunctionsforhandlingchar
arrays.First,therearetwolooselyorganizedfamiliesofthem:theplaingroup,andtheonesthatrequire
youtosupplyacountofthenumberofcharacterstobeconsideredintheoperationathand.Therosterof
functionsintheCchararraylibraryshockstheunsuspectinguserwithalonglistofcryptic,mostly
unpronounceablenames.Althoughthetypeandnumberofargumentstothefunctionsaresomewhat
consistent,tousethemproperlyyoumustbeattentivetodetailsoffunctionnamingandparameter
passing.
ThesecondinherenttrapofthestandardCchararraytoolsisthattheyallrelyexplicitlyonthe
assumptionthatthecharacterarrayincludesanullterminator.Ifbyoversightorerrorthenullisomitted
oroverwritten,thereslittletokeeptheCchararrayfunctionsfrommanipulatingthememorybeyondthe
limitsoftheallocatedspace,sometimeswithdisastrousresults.
C++providesavastimprovementintheconvenienceandsafetyofstringobjects.Forpurposesofactual
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
67/458
5/3/2015
ThinkinginC++2ndedVolume2
stringhandlingoperations,thereareaboutthesamenumberofdistinctmemberfunctionnamesinthe
stringclassastherearefunctionsintheClibrary,butbecauseofoverloadingthefunctionalityismuch
greater.Coupledwithsensiblenamingpracticesandthejudicioususeofdefaultarguments,thesefeatures
combinetomakethestringclassmucheasiertousethantheClibrarychararrayfunctions.
Appending,inserting,
andconcatenatingstrings
OneofthemostvaluableandconvenientaspectsofC++stringsisthattheygrowasneeded,without
interventiononthepartoftheprogrammer.Notonlydoesthismakestringhandlingcodeinherentlymore
trustworthy,italsoalmostentirelyeliminatesatedioushousekeepingchorekeepingtrackofthebounds
ofthestoragewhereyourstringslive.Forexample,ifyoucreateastringobjectandinitializeitwitha
stringof50copiesofX,andlaterstoreinit50copiesofZowie,theobjectitselfwillreallocatesufficient
storagetoaccommodatethegrowthofthedata.Perhapsnowhereisthispropertymoreappreciatedthan
whenthestringsmanipulatedinyourcodechangesizeandyoudontknowhowbigthechangeis.The
stringmemberfunctionsappend()andinsert()transparentlyreallocatestoragewhenastringgrows:
//:C03:StrSize.cpp
#include<string>
#include<iostream>
usingnamespacestd
intmain(){
stringbigNews("IsawElvisinaUFO.")
cout<<bigNews<<endl
//Howmuchdatahaveweactuallygot?
cout<<"Size="<<bigNews.size()<<endl
//Howmuchcanwestorewithoutreallocating?
cout<<"Capacity="<<bigNews.capacity()<<endl
//InsertthisstringinbigNewsimmediately
//beforebigNews[1]:
bigNews.insert(1,"thoughtI")
cout<<bigNews<<endl
cout<<"Size="<<bigNews.size()<<endl
cout<<"Capacity="<<bigNews.capacity()<<endl
//Makesurethattherewillbethismuchspace
bigNews.reserve(500)
//Addthistotheendofthestring:
bigNews.append("I'vebeenworkingtoohard.")
cout<<bigNews<<endl
cout<<"Size="<<bigNews.size()<<endl
cout<<"Capacity="<<bigNews.capacity()<<endl
}///:~
Hereistheoutputfromoneparticularcompiler:
IsawElvisinaUFO.
Size=22
Capacity=31
IthoughtIsawElvisinaUFO.
Size=32
Capacity=47
IthoughtIsawElvisinaUFO.I'vebeen
workingtoohard.
Size=59
Capacity=511
Thisexampledemonstratesthateventhoughyoucansafelyrelinquishmuchoftheresponsibilityfor
allocatingandmanagingthememoryyourstringsoccupy,C++stringsprovideyouwithseveraltoolsto
monitorandmanagetheirsize.Noticetheeasewithwhichwechangedthesizeofthestorageallocatedto
thestring.Thesize()functionreturnsthenumberofcharacterscurrentlystoredinthestringandis
identicaltothelength()memberfunction.Thecapacity()functionreturnsthesizeofthecurrent
underlyingallocation,meaningthenumberofcharactersthestringcanholdwithoutrequestingmore
storage.Thereserve()functionisanoptimizationmechanismthatindicatesyourintentiontospecifya
certainamountofstorageforfutureusecapacity()alwaysreturnsavalueatleastaslargeasthemost
recentcalltoreserve().Aresize()functionappendsspacesifthenewsizeisgreaterthanthecurrent
stringsizeortruncatesthestringotherwise.(Anoverloadofresize()canspecifyadifferentcharacterto
append.)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
68/458
5/3/2015
ThinkinginC++2ndedVolume2
Theexactfashionthatthestringmemberfunctionsallocatespaceforyourdatadependsonthe
implementationofthelibrary.Whenwetestedoneimplementationwiththepreviousexample,itappeared
thatreallocationsoccurredonevenword(thatis,fullinteger)boundaries,withonebyteheldback.The
architectsofthestringclasshaveendeavoredtomakeitpossibletomixtheuseofCchararraysand
C++stringobjects,soitislikelythatfiguresreportedbyStrSize.cppforcapacityreflectthat,inthis
particularimplementation,abyteissetasidetoeasilyaccommodatetheinsertionofanullterminator.
Replacingstringcharacters
Theinsert()functionisparticularlynicebecauseitabsolvesyoufrommakingsuretheinsertionof
charactersinastringwontoverrunthestoragespaceoroverwritethecharactersimmediatelyfollowing
theinsertionpoint.Spacegrows,andexistingcharacterspolitelymoveovertoaccommodatethenew
elements.Sometimesthismightnotbewhatyouwant.Ifyouwantthesizeofthestringtoremain
unchanged,usethereplace()functiontooverwritecharacters.Thereareanumberofoverloaded
versionsofreplace(),butthesimplestonetakesthreearguments:anintegerindicatingwheretostartin
thestring,anintegerindicatinghowmanycharacterstoeliminatefromtheoriginalstring,andthe
replacementstring(whichcanbeadifferentnumberofcharactersthantheeliminatedquantity).Heresa
simpleexample:
//:C03:StringReplace.cpp
//Simplefindandreplaceinstrings.
#include<cassert>
#include<string>
usingnamespacestd
intmain(){
strings("Apieceoftext")
stringtag("$tag$")
s.insert(8,tag+'')
assert(s=="Apiece$tag$oftext")
intstart=s.find(tag)
assert(start==8)
assert(tag.size()==5)
s.replace(start,tag.size(),"hellothere")
assert(s=="Apiecehellothereoftext")
}///:~
Thetagisfirstinsertedintos(noticethattheinserthappensbeforethevalueindicatingtheinsertpoint
andthatanextraspacewasaddedaftertag),andthenitisfoundandreplaced.
Youshouldchecktoseeifyouvefoundanythingbeforeyouperformareplace().Thepreviousexample
replaceswithachar*,buttheresanoverloadedversionthatreplaceswithastring.Heresamore
completedemonstrationreplace():
//:C03:Replace.cpp
#include<cassert>
#include<cstddef>//Forsize_t
#include<string>
usingnamespacestd
voidreplaceChars(string&modifyMe,
conststring&findMe,conststring&newChars){
//LookinmodifyMeforthe"findstring"
//startingatposition0:
size_ti=modifyMe.find(findMe,0)
//Didwefindthestringtoreplace?
if(i!=string::npos)
//ReplacethefindstringwithnewChars:
modifyMe.replace(i,findMe.size(),newChars)
}
intmain(){
stringbigNews="IthoughtIsawElvisinaUFO."
"Ihavebeenworkingtoohard."
stringreplacement("wig")
stringfindMe("UFO")
//Find"UFO"inbigNewsandoverwriteit:
replaceChars(bigNews,findMe,replacement)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
69/458
5/3/2015
ThinkinginC++2ndedVolume2
assert(bigNews=="IthoughtIsawElvisina"
"wig.Ihavebeenworkingtoohard.")
}///:~
Ifreplacedoesntfindthesearchstring,itreturnsstring::npos.Thenposdatamemberisastatic
constantmemberofthestringclassthatrepresentsanonexistentcharacterposition.[33]
Unlikeinsert(),replace()wontgrowthestringsstoragespaceifyoucopynewcharactersintothe
middleofanexistingseriesofarrayelements.However,itwillgrowthestoragespaceifneeded,for
example,whenyoumakeareplacementthatwouldexpandtheoriginalstringbeyondtheendofthe
currentallocation.Heresanexample:
//:C03:ReplaceAndGrow.cpp
#include<cassert>
#include<string>
usingnamespacestd
intmain(){
stringbigNews("Ihavebeenworkingthegrave.")
stringreplacement("yardshift.")
//Thefirstargumentsays"replacechars
//beyondtheendoftheexistingstring":
bigNews.replace(bigNews.size()1,
replacement.size(),replacement)
assert(bigNews=="Ihavebeenworkingthe"
"graveyardshift.")
}///:~
Thecalltoreplace()beginsreplacingbeyondtheendoftheexistingarray,whichisequivalenttoan
appendoperation.Noticethatinthisexamplereplace()expandsthearrayaccordingly.
Youmayhavebeenhuntingthroughthischaptertryingtodosomethingrelativelysimplesuchasreplace
alltheinstancesofonecharacterwithadifferentcharacter.Uponfindingthepreviousmaterialon
replacing,youthoughtyoufoundtheanswer,butthenyoustartedseeinggroupsofcharactersandcounts
andotherthingsthatlookedabittoocomplex.Doesntstringhaveawaytojustreplaceonecharacter
withanothereverywhere?
Youcaneasilywritesuchafunctionusingthefind()andreplace()memberfunctionsasfollows:
//:C03:ReplaceAll.h
#ifndefREPLACEALL_H
#defineREPLACEALL_H
#include<string>
std::string&replaceAll(std::string&context,
conststd::string&from,conststd::string&to)
#endif//REPLACEALL_H///:~
//:C03:ReplaceAll.cpp{O}
#include<cstddef>
#include"ReplaceAll.h"
usingnamespacestd
string&replaceAll(string&context,conststring&from,
conststring&to){
size_tlookHere=0
size_tfoundHere
while((foundHere=context.find(from,lookHere))
!=string::npos){
context.replace(foundHere,from.size(),to)
lookHere=foundHere+to.size()
}
returncontext
}///:~
Theversionoffind()usedheretakesasasecondargumentthepositiontostartlookinginandreturns
string::nposifitdoesntfindit.ItisimportanttoadvancethepositionheldinthevariablelookHerepast
thereplacementstring,incasefromisasubstringofto.ThefollowingprogramteststhereplaceAll
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
70/458
5/3/2015
ThinkinginC++2ndedVolume2
function:
//:C03:ReplaceAllTest.cpp
//{L}ReplaceAll
#include<cassert>
#include<iostream>
#include<string>
#include"ReplaceAll.h"
usingnamespacestd
intmain(){
stringtext="aman,aplan,acanal,Panama"
replaceAll(text,"an","XXX")
assert(text=="amXXX,aplXXX,acXXXal,PXXXama")
}///:~
Asyoucansee,thestringclassbyitselfdoesntsolveallpossibleproblems.Manysolutionshavebeen
lefttothealgorithmsintheStandardlibrary[34]becausethestringclasscanlookjustlikeanSTL
sequence(byvirtueoftheiteratorsdiscussedearlier).Allthegenericalgorithmsworkonarangeof
elementswithinacontainer.Usuallythatrangeisjustfromthebeginningofthecontainertotheend.A
stringobjectlookslikeacontainerofcharacters:togetthebeginningoftherangeyouuse
string::begin(),andtogettheendoftherangeyouusestring::end().Thefollowingexampleshows
theuseofthereplace()algorithmtoreplacealltheinstancesofthesinglecharacterXwithY:
//:C03:StringCharReplace.cpp
#include<algorithm>
#include<cassert>
#include<string>
usingnamespacestd
intmain(){
strings("aaaXaaaXXaaXXXaXXXXaaa")
replace(s.begin(),s.end(),'X','Y')
assert(s=="aaaYaaaYYaaYYYaYYYYaaa")
}///:~
Noticethatthisreplace()isnotcalledasamemberfunctionofstring.Also,unlikethe
string::replace()functionsthatonlyperformonereplacement,thereplace()algorithmreplacesall
instancesofonecharacterwithanother.
Thereplace()algorithmonlyworkswithsingleobjects(inthiscase,charobjects)andwillnotreplace
quotedchararraysorstringobjects.SinceastringbehaveslikeanSTLsequence,anumberofother
algorithmscanbeappliedtoit,whichmightsolveotherproblemsthatarenotdirectlyaddressedbythe
stringmemberfunctions.
Concatenationusing
nonmemberoverloadedoperators
OneofthemostdelightfuldiscoveriesawaitingaCprogrammerlearningaboutC++stringhandlingishow
simplystringscanbecombinedandappendedusingoperator+andoperator+=.Theseoperatorsmake
combiningstringssyntacticallysimilartoaddingnumericdata:
//:C03:AddStrings.cpp
#include<string>
#include<cassert>
usingnamespacestd
intmain(){
strings1("This")
strings2("That")
strings3("Theother")
//operator+concatenatesstrings
s1=s1+s2
assert(s1=="ThisThat")
//Anotherwaytoconcatenatesstrings
s1+=s3
assert(s1=="ThisThatTheother")
//Youcanindexthestringontheright
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
71/458
5/3/2015
ThinkinginC++2ndedVolume2
s1+=s3+s3[4]+"oohlala"
assert(s1=="ThisThatTheotherTheotherooohlala")
}///:~
Usingtheoperator+andoperator+=operatorsisaflexibleandconvenientwaytocombinestringdata.
Ontherightsideofthestatement,youcanusealmostanytypethatevaluatestoagroupofoneormore
characters.
Searchinginstrings
Thefindfamilyofstringmemberfunctionslocatesacharacterorgroupofcharacterswithinagiven
string.Herearethemembersofthefindfamilyandtheirgeneralusage:
stringfindmemberfunction
What/howitfinds
find()
Searchesastringforaspecified
characterorgroupofcharactersand
returnsthestartingpositionofthe
firstoccurrencefoundornposifno
matchisfound.
find_first_of()
Searchesatargetstringandreturns
thepositionofthefirstmatchofany
characterinaspecifiedgroup.Ifno
matchisfound,itreturnsnpos.
find_last_of()
Searchesatargetstringandreturns
thepositionofthelastmatchofany
characterinaspecifiedgroup.Ifno
matchisfound,itreturnsnpos.
find_first_not_of()
Searchesatargetstringandreturns
thepositionofthefirstelementthat
doesntmatchanycharacterina
specifiedgroup.Ifnosuchelementis
found,itreturnsnpos.
find_last_not_of()
Searchesatargetstringandreturns
thepositionoftheelementwiththe
largestsubscriptthatdoesntmatch
anycharacterinaspecifiedgroup.If
nosuchelementisfound,itreturns
npos.
rfind()
Searchesastringfromendto
beginningforaspecifiedcharacteror
groupofcharactersandreturnsthe
startingpositionofthematchifoneis
found.Ifnomatchisfound,itreturns
npos.
Thesimplestuseoffind()searchesforoneormorecharactersinastring.Thisoverloadedversionof
find()takesaparameterthatspecifiesthecharacter(s)forwhichtosearchandoptionallyaparameter
thattellsitwhereinthestringtobeginsearchingfortheoccurrenceofasubstring.(Thedefaultpositionat
whichtobeginsearchingis0.)Bysettingthecalltofindinsidealoop,youcaneasilymovethrougha
string,repeatingasearchtofindalltheoccurrencesofagivencharacterorgroupofcharacterswithinthe
string.
ThefollowingprogramusesthemethodofTheSieveofEratosthenestofindprimenumberslessthan50.
Thismethodstartswiththenumber2,marksallsubsequentmultiplesof2asnotprime,andrepeatsthe
processforthenextprimecandidate.TheSieveTestconstructorinitializessieveCharsbysettingthe
initialsizeofthecharacterarrayandwritingthevaluePtoeachofitsmembers.
//:C03:Sieve.h
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
72/458
5/3/2015
ThinkinginC++2ndedVolume2
#ifndefSIEVE_H
#defineSIEVE_H
#include<cmath>
#include<cstddef>
#include<string>
#include"../TestSuite/Test.h"
usingstd::size_t
usingstd::sqrt
usingstd::string
classSieveTest:publicTestSuite::Test{
stringsieveChars
public:
//Createa50charstringandseteach
//elementto'P'forPrime:
SieveTest():sieveChars(50,'P'){}
voidrun(){
findPrimes()
testPrimes()
}
boolisPrime(intp){
if(p==0||p==1)returnfalse
introot=int(sqrt(double(p)))
for(inti=2i<=root++i)
if(p%i==0)returnfalse
returntrue
}
voidfindPrimes(){
//Bydefinitionneither0nor1isprime.
//Changetheseelementsto"N"forNotPrime:
sieveChars.replace(0,2,"NN")
//Walkthroughthearray:
size_tsieveSize=sieveChars.size()
introot=int(sqrt(double(sieveSize)))
for(inti=2i<=root++i)
//Findallthemultiples:
for(size_tfactor=2factor*i<sieveSize
++factor)
sieveChars[factor*i]='N'
}
voidtestPrimes(){
size_ti=sieveChars.find('P')
while(i!=string::npos){
test_(isPrime(i++))
i=sieveChars.find('P',i)
}
i=sieveChars.find_first_not_of('P')
while(i!=string::npos){
test_(!isPrime(i++))
i=sieveChars.find_first_not_of('P',i)
}
}
}
#endif//SIEVE_H///:~
//:C03:Sieve.cpp
//{L}../TestSuite/Test
#include"Sieve.h"
intmain(){
SieveTestt
t.run()
returnt.report()
}///:~
Thefind()functioncanwalkforwardthroughastring,detectingmultipleoccurrencesofacharacterora
groupofcharacters,andfind_first_not_of()findsothercharactersorsubstrings.
Therearenofunctionsinthestringclasstochangethecaseofastring,butyoucaneasilycreatethese
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
73/458
5/3/2015
ThinkinginC++2ndedVolume2
functionsusingtheStandardClibraryfunctionstoupper()andtolower(),whichchangethecaseofone
characteratatime.Thefollowingexampleillustratesacaseinsensitivesearch:
//:C03:Find.h
#ifndefFIND_H
#defineFIND_H
#include<cctype>
#include<cstddef>
#include<string>
#include"../TestSuite/Test.h"
usingstd::size_t
usingstd::string
usingstd::tolower
usingstd::toupper
//Makeanuppercasecopyofs
inlinestringupperCase(conststring&s){
stringupper(s)
for(size_ti=0i<s.length()++i)
upper[i]=toupper(upper[i])
returnupper
}
//Makealowercasecopyofs
inlinestringlowerCase(conststring&s){
stringlower(s)
for(size_ti=0i<s.length()++i)
lower[i]=tolower(lower[i])
returnlower
}
classFindTest:publicTestSuite::Test{
stringchooseOne
public:
FindTest():chooseOne("Eenie,Meenie,Miney,Mo"){}
voidtestUpper(){
stringupper=upperCase(chooseOne)
conststringLOWER="abcdefghijklmnopqrstuvwxyz"
test_(upper.find_first_of(LOWER)==string::npos)
}
voidtestLower(){
stringlower=lowerCase(chooseOne)
conststringUPPER="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
test_(lower.find_first_of(UPPER)==string::npos)
}
voidtestSearch(){
//Casesensitivesearch
size_ti=chooseOne.find("een")
test_(i==8)
//Searchlowercase:
stringtest=lowerCase(chooseOne)
i=test.find("een")
test_(i==0)
i=test.find("een",++i)
test_(i==8)
i=test.find("een",++i)
test_(i==string::npos)
//Searchuppercase:
test=upperCase(chooseOne)
i=test.find("EEN")
test_(i==0)
i=test.find("EEN",++i)
test_(i==8)
i=test.find("EEN",++i)
test_(i==string::npos)
}
voidrun(){
testUpper()
testLower()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
74/458
5/3/2015
ThinkinginC++2ndedVolume2
testSearch()
}
}
#endif//FIND_H///:~
//:C03:Find.cpp
//{L}../TestSuite/Test
#include"Find.h"
#include"../TestSuite/Test.h"
intmain(){
FindTestt
t.run()
returnt.report()
}///:~
BoththeupperCase()andlowerCase()functionsfollowthesameform:theymakeacopyofthe
argumentstringandchangethecase.TheFind.cppprogramisntthebestsolutiontothecasesensitivity
problem,sowellrevisititwhenweexaminestringcomparisons.
Findinginreverse
Ifyouneedtosearchthroughastringfromendtobeginning(tofindthedatainlastin/firstoutorder),
youcanusethestringmemberfunctionrfind():
//:C03:Rparse.h
#ifndefRPARSE_H
#defineRPARSE_H
#include<cstddef>
#include<string>
#include<vector>
#include"../TestSuite/Test.h"
usingstd::size_t
usingstd::string
usingstd::vector
classRparseTest:publicTestSuite::Test{
//Tostorethewords:
vector<string>strings
public:
voidparseForData(){
//The''characterswillbedelimiters
strings("now.sensemaketogoingisThis")
//Thelastelementofthestring:
intlast=s.size()
//Thebeginningofthecurrentword:
size_tcurrent=s.rfind('')
//Walkbackwardthroughthestring:
while(current!=string::npos){
//Pusheachwordintothevector.
//Currentisincrementedbeforecopying
//toavoidcopyingthedelimiter:
++current
strings.push_back(s.substr(current,lastcurrent))
//Backoverthedelimiterwejustfound,
//andsetlasttotheendofthenextword:
current=2
last=current+1
//Findthenextdelimiter:
current=s.rfind('',current)
}
//Pickupthefirstwordit'snot
//precededbyadelimiter:
strings.push_back(s.substr(0,last))
}
voidtestData(){
//Testthemintheneworder:
test_(strings[0]=="This")
test_(strings[1]=="is")
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
75/458
5/3/2015
ThinkinginC++2ndedVolume2
test_(strings[2]=="going")
test_(strings[3]=="to")
test_(strings[4]=="make")
test_(strings[5]=="sense")
test_(strings[6]=="now.")
stringsentence
for(size_ti=0i<strings.size()1i++)
sentence+=strings[i]+=""
//Manuallyputlastwordintoavoidanextraspace:
sentence+=strings[strings.size()1]
test_(sentence=="Thisisgoingtomakesensenow.")
}
voidrun(){
parseForData()
testData()
}
}
#endif//RPARSE_H///:~
//:C03:Rparse.cpp
//{L}../TestSuite/Test
#include"Rparse.h"
intmain(){
RparseTestt
t.run()
returnt.report()
}///:~
Thestringmemberfunctionrfind()backsthroughthestringlookingfortokensandreportsthearray
indexofmatchingcharactersorstring::nposifitisunsuccessful.
Findingfirst/lastofasetofcharacters
Thefind_first_of()andfind_last_of()memberfunctionscanbeconvenientlyputtoworktocreatea
littleutilitythatwillstripwhitespacecharactersfrombothendsofastring.Noticethatitdoesnttouchthe
originalstring,butinsteadreturnsanewstring:
//:C03:Trim.h
//Generaltooltostripspacesfrombothends.
#ifndefTRIM_H
#defineTRIM_H
#include<string>
#include<cstddef>
inlinestd::stringtrim(conststd::string&s){
if(s.length()==0)
returns
std::size_tbeg=s.find_first_not_of("\a\b\f\n\r\t\v")
std::size_tend=s.find_last_not_of("\a\b\f\n\r\t\v")
if(beg==std::string::npos)//Nononspaces
return""
returnstd::string(s,beg,endbeg+1)
}
#endif//TRIM_H///:~
Thefirsttestchecksforanemptystringinthatcase,notestsaremade,andacopyisreturned.Notice
thatoncetheendpointsarefound,thestringconstructorbuildsanewstringfromtheoldone,givingthe
startingcountandthelength.
Testingsuchageneralpurposetoolneedstobethorough:
//:C03:TrimTest.h
#ifndefTRIMTEST_H
#defineTRIMTEST_H
#include"Trim.h"
#include"../TestSuite/Test.h"
classTrimTest:publicTestSuite::Test{
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
76/458
5/3/2015
ThinkinginC++2ndedVolume2
enum{NTESTS=11}
staticstd::strings[NTESTS]
public:
voidtestTrim(){
test_(trim(s[0])=="abcdefghijklmnop")
test_(trim(s[1])=="abcdefghijklmnop")
test_(trim(s[2])=="abcdefghijklmnop")
test_(trim(s[3])=="a")
test_(trim(s[4])=="ab")
test_(trim(s[5])=="abc")
test_(trim(s[6])=="abc")
test_(trim(s[7])=="abc")
test_(trim(s[8])=="a\tb\tc")
test_(trim(s[9])=="")
test_(trim(s[10])=="")
}
voidrun(){
testTrim()
}
}
#endif//TRIMTEST_H///:~
//:C03:TrimTest.cpp{O}
#include"TrimTest.h"
//Initializestaticdata
std::stringTrimTest::s[TrimTest::NTESTS]={
"\tabcdefghijklmnop\t",
"abcdefghijklmnop\t",
"\tabcdefghijklmnop",
"a","ab","abc","abc",
"\tabc\t","\ta\tb\tc\t",
"\t\n\r\v\f",
""//Mustalsotesttheemptystring
}///:~
//:C03:TrimTestMain.cpp
//{L}../TestSuite/TestTrimTest
#include"TrimTest.h"
intmain(){
TrimTestt
t.run()
returnt.report()
}///:~
Inthearrayofstrings,youcanseethatthecharacterarraysareautomaticallyconvertedtostring
objects.Thisarrayprovidescasestochecktheremovalofspacesandtabsfrombothends,aswellas
ensuringthatspacesandtabsarenotremovedfromthemiddleofastring.
Removingcharactersfromstrings
Removingcharactersiseasyandefficientwiththeerase()memberfunction,whichtakestwoarguments:
wheretostartremovingcharacters(whichdefaultsto0),andhowmanytoremove(whichdefaultsto
string::npos).Ifyouspecifymorecharactersthanremaininthestring,theremainingcharactersareall
erasedanyway(socallingerase()withoutanyargumentsremovesallcharactersfromastring).
SometimesitsusefultotakeanHTMLfileandstripitstagsandspecialcharacterssothatyouhave
somethingapproximatingthetextthatwouldbedisplayedintheWebbrowser,onlyasaplaintextfile.The
followingexampleuseserase()todothejob:
//:C03:HTMLStripper.cpp{RunByHand}
//{L}ReplaceAll
//Filtertoremovehtmltagsandmarkers.
#include<cassert>
#include<cmath>
#include<cstddef>
#include<fstream>
#include<iostream>
#include<string>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
77/458
5/3/2015
ThinkinginC++2ndedVolume2
#include"ReplaceAll.h"
#include"../require.h"
usingnamespacestd
string&stripHTMLTags(string&s){
staticboolinTag=false
booldone=false
while(!done){
if(inTag){
//ThepreviouslinestartedanHTMLtag
//butdidn'tfinish.Mustsearchfor'>'.
size_trightPos=s.find('>')
if(rightPos!=string::npos){
inTag=false
s.erase(0,rightPos+1)
}
else{
done=true
s.erase()
}
}
else{
//Lookforstartoftag:
size_tleftPos=s.find('<')
if(leftPos!=string::npos){
//Seeiftagcloseisinthisline:
size_trightPos=s.find('>')
if(rightPos==string::npos){
inTag=done=true
s.erase(leftPos)
}
else
s.erase(leftPos,rightPosleftPos+1)
}
else
done=true
}
}
//RemoveallspecialHTMLcharacters
replaceAll(s,"<","<")
replaceAll(s,">",">")
replaceAll(s,"&","&")
replaceAll(s," ","")
//Etc...
returns
}
intmain(intargc,char*argv[]){
requireArgs(argc,1,
"usage:HTMLStripperInputFile")
ifstreamin(argv[1])
assure(in,argv[1])
strings
while(getline(in,s))
if(!stripHTMLTags(s).empty())
cout<<s<<endl
}///:~
ThisexamplewillevenstripHTMLtagsthatspanmultiplelines.[35]Thisisaccomplishedwiththestatic
flag,inTag,whichistruewheneverthestartofatagisfound,buttheaccompanyingtagendisnotfound
inthesameline.Allformsoferase()appearinthestripHTMLFlags()function.[36]Theversionof
getline()weusehereisa(global)functiondeclaredinthe<string>headerandishandybecauseit
storesanarbitrarilylonglineinitsstringargument.Youdontneedtoworryaboutthedimensionofa
characterarrayasyoudowithistream::getline().NoticethatthisprogramusesthereplaceAll()
functionfromearlierinthischapter.Inthenextchapter,wellusestringstreamstocreateamoreelegant
solution.
Comparingstrings
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
78/458
5/3/2015
ThinkinginC++2ndedVolume2
Comparingstringsisinherentlydifferentfromcomparingnumbers.Numbershaveconstant,universally
meaningfulvalues.Toevaluatetherelationshipbetweenthemagnitudesoftwostrings,youmustmakea
lexicalcomparison.Lexicalcomparisonmeansthatwhenyoutestacharactertoseeifitisgreaterthan
orlessthananothercharacter,youareactuallycomparingthenumericrepresentationofthose
charactersasspecifiedinthecollatingsequenceofthecharactersetbeingused.Mostoftenthiswillbethe
ASCIIcollatingsequence,whichassignstheprintablecharactersfortheEnglishlanguagenumbersinthe
range32through127decimal.IntheASCIIcollatingsequence,thefirstcharacterinthelististhespace,
followedbyseveralcommonpunctuationmarks,andthenuppercaseandlowercaseletters.Withrespectto
thealphabet,thismeansthatthelettersnearerthefronthavelowerASCIIvaluesthanthosenearerthe
end.Withthesedetailsinmind,itbecomeseasiertorememberthatwhenalexicalcomparisonthat
reportss1isgreaterthans2,itsimplymeansthatwhenthetwowerecompared,thefirstdiffering
characterins1camelaterinthealphabetthanthecharacterinthatsamepositionins2.
C++providesseveralwaystocomparestrings,andeachhasadvantages.Thesimplesttousearethe
nonmember,overloadedoperatorfunctions:operator==,operator!=operator>,operator<,
operator>=,andoperator<=.
//:C03:CompStr.h
#ifndefCOMPSTR_H
#defineCOMPSTR_H
#include<string>
#include"../TestSuite/Test.h"
usingstd::string
classCompStrTest:publicTestSuite::Test{
public:
voidrun(){
//Stringstocompare
strings1("This")
strings2("That")
test_(s1==s1)
test_(s1!=s2)
test_(s1>s2)
test_(s1>=s2)
test_(s1>=s1)
test_(s2<s1)
test_(s2<=s1)
test_(s1<=s1)
}
}
#endif//COMPSTR_H///:~
//:C03:CompStr.cpp
//{L}../TestSuite/Test
#include"CompStr.h"
intmain(){
CompStrTestt
t.run()
returnt.report()
}///:~
Theoverloadedcomparisonoperatorsareusefulforcomparingbothfullstringsandindividualstring
characterelements.
Noticeinthefollowingexampletheflexibilityofargumenttypesonboththeleftandrightsideofthe
comparisonoperators.Forefficiency,thestringclassprovidesoverloadedoperatorsforthedirect
comparisonofstringobjects,quotedliterals,andpointerstoCstylestringswithouthavingtocreate
temporarystringobjects.
//:C03:Equivalence.cpp
#include<iostream>
#include<string>
usingnamespacestd
intmain(){
strings2("That"),s1("This")
//Thelvalueisaquotedliteral
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
79/458
5/3/2015
ThinkinginC++2ndedVolume2
//andthervalueisastring:
if("That"==s2)
cout<<"Amatch"<<endl
//Theleftoperandisastringandtherightis
//apointertoaCstylenullterminatedstring:
if(s1!=s2.c_str())
cout<<"Nomatch"<<endl
}///:~
Thec_str()functionreturnsaconstchar*thatpointstoaCstyle,nullterminatedstringequivalentto
thecontentsofthestringobject.ThiscomesinhandywhenyouwanttopassastringtoastandardC
function,suchasatoi()oranyofthefunctionsdefinedinthe<cstring>header.Itisanerrortousethe
valuereturnedbyc_str()asnonconstargumenttoanyfunction.
Youwontfindthelogicalnot(!)orthelogicalcomparisonoperators(&&and||)amongoperatorsfora
string.(NeitherwillyoufindoverloadedversionsofthebitwiseCoperators&,|,^,or~.)Theoverloaded
nonmembercomparisonoperatorsforthestringclassarelimitedtothesubsetthathasclear,unambiguous
applicationtosinglecharactersorgroupsofcharacters.
Thecompare()memberfunctionoffersyouagreatdealmoresophisticatedandprecisecomparisonthan
thenonmemberoperatorset.Itprovidesoverloadedversionstocompare:
Twocompletestrings.
Partofeitherstringtoacompletestring.
Subsetsoftwostrings.
Thefollowingexamplecomparescompletestrings:
//:C03:Compare.cpp
//Demonstratescompare()andswap().
#include<cassert>
#include<string>
usingnamespacestd
intmain(){
stringfirst("This")
stringsecond("That")
assert(first.compare(first)==0)
assert(second.compare(second)==0)
//Whichislexicallygreater?
assert(first.compare(second)>0)
assert(second.compare(first)<0)
first.swap(second)
assert(first.compare(second)<0)
assert(second.compare(first)>0)
}///:~
Theswap()functioninthisexampledoeswhatitsnameimplies:itexchangesthecontentsofitsobject
andargument.Tocompareasubsetofthecharactersinoneorbothstrings,youaddargumentsthatdefine
wheretostartthecomparisonandhowmanycharacterstoconsider.Forexample,wecanusethe
followingoverloadedversionofcompare():
s1.compare(s1StartPos,s1NumberChars,s2,s2StartPos,
s2NumberChars)
Heresanexample:
//:C03:Compare2.cpp
//Illustrateoverloadedcompare().
#include<cassert>
#include<string>
usingnamespacestd
intmain(){
stringfirst("Thisisadaythatwillliveininfamy")
stringsecond("Idon'tbelievethatthisiswhat"
"Isignedupfor")
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
80/458
5/3/2015
ThinkinginC++2ndedVolume2
//Compare"hisis"inbothstrings:
assert(first.compare(1,7,second,22,7)==0)
//Compare"hisisa"to"hisisw":
assert(first.compare(1,9,second,22,9)<0)
}///:~
Intheexamplessofar,wehaveusedCstylearrayindexingsyntaxtorefertoanindividualcharacterina
string.C++stringsprovideanalternativetothes[n]notation:theat()member.Thesetwoindexing
mechanismsproducethesameresultinC++ifallgoeswell:
//:C03:StringIndexing.cpp
#include<cassert>
#include<string>
usingnamespacestd
intmain(){
strings("1234")
assert(s[1]=='2')
assert(s.at(1)=='2')
}///:~
Thereisoneimportantdifference,however,between[]andat().Whenyoutrytoreferenceanarray
elementthatisoutofbounds,at()willdoyouthekindnessofthrowinganexception,whileordinary[]
subscriptingsyntaxwillleaveyoutoyourowndevices:
//:C03:BadStringIndexing.cpp
#include<exception>
#include<iostream>
#include<string>
usingnamespacestd
intmain(){
strings("1234")
//at()savesyoubythrowinganexception:
try{
s.at(5)
}catch(exception&e){
cerr<<e.what()<<endl
}
}///:~
Responsibleprogrammerswillnotuseerrantindexes,butshouldyouwanttobenefitsofautomaticindex
checking,usingat()inplaceof[]willgiveyouachancetogracefullyrecoverfromreferencestoarray
elementsthatdontexist.Executionofthisprogramononeofourtestcompilersgavethefollowingoutput:
invalidstringposition
Theat()memberthrowsanobjectofclassout_of_range,whichderives(ultimately)from
std::exception.Bycatchingthisobjectinanexceptionhandler,youcantakeappropriateremedialactions
suchasrecalculatingtheoffendingsubscriptorgrowingthearray.Usingstring::operator[]()givesno
suchprotectionandisasdangerousaschararrayprocessinginC.[37]
Stringsandcharactertraits
TheprogramFind.cppearlierinthischapterleadsustoasktheobviousquestion:Whyisntcase
insensitivecomparisonpartofthestandardstringclass?Theanswerprovidesinterestingbackgroundon
thetruenatureofC++stringobjects.
Considerwhatitmeansforacharactertohavecase.WrittenHebrew,Farsi,andKanjidontusethe
conceptofupperandlowercase,soforthoselanguagesthisideahasnomeaning.Itwouldseemthatif
therewereawaytodesignatesomelanguagesasalluppercaseoralllowercase,wecoulddesigna
generalizedsolution.However,somelanguagesthatemploytheconceptofcasealsochangethemeaning
ofparticularcharacterswithdiacriticalmarks,forexample:thecedillainSpanish,thecircumflexin
French,andtheumlautinGerman.Forthisreason,anycasesensitivecollatingschemethatattemptstobe
comprehensivewillbenightmarishlycomplextouse.
AlthoughweusuallytreattheC++stringasaclass,thisisreallynotthecase.Thestringtypeisa
specializationofamoregeneralconstituent,thebasic_string<>template.Observehowstringis
[38]
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
81/458
5/3/2015
ThinkinginC++2ndedVolume2
[38]
declaredintheStandardC++headerfile:
typedefbasic_string<char>string
Tounderstandthenatureofthestringclass,lookatthebasic_string<>template:
template<classcharT,classtraits=char_traits<charT>,
classallocator=allocator<charT>>classbasic_string
InChapter5,weexaminetemplatesingreatdetail(muchmorethaninChapter16ofVolume1).Fornow,
justnoticethatthestringtypeiscreatedwhenthebasic_stringtemplateisinstantiatedwithchar.
Insidethebasic_string<>templatedeclaration,theline:
classtraits=char_traits<charT>,
tellsusthatthebehavioroftheclassmadefromthebasic_string<>templateisspecifiedbyaclass
basedonthetemplatechar_traits<>.Thus,thebasic_string<>templateproducesstringoriented
classesthatmanipulatetypesotherthanchar(widecharacters,forexample).Todothis,the
char_traits<>templatecontrolsthecontentandcollatingbehaviorsofavarietyofcharactersetsusing
thecharactercomparisonfunctionseq()(equal),ne()(notequal),andlt()(lessthan).The
basic_string<>stringcomparisonfunctionsrelyonthese.
Thisiswhythestringclassdoesntincludecaseinsensitivememberfunctions:thatsnotinitsjob
description.Tochangethewaythestringclasstreatscharactercomparison,youmustsupplyadifferent
char_traits<>templatebecausethatdefinesthebehavioroftheindividualcharactercomparison
memberfunctions.
Youcanusethisinformationtomakeanewtypeofstringclassthatignorescase.First,welldefineanew
caseinsensitivechar_traits<>templatethatinheritsfromtheexistingtemplate.Next,welloverride
onlythemembersweneedtochangetomakecharacterbycharactercomparisoncaseinsensitive.(In
additiontothethreelexicalcharactercomparisonmembersmentionedearlier,wellalsosupplyanew
implementationforthechar_traitsfunctionsfind()andcompare()).Finally,welltypedefanew
classbasedonbasic_string,butusingthecaseinsensitiveichar_traitstemplateforitssecond
argument:
//:C03:ichar_traits.h
//Creatingyourowncharactertraits.
#ifndefICHAR_TRAITS_H
#defineICHAR_TRAITS_H
#include<cassert>
#include<cctype>
#include<cmath>
#include<cstddef>
#include<ostream>
#include<string>
usingstd::allocator
usingstd::basic_string
usingstd::char_traits
usingstd::ostream
usingstd::size_t
usingstd::string
usingstd::toupper
usingstd::tolower
structichar_traits:char_traits<char>{
//We'llonlychangecharacterby
//charactercomparisonfunctions
staticbooleq(charc1st,charc2nd){
returntoupper(c1st)==toupper(c2nd)
}
staticboolne(charc1st,charc2nd){
return!eq(c1st,c2nd)
}
staticboollt(charc1st,charc2nd){
returntoupper(c1st)<toupper(c2nd)
}
staticint
compare(constchar*str1,constchar*str2,size_tn){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
82/458
5/3/2015
ThinkinginC++2ndedVolume2
for(size_ti=0i<n++i){
if(str1==0)
return1
elseif(str2==0)
return1
elseif(tolower(*str1)<tolower(*str2))
return1
elseif(tolower(*str1)>tolower(*str2))
return1
assert(tolower(*str1)==tolower(*str2))
++str1++str2//Comparetheotherchars
}
return0
}
staticconstchar*
find(constchar*s1,size_tn,charc){
while(n>0)
if(toupper(*s1)==toupper(c))
returns1
else
++s1
return0
}
}
typedefbasic_string<char,ichar_traits>istring
inlineostream&operator<<(ostream&os,constistring&s){
returnos<<string(s.c_str(),s.length())
}
#endif//ICHAR_TRAITS_H///:~
Weprovideatypedefnamedistringsothatourclasswillactlikeanordinarystringineveryway,
exceptthatitwillmakeallcomparisonswithoutrespecttocase.Forconvenience,wevealsoprovidedan
overloadedoperator<<()sothatyoucanprintistrings.Heresanexample:
//:C03:ICompare.cpp
#include<cassert>
#include<iostream>
#include"ichar_traits.h"
usingnamespacestd
intmain(){
//Thesamelettersexceptforcase:
istringfirst="tHis"
istringsecond="ThIS"
cout<<first<<endl
cout<<second<<endl
assert(first.compare(second)==0)
assert(first.find('h')==1)
assert(first.find('I')==2)
assert(first.find('x')==string::npos)
}///:~
Thisisjustatoyexample.Tomakeistringfullyequivalenttostring,wedhavetocreatetheother
functionsnecessarytosupportthenewistringtype.
The<string>headerprovidesawidestringclassviathefollowingtypedef:
typedefbasic_string<wchar_t>wstring
Widestringsupportalsorevealsitselfinwidestreams(wostreaminplaceofostream,alsodefinedin
<iostream>)andintheheader<cwctype>,awidecharacterversionof<cctype>.Thisalongwiththe
wchar_tspecializationofchar_traitsinthestandardlibraryallowsustodoawidecharacterversionof
ichar_traits:
//:C03:iwchar_traits.h{g++}
//Creatingyourownwidecharactertraits.
#ifndefIWCHAR_TRAITS_H
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
83/458
5/3/2015
ThinkinginC++2ndedVolume2
#defineIWCHAR_TRAITS_H
#include<cassert>
#include<cmath>
#include<cstddef>
#include<cwctype>
#include<ostream>
#include<string>
usingstd::allocator
usingstd::basic_string
usingstd::char_traits
usingstd::size_t
usingstd::towlower
usingstd::towupper
usingstd::wostream
usingstd::wstring
structiwchar_traits:char_traits<wchar_t>{
//We'llonlychangecharacterby
//charactercomparisonfunctions
staticbooleq(wchar_tc1st,wchar_tc2nd){
returntowupper(c1st)==towupper(c2nd)
}
staticboolne(wchar_tc1st,wchar_tc2nd){
returntowupper(c1st)!=towupper(c2nd)
}
staticboollt(wchar_tc1st,wchar_tc2nd){
returntowupper(c1st)<towupper(c2nd)
}
staticintcompare(
constwchar_t*str1,constwchar_t*str2,size_tn){
for(size_ti=0i<ni++){
if(str1==0)
return1
elseif(str2==0)
return1
elseif(towlower(*str1)<towlower(*str2))
return1
elseif(towlower(*str1)>towlower(*str2))
return1
assert(towlower(*str1)==towlower(*str2))
++str1++str2//Comparetheotherwchar_ts
}
return0
}
staticconstwchar_t*
find(constwchar_t*s1,size_tn,wchar_tc){
while(n>0)
if(towupper(*s1)==towupper(c))
returns1
else
++s1
return0
}
}
typedefbasic_string<wchar_t,iwchar_traits>iwstring
inlinewostream&operator<<(wostream&os,
constiwstring&s){
returnos<<wstring(s.c_str(),s.length())
}
#endif//IWCHAR_TRAITS_H///:~
Asyoucansee,thisismostlyanexerciseinplacingawintheappropriateplaceinthesourcecode.The
testprogramlookslikethis:
//:C03:IWCompare.cpp{g++}
#include<cassert>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
84/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<iostream>
#include"iwchar_traits.h"
usingnamespacestd
intmain(){
//Thesamelettersexceptforcase:
iwstringwfirst=L"tHis"
iwstringwsecond=L"ThIS"
wcout<<wfirst<<endl
wcout<<wsecond<<endl
assert(wfirst.compare(wsecond)==0)
assert(wfirst.find('h')==1)
assert(wfirst.find('I')==2)
assert(wfirst.find('x')==wstring::npos)
}///:~
Unfortunately,somecompilersstilldonotproviderobustsupportforwidecharacters.
Astringapplication
Ifyouvelookedatthesamplecodeinthisbookclosely,youvenoticedthatcertaintokensinthe
commentssurroundthecode.TheseareusedbyaPythonprogramthatBrucewrotetoextractthecode
intofilesandsetupmakefilesforbuildingthecode.Forexample,adoubleslashfollowedbyacolonatthe
beginningofalinedenotesthefirstlineofasourcefile.Therestofthelinecontainsinformationdescribing
thefilesnameandlocationandwhetheritshouldbeonlycompiledratherthanfullybuiltintoan
executablefile.Forexample,thefirstlineinthepreviousprogramabovecontainsthestring
C03:IWCompare.cpp,indicatingthatthefileIWCompare.cppshouldbeextractedintothedirectory
C03.
Thelastlineofasourcefilecontainsatripleslashfollowedbyacolonandatilde.Ifthefirstlinehasan
exclamationpointimmediatelyafterthecolon,thefirstandlastlinesofthesourcecodearenottobe
outputtothefile(thisisfordataonlyfiles).(Ifyourewonderingwhywereavoidingshowingyouthese
tokens,itsbecausewedontwanttobreakthecodeextractorwhenappliedtothetextofthebook!)
BrucesPythonprogramdoesalotmorethanjustextractcode.Ifthetoken{O}followsthefilename,
itsmakefileentrywillonlybesetuptocompilethefileandnottolinkitintoanexecutable.(TheTest
FrameworkinChapter2isbuiltthisway.)Tolinksuchafilewithanothersourceexample,thetarget
executablessourcefilewillcontainan{L}directive,asin:
//{L}../TestSuite/Test
Thissectionwillpresentaprogramtojustextractallthecodesothatyoucancompileandinspectit
manually.Youcanusethisprogramtoextractallthecodeinthisbookbysavingthedocumentfileasa
textfile[39](letscallitTICV2.txt)andbyexecutingsomethinglikethefollowingonashellcommandline:
C:>extractCodeTICV2.txt/TheCode
ThiscommandreadsthetextfileTICV2.txtandwritesallthesourcecodefilesinsubdirectoriesunderthe
topleveldirectory/TheCode.Thedirectorytreewilllooklikethefollowing:
TheCode/
C0B/
C01/
C02/
C03/
C04/
C05/
C06/
C07/
C08/
C09/
C10/
C11/
TestSuite/
Thesourcefilescontainingtheexamplesfromeachchapterwillbeinthecorrespondingdirectory.
Herestheprogram:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
85/458
5/3/2015
ThinkinginC++2ndedVolume2
//:C03:ExtractCode.cpp{edg}{RunByHand}
//Extractscodefromtext.
#include<cassert>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<fstream>
#include<iostream>
#include<string>
usingnamespacestd
//LegacynonstandardCheaderformkdir()
#ifdefined(__GNUC__)||defined(__MWERKS__)
#include<sys/stat.h>
#elifdefined(__BORLANDC__)||defined(_MSC_VER)\
||defined(__DMC__)
#include<direct.h>
#else
#errorCompilernotsupported
#endif
//Checktoseeifdirectoryexists
//byattemptingtoopenanewfile
//foroutputwithinit.
boolexists(stringfname){
size_tlen=fname.length()
if(fname[len1]!='/'&&fname[len1]!='\\')
fname.append("/")
fname.append("000.tmp")
ofstreamoutf(fname.c_str())
boolexistFlag=outf
if(outf){
outf.close()
remove(fname.c_str())
}
returnexistFlag
}
intmain(intargc,char*argv[]){
//Seeifinputfilenameprovided
if(argc==1){
cerr<<"usage:extractCodefile[dir]"<<endl
exit(EXIT_FAILURE)
}
//Seeifinputfileexists
ifstreaminf(argv[1])
if(!inf){
cerr<<"erroropeningfile:"<<argv[1]<<endl
exit(EXIT_FAILURE)
}
//Checkforoptionaloutputdirectory
stringroot("./")//currentisdefault
if(argc==3){
//Seeifoutputdirectoryexists
root=argv[2]
if(!exists(root)){
cerr<<"nosuchdirectory:"<<root<<endl
exit(EXIT_FAILURE)
}
size_trootLen=root.length()
if(root[rootLen1]!='/'&&root[rootLen1]!='\\')
root.append("/")
}
//Readinputfilelinebyline
//checkingforcodedelimiters
stringline
boolinCode=false
boolprintDelims=true
ofstreamoutf
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
86/458
5/3/2015
ThinkinginC++2ndedVolume2
while(getline(inf,line)){
size_tfindDelim=line.find("//""/:~")
if(findDelim!=string::npos){
//Outputlastlineandclosefile
if(!inCode){
cerr<<"Linesoutoforder"<<endl
exit(EXIT_FAILURE)
}
assert(outf)
if(printDelims)
outf<<line<<endl
outf.close()
inCode=false
printDelims=true
}else{
findDelim=line.find("//"":")
if(findDelim==0){
//Checkfor'!'directive
if(line[3]=='!'){
printDelims=false
++findDelim//Toskip'!'fornextsearch
}
//Extractsubdirectoryname,ifany
size_tstartOfSubdir=
line.find_first_not_of("\t",findDelim+3)
findDelim=line.find(':',startOfSubdir)
if(findDelim==string::npos){
cerr<<"missingfilenameinformation\n"<<endl
exit(EXIT_FAILURE)
}
stringsubdir
if(findDelim>startOfSubdir)
subdir=line.substr(startOfSubdir,
findDelimstartOfSubdir)
//Extractfilename(betterbeone!)
size_tstartOfFile=findDelim+1
size_tendOfFile=
line.find_first_of("\t",startOfFile)
if(endOfFile==startOfFile){
cerr<<"missingfilename"<<endl
exit(EXIT_FAILURE)
}
//WehaveallthepiecesbuildfullPathname
stringfullPath(root)
if(subdir.length()>0)
fullPath.append(subdir).append("/")
assert(fullPath[fullPath.length()1]=='/')
if(!exists(fullPath))
#ifdefined(__GNUC__)||defined(__MWERKS__)
mkdir(fullPath.c_str(),0)//Createsubdir
#else
mkdir(fullPath.c_str())//Createsubdir
#endif
fullPath.append(line.substr(startOfFile,
endOfFilestartOfFile))
outf.open(fullPath.c_str())
if(!outf){
cerr<<"erroropening"<<fullPath
<<"foroutput"<<endl
exit(EXIT_FAILURE)
}
inCode=true
cout<<"Processing"<<fullPath<<endl
if(printDelims)
outf<<line<<endl
}
elseif(inCode){
assert(outf)
outf<<line<<endl//Outputmiddlecodeline
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
87/458
5/3/2015
ThinkinginC++2ndedVolume2
}
}
}
exit(EXIT_SUCCESS)
}///:~
First,youllnoticesomeconditionalcompilationdirectives.Themkdir()function,whichcreatesa
directoryinthefilesystem,isdefinedbythePOSIX[40]standardintheheader<sys/stat.h>.
Unfortunately,manycompilersstilluseadifferentheader(<direct.h>).Therespectivesignaturesfor
mkdir()alsodiffer:POSIXspecifiestwoarguments,theolderversionsjustone.Forthisreason,thereis
moreconditionalcompilationlaterintheprogramtochoosetherightcalltomkdir().Wenormallydont
useconditionalcompilationintheexamplesinthisbook,butthisparticularprogramistoousefulnottoput
alittleextraworkinto,sinceyoucanuseittoextractallthecodewithit.
Theexists()functioninExtractCode.cpptestswhetheradirectoryexistsbyopeningatemporaryfilein
it.Iftheopenfails,thedirectorydoesntexist.Youremoveafilebysendingitsnameasachar*to
std::remove().
Themainprogramvalidatesthecommandlineargumentsandthenreadstheinputfilealineatatime,
lookingforthespecialsourcecodedelimiters.TheBooleanflaginCodeindicatesthattheprogramisinthe
middleofasourcefile,solinesshouldbeoutput.TheprintDelimsflagwillbetrueiftheopeningtokenis
notfollowedbyanexclamationpointotherwisethefirstandlastlinesarenotwritten.Itisimportantto
checkfortheclosingdelimiterfirst,becausethestarttokenisasubset,andsearchingforthestarttoken
firstwouldreturnasuccessfulfindforbothcases.Ifweencountertheclosingtoken,weverifythatweare
inthemiddleofprocessingasourcefileotherwise,somethingiswrongwiththewaythedelimitersare
laidoutinthetextfile.IfinCodeistrue,alliswell,andwe(optionally)writethelastlineandclosethe
file.Whentheopeningtokenisfound,weparsethedirectoryandfilenamecomponentsandopenthefile.
Thefollowingstringrelatedfunctionswereusedinthisexample:length(),append(),getline(),
find()(twoversions),find_first_not_of(),substr(),find_first_of(),c_str(),and,ofcourse,
operator<<().
Summary
C++stringobjectsprovidedeveloperswithanumberofgreatadvantagesovertheirCcounterparts.For
themostpart,thestringclassmakesreferringtostringswithcharacterpointersunnecessary.This
eliminatesanentireclassofsoftwaredefectsthatarisefromtheuseofuninitializedandincorrectlyvalued
pointers.
C++stringsdynamicallyandtransparentlygrowtheirinternaldatastoragespacetoaccommodate
increasesinthesizeofthestringdata.Whenthedatainastringgrowsbeyondthelimitsofthememory
initiallyallocatedtoit,thestringobjectwillmakethememorymanagementcallsthattakespacefromand
returnspacetotheheap.Consistentallocationschemespreventmemoryleaksandhavethepotentialtobe
muchmoreefficientthanrollyourownmemorymanagement.
Thestringclassmemberfunctionsprovideafairlycomprehensivesetoftoolsforcreating,modifying,and
searchinginstrings.Stringcomparisonsarealwayscasesensitive,butyoucanworkaroundthisby
copyingstringdatatoCstylenullterminatedstringsandusingcaseinsensitivestringcomparison
functions,temporarilyconvertingthedataheldinstringobjectstoasinglecase,orbycreatingacase
insensitivestringclassthatoverridesthecharactertraitsusedtocreatethebasic_stringobject.
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.Writeandtestafunctionthatreversestheorderofthecharactersinastring.
2.Apalindromeisawordorgroupofwordsthatreadthesameforwardandbackward.Forexample
madamorwow.Writeaprogramthattakesastringargumentfromthecommandlineand,using
thefunctionfromthepreviousexercise,printswhetherthestringwasapalindromeornot.
3.MakeyourprogramfromExercise2returntrueevenifsymmetriclettersdifferincase.For
example,Civicwouldstillreturntruealthoughthefirstletteriscapitalized.
4.ChangeyourprogramfromExercise3toignorepunctuationandspacesaswell.ForexampleAble
wasI,ereIsawElba.wouldreporttrue.
5.Usingthefollowingstringdeclarationsandonlychars(nostringliteralsormagicnumbers):
stringone("Iwalkeddownthecanyonwiththemovingmountainbikers.")
stringtwo("Thebikerspassedbymetoocloseforcomfort.")
stringthree("Iwenthikinginstead.")
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
88/458
5/3/2015
ThinkinginC++2ndedVolume2
producethefollowingsentence:
Imoveddownthecanyonwiththemountainbikers.Themountainbikerspassedbyme
toocloseforcomfort.SoIwenthikinginstead.
6.Writeaprogramnamedreplacethattakesthreecommandlineargumentsrepresentinganinput
textfile,astringtoreplace(callitfrom),andareplacementstring(callitto).Theprogramshould
writeanewfiletostandardoutputwithalloccurrencesoffromreplacedbyto.
7.Repeatthepreviousexercisebutreplaceallinstancesoffromregardlessofcase.
8.MakeyourprogramfromExercise3takeafilenamefromthecommandline,andthendisplayall
wordsthatarepalindromes(ignoringcase)inthefile.Donotdisplayduplicates(eveniftheircase
differs).Donottrytolookforpalindromesthatarelargerthanaword(unlikeinExercise4).
9.ModifyHTMLStripper.cppsothatwhenitencountersatag,itdisplaysthetagsname,then
displaysthefilescontentsbetweenthetagandthefilesendingtag.Assumenonestingoftags,and
thatalltagshaveendingtags(denotedwith</TAGNAME>).
10.Writeaprogramthattakesthreecommandlinearguments(afilenameandtwostrings)anddisplays
totheconsolealllinesinthefilethathavebothstringsintheline,eitherstring,onlyonestring,or
neitherstring,basedonuserinputatthebeginningoftheprogram(theuserwillchoosewhich
matchingmodetouse).Forallbuttheneitherstringoption,highlighttheinputstring(s)byplacing
anasterisk(*)atthebeginningandendofeachstringsoccurrencewhenitisdisplayed.
11.Writeaprogramthattakestwocommandlinearguments(afilenameandastring)andcountsthe
numberoftimesthestringoccursinthefile,evenasasubstring(butignoringoverlaps).For
example,aninputstringofbawouldmatchtwiceinthewordbasketball,butaninputstringof
anawouldmatchonlyonceinthewordbanana.Displaytotheconsolethenumberoftimesthe
stringismatchedinthefile,aswellastheaveragelengthofthewordswherethestringoccurred.
(Ifthestringoccursmorethanonceinaword,onlycountthewordonceinfiguringtheaverage.)
12.Writeaprogramthattakesafilenamefromthecommandlineandprofilesthecharacterusage,
includingpunctuationandspaces(allcharactervaluesof0x21[33]through0x7E[126],aswellas
thespacecharacter).Thatis,countthenumberofoccurrencesofeachcharacterinthefile,then
displaytheresultssortedeithersequentially(space,then!,",#,etc.)orbyascendingor
descendingfrequencybasedonuserinputatthebeginningoftheprogram.Forspace,displaythe
wordSpaceinsteadofthecharacter''.Asamplerunmightlooksomethinglikethis:
Formatsequentially,ascending,ordescending(S/A/D):D
t:526
r:490
etc.
13.Usingfind()andrfind(),writeaprogramthattakestwocommandlinearguments(afilenameand
astring)anddisplaysthefirstandlastwords(andtheirindexes)notmatchingthestring,aswellas
theindexesofthefirstandlastinstancesofthestring.DisplayNotFoundifanyofthesearches
fail.
14.Usingthefind_first_offamilyoffunctions(butnotexclusively),writeaprogramthatwillremove
allnonalphanumericcharactersexceptspacesandperiodsfromafile,thencapitalizethefirstletter
followingaperiod.
15.Againusingthefind_first_offamilyoffunctions,writeaprogramthatacceptsafilenameasa
commandlineargumentandthenformatsallnumbersinthefiletocurrency.Ignoredecimalpoints
afterthefirstuntilanonnumericcharacterisfound,androundtothenearesthundredth.For
example,thestring12.399abc29.00.6awouldbeformatted(intheUSA)to$12.40abc$29.01a.
16.Writeaprogramthatacceptstwocommandlinearguments(afilenameandanumber)and
scrambleseachwordinthefilebyrandomlyswitchingtwoofitslettersthenumberoftimes
specifiedinthesecondargument.(Thatis,if0ispassedintoyourprogramfromthecommandline,
thewordsshouldnotbescrambledif1ispassedin,onepairofrandomlychosenlettersshouldbe
swapped,foraninputof2,tworandompairsshouldbeswapped,etc.).
17.Writeaprogramthatacceptsafilenamefromthecommandlineanddisplaysthenumberof
sentences(definedasthenumberofperiodsinthefile),averagenumberofcharactersper
sentence,andthetotalnumberofcharactersinthefile.
18.Provetoyourselfthattheat()memberfunctionreallywillthrowanexceptionifanattemptismade
togooutofbounds,andthattheindexingoperator([])wont.
4:Iostreams
YoucandomuchmorewiththegeneralI/OproblemthanjusttakestandardI/O
andturnitintoaclass.
WouldntitbeniceifyoucouldmakealltheusualreceptaclesstandardI/O,files,andevenblocksof
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
89/458
5/3/2015
ThinkinginC++2ndedVolume2
memorylookthesamesothatyouneedtorememberonlyoneinterface?Thatstheideabehind
iostreams.Theyremucheasier,safer,andsometimesevenmoreefficientthantheassortedfunctions
fromtheStandardCstdiolibrary.
TheiostreamsclassesareusuallythefirstpartoftheC++librarythatnewC++programmerslearnto
use.ThischapterdiscusseshowiostreamsareanimprovementoverCsstdiofacilitiesandexploresthe
behavioroffileandstringstreamsinadditiontothestandardconsolestreams.
Whyiostreams?
YoumightwonderwhatswrongwiththegoodoldClibrary.WhynotwraptheClibraryinaclassandbe
donewithit?Sometimesthisisafinesolution.Forexample,supposeyouwanttomakesurethatthefile
representedbyastdioFILEpointerisalwayssafelyopenedandproperlyclosedwithouthavingtorelyon
theusertoremembertocalltheclose()function.Thefollowingprogramissuchanattempt:
//:C04:FileClass.h
//stdiofileswrapped.
#ifndefFILECLASS_H
#defineFILECLASS_H
#include<cstdio>
#include<stdexcept>
classFileClass{
std::FILE*f
public:
structFileClassError:std::runtime_error{
FileClassError(constchar*msg)
:std::runtime_error(msg){}
}
FileClass(constchar*fname,constchar*mode="r")
~FileClass()
std::FILE*fp()
}
#endif//FILECLASS_H///:~
WhenyouperformfileI/OinC,youworkwithanakedpointertoaFILEstruct,butthisclasswraps
aroundthepointerandguaranteesitisproperlyinitializedandcleanedupusingtheconstructorand
destructor.Thesecondconstructorargumentisthefilemode,whichdefaultstorforread.
TofetchthevalueofthepointertouseinthefileI/Ofunctions,youusethefp()accessfunction.Hereare
thememberfunctiondefinitions:
//:C04:FileClass.cpp{O}
//FileClassImplementation.
#include"FileClass.h"
#include<cstdlib>
#include<cstdio>
usingnamespacestd
FileClass::FileClass(constchar*fname,constchar*mode){
if((f=fopen(fname,mode))==0)
throwFileClassError("Erroropeningfile")
}
FileClass::~FileClass(){fclose(f)}
FILE*FileClass::fp(){returnf}///:~
Theconstructorcallsfopen(),asyouwouldnormallydo,butitalsoensuresthattheresultisntzero,
whichindicatesafailureuponopeningthefile.Ifthefiledoesnotopenasexpected,anexceptionis
thrown.
Thedestructorclosesthefile,andtheaccessfunctionfp()returnsf.Heresasimpleexampleusing
FileClass:
//:C04:FileClassTest.cpp
//{L}FileClass
#include<cstdlib>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
90/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<iostream>
#include"FileClass.h"
usingnamespacestd
intmain(){
try{
FileClassf("FileClassTest.cpp")
constintBSIZE=100
charbuf[BSIZE]
while(fgets(buf,BSIZE,f.fp()))
fputs(buf,stdout)
}catch(FileClass::FileClassError&e){
cout<<e.what()<<endl
returnEXIT_FAILURE
}
returnEXIT_SUCCESS
}//Fileautomaticallyclosedbydestructor
///:~
YoucreatetheFileClassobjectanduseitinnormalCfileI/Ofunctioncallsbycallingfp().Whenyoure
donewithit,justforgetaboutitthefileisclosedbythedestructorattheendofitsscope.
EventhoughtheFILEpointerisprivate,itisntparticularlysafebecausefp()retrievesit.Sincetheonly
effectseemstobeguaranteedinitializationandcleanup,whynotmakeitpublicoruseastructinstead?
Noticethatwhileyoucangetacopyoffusingfp(),youcannotassigntofthatscompletelyunderthe
controloftheclass.Aftercapturingthepointerreturnedbyfp(),theclientprogrammercanstillassignto
thestructureelementsorevencloseit,sothesafetyisinguaranteeingavalidFILEpointerratherthan
propercontentsofthestructure.
Ifyouwantcompletesafety,youmustpreventtheuserfromdirectlyaccessingtheFILEpointer.Some
versionofallthenormalfileI/Ofunctionsmustshowupasclassmemberssothateverythingyoucando
withtheCapproachisavailableintheC++class:
//:C04:Fullwrap.h
//CompletelyhiddenfileIO.
#ifndefFULLWRAP_H
#defineFULLWRAP_H
#include<cstddef>
#include<cstdio>
#undefgetc
#undefputc
#undefungetc
usingstd::size_t
usingstd::fpos_t
classFile{
std::FILE*f
std::FILE*F()//Producescheckedpointertof
public:
File()//Createobjectbutdon'topenfile
File(constchar*path,constchar*mode="r")
~File()
intopen(constchar*path,constchar*mode="r")
intreopen(constchar*path,constchar*mode)
intgetc()
intungetc(intc)
intputc(intc)
intputs(constchar*s)
char*gets(char*s,intn)
intprintf(constchar*format,...)
size_tread(void*ptr,size_tsize,size_tn)
size_twrite(constvoid*ptr,size_tsize,size_tn)
inteof()
intclose()
intflush()
intseek(longoffset,intwhence)
intgetpos(fpos_t*pos)
intsetpos(constfpos_t*pos)
longtell()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
91/458
5/3/2015
ThinkinginC++2ndedVolume2
voidrewind()
voidsetbuf(char*buf)
intsetvbuf(char*buf,inttype,size_tsz)
interror()
voidclearErr()
}
#endif//FULLWRAP_H///:~
ThisclasscontainsalmostallthefileI/Ofunctionsfrom<cstdio>.(vfprintf()ismissingitimplements
theprintf()memberfunction.)
Filehasthesameconstructorasinthepreviousexample,anditalsohasadefaultconstructor.Thedefault
constructorisimportantifyouwanttocreateanarrayofFileobjectsoruseaFileobjectasamemberof
anotherclasswheretheinitializationdoesnthappenintheconstructor,butsometimeaftertheenclosing
objectiscreated.
ThedefaultconstructorsetstheprivateFILEpointerftozero.Butnow,beforeanyreferencetof,its
valuemustbecheckedtoensureitisntzero.ThisisaccomplishedwithF(),whichisprivatebecauseit
isintendedtobeusedonlybyothermemberfunctions.(Wedontwanttogivetheuserdirectaccesstothe
underlyingFILEstructureinthisclass.)
Thisapproachisnotaterriblesolutionbyanymeans.Itsquitefunctional,andyoucouldimaginemaking
similarclassesforstandard(console)I/Oandforincoreformatting(reading/writingapieceofmemory
ratherthanafileortheconsole).
Thestumblingblockistheruntimeinterpreterusedforthevariableargumentlistfunctions.Thisisthe
codethatparsesyourformatstringatruntimeandgrabsandinterpretsargumentsfromthevariable
argumentlist.Itsaproblemforfourreasons.
1.Evenifyouuseonlyafractionofthefunctionalityoftheinterpreter,thewholethinggetsloadedinto
yourexecutable.Soifyousayprintf("%c",'x'),youllgetthewholepackage,includingtheparts
thatprintfloatingpointnumbersandstrings.Theresnostandardoptionforreducingtheamountof
spaceusedbytheprogram.
2.Becausetheinterpretationhappensatruntime,youcantgetridofaperformanceoverhead.Its
frustratingbecausealltheinformationisthereintheformatstringatcompiletime,butitsnot
evaluateduntilruntime.However,ifyoucouldparsetheargumentsintheformatstringatcompile
time,youcouldmakedirectfunctioncallsthathavethepotentialtobemuchfasterthanaruntime
interpreter(althoughtheprintf()familyoffunctionsisusuallyquitewelloptimized).
3.Becausetheformatstringisnotevaluateduntilruntime,therecanbenocompiletimeerror
checking.Youreprobablyfamiliarwiththisproblemifyouvetriedtofindbugsthatcamefrom
usingthewrongnumberortypeofargumentsinaprintf()statement.C++makesabigdealoutof
compiletimeerrorcheckingtofinderrorsearlyandmakeyourlifeeasier.Itseemsashameto
throwtypesafetyawayforanI/Olibrary,especiallysinceI/Oisusedalot.
4.ForC++,themostcrucialproblemisthattheprintf()familyoffunctionsisnotparticularly
extensible.TheyrereallydesignedtohandleonlythebasicdatatypesinC(char,int,float,
double,wchar_t,char*,wchar_t*,andvoid*)andtheirvariations.Youmightthinkthatevery
timeyouaddanewclass,youcouldaddoverloadedprintf()andscanf()functions(andtheir
variantsforfilesandstrings),butremember,overloadedfunctionsmusthavedifferenttypesintheir
argumentlists,andtheprintf()familyhidesitstypeinformationintheformatstringandinthe
variableargumentlist.ForalanguagesuchasC++,whosegoalistobeabletoeasilyaddnewdata
types,thisisanunacceptablerestriction.
Iostreamstotherescue
TheseissuesmakeitclearthatI/OisoneofthefirstprioritiesfortheStandardC++classlibraries.
Becausehello,worldisthefirstprogramjustabouteveryonewritesinanewlanguage,andbecauseI/O
ispartofvirtuallyeveryprogram,theI/OlibraryinC++mustbeparticularlyeasytouse.Italsohasthe
muchgreaterchallengethatitmustaccommodateanynewclass.Thus,itsconstraintsrequirethatthis
foundationclasslibrarybeatrulyinspireddesign.Inadditiontogainingagreatdealofleverageand
clarityinyourdealingswithI/Oandformatting,youllalsoseeinthischapterhowareallypowerfulC++
librarycanwork.
Insertersandextractors
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
92/458
5/3/2015
ThinkinginC++2ndedVolume2
Astreamisanobjectthattransportsandformatscharactersofafixedwidth.Youcanhaveaninput
stream(viadescendantsoftheistreamclass),anoutputstream(withostreamobjects),orastreamthat
doesbothsimultaneously(withobjectsderivedfromiostream).Theiostreamslibraryprovidesdifferent
typesofsuchclasses:ifstream,ofstream,andfstreamforfiles,andistringstream,ostringstream,
andstringstreamforinterfacingwiththeStandardC++stringclass.Allthesestreamclasseshave
nearlyidenticalinterfaces,soyoucanusestreamsinauniformmanner,whetheryoureworkingwitha
file,standardI/O,aregionofmemory,orastringobject.Thesingleinterfaceyoulearnalsoworksfor
extensionsaddedtosupportnewclasses.Somefunctionsimplementyourformattingcommands,andsome
functionsreadandwritecharacterswithoutformatting.
Thestreamclassesmentionedearlierareactuallytemplatespecializations,[41]muchlikethestandard
stringclassisaspecializationofthebasic_stringtemplate.Thebasicclassesintheiostreams
inheritancehierarchyareshowninthefollowingfigure:
Theios_baseclassdeclareseverythingthatiscommontoallstreams,independentofthetypeof
characterthestreamhandles.Thesedeclarationsaremostlyconstantsandfunctionstomanagethem,
someofwhichyoullseethroughoutthischapter.Therestoftheclassesaretemplatesthathavethe
underlyingcharactertypeasaparameter.Theistreamclass,forexample,isdefinedasfollows:
typedefbasic_istream<char>istream
Alltheclassesmentionedearlieraredefinedviasimilartypedefinitions.Therearealsotypedefinitionsfor
allstreamclassesusingwchar_t(thewidecharactertypediscussedinChapter3)insteadofchar.Well
lookattheseattheendofthischapter.Thebasic_iostemplatedefinesfunctionscommontobothinput
andoutput,butthatdependsontheunderlyingcharactertype(wewontusethesemuch).Thetemplate
basic_istreamdefinesgenericfunctionsforinput,andbasic_ostreamdoesthesameforoutput.The
classesforfileandstringstreamsintroducedlateraddfunctionalityfortheirspecificstreamtypes.
Intheiostreamslibrary,twooperatorsareoverloadedtosimplifytheuseofiostreams.Theoperator<<
isoftenreferredtoasaninserterforiostreams,andtheoperator>>isoftenreferredtoasanextractor.
Extractorsparsetheinformationthatsexpectedbythedestinationobjectaccordingtoitstype.Toseean
exampleofthis,youcanusethecinobject,whichistheiostreamequivalentofstdininC,thatis,
redirectablestandardinput.Thisobjectispredefinedwheneveryouincludethe<iostream>header.
inti
cin>>i
floatf
cin>>f
charc
cin>>c
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
93/458
5/3/2015
ThinkinginC++2ndedVolume2
charbuf[100]
cin>>buf
Theresanoverloadedoperator>>foreverybuiltindatatype.Youcanalsooverloadyourown,asyoull
seelater.
Tofindoutwhatyouhaveinthevariousvariables,youcanusethecoutobject(correspondingtostandard
outputtheresalsoacerrobjectcorrespondingtostandarderror)withtheinserter<<:
cout<<"i="
cout<<i
cout<<"\n"
cout<<"f="
cout<<f
cout<<"\n"
cout<<"c="
cout<<c
cout<<"\n"
cout<<"buf="
cout<<buf
cout<<"\n"
Thisistediousanddoesntseemlikemuchofanimprovementoverprintf(),despiteimprovedtype
checking.Fortunately,theoverloadedinsertersandextractorsaredesignedtobechainedintoamore
complexexpressionthatismucheasiertowrite(andread):
cout<<"i="<<i<<endl
cout<<"f="<<f<<endl
cout<<"c="<<c<<endl
cout<<"buf="<<buf<<endl
Defininginsertersandextractorsforyourownclassesisjustamatterofoverloadingtheassociated
operatorstodotherightthings,namely:
Makethefirstparameteranonconstreferencetothestream(istreamforinput,ostreamfor
output).
Performtheoperationbyinserting/extractingdatato/fromthestream(byprocessingthe
componentsoftheobject).
Returnareferencetothestream.
Thestreamshouldbenonconstbecauseprocessingstreamdatachangesthestateofthestream.By
returningthestream,youallowforchainingstreamoperationsinasinglestatement,asshownearlier.
Asanexample,considerhowtooutputtherepresentationofaDateobjectinMMDDYYYYformat.The
followinginserterdoesthejob:
ostream&operator<<(ostream&os,constDate&d){
charfillc=os.fill('0')
os<<setw(2)<<d.getMonth()<<''
<<setw(2)<<d.getDay()<<''
<<setw(4)<<setfill(fillc)<<d.getYear()
returnos
}
ThisfunctioncannotbeamemberoftheDateclassbecausetheleftoperandofthe<<operatormustbe
theoutputstream.Thefill()memberfunctionofostreamchangesthepaddingcharacterusedwhenthe
widthofanoutputfield,determinedbythemanipulatorsetw(),isgreaterthanneededforthedata.We
usea0charactersothatmonthsprecedingOctoberwilldisplayaleadingzero,suchas09for
September.Thefill()functionalsoreturnsthepreviousfillcharacter(whichdefaultstoasinglespace)so
thatwecanrestoreitlaterwiththemanipulatorsetfill().Wediscussmanipulatorsindepthlaterinthis
chapter.
Extractorsrequirealittlemorecarebecausethingscangowrongwithinputdata.Thewaytosignala
streamerroristosetthestreamsfailbit,asfollows:
istream&operator>>(istream&is,Date&d){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
94/458
5/3/2015
ThinkinginC++2ndedVolume2
is>>d.month
chardash
is>>dash
if(dash!='')
is.setstate(ios::failbit)
is>>d.day
is>>dash
if(dash!='')
is.setstate(ios::failbit)
is>>d.year
returnis
}
Whenanerrorbitissetinastream,allfurtherstreamsoperationsareignoreduntilthestreamisrestored
toagoodstate(explainedshortly).Thatswhythecodeabovecontinuesextractingevenif
ios::failbitgetsset.Thisimplementationissomewhatforgivinginthatitallowswhitespacebetweenthe
numbersanddashesinadatestring(becausethe>>operatorskipswhitespacebydefaultwhenreading
builtintypes).Thefollowingarevaliddatestringsforthisextractor:
"08102003"
"8102003"
"08102003"
butthesearenot:
"A102003"//Noalphacharactersallowed
"08%10/2003"//Onlydashesallowedasadelimiter
WelldiscussstreamstateinmoredepthinthesectionHandlingstreamerrorslaterinthischapter.
Commonusage
AstheDateextractorillustrated,youmustbeonguardforerroneousinput.Iftheinputproducesan
unexpectedvalue,theprocessisskewed,anditsdifficulttorecover.Inaddition,formattedinputdefaults
towhitespacedelimiters.Considerwhathappenswhenwecollectthecodefragmentsfromearlierinthis
chapterintoasingleprogram:
//:C04:Iosexamp.cpp{RunByHand}
//Iostreamexamples.
#include<iostream>
usingnamespacestd
intmain(){
inti
cin>>i
floatf
cin>>f
charc
cin>>c
charbuf[100]
cin>>buf
cout<<"i="<<i<<endl
cout<<"f="<<f<<endl
cout<<"c="<<c<<endl
cout<<"buf="<<buf<<endl
cout<<flush
cout<<hex<<"0x"<<i<<endl
}///:~
andgiveitthefollowinginput:
121.4cthisisatest
Weexpectthesameoutputasifwegaveit
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
95/458
5/3/2015
ThinkinginC++2ndedVolume2
12
1.4
c
thisisatest
buttheoutputis,somewhatunexpectedly
i=12
f=1.4
c=c
buf=this
0xc
Noticethatbufgotonlythefirstwordbecausetheinputroutinelookedforaspacetodelimittheinput,
whichitsawafterthis.Inaddition,ifthecontinuousinputstringislongerthanthestorageallocatedfor
buf,weoverrunthebuffer.
Inpractice,youllusuallywanttogetinputfrominteractiveprogramsalineatatimeasasequenceof
characters,scanthem,andthenperformconversionsoncetheyresafelyinabuffer.Thiswayyoudont
needtoworryabouttheinputroutinechokingonunexpecteddata.
Anotherconsiderationisthewholeconceptofacommandlineinterface.Thismadesenseinthepastwhen
theconsolewaslittlemorethanaglasstypewriter,buttheworldisrapidlychangingtoonewherethe
graphicaluserinterface(GUI)dominates.WhatisthemeaningofconsoleI/Oinsuchaworld?Itmakes
muchmoresensetoignorecinaltogether,otherthanforsimpleexamplesortests,andtakethefollowing
approaches:
1.Ifyourprogramrequiresinput,readthatinputfromafileyoullsoonseethatitsremarkablyeasy
tousefileswithiostreams.IostreamsforfilesstillworksfinewithaGUI.
2.Readtheinputwithoutattemptingtoconvertit,aswejustsuggested.Whentheinputissomeplace
whereitcantfoulthingsupduringconversion,youcansafelyscanit.
3.Outputisdifferent.IfyoureusingaGUI,coutdoesntnecessarilywork,andyoumustsendittoa
file(whichisidenticaltosendingittocout)orusetheGUIfacilitiesfordatadisplay.Otherwiseit
oftenmakessensetosendittocout.Inbothcases,theoutputformattingfunctionsofiostreamsare
highlyuseful.
Anothercommonpracticesavescompiletimeonlargeprojects.Consider,forexample,howyouwould
declaretheDatestreamoperatorsintroducedearlierinthechapterinaheaderfile.Youonlyneedto
includetheprototypesforthefunctions,soitsnotreallynecessarytoincludetheentire<iostream>
headerinDate.h.Thestandardpracticeistoonlydeclareclasses,somethinglikethis:
classostream
Thisisanageoldtechniqueforseparatinginterfacefromimplementationandisoftencalledaforward
declaration(andostreamatthispointwouldbeconsideredanincompletetype,sincetheclassdefinition
hasnotyetbeenseenbythecompiler).
Thiswillnotworkasis,however,fortworeasons:
1.Thestreamclassesaredefinedinthestdnamespace.
2.Theyaretemplates.
Theproperdeclarationwouldbe:
namespacestd{
template<classcharT,classtraits=char_traits<charT>>
classbasic_ostream
typedefbasic_ostream<char>ostream
}
(Asyoucansee,likethestringclass,thestreamsclassesusethecharactertraitsclassesmentionedin
Chapter3).Sinceitwouldbeterriblytedioustotypeallthatforeverystreamclassyouwanttoreference,
thestandardprovidesaheaderthatdoesitforyou:<iosfwd>.TheDateheaderwouldthenlook
somethinglikethis:
//Date.h
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
96/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<iosfwd>
classDate{
friendstd::ostream&operator<<(std::ostream&,
constDate&)
friendstd::istream&operator>>(std::istream&,Date&)
//Etc.
Lineorientedinput
Tograbinputalineatatime,youhavethreechoices:
Thememberfunctionget()
Thememberfunctiongetline()
Theglobalfunctiongetline()definedinthe<string>header
Thefirsttwofunctionstakethreearguments:
1.Apointertoacharacterbufferinwhichtostoretheresult.
2.Thesizeofthatbuffer(soitsnotoverrun).
3.Theterminatingcharacter,toknowwhentostopreadinginput.
Theterminatingcharacterhasadefaultvalueof'\n',whichiswhatyoullusuallyuse.Bothfunctionsstore
azerointheresultbufferwhentheyencountertheterminatingcharacterintheinput.
Sowhatsthedifference?Subtle,butimportant:get()stopswhenitseesthedelimiterintheinput
stream,butitdoesntextractitfromtheinputstream.Thus,ifyoudidanotherget()usingthesame
delimiter,itwouldimmediatelyreturnwithnofetchedinput.(Presumably,youeitheruseadifferent
delimiterinthenextget()statementoradifferentinputfunction.)Thegetline()function,ontheother
hand,extractsthedelimiterfromtheinputstream,butstilldoesntstoreitintheresultbuffer.
Thegetline()functiondefinedin<string>isconvenient.Itisnotamemberfunction,butratherastand
alonefunctiondeclaredinthenamespacestd.Ittakesonlytwonondefaultarguments,theinputstream
andthestringobjecttopopulate.Likeitsnamesake,itreadscharactersuntilitencountersthefirst
occurrenceofthedelimiter('\n'bydefault)andconsumesanddiscardsthedelimiter.Theadvantageof
thisfunctionisthatitreadsintoastringobject,soyoudontneedtoworryaboutbuffersize.
Generally,whenyoureprocessingatextfilethatyoureadalineatatime,youllwanttouseoneofthe
getline()functions.
Overloadedversionsofget()
Theget()functionalsocomesinthreeotheroverloadedversions:onewithnoargumentsthatreturnsthe
nextcharacterusinganintreturnvalueonethatstuffsacharacterintoitscharargumentusinga
referenceandonethatstoresdirectlyintotheunderlyingbufferstructureofanotheriostreamobject.The
latterisexploredlaterinthechapter.
Readingrawbytes
Ifyouknowexactlywhatyouredealingwithandwanttomovethebytesdirectlyintoavariable,anarray,
orastructureinmemory,youcanusetheunformattedI/Ofunctionread().Thefirstargumentforthis
functionisapointertothedestinationmemory,andthesecondisthenumberofbytestoread.Thisis
especiallyusefulifyouvepreviouslystoredtheinformationtoafile,forexample,inbinaryformusingthe
complementarywrite()memberfunctionforanoutputstream(usingthesamecompiler,ofcourse).
Youllseeexamplesofallthesefunctionslater.
Handlingstreamerrors
TheDateextractorshownearliersetsastreamsfailbitundercertainconditions.Howdoestheuserknow
whensuchafailureoccurs?Youcandetectstreamerrorsbyeithercallingcertainstreammember
functionstoseeifanerrorstatehasoccurred,orifyoudontcarewhattheparticularerrorwas,youcan
justevaluatethestreaminaBooleancontext.Bothtechniquesderivefromthestateofastreamserror
bits.
Streamstate
[42]
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
97/458
5/3/2015
ThinkinginC++2ndedVolume2
Theios_baseclass,fromwhichiosderives,[42]definesfourflagsthatyoucanusetotestthestateofa
stream:
Flag
Meaning
badbit
Somefatal(perhapsphysical)erroroccurred.
Thestreamshouldbeconsideredunusable.
eofbit
Endofinputhasoccurred(eitherby
encounteringthephysicalendofafilestream
orbytheuserterminatingaconsolestream,
suchaswithCtrlZorCtrlD).
failbit
AnI/Ooperationfailed,mostlikelybecauseof
invaliddata(e.g.,letterswerefoundwhen
tryingtoreadanumber).Thestreamisstill
usable.Thefailbitflagisalsosetwhenendof
inputoccurs.
goodbit
Alliswellnoerrors.Endofinputhasnotyet
occurred.
Youcantestwhetheranyoftheseconditionshaveoccurredbycallingcorrespondingmemberfunctionsthat
returnaBooleanvalueindicatingwhetheranyofthesehavebeenset.Thegood()streammember
functionreturnstrueifnoneoftheotherthreebitsareset.Theeof()functionreturnstrueifeofbitis
set,whichhappenswithanattempttoreadfromastreamthathasnomoredata(usuallyafile).Because
endofinputhappensinC++whentryingtoreadpasttheendofthephysicalmedium,failbitisalsoset
toindicatethattheexpecteddatawasnotsuccessfullyread.Thefail()functionreturnstrueifeither
failbitorbadbitisset,andbad()returnstrueonlyifthebadbitisset.
Onceanyoftheerrorbitsinastreamsstateareset,theyremainset,whichisnotalwayswhatyouwant.
Whenreadingafile,youmightwanttorepositiontoanearlierplaceinthefilebeforeendoffileoccurred.
Justmovingthefilepointerdoesntautomaticallyreseteofbitorfailbityoumustdoityourselfwiththe
clear()function,likethis:
myStream.clear()//Clearsallerrorbits
Aftercallingclear(),good()willreturntrueifcalledimmediately.AsyousawintheDateextractor
earlier,thesetstate()functionsetsthebitsyoupassit.Itturnsoutthatsetstate()doesntaffectany
otherbitsiftheyrealreadyset,theystayset.Ifyouwanttosetcertainbitsbutatthesametimereset
alltherest,youcancallanoverloadedversionofclear(),passingitabitwiseexpressionrepresenting
thebitsyouwanttoset,asin:
myStream.clear(ios::failbit|ios::eofbit)
Mostofthetimeyouwontbeinterestedincheckingthestreamstatebitsindividually.Usuallyyoujust
wanttoknowifeverythingisokay.Thisisthecasewhenyoureadafilefrombeginningtoendyoujust
wanttoknowwhentheinputdataisexhausted.Youcanuseaconversionfunctiondefinedforvoid*thatis
automaticallycalledwhenastreamoccursinaBooleanexpression.Readingastreamuntilendofinput
usingthisidiomlookslikethefollowing:
inti
while(myStream>>i)
cout<<i<<endl
Rememberthatoperator>>()returnsitsstreamargument,sothewhilestatementaboveteststhe
streamasaBooleanexpression.ThisparticularexampleassumesthattheinputstreammyStream
containsintegersseparatedbywhitespace.Thefunctionios_base::operatorvoid*()simplycalls
good()onitsstreamandreturnstheresult.[43]Becausemoststreamoperationsreturntheirstream,
usingthisidiomisconvenient.
Streamsandexceptions
IostreamsexistedaspartofC++longbeforetherewereexceptions,socheckingstreamstatemanually
wasjustthewaythingsweredone.Forbackwardcompatibility,thisisstillthestatusquo,butmodern
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
98/458
5/3/2015
ThinkinginC++2ndedVolume2
iostreamscanthrowexceptionsinstead.Theexceptions()streammemberfunctiontakesaparameter
representingthestatebitsforwhichyouwantexceptionstobethrown.Wheneverthestreamencounters
suchastate,itthrowsanexceptionoftypestd::ios_base::failure,whichinheritsfromstd::exception.
Althoughyoucantriggerafailureexceptionforanyofthefourstreamstates,itsnotnecessarilyagood
ideatoenableexceptionsforallofthem.AsChapter1explains,useexceptionsfortrulyexceptional
conditions,butendoffileisnotonlynotexceptionalitsexpected!Forthatreason,youmightwantto
enableexceptionsonlyfortheerrorsrepresentedbybadbit,whichyouwoulddolikethis:
myStream.exceptions(ios::badbit)
Youenableexceptionsonastreambystreambasis,sinceexceptions()isamemberfunctionfor
streams.Theexceptions()functionreturnsabitmask[44](oftypeiostate,whichissomecompiler
dependenttypeconvertibletoint)indicatingwhichstreamstateswillcauseexceptions.Ifthosestates
havealreadybeenset,anexceptionisthrownimmediately.Ofcourse,ifyouuseexceptionsinconnection
withstreams,youhadbetterbereadytocatchthem,whichmeansthatyouneedtowrapallstream
processingwithatryblockthathasanios::failurehandler.Manyprogrammersfindthistediousandjust
checkstatesmanuallywheretheyexpecterrorstooccur(since,forexample,theydontexpectbad()to
returntruemostofthetimeanyway).Thisisanotherreasonthathavingstreamsthrowexceptionsis
optionalandnotthedefault.Inanycase,youcanchoosehowyouwanttohandlestreamerrors.Forthe
samereasonsthatwerecommendusingexceptionsforerrorhandlinginothercontexts,wedosohere.
Fileiostreams
ManipulatingfileswithiostreamsismucheasierandsaferthanusingstdioinC.Allyoudotoopenafileis
createanobjecttheconstructordoesthework.Youdontneedtoexplicitlycloseafile(althoughyoucan,
usingtheclose()memberfunction)becausethedestructorwillcloseitwhentheobjectgoesoutof
scope.Tocreateafilethatdefaultstoinput,makeanifstreamobject.Tocreateonethatdefaultsto
output,makeanofstreamobject.Anfstreamobjectcandobothinputandoutput.
Thefilestreamclassesfitintotheiostreamsclassesasshowninthefollowingfigure:
Asbefore,theclassesyouactuallyusearetemplatespecializationsdefinedbytypedefinitions.For
example,ifstream,whichprocessesfilesofchar,isdefinedas
typedefbasic_ifstream<char>ifstream
AFileProcessingExample
Heresanexamplethatshowsmanyofthefeaturesdiscussedsofar.Noticetheinclusionof<fstream>to
declarethefileI/Oclasses.Althoughonmanyplatformsthiswillalsoinclude<iostream>automatically,
compilersarenotrequiredtodoso.Ifyouwantportablecode,alwaysincludebothheaders.
//:C04:Strfile.cpp
//StreamI/Owithfiles
//Thedifferencebetweenget()&getline().
#include<fstream>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
99/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<iostream>
#include"../require.h"
usingnamespacestd
intmain(){
constintSZ=100//Buffersize
charbuf[SZ]
{
ifstreamin("Strfile.cpp")//Read
assure(in,"Strfile.cpp")//Verifyopen
ofstreamout("Strfile.out")//Write
assure(out,"Strfile.out")
inti=1//Linecounter
//Alessconvenientapproachforlineinput:
while(in.get(buf,SZ)){//Leaves\nininput
in.get()//Throwawaynextcharacter(\n)
cout<<buf<<endl//Mustadd\n
//FileoutputjustlikestandardI/O:
out<<i++<<":"<<buf<<endl
}
}//Destructorsclosein&out
ifstreamin("Strfile.out")
assure(in,"Strfile.out")
//Moreconvenientlineinput:
while(in.getline(buf,SZ)){//Removes\n
char*cp=buf
while(*cp!=':')
++cp
cp+=2//Past":"
cout<<cp<<endl//Muststilladd\n
}
}///:~
Thecreationofboththeifstreamandofstreamarefollowedbyanassure()toguaranteethefilewas
successfullyopened.Hereagaintheobject,usedinasituationwherethecompilerexpectsaBoolean
result,producesavaluethatindicatessuccessorfailure.
Thefirstwhileloopdemonstratestheuseoftwoformsoftheget()function.Thefirstgetscharacters
intoabufferandputsazeroterminatorinthebufferwheneitherSZ1charactershavebeenreadorthe
thirdargument(defaultedto'\n')isencountered.Theget()functionleavestheterminatorcharacterin
theinputstream,sothisterminatormustbethrownawayviain.get()usingtheformofget()withno
argument,whichfetchesasinglebyteandreturnsitasanint.Youcanalsousetheignore()member
function,whichhastwodefaultarguments.Thefirstargumentisthenumberofcharacterstothrowaway
anddefaultstoone.Thesecondargumentisthecharacteratwhichtheignore()functionquits(after
extractingit)anddefaultstoEOF.
Next,youseetwooutputstatementsthatlooksimilar:onetocoutandonetothefileout.Noticethe
conveniencehereyoudontneedtoworryabouttheobjecttypebecausetheformattingstatementswork
thesamewithallostreamobjects.Thefirstoneechoesthelinetostandardoutput,andthesecondwrites
thelineouttothenewfileandincludesalinenumber.
Todemonstrategetline(),openthefilewejustcreatedandstripoffthelinenumbers.Toensurethefile
isproperlyclosedbeforeopeningittoread,youhavetwochoices.Youcansurroundthefirstpartofthe
programwithbracestoforcetheoutobjectoutofscope,thuscallingthedestructorandclosingthefile,
whichisdonehere.Youcanalsocallclose()forbothfilesifyoudothis,youcanevenreusethein
objectbycallingtheopen()memberfunction.
Thesecondwhileloopshowshowgetline()removestheterminatorcharacter(itsthirdargument,which
defaultsto'\n')fromtheinputstreamwhenitsencountered.Althoughgetline(),likeget(),putsazero
inthebuffer,itstilldoesntinserttheterminatingcharacter.
Thisexample,aswellasmostoftheexamplesinthischapter,assumesthateachcalltoanyoverloadof
getline()willencounteranewlinecharacter.Ifthisisnotthecase,theeofbitstateofthestreamwillbe
setandthecalltogetline()willreturnfalse,causingtheprogramtolosethelastlineofinput.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
100/458
5/3/2015
ThinkinginC++2ndedVolume2
Openmodes
Youcancontrolthewayafileisopenedbyoverridingtheconstructorsdefaultarguments.Thefollowing
tableshowstheflagsthatcontrolthemodeofthefile:
Flag
Function
ios::in
Opensaninputfile.Usethisasanopenmode
foranofstreamtopreventtruncatingan
existingfile.
ios::out
Opensanoutputfile.Whenusedforan
ofstreamwithoutios::app,ios::ateor
ios::in,ios::truncisimplied.
ios::app
Opensanoutputfileforappendingonly.
ios::ate
Opensanexistingfile(eitherinputoroutput)
andseekstotheend.
ios::trunc
Truncatestheoldfileifitalreadyexists.
ios::binary
Opensafileinbinarymode.Thedefaultistext
mode.
Youcancombinetheseflagsusingabitwiseoroperation.
Thebinaryflag,whileportable,onlyhasaneffectonsomenonUNIXsystems,suchasoperatingsystems
derivedfromMSDOS,thathavespecialconventionsforstoringendoflinedelimiters.Forexample,on
MSDOSsystemsintextmode(whichisthedefault),everytimeyououtputanewlinecharacter('\n'),the
filesystemactuallyoutputstwocharacters,acarriagereturn/linefeedpair(CRLF),whichisthepairof
ASCIIcharacters0x0Dand0x0A.Conversely,whenyoureadsuchafilebackintomemoryintextmode,
eachoccurrenceofthispairofbytescausesa'\n'tobesenttotheprograminitsplace.Ifyouwantto
bypassthisspecialprocessing,youopenfilesinbinarymode.Binarymodehasnothingwhatsoevertodo
withwhetheryoucanwriterawbytestoafileyoualwayscan(bycallingwrite()).Youshould,
however,openafileinbinarymodewhenyoullbeusingread()orwrite(),becausethesefunctions
takeabytecountparameter.Havingtheextra'\r'characterswillthrowyourbytecountoffinthose
instances.Youshouldalsoopenafileinbinarymodeifyouregoingtousethestreampositioning
commandsdiscussedlaterinthischapter.
Youcanopenafileforbothinputandoutputbydeclaringanfstreamobject.Whendeclaringanfstream
object,youmustuseenoughoftheopenmodeflagsmentionedearliertoletthefilesystemknowwhether
youwanttoinput,output,orboth.Toswitchfromoutputtoinput,youneedtoeitherflushthestreamor
changethefileposition.Tochangefrominputtooutput,changethefileposition.Tocreateafileviaan
fstreamobject,usetheios::truncopenmodeflagintheconstructorcalltodobothinputandoutput.
Iostreambuffering
Gooddesignpracticedictatesthat,wheneveryoucreateanewclass,youshouldendeavortohidethe
detailsoftheunderlyingimplementationasmuchaspossiblefromtheuseroftheclass.Youshowthem
onlywhattheyneedtoknowandmaketherestprivatetoavoidconfusion.Whenusinginsertersand
extractors,younormallydontknoworcarewherethebytesarebeingproducedorconsumed,whether
youredealingwithstandardI/O,files,memory,orsomenewlycreatedclassordevice.
Atimecomes,however,whenitisimportanttocommunicatewiththepartoftheiostreamthatproduces
andconsumesbytes.Toprovidethispartwithacommoninterfaceandstillhideitsunderlying
implementation,thestandardlibraryabstractsitintoitsownclass,calledstreambuf.Eachiostream
objectcontainsapointertosomekindofstreambuf.(Thetypedependsonwhetheritdealswithstandard
I/O,files,memory,andsoon.)Youcanaccessthestreambufdirectlyforexample,youcanmoveraw
bytesintoandoutofthestreambufwithoutformattingthemthroughtheenclosingiostream.Thisis
accomplishedbycallingmemberfunctionsforthestreambufobject.
Currently,themostimportantthingforyoutoknowisthateveryiostreamobjectcontainsapointertoa
streambufobject,andthestreambufobjecthassomememberfunctionsyoucancallifnecessary.For
fileandstringstreams,therearespecializedtypesofstreambuffers,asthefollowingfigureillustrates:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
101/458
5/3/2015
ThinkinginC++2ndedVolume2
Toallowyoutoaccessthestreambuf,everyiostreamobjecthasamemberfunctioncalledrdbuf()that
returnsthepointertotheobjectsstreambuf.Thiswayyoucancallanymemberfunctionforthe
underlyingstreambuf.However,oneofthemostinterestingthingsyoucandowiththestreambuf
pointeristoconnectittoanotheriostreamobjectusingthe<<operator.Thisdrainsallthecharacters
fromyourobjectintotheoneontheleftsideofthe<<.Ifyouwanttomoveallthecharactersfromone
iostreamtoanother,youdontneedtogothroughthetedium(andpotentialcodingerrors)ofreadingthem
onecharacteroronelineatatime.Thisisamuchmoreelegantapproach.
Heresasimpleprogramthatopensafileandsendsthecontentstostandardoutput(similartothe
previousexample):
//:C04:Stype.cpp
//Typeafiletostandardoutput.
#include<fstream>
#include<iostream>
#include"../require.h"
usingnamespacestd
intmain(){
ifstreamin("Stype.cpp")
assure(in,"Stype.cpp")
cout<<in.rdbuf()//Outputsentirefile
}///:~
Anifstreamiscreatedusingthesourcecodefileforthisprogramasanargument.Theassure()function
reportsafailureifthefilecannotbeopened.Alltheworkreallyhappensinthestatement
cout<<in.rdbuf()
whichsendstheentirecontentsofthefiletocout.Thisisnotonlymoresuccincttocode,itisoftenmore
efficientthanmovingthebytesoneatatime.
Aformofget()writesdirectlyintothestreambufofanotherobject.Thefirstargumentisareferenceto
thedestinationstreambuf,andthesecondistheterminatingcharacter(\nbydefault),whichstopsthe
get()function.Sothereisyetanotherwaytoprintafiletostandardoutput:
//:C04:Sbufget.cpp
//Copiesafiletostandardoutput.
#include<fstream>
#include<iostream>
#include"../require.h"
usingnamespacestd
intmain(){
ifstreamin("Sbufget.cpp")
assure(in)
streambuf&sb=*cout.rdbuf()
while(!in.get(sb).eof()){
if(in.fail())//Foundblankline
in.clear()
cout<<char(in.get())//Process'\n'
}
}///:~
Therdbuf()functionreturnsapointer,soitmustbedereferencedtosatisfythefunctionsneedtoseean
object.Streambuffersarenotmeanttobecopied(theyhavenocopyconstructor),sowedefinesbasa
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
102/458
5/3/2015
ThinkinginC++2ndedVolume2
referencetocoutsstreambuffer.Weneedthecallstofail()andclear()incasetheinputfilehasa
blankline(thisonedoes).Whenthisparticularoverloadedversionofget()seestwonewlinesinarow
(evidenceofablankline),itsetstheinputstreamsfailbit,sowemustcallclear()toresetitsothatthe
streamcancontinuetoberead.Thesecondcalltoget()extractsandechoeseachnewlinedelimiter.
(Remember,theget()functiondoesntextractitsdelimiterlikegetline()does.)
Youprobablywontneedtouseatechniquelikethisoften,butitsnicetoknowitexists.[45]
Seekinginiostreams
Eachtypeofiostreamhasaconceptofwhereitsnextcharacterwillcomefrom(ifitsanistream)orgo
(ifitsanostream).Insomesituations,youmightwanttomovethisstreamposition.Youcandosousing
twomodels:oneusesanabsolutelocationinthestreamcalledthestreamposthesecondworkslikethe
StandardClibraryfunctionsfseek()forafileandmovesagivennumberofbytesfromthebeginning,
end,orcurrentpositioninthefile.
Thestreamposapproachrequiresthatyoufirstcallatellfunction:tellp()foranostreamortellg()
foranistream.(Thepreferstotheputpointer,andthegreferstothegetpointer.)Thisfunction
returnsastreamposyoucanlateruseincallstoseekp()foranostreamorseekg()foranistream
whenyouwanttoreturntothatpositioninthestream.
Thesecondapproachisarelativeseekandusesoverloadedversionsofseekp()andseekg().Thefirst
argumentisthenumberofcharacterstomove:itcanbepositiveornegative.Thesecondargumentisthe
seekdirection:
ios::beg
Frombeginningofstream
ios::cur
Currentpositioninstream
ios::end
Fromendofstream
Heresanexamplethatshowsthemovementthroughafile,butremember,yourenotlimitedtoseeking
withinfilesasyouarewithCsstdio.WithC++,youcanseekinanytypeofiostream(althoughthe
standardstreamobjects,suchascinandcout,explicitlydisallowit):
//:C04:Seeking.cpp
//Seekinginiostreams.
#include<cassert>
#include<cstddef>
#include<cstring>
#include<fstream>
#include"../require.h"
usingnamespacestd
intmain(){
constintSTR_NUM=5,STR_LEN=30
charorigData[STR_NUM][STR_LEN]={
"Hickorydickorydus...",
"AreyoutiredofC++?",
"Well,ifyouhave,",
"That'sjusttoobad,",
"There'splentymoreforus!"
}
charreadData[STR_NUM][STR_LEN]={{0}}
ofstreamout("Poem.bin",ios::out|ios::binary)
assure(out,"Poem.bin")
for(inti=0i<STR_NUMi++)
out.write(origData[i],STR_LEN)
out.close()
ifstreamin("Poem.bin",ios::in|ios::binary)
assure(in,"Poem.bin")
in.read(readData[0],STR_LEN)
assert(strcmp(readData[0],"Hickorydickorydus...")
==0)
//SeekSTR_LENbytesfromtheendoffile
in.seekg(STR_LEN,ios::end)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
103/458
5/3/2015
ThinkinginC++2ndedVolume2
in.read(readData[1],STR_LEN)
assert(strcmp(readData[1],"There'splentymoreforus!")
==0)
//Absoluteseek(likeusingoperator[]withafile)
in.seekg(3*STR_LEN)
in.read(readData[2],STR_LEN)
assert(strcmp(readData[2],"That'sjusttoobad,")==0)
//Seekbackwardsfromcurrentposition
in.seekg(STR_LEN*2,ios::cur)
in.read(readData[3],STR_LEN)
assert(strcmp(readData[3],"Well,ifyouhave,")==0)
//Seekfromthebeginingofthefile
in.seekg(1*STR_LEN,ios::beg)
in.read(readData[4],STR_LEN)
assert(strcmp(readData[4],"AreyoutiredofC++?")
==0)
}///:~
Thisprogramwritesapoemtoafileusingabinaryoutputstream.Sincewereopenitasanifstream,we
useseekg()topositionthegetpointer.Asyoucansee,youcanseekfromthebeginningorendofthe
fileorfromthecurrentfileposition.Obviously,youmustprovideapositivenumbertomovefromthe
beginningofthefileandanegativenumbertomovebackfromtheend.
Nowthatyouknowaboutthestreambufandhowtoseek,youcanunderstandanalternativemethod
(besidesusinganfstreamobject)forcreatingastreamobjectthatwillbothreadandwriteafile.The
followingcodefirstcreatesanifstreamwithflagsthatsayitsbothaninputandanoutputfile.Youcant
writetoanifstream,soyouneedtocreateanostreamwiththeunderlyingstreambuffer:
ifstreamin("filename",ios::in|ios::out)
ostreamout(in.rdbuf())
Youmightwonderwhathappenswhenyouwritetooneoftheseobjects.Heresanexample:
//:C04:Iofile.cpp
//Reading&writingonefile.
#include<fstream>
#include<iostream>
#include"../require.h"
usingnamespacestd
intmain(){
ifstreamin("Iofile.cpp")
assure(in,"Iofile.cpp")
ofstreamout("Iofile.out")
assure(out,"Iofile.out")
out<<in.rdbuf()//Copyfile
in.close()
out.close()
//Openforreadingandwriting:
ifstreamin2("Iofile.out",ios::in|ios::out)
assure(in2,"Iofile.out")
ostreamout2(in2.rdbuf())
cout<<in2.rdbuf()//Printwholefile
out2<<"Wheredoesthisendup?"
out2.seekp(0,ios::beg)
out2<<"Andwhataboutthis?"
in2.seekg(0,ios::beg)
cout<<in2.rdbuf()
}///:~
Thefirstfivelinescopythesourcecodeforthisprogramintoafilecallediofile.outandthenclosethe
files.Thisgivesusasafetextfiletoplaywith.Thentheaforementionedtechniqueisusedtocreatetwo
objectsthatreadandwritetothesamefile.Incout<<in2.rdbuf(),youcanseethegetpointeris
initializedtothebeginningofthefile.Theputpointer,however,issettotheendofthefilebecause
Wheredoesthisendup?appearsappendedtothefile.However,iftheputpointerismovedtothe
beginningwithaseekp(),alltheinsertedtextoverwritestheexistingtext.Bothwritesareseenwhenthe
getpointerismovedbacktothebeginningwithaseekg(),andthefileisdisplayed.Thefileis
automaticallysavedandclosedwhenout2goesoutofscopeanditsdestructoriscalled.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
104/458
5/3/2015
ThinkinginC++2ndedVolume2
Stringiostreams
Astringstreamworksdirectlywithmemoryinsteadofafileorstandardoutput.Itusesthesamereading
andformattingfunctionsthatyouusewithcinandcouttomanipulatebytesinmemory.Onoldcomputers,
thememorywasreferredtoascore,sothistypeoffunctionalityisoftencalledincoreformatting.
Theclassnamesforstringstreamsechothoseforfilestreams.Ifyouwanttocreateastringstreamto
extractcharactersfrom,youcreateanistringstream.Ifyouwanttoputcharactersintoastringstream,
youcreateanostringstream.Alldeclarationsforstringstreamsareinthestandardheader<sstream>.
Asusual,thereareclasstemplatesthatfitintotheiostreamshierarchy,asshowninthefollowingfigure:
Inputstringstreams
Toreadfromastringusingstreamoperations,youcreateanistringstreamobjectinitializedwiththe
string.Thefollowingprogramshowshowtouseanistringstreamobject:
//:C04:Istring.cpp
//Inputstringstreams.
#include<cassert>
#include<cmath>//Forfabs()
#include<iostream>
#include<limits>//Forepsilon()
#include<sstream>
#include<string>
usingnamespacestd
intmain(){
istringstreams("471.414Thisisatest")
inti
doublef
s>>i>>f//Whitespacedelimitedinput
assert(i==47)
doublerelerr=(fabs(f)1.414)/1.414
assert(relerr<=numeric_limits<double>::epsilon())
stringbuf2
s>>buf2
assert(buf2=="This")
cout<<s.rdbuf()//"isatest"
}///:~
Youcanseethatthisisamoreflexibleandgeneralapproachtotransformingcharacterstringstotyped
valuesthanthestandardClibraryfunctionssuchasatof()oratoi(),eventhoughthelattermaybe
moreefficientforsingleconversions.
Intheexpressions>>i>>f,thefirstnumberisextractedintoi,andthesecondintof.Thisisntthe
firstwhitespacedelimitedsetofcharactersbecauseitdependsonthedatatypeitsbeingextractedinto.
Forexample,ifthestringwereinstead,1.41447Thisisatest,theniwouldgetthevalue1because
theinputroutinewouldstopatthedecimalpoint.Thenfwouldget0.414.Thiscouldbeusefulifyouwant
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
105/458
5/3/2015
ThinkinginC++2ndedVolume2
tobreakafloatingpointnumberintoawholenumberandafractionpart.Otherwiseitwouldseemtobe
anerror.Thesecondassert()calculatestherelativeerrorbetweenwhatwereadandwhatweexpected
itsalwaysbettertodothisthantocomparefloatingpointnumbersforequality.Theconstantreturnedby
epsilon(),definedin<limits>,representsthemachineepsilonfordoubleprecisionnumbers,whichis
thebesttoleranceyoucanexpectcomparisonsofdoublestosatisfy.[46]
Asyoumayalreadyhaveguessed,buf2doesntgettherestofthestring,justthenextwhitespace
delimitedword.Ingeneral,itsbesttousetheextractoriniostreamswhenyouknowtheexactsequence
ofdataintheinputstreamandyoureconvertingtosometypeotherthanacharacterstring.However,if
youwanttoextracttherestofthestringallatonceandsendittoanotheriostream,youcanuserdbuf()
asshown.
TotesttheDateextractoratthebeginningofthischapter,weusedaninputstringstreamwiththe
followingtestprogram:
//:C04:DateIOTest.cpp
//{L}../C02/Date
#include<iostream>
#include<sstream>
#include"../C02/Date.h"
usingnamespacestd
voidtestDate(conststring&s){
istringstreamos(s)
Dated
os>>d
if(os)
cout<<d<<endl
else
cout<<"inputerrorwith\""<<s<<"\""<<endl
}
intmain(){
testDate("08102003")
testDate("8102003")
testDate("08102003")
testDate("A102003")
testDate("08%10/2003")
}///:~
Eachstringliteralinmain()ispassedbyreferencetotestDate(),whichinturnwrapsitinan
istringstreamsowecantestthestreamextractorwewroteforDateobjects.ThefunctiontestDate()
alsobeginstotesttheinserter,operator<<().
Outputstringstreams
Tocreateanoutputstringstream,youjustcreateanostringstreamobject,whichmanagesadynamically
sizedcharacterbuffertoholdwhateveryouinsert.Togettheformattedresultasastringobject,youcall
thestr()memberfunction.Heresanexample:
//:C04:Ostring.cpp{RunByHand}
//Illustratesostringstream.
#include<iostream>
#include<sstream>
#include<string>
usingnamespacestd
intmain(){
cout<<"typeanint,afloatandastring:"
inti
floatf
cin>>i>>f
cin>>ws//Throwawaywhitespace
stringstuff
getline(cin,stuff)//Getrestoftheline
ostringstreamos
os<<"integer="<<i<<endl
os<<"float="<<f<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
106/458
5/3/2015
ThinkinginC++2ndedVolume2
os<<"string="<<stuff<<endl
stringresult=os.str()
cout<<result<<endl
}///:~
ThisissimilartotheIstring.cppexampleearlierthatfetchedanintandafloat.Asampleexecution
follows(thekeyboardinputisinboldtype).
typeanint,afloatandastring:1020.5theend
integer=10
float=20.5
string=theend
Youcanseethat,liketheotheroutputstreams,youcanusetheordinaryformattingtools,suchasthe<<
operatorandendl,tosendbytestotheostringstream.Thestr()functionreturnsanewstringobject
everytimeyoucallitsotheunderlyingstringbufobjectownedbythestringstreamisleftundisturbed.
Inthepreviouschapter,wepresentedaprogram,HTMLStripper.cpp,thatremovedallHTMLtagsand
specialcodesfromatextfile.Aspromised,hereisamoreelegantversionusingstringstreams.
//:C04:HTMLStripper2.cpp{RunByHand}
//{L}../C03/ReplaceAll
//Filtertoremovehtmltagsandmarkers.
#include<cstddef>
#include<cstdlib>
#include<fstream>
#include<iostream>
#include<sstream>
#include<stdexcept>
#include<string>
#include"../C03/ReplaceAll.h"
#include"../require.h"
usingnamespacestd
string&stripHTMLTags(string&s)throw(runtime_error){
size_tleftPos
while((leftPos=s.find('<'))!=string::npos){
size_trightPos=s.find('>',leftPos+1)
if(rightPos==string::npos){
ostringstreammsg
msg<<"IncompleteHTMLtagstartinginposition"
<<leftPos
throwruntime_error(msg.str())
}
s.erase(leftPos,rightPosleftPos+1)
}
//RemoveallspecialHTMLcharacters
replaceAll(s,"<","<")
replaceAll(s,">",">")
replaceAll(s,"&","&")
replaceAll(s," ","")
//Etc...
returns
}
intmain(intargc,char*argv[]){
requireArgs(argc,1,
"usage:HTMLStripper2InputFile")
ifstreamin(argv[1])
assure(in,argv[1])
//Readentirefileintostringthenstrip
ostringstreamss
ss<<in.rdbuf()
try{
strings=ss.str()
cout<<stripHTMLTags(s)<<endl
returnEXIT_SUCCESS
}catch(runtime_error&x){
cout<<x.what()<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
107/458
5/3/2015
ThinkinginC++2ndedVolume2
returnEXIT_FAILURE
}
}///:~
Inthisprogramwereadtheentirefileintoastringbyinsertingardbuf()calltothefilestreamintoan
ostringstream.NowitsaneasymattertosearchforHTMLdelimiterpairsanderasethemwithouthaving
toworryaboutcrossinglineboundarieslikewehadtowiththepreviousversioninChapter3.
Thefollowingexampleshowshowtouseabidirectional(thatis,read/write)stringstream:
//:C04:StringSeeking.cpp{bor}{dmc}
//Readsandwritesastringstream.
#include<cassert>
#include<sstream>
#include<string>
usingnamespacestd
intmain(){
stringtext="Wewillhooknofish"
stringstreamss(text)
ss.seekp(0,ios::end)
ss<<"beforeitstime."
assert(ss.str()==
"Wewillhooknofishbeforeitstime.")
//Change"hook"to"ship"
ss.seekg(8,ios::beg)
stringword
ss>>word
assert(word=="hook")
ss.seekp(8,ios::beg)
ss<<"ship"
//Change"fish"to"code"
ss.seekg(16,ios::beg)
ss>>word
assert(word=="fish")
ss.seekp(16,ios::beg)
ss<<"code"
assert(ss.str()==
"Wewillshipnocodebeforeitstime.")
ss.str("Ahorseofadifferentcolor.")
assert(ss.str()=="Ahorseofadifferentcolor.")
}///:~
Asalways,tomovetheputpointer,youcallseekp(),andtorepositionthegetpointer,youcall
seekg().Eventhoughwedidntshowitwiththisexample,stringstreamsarealittlemoreforgivingthan
filestreamsinthatyoucanswitchfromreadingtowritingorviceversaatanytime.Youdontneedto
repositionthegetorputpointersorflushthestream.Thisprogramalsoillustratestheoverloadofstr()
thatreplacesthestreamsunderlyingstringbufwithanewstring.
Outputstreamformatting
Thegoaloftheiostreamsdesignistoallowyoutoeasilymoveand/orformatcharacters.Itcertainly
wouldntbeusefulifyoucouldntdomostoftheformattingprovidedbyCsprintf()familyoffunctions.
Inthissection,youlllearnalltheoutputformattingfunctionsthatareavailableforiostreams,soyoucan
formatyourbytesthewayyouwantthem.
Theformattingfunctionsiniostreamscanbesomewhatconfusingatfirstbecausetheresoftenmorethan
onewaytocontroltheformatting:throughbothmemberfunctionsandmanipulators.Tofurtherconfuse
things,agenericmemberfunctionsetsstateflagstocontrolformatting,suchasleftorrightjustification,
touseuppercaselettersforhexnotation,toalwaysuseadecimalpointforfloatingpointvalues,andso
on.Ontheotherhand,separatememberfunctionssetandreadvaluesforthefillcharacter,thefield
width,andtheprecision.
Inanattempttoclarifyallthis,wellfirstexaminetheinternalformattingdataofaniostream,alongwith
thememberfunctionsthatcanmodifythatdata.(Everythingcanbecontrolledthroughthemember
functions,ifdesired.)Wellcoverthemanipulatorsseparately.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
108/458
5/3/2015
ThinkinginC++2ndedVolume2
Formatflags
Theclassioscontainsdatamemberstostorealltheformattinginformationpertainingtoastream.Some
ofthisdatahasarangeofvaluesandisstoredinvariables:thefloatingpointprecision,theoutputfield
width,andthecharacterusedtopadtheoutput(normallyaspace).Therestoftheformattingis
determinedbyflags,whichareusuallycombinedtosavespaceandarereferredtocollectivelyasthe
formatflags.Youcanfindoutthevalueoftheformatflagswiththeios::flags()memberfunction,which
takesnoargumentsandreturnsanobjectoftypefmtflags(usuallyasynonymforlong)thatcontainsthe
currentformatflags.Alltherestofthefunctionsmakechangestotheformatflagsandreturntheprevious
valueoftheformatflags.
fmtflagsios::flags(fmtflagsnewflags)
fmtflagsios::setf(fmtflagsored_flag)
fmtflagsios::unsetf(fmtflagsclear_flag)
fmtflagsios::setf(fmtflagsbits,fmtflagsfield)
Thefirstfunctionforcesalltheflagstochange,whichissometimeswhatyouwant.Moreoften,youchange
oneflagatatimeusingtheremainingthreefunctions.
Theuseofsetf()canseemsomewhatconfusing.Toknowwhichoverloadedversiontouse,youmust
knowwhattypeofflagyourechanging.Therearetwotypesofflags:thosethataresimplyonoroff,and
thosethatworkinagroupwithotherflags.Theon/offflagsarethesimplesttounderstandbecauseyou
turnthemonwithsetf(fmtflags)andoffwithunsetf(fmtflags).Theseflagsareshowninthefollowing
table:
on/offflag
Effect
ios::skipws
Skipwhitespace.(Forinputthisis
thedefault.)
ios::showbase
Indicatethenumericbase(asset,for
example,bydec,oct,orhex)when
printinganintegralvalue.Input
streamsalsorecognizethebase
prefixwhenshowbaseison.
ios::showpoint
Showdecimalpointandtrailingzeros
forfloatingpointvalues.
ios::uppercase
DisplayuppercaseAFfor
hexadecimalvaluesandEfor
scientificvalues.
ios::showpos
Showplussign(+)forpositive
values.
ios::unitbuf
Unitbuffering.Thestreamis
flushedaftereachinsertion.
Forexample,toshowtheplussignforcout,yousaycout.setf(ios::showpos).Tostopshowingtheplus
sign,yousaycout.unsetf(ios::showpos).
Theunitbufflagcontrolsunitbuffering,whichmeansthateachinsertionisflushedtoitsoutputstream
immediately.Thisishandyforerrortracing,sothatincaseofaprogramcrash,yourdataisstillwrittento
thelogfile.Thefollowingprogramillustratesunitbuffering:
//:C04:Unitbuf.cpp{RunByHand}
#include<cstdlib>//Forabort()
#include<fstream>
usingnamespacestd
intmain(){
ofstreamout("log.txt")
out.setf(ios::unitbuf)
out<<"one"<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
109/458
5/3/2015
ThinkinginC++2ndedVolume2
out<<"two"<<endl
abort()
}///:~
Itisnecessarytoturnonunitbufferingbeforeanyinsertionsaremadetothestream.Whenwe
commentedoutthecalltosetf(),oneparticularcompilerhadwrittenonlytheletterotothefilelog.txt.
Withunitbuffering,nodatawaslost.
Thestandarderroroutputstreamcerrhasunitbufferingturnedonbydefault.Thereisacostforunit
buffering,soifanoutputstreamisheavilyused,dontenableunitbufferingunlessefficiencyisnota
consideration.
Formatfields
Thesecondtypeofformattingflagsworkinagroup.Onlyoneoftheseflagscanbesetatatime,likethe
buttonsonoldcarradiosyoupushonein,therestpopout.Unfortunatelythisdoesnthappen
automatically,andyoumustpayattentiontowhatflagsyouresettingsothatyoudontaccidentallycall
thewrongsetf()function.Forexample,theresaflagforeachofthenumberbases:hexadecimal,
decimal,andoctal.Collectively,theseflagsarereferredtoastheios::basefield.Iftheios::decflagis
setandyoucallsetf(ios::hex),youllsettheios::hexflag,butyouwontcleartheios::decbit,resulting
inundefinedbehavior.Instead,callthesecondformofsetf()likethis:setf(ios::hex,ios::basefield).
Thisfunctionfirstclearsallthebitsintheios::basefieldandthensetsios::hex.Thus,thisformof
setf()ensuresthattheotherflagsinthegrouppopoutwheneveryousetone.Theios::hex
manipulatordoesallthisforyou,automatically,soyoudontneedtoconcernyourselfwiththeinternal
detailsoftheimplementationofthisclassortoevencarethatitsasetofbinaryflags.Lateryoullsee
thattherearemanipulatorstoprovideequivalentfunctionalityinalltheplacesyouwouldusesetf().
Herearetheflaggroupsandtheireffects:
ios::basefield
Effect
ios::dec
Formatintegralvaluesinbase10
(decimal)(thedefaultradixnoprefixis
visible).
ios::hex
Formatintegralvaluesinbase16
(hexadecimal).
ios::oct
Formatintegralvaluesinbase8(octal).
ios::floatfield
Effect
ios::scientific
Displayfloatingpointnumbersinscientific
format.Precisionfieldindicatesnumberof
digitsafterthedecimalpoint.
ios::fixed
Displayfloatingpointnumbersinfixed
format.Precisionfieldindicatesnumberof
digitsafterthedecimalpoint.
automatic(Neither
bitisset.)
Precisionfieldindicatesthetotalnumber
ofsignificantdigits.
ios::adjustfield
Effect
ios::left
Leftalignvaluespadontherightwiththe
fillcharacter.
ios::right
Rightalignvalues.Padontheleftwiththe
fillcharacter.Thisisthedefault
alignment.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
110/458
5/3/2015
ThinkinginC++2ndedVolume2
ios::internal
Addfillcharactersafteranyleadingsign
orbaseindicator,butbeforethevalue.(In
otherwords,thesign,ifprinted,isleft
justifiedwhilethenumberisright
justified.)
Width,fill,andprecision
Theinternalvariablesthatcontrolthewidthoftheoutputfield,thefillcharacterusedtopadanoutput
field,andtheprecisionforprintingfloatingpointnumbersarereadandwrittenbymemberfunctionsofthe
samename.
Function
Effect
intios::width()
Returnsthecurrentwidth.Default
is0.Usedforbothinsertionand
extraction.
intios::width(intn)
Setsthewidth,returnsthe
previouswidth.
intios::fill()
Returnsthecurrentfillcharacter.
Defaultisspace.
intios::fill(intn)
Setsthefillcharacter,returnsthe
previousfillcharacter.
intios::precision()
Returnscurrentfloatingpoint
precision.Defaultis6.
intios::precision(intn)
Setsfloatingpointprecision,
returnspreviousprecision.See
ios::floatfieldtableforthe
meaningofprecision.
Thefillandprecisionvaluesarefairlystraightforward,butwidthrequiressomeexplanation.Whenthe
widthiszero,insertingavalueproducestheminimumnumberofcharactersnecessarytorepresentthat
value.Apositivewidthmeansthatinsertingavaluewillproduceatleastasmanycharactersasthewidth
ifthevaluehasfewerthanwidthcharacters,thefillcharacterpadthefield.However,thevaluewillnever
betruncated,soifyoutrytoprint123withawidthoftwo,youllstillget123.Thefieldwidthspecifiesa
minimumnumberofcharacterstheresnowaytospecifyamaximumnumber.
Thewidthisalsodistinctlydifferentbecauseitsresettozerobyeachinserterorextractorthatcouldbe
influencedbyitsvalue.Itsreallynotastatevariable,butratheranimplicitargumenttotheinsertersand
extractors.Ifyouwantaconstantwidth,callwidth()aftereachinsertionorextraction.
Anexhaustiveexample
Tomakesureyouknowhowtocallallthefunctionspreviouslydiscussed,heresanexamplethatcalls
themall:
//:C04:Format.cpp
//FormattingFunctions.
#include<fstream>
#include<iostream>
#include"../require.h"
usingnamespacestd
#defineD(A)T<<#A<<endlA
intmain(){
ofstreamT("format.out")
assure(T)
D(inti=47)
D(floatf=2300114.414159)
constchar*s="Isthereanymore?"
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
111/458
5/3/2015
ThinkinginC++2ndedVolume2
D(T.setf(ios::unitbuf))
D(T.setf(ios::showbase))
D(T.setf(ios::uppercase|ios::showpos))
D(T<<i<<endl)//Defaultisdec
D(T.setf(ios::hex,ios::basefield))
D(T<<i<<endl)
D(T.setf(ios::oct,ios::basefield))
D(T<<i<<endl)
D(T.unsetf(ios::showbase))
D(T.setf(ios::dec,ios::basefield))
D(T.setf(ios::left,ios::adjustfield))
D(T.fill('0'))
D(T<<"fillchar:"<<T.fill()<<endl)
D(T.width(10))
T<<i<<endl
D(T.setf(ios::right,ios::adjustfield))
D(T.width(10))
T<<i<<endl
D(T.setf(ios::internal,ios::adjustfield))
D(T.width(10))
T<<i<<endl
D(T<<i<<endl)//Withoutwidth(10)
D(T.unsetf(ios::showpos))
D(T.setf(ios::showpoint))
D(T<<"prec="<<T.precision()<<endl)
D(T.setf(ios::scientific,ios::floatfield))
D(T<<endl<<f<<endl)
D(T.unsetf(ios::uppercase))
D(T<<endl<<f<<endl)
D(T.setf(ios::fixed,ios::floatfield))
D(T<<f<<endl)
D(T.precision(20))
D(T<<"prec="<<T.precision()<<endl)
D(T<<endl<<f<<endl)
D(T.setf(ios::scientific,ios::floatfield))
D(T<<endl<<f<<endl)
D(T.setf(ios::fixed,ios::floatfield))
D(T<<f<<endl)
D(T.width(10))
T<<s<<endl
D(T.width(40))
T<<s<<endl
D(T.setf(ios::left,ios::adjustfield))
D(T.width(40))
T<<s<<endl
}///:~
Thisexampleusesatricktocreateatracefilesothatyoucanmonitorwhatshappening.ThemacroD(a)
usesthepreprocessorstringizingtoturnaintoastringtodisplay.Thenitreiteratesasothestatement
isexecuted.ThemacrosendsalltheinformationtoafilecalledT,whichisthetracefile.Theoutputis
inti=47
floatf=2300114.414159
T.setf(ios::unitbuf)
T.setf(ios::showbase)
T.setf(ios::uppercase|ios::showpos)
T<<i<<endl
+47
T.setf(ios::hex,ios::basefield)
T<<i<<endl
0X2F
T.setf(ios::oct,ios::basefield)
T<<i<<endl
057
T.unsetf(ios::showbase)
T.setf(ios::dec,ios::basefield)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
112/458
5/3/2015
ThinkinginC++2ndedVolume2
T.setf(ios::left,ios::adjustfield)
T.fill('0')
T<<"fillchar:"<<T.fill()<<endl
fillchar:0
T.width(10)
+470000000
T.setf(ios::right,ios::adjustfield)
T.width(10)
0000000+47
T.setf(ios::internal,ios::adjustfield)
T.width(10)
+000000047
T<<i<<endl
+47
T.unsetf(ios::showpos)
T.setf(ios::showpoint)
T<<"prec="<<T.precision()<<endl
prec=6
T.setf(ios::scientific,ios::floatfield)
T<<endl<<f<<endl
2.300114E+06
T.unsetf(ios::uppercase)
T<<endl<<f<<endl
2.300114e+06
T.setf(ios::fixed,ios::floatfield)
T<<f<<endl
2300114.500000
T.precision(20)
T<<"prec="<<T.precision()<<endl
prec=20
T<<endl<<f<<endl
2300114.50000000000000000000
T.setf(ios::scientific,ios::floatfield)
T<<endl<<f<<endl
2.30011450000000000000e+06
T.setf(ios::fixed,ios::floatfield)
T<<f<<endl
2300114.50000000000000000000
T.width(10)
Isthereanymore?
T.width(40)
0000000000000000000000Isthereanymore?
T.setf(ios::left,ios::adjustfield)
T.width(40)
Isthereanymore?0000000000000000000000
Studyingthisoutputshouldclarifyyourunderstandingoftheiostreamformattingmemberfunctions.
Manipulators
Asyoucanseefromthepreviousprogram,callingthememberfunctionsforstreamformattingoperations
cangetabittedious.Tomakethingseasiertoreadandwrite,asetofmanipulatorsissuppliedto
duplicatetheactionsprovidedbythememberfunctions.Manipulatorsareaconveniencebecauseyoucan
insertthemfortheireffectwithinacontainingexpressionyoudontneedtocreateaseparatefunctioncall
statement.
Manipulatorschangethestateofthestreaminsteadof(orinadditionto)processingdata.Whenyouinsert
endlinanoutputexpression,forexample,itnotonlyinsertsanewlinecharacter,butitalsoflushesthe
stream(thatis,putsoutallpendingcharactersthathavebeenstoredintheinternalstreambufferbutnot
yetoutput).Youcanalsojustflushastreamlikethis:
cout<<flush
whichcausesacalltotheflush()memberfunction,asin:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
113/458
5/3/2015
ThinkinginC++2ndedVolume2
cout.flush()
asasideeffect(nothingisinsertedintothestream).Additionalbasicmanipulatorswillchangethenumber
basetooct(octal),dec(decimal)orhex(hexadecimal):
cout<<hex<<"0x"<<i<<endl
Inthiscase,numericoutputwillcontinueinhexadecimalmodeuntilyouchangeitbyinsertingeitherdec
oroctintheoutputstream.
Theresalsoamanipulatorforextractionthateatswhitespace:
cin>>ws
Manipulatorswithnoargumentsareprovidedin<iostream>.Theseincludedec,oct,andhex,which
performthesameactionas,respectively,setf(ios::dec,ios::basefield),setf(ios::oct,ios::basefield),
andsetf(ios::hex,ios::basefield),albeitmoresuccinctly.The<iostream>headeralsoincludesws,
endl,andflushandtheadditionalsetshownhere:
Manipulator
Effect
showbase
noshowbase
Indicatethenumericbase(dec,oct,or
hex)whenprintinganintegralvalue.
showpos
noshowpos
Showplussign(+)forpositivevalues.
uppercase
nouppercase
DisplayuppercaseAFforhexadecimal
values,anddisplayEforscientific
values.
showpoint
noshowpoint
Showdecimalpointandtrailingzeros
forfloatingpointvalues.
skipws
noskipws
Skipwhitespaceoninput.
left
right
internal
Leftalign,padonright.
Rightalign,padonleft.
Fillbetweenleadingsignorbase
indicatorandvalue.
scientific
fixed
Indicatesthedisplaypreferencefor
floatingpointoutput(scientificnotation
vs.fixedpointdecimal).
Manipulatorswitharguments
Therearesixstandardmanipulators,suchassetw(),thattakearguments.Thesearedefinedinthe
headerfile<iomanip>,andaresummarizedinthefollowingtable:
Manipulator
effect
setiosflags(fmtflagsn)
Equivalenttoacalltosetf(n).
Thesettingremainsineffect
untilthenextchange,suchas
ios::setf().
resetiosflags(fmtflagsn)
Clearsonlytheformatflags
specifiedbyn.Thesetting
remainsineffectuntilthenext
change,suchasios::unsetf().
setbase(basen)
Changesbaseton,wherenis
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
114/458
5/3/2015
ThinkinginC++2ndedVolume2
10,8,or16.(Anythingelse
resultsin0.)Ifniszero,output
isbase10,butinputusestheC
conventions:10is10,010is8,
and0xfis15.Youmightaswell
usedec,oct,andhexfor
output.
setfill(charn)
Changesthefillcharacterton,
suchasios::fill().
setprecision(intn)
Changestheprecisionton,such
asios::precision().
setw(intn)
Changesthefieldwidthton,
suchasios::width().
Ifyouredoingalotofformatting,youcanseehowusingmanipulatorsinsteadofcallingstreammember
functionscancleanupyourcode.Asanexample,herestheprogramfromtheprevioussectionrewritten
tousethemanipulators.(TheD()macroisremovedtomakeiteasiertoread.)
//:C04:Manips.cpp
//Format.cppusingmanipulators.
#include<fstream>
#include<iomanip>
#include<iostream>
usingnamespacestd
intmain(){
ofstreamtrc("trace.out")
inti=47
floatf=2300114.414159
char*s="Isthereanymore?"
trc<<setiosflags(ios::unitbuf
|ios::showbase|ios::uppercase
|ios::showpos)
trc<<i<<endl
trc<<hex<<i<<endl
<<oct<<i<<endl
trc.setf(ios::left,ios::adjustfield)
trc<<resetiosflags(ios::showbase)
<<dec<<setfill('0')
trc<<"fillchar:"<<trc.fill()<<endl
trc<<setw(10)<<i<<endl
trc.setf(ios::right,ios::adjustfield)
trc<<setw(10)<<i<<endl
trc.setf(ios::internal,ios::adjustfield)
trc<<setw(10)<<i<<endl
trc<<i<<endl//Withoutsetw(10)
trc<<resetiosflags(ios::showpos)
<<setiosflags(ios::showpoint)
<<"prec="<<trc.precision()<<endl
trc.setf(ios::scientific,ios::floatfield)
trc<<f<<resetiosflags(ios::uppercase)<<endl
trc.setf(ios::fixed,ios::floatfield)
trc<<f<<endl
trc<<f<<endl
trc<<setprecision(20)
trc<<"prec="<<trc.precision()<<endl
trc<<f<<endl
trc.setf(ios::scientific,ios::floatfield)
trc<<f<<endl
trc.setf(ios::fixed,ios::floatfield)
trc<<f<<endl
trc<<f<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
115/458
5/3/2015
ThinkinginC++2ndedVolume2
trc<<setw(10)<<s<<endl
trc<<setw(40)<<s<<endl
trc.setf(ios::left,ios::adjustfield)
trc<<setw(40)<<s<<endl
}///:~
Youcanseethatalotofthemultiplestatementshavebeencondensedintoasinglechainedinsertion.
Noticethecalltosetiosflags()inwhichthebitwiseORoftheflagsispassed.Thiscouldalsohavebeen
donewithsetf()andunsetf()asinthepreviousexample.
Whenusingsetw()withanoutputstream,theoutputexpressionisformattedintoatemporarystringthat
ispaddedwiththecurrentfillcharacterifneeded,asdeterminedbycomparingthelengthoftheformatted
resulttotheargumentofsetw().Inotherwords,setw()affectstheresultstringofaformattedoutput
operation.Likewise,usingsetw()withinputstreamsonlyismeaningfulwhenreadingstrings,asthe
followingexamplemakesclear:
//:C04:InputWidth.cpp
//Showslimitationsofsetwwithinput.
#include<cassert>
#include<cmath>
#include<iomanip>
#include<limits>
#include<sstream>
#include<string>
usingnamespacestd
intmain(){
istringstreamis("one2.34five")
stringtemp
is>>setw(2)>>temp
assert(temp=="on")
is>>setw(2)>>temp
assert(temp=="e")
doublex
is>>setw(2)>>x
doublerelerr=fabs(x2.34)/x
assert(relerr<=numeric_limits<double>::epsilon())
}///:~
Ifyouattempttoreadastring,setw()willcontrolthenumberofcharactersextractedquitenicelyupto
apoint.Thefirstextractiongetstwocharacters,butthesecondonlygetsone,eventhoughweaskedfor
two.Thatisbecauseoperator>>()useswhitespaceasadelimiter(unlessyouturnofftheskipws
flag).Whentryingtoreadanumber,however,suchasx,youcannotusesetw()tolimitthecharacters
read.Withinputstreams,useonlysetw()forextractingstrings.
Creatingmanipulators
Sometimesyoudliketocreateyourownmanipulators,anditturnsouttoberemarkablysimple.Azero
argumentmanipulatorsuchasendlissimplyafunctionthattakesasitsargumentanostreamreference
andreturnsanostreamreference.Thedeclarationforendlis
ostream&endl(ostream&)
Now,whenyousay:
cout<<"howdy"<<endl
theendlproducestheaddressofthatfunction.Sothecompilerasks,Isthereafunctionthatcanbe
appliedherethattakestheaddressofafunctionasitsargument?Predefinedfunctionsin<iostream>do
thistheyrecalledapplicators(becausetheyapplyafunctiontoastream).Theapplicatorcallsitsfunction
argument,passingittheostreamobjectasitsargument.Youdontneedtoknowhowapplicatorsworkto
createyourownmanipulatoryouonlyneedtoknowthattheyexist.Heresthe(simplified)codeforan
ostreamapplicator:
ostream&ostream::operator<<(ostream&(*pf)(ostream&)){
returnpf(*this)
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
116/458
5/3/2015
ThinkinginC++2ndedVolume2
Theactualdefinitionisalittlemorecomplicatedsinceitinvolvestemplates,butthiscodeillustratesthe
technique.Whenafunctionsuchas*pf(thattakesastreamparameterandreturnsastreamreference)is
insertedintoastream,thisapplicatorfunctioniscalled,whichinturnexecutesthefunctiontowhichpf
points.Applicatorsforios_base,basic_ios,basic_ostream,andbasic_istreamarepredefinedinthe
StandardC++library.
Toillustratetheprocess,heresatrivialexamplethatcreatesamanipulatorcallednlthatisequivalentto
justinsertinganewlineintoastream(i.e.,noflushingofthestreamoccurs,aswithendl):
//:C04:nl.cpp
//Creatingamanipulator.
#include<iostream>
usingnamespacestd
ostream&nl(ostream&os){
returnos<<'\n'
}
intmain(){
cout<<"newlines"<<nl<<"between"<<nl
<<"each"<<nl<<"word"<<nl
}///:~
Whenyouinsertnlintoanoutputstream,suchascout,thefollowingsequenceofcallsensues:
cout.operator<<(nl)nl(cout)
Theexpression
os<<'\n'
insidenl()callsostream::operator(char),whichreturnsthestream,whichiswhatisultimately
returnedfromnl().[47]
Effectors
Asyouveseen,zeroargumentmanipulatorsareeasytocreate.Butwhatifyouwanttocreatea
manipulatorthattakesarguments?Ifyouinspectthe<iomanip>header,youllseeatypecalledsmanip,
whichiswhatthemanipulatorswithargumentsreturn.Youmightbetemptedtosomehowusethattypeto
defineyourownmanipulators,butdontdoit.Thesmaniptypeisimplementationdependentandthusnot
portable.Fortunately,youcandefinesuchmanipulatorsinastraightforwardwaywithoutanyspecial
machinery,basedonatechniqueintroducedbyJerrySchwarz,calledaneffector.[48]Aneffectorisasimple
classwhoseconstructorformatsastringrepresentingthedesiredoperation,alongwithanoverloaded
operator<<toinsertthatstringintoastream.Heresanexamplewithtwoeffectors.Thefirstoutputsa
truncatedcharacterstring,andthesecondprintsanumberinbinary.
//:C04:Effector.cpp
//JerrySchwarz's"effectors."
#include<cassert>
#include<limits>//Formax()
#include<sstream>
#include<string>
usingnamespacestd
//Putoutaprefixofastring:
classFixw{
stringstr
public:
Fixw(conststring&s,intwidth):str(s,0,width){}
friendostream&operator<<(ostream&os,constFixw&fw){
returnos<<fw.str
}
}
//Printanumberinbinary:
typedefunsignedlongulong
classBin{
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
117/458
5/3/2015
ThinkinginC++2ndedVolume2
ulongn
public:
Bin(ulongnn){n=nn}
friendostream&operator<<(ostream&os,constBin&b){
constulongULMAX=numeric_limits<ulong>::max()
ulongbit=~(ULMAX>>1)//Topbitset
while(bit){
os<<(b.n&bit?'1':'0')
bit>>=1
}
returnos
}
}
intmain(){
stringwords="Thingsthatmakeushappy,makeuswise"
for(inti=words.size()i>=0){
ostringstreams
s<<Fixw(words,i)
assert(s.str()==words.substr(0,i))
}
ostringstreamxs,ys
xs<<Bin(0xCAFEBABEUL)
assert(xs.str()==
"1100""1010""1111""1110""1011""1010""1011""1110")
ys<<Bin(0x76543210UL)
assert(ys.str()==
"0111""0110""0101""0100""0011""0010""0001""0000")
}///:~
TheconstructorforFixwcreatesashortenedcopyofitschar*argument,andthedestructorreleasesthe
memorycreatedforthiscopy.Theoverloadedoperator<<takesthecontentsofitssecondargument,the
Fixwobject,insertsitintothefirstargument,theostream,andthenreturnstheostreamsothatitcan
beusedinachainedexpression.WhenyouuseFixwinanexpressionlikethis:
cout<<Fixw(string,i)<<endl
atemporaryobjectiscreatedbythecalltotheFixwconstructor,andthattemporaryobjectispassedto
operator<<.Theeffectisthatofamanipulatorwitharguments.ThetemporaryFixwobjectpersistsuntil
theendofthestatement.
TheBineffectorreliesonthefactthatshiftinganunsignednumbertotherightshiftszerosintothehigh
bits.Weusenumeric_limits<unsignedlong>::max()(thelargestunsignedlongvalue,fromthe
standardheader<limits>)toproduceavaluewiththehighbitset,andthisvalueismovedacrossthe
numberinquestion(byshiftingittotheright),maskingeachbitinturn.Wevejuxtaposedstringliteralsin
thecodeforreadabilitytheseparatestringsareconcatenatedintoasinglestringbythecompiler.
Historically,theproblemwiththistechniquewasthatonceyoucreatedaclasscalledFixwforchar*or
Binforunsignedlong,nooneelsecouldcreateadifferentFixworBinclassfortheirtype.However,
withnamespaces,thisproblemiseliminated.Effectorsandmanipulatorsarentequivalent,althoughthey
canoftenbeusedtosolvethesameproblem.Ifyoufindthataneffectorisntenough,youwillneedto
conquerthecomplexityofmanipulators.
Iostreamexamples
Inthissectionyoullseeexamplesthatusewhatyouvelearnedinthischapter.Althoughmanytoolsexist
tomanipulatebytes(streameditorssuchassedandawkfromUNIXareperhapsthemostwellknown,
butatexteditoralsofitsthiscategory),theygenerallyhavesomelimitations.Bothsedandawkcanbe
slowandcanonlyhandlelinesinaforwardsequence,andtexteditorsusuallyrequirehumaninteraction,
oratleastlearningaproprietarymacrolanguage.Theprogramsyouwritewithiostreamshavenoneof
theselimitations:theyrefast,portable,andflexible.
Maintainingclasslibrarysourcecode
Generally,whenyoucreateaclass,youthinkinlibraryterms:youmakeaheaderfileName.hforthe
classdeclaration,andthencreateafilecalledName.cppwherethememberfunctionsareimplemented.
Thesefileshavecertainrequirements:aparticularcodingstandard(theprogramshownhereusesthe
codingformatforthisbook),andpreprocessorstatementssurroundingthecodeintheheaderfileto
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
118/458
5/3/2015
ThinkinginC++2ndedVolume2
preventmultipledeclarationsofclasses.(Multipledeclarationsconfusethecompileritdoesntknowwhich
oneyouwanttouse.Theycouldbedifferent,soitthrowsupitshandsandgivesanerrormessage.)
Thisexamplecreatesanewheader/implementationpairoffilesormodifiesanexistingpair.Ifthefiles
alreadyexist,itchecksandpotentiallymodifiesthefiles,butiftheydontexist,itcreatesthemusingthe
properformat.
//:C04:Cppcheck.cpp
//Configures.h&.cppfilestoconformtostyle
//standard.Testsexistingfilesforconformance.
#include<fstream>
#include<sstream>
#include<string>
#include<cstddef>
#include"../require.h"
usingnamespacestd
boolstartsWith(conststring&base,conststring&key){
returnbase.compare(0,key.size(),key)==0
}
voidcppCheck(stringfileName){
enumbufs{BASE,HEADER,IMPLEMENT,HLINE1,GUARD1,
GUARD2,GUARD3,CPPLINE1,INCLUDE,BUFNUM}
stringpart[BUFNUM]
part[BASE]=fileName
//Findany'.'inthestring:
size_tloc=part[BASE].find('.')
if(loc!=string::npos)
part[BASE].erase(loc)//Stripextension
//Forcetouppercase:
for(size_ti=0i<part[BASE].size()i++)
part[BASE][i]=toupper(part[BASE][i])
//Createfilenamesandinternallines:
part[HEADER]=part[BASE]+".h"
part[IMPLEMENT]=part[BASE]+".cpp"
part[HLINE1]="//"":"+part[HEADER]
part[GUARD1]="#ifndef"+part[BASE]+"_H"
part[GUARD2]="#define"+part[BASE]+"_H"
part[GUARD3]="#endif//"+part[BASE]+"_H"
part[CPPLINE1]=string("//")+":"+part[IMPLEMENT]
part[INCLUDE]="#include\""+part[HEADER]+"\""
//First,trytoopenexistingfiles:
ifstreamexisth(part[HEADER].c_str()),
existcpp(part[IMPLEMENT].c_str())
if(!existh){//Doesn'texistcreateit
ofstreamnewheader(part[HEADER].c_str())
assure(newheader,part[HEADER].c_str())
newheader<<part[HLINE1]<<endl
<<part[GUARD1]<<endl
<<part[GUARD2]<<endl<<endl
<<part[GUARD3]<<endl
}else{//Alreadyexistsverifyit
stringstreamhfile//Write&read
ostringstreamnewheader//Write
hfile<<existh.rdbuf()
//Checkthatfirstthreelinesconform:
boolchanged=false
strings
hfile.seekg(0)
getline(hfile,s)
boollineUsed=false
//Thecalltogood()isforMicrosoft(latertoo):
for(intline=HLINE1hfile.good()&&line<=GUARD2
++line){
if(startsWith(s,part[line])){
newheader<<s<<endl
lineUsed=true
if(getline(hfile,s))
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
119/458
5/3/2015
ThinkinginC++2ndedVolume2
lineUsed=false
}else{
newheader<<part[line]<<endl
changed=true
lineUsed=false
}
}
//Copyrestoffile
if(!lineUsed)
newheader<<s<<endl
newheader<<hfile.rdbuf()
//CheckforGUARD3
stringhead=hfile.str()
if(head.find(part[GUARD3])==string::npos){
newheader<<part[GUARD3]<<endl
changed=true
}
//Iftherewerechanges,overwritefile:
if(changed){
existh.close()
ofstreamnewH(part[HEADER].c_str())
assure(newH,part[HEADER].c_str())
newH<<"//@//\n"//Changemarker
<<newheader.str()
}
}
if(!existcpp){//Createcppfile
ofstreamnewcpp(part[IMPLEMENT].c_str())
assure(newcpp,part[IMPLEMENT].c_str())
newcpp<<part[CPPLINE1]<<endl
<<part[INCLUDE]<<endl
}else{//Alreadyexistsverifyit
stringstreamcppfile
ostringstreamnewcpp
cppfile<<existcpp.rdbuf()
//Checkthatfirsttwolinesconform:
boolchanged=false
strings
cppfile.seekg(0)
getline(cppfile,s)
boollineUsed=false
for(intline=CPPLINE1
cppfile.good()&&line<=INCLUDE++line){
if(startsWith(s,part[line])){
newcpp<<s<<endl
lineUsed=true
if(getline(cppfile,s))
lineUsed=false
}else{
newcpp<<part[line]<<endl
changed=true
lineUsed=false
}
}
//Copyrestoffile
if(!lineUsed)
newcpp<<s<<endl
newcpp<<cppfile.rdbuf()
//Iftherewerechanges,overwritefile:
if(changed){
existcpp.close()
ofstreamnewCPP(part[IMPLEMENT].c_str())
assure(newCPP,part[IMPLEMENT].c_str())
newCPP<<"//@//\n"//Changemarker
<<newcpp.str()
}
}
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
120/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(intargc,char*argv[]){
if(argc>1)
cppCheck(argv[1])
else
cppCheck("cppCheckTest.h")
}///:~
FirstnoticetheusefulfunctionstartsWith(),whichdoesjustwhatitsnamesaysitreturnstrueifthe
firststringargumentstartswiththesecondargument.Thisisusedwhenlookingfortheexpected
commentsandincluderelatedstatements.Havingthearrayofstrings,part,allowsforeasylooping
throughtheseriesofexpectedstatementsinsourcecode.Ifthesourcefiledoesntexist,wemerelywrite
thestatementstoanewfileofthegivenname.Ifthefiledoesexist,wesearchalineatatime,verifying
thattheexpectedlinesoccur.Iftheyarenotpresent,theyareinserted.Specialcaremustbetakento
makesurewedontdropexistinglines(seewhereweusetheBooleanvariablelineUsed).Noticethatwe
useastringstreamforanexistingfile,sowecanfirstwritethecontentsofthefiletoitandthenread
fromandsearchit.
ThenamesintheenumerationareBASE,thecapitalizedbasefilenamewithoutextensionHEADER,the
headerfilenameIMPLEMENT,theimplementationfile(cpp)nameHLINE1,theskeletonfirstlineof
theheaderfileGUARD1,GUARD2,andGUARD3,theguardlinesintheheaderfile(topreventmultiple
inclusion)CPPLINE1,theskeletonfirstlineofthecppfileandINCLUDE,thelineinthecppfilethat
includestheheaderfile.
Ifyourunthisprogramwithoutanyarguments,thefollowingtwofilesarecreated:
//CPPCHECKTEST.h
#ifndefCPPCHECKTEST_H
#defineCPPCHECKTEST_H
#endif//CPPCHECKTEST_H
//CPPCHECKTEST.cpp
#include"CPPCHECKTEST.h"
(Weremovedthecolonafterthedoubleslashinthefirstcommentlinessoasnottoconfusethebooks
codeextractor.ItwillappearintheactualoutputproducedbycppCheck.)
Youcanexperimentbyremovingselectedlinesfromthesefilesandrerunningtheprogram.Eachtimeyou
willseethatthecorrectlinesareaddedbackin.Whenafileismodified,thestring//@//isplacedas
thefirstlineofthefiletobringthechangetoyourattention.Youwillneedtoremovethislinebeforeyou
processthefileagain(otherwisecppcheckwillassumetheinitialcommentlineismissing).
Detectingcompilererrors
Allthecodeinthisbookisdesignedtocompileasshownwithouterrors.Linesofcodethatshouldgenerate
acompiletimeerrormaybecommentedoutwiththespecialcommentsequence//!.Thefollowing
programwillremovethesespecialcommentsandappendanumberedcommenttotheline.Whenyourun
yourcompiler,itshouldgenerateerrormessages,andyouwillseeallthenumbersappearwhenyou
compileallthefiles.Thisprogramalsoappendsthemodifiedlinetoaspecialfilesothatyoucaneasily
locateanylinesthatdontgenerateerrors.
//:C04:Showerr.cpp{RunByHand}
//Uncommenterrorgenerators.
#include<cstddef>
#include<cstdlib>
#include<cstdio>
#include<fstream>
#include<iostream>
#include<sstream>
#include<string>
#include"../require.h"
usingnamespacestd
conststringUSAGE=
"usage:showerrfilenamechapnum\n"
"wherefilenameisaC++sourcefile\n"
"andchapnumisthechapternameit'sin.\n"
"Findslinescommentedwith//!andremoves\n"
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
121/458
5/3/2015
ThinkinginC++2ndedVolume2
"thecomment,appending//(#)where#isunique\n"
"acrossallfiles,soyoucandetermine\n"
"ifyourcompilerfindstheerror.\n"
"showerr/r\n"
"resetstheuniquecounter."
classShowerr{
constintCHAP
conststringMARKER,FNAME
//Filecontainingerrornumbercounter:
conststringERRNUM
//Filecontainingerrorlines:
conststringERRFILE
stringstreamedited//Editedfile
intcounter
public:
Showerr(conststring&f,conststring&en,
conststring&ef,intc)
:CHAP(c),MARKER("//!"),FNAME(f),ERRNUM(en),
ERRFILE(ef),counter(0){}
voidreplaceErrors(){
ifstreaminfile(FNAME.c_str())
assure(infile,FNAME.c_str())
ifstreamcount(ERRNUM.c_str())
if(count)count>>counter
intlinecount=1
stringbuf
ofstreamerrlines(ERRFILE.c_str(),ios::app)
assure(errlines,ERRFILE.c_str())
while(getline(infile,buf)){
//Findmarkeratstartofline:
size_tpos=buf.find(MARKER)
if(pos!=string::npos){
//Erasemarker:
buf.erase(pos,MARKER.size()+1)
//Appendcounter&errorinfo:
ostringstreamout
out<<buf<<"//("<<++counter<<")"
<<"Chapter"<<CHAP
<<"File:"<<FNAME
<<"Line"<<linecount<<endl
edited<<out.str()
errlines<<out.str()//Appenderrorfile
}
else
edited<<buf<<"\n"//Justcopy
++linecount
}
}
voidsaveFiles(){
ofstreamoutfile(FNAME.c_str())//Overwrites
assure(outfile,FNAME.c_str())
outfile<<edited.rdbuf()
ofstreamcount(ERRNUM.c_str())//Overwrites
assure(count,ERRNUM.c_str())
count<<counter//Savenewcounter
}
}
intmain(intargc,char*argv[]){
conststringERRCOUNT("../errnum.txt"),
ERRFILE("../errlines.txt")
requireMinArgs(argc,1,USAGE.c_str())
if(argv[1][0]=='/'||argv[1][0]==''){
//Allowforotherswitches:
switch(argv[1][1]){
case'r':case'R':
cout<<"resetcounter"<<endl
remove(ERRCOUNT.c_str())//Deletefiles
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
122/458
5/3/2015
ThinkinginC++2ndedVolume2
remove(ERRFILE.c_str())
returnEXIT_SUCCESS
default:
cerr<<USAGE<<endl
returnEXIT_FAILURE
}
}
if(argc==3){
Showerrs(argv[1],ERRCOUNT,ERRFILE,atoi(argv[2]))
s.replaceErrors()
s.saveFiles()
}
}///:~
Youcanreplacethemarkerwithoneofyourchoice.
Eachfileisreadalineatatime,andeachlineissearchedforthemarkerappearingattheheadofthe
linethelineismodifiedandputintotheerrorlinelistandintothestringstream,edited.Whenthewhole
fileisprocessed,itisclosed(byreachingtheendofascope),itisreopenedasanoutputfile,andedited
ispouredintothefile.Alsonoticethecounterissavedinanexternalfile.Thenexttimethisprogramis
invoked,itcontinuestoincrementthecounter.
Asimpledatalogger
Thisexampleshowsanapproachyoumighttaketologdatatodiskandlaterretrieveitforprocessing.It
ismeanttoproduceatemperaturedepthprofileoftheoceanatvariouspoints.TheDataPointclassholds
thedata:
//:C04:DataLogger.h
//Dataloggerrecordlayout.
#ifndefDATALOG_H
#defineDATALOG_H
#include<ctime>
#include<iosfwd>
#include<string>
usingstd::ostream
structCoord{
intdeg,min,sec
Coord(intd=0,intm=0,ints=0)
:deg(d),min(m),sec(s){}
std::stringtoString()const
}
ostream&operator<<(ostream&,constCoord&)
classDataPoint{
std::time_ttimestamp//Time&day
Coordlatitude,longitude
doubledepth,temperature
public:
DataPoint(std::time_tts,constCoord&lat,
constCoord&lon,doubledep,doubletemp)
:timestamp(ts),latitude(lat),longitude(lon),
depth(dep),temperature(temp){}
DataPoint():timestamp(0),depth(0),temperature(0){}
friendostream&operator<<(ostream&,constDataPoint&)
}
#endif//DATALOG_H///:~
ADataPointconsistsofatimestamp,whichisstoredasatime_tvalueasdefinedin<ctime>,
longitudeandlatitudecoordinates,andvaluesfordepthandtemperature.Weuseinsertersforeasy
formatting.Herestheimplementationfile:
//:C04:DataLogger.cpp{O}
//Datapointimplementations.
#include"DataLogger.h"
#include<iomanip>
#include<iostream>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
123/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<sstream>
#include<string>
usingnamespacestd
ostream&operator<<(ostream&os,constCoord&c){
returnos<<c.deg<<'*'<<c.min<<'\''
<<c.sec<<'"'
}
stringCoord::toString()const{
ostringstreamos
os<<*this
returnos.str()
}
ostream&operator<<(ostream&os,constDataPoint&d){
os.setf(ios::fixed,ios::floatfield)
charfillc=os.fill('0')//Padonleftwith'0'
tm*tdata=localtime(&d.timestamp)
os<<setw(2)<<tdata>tm_mon+1<<'\\'
<<setw(2)<<tdata>tm_mday<<'\\'
<<setw(2)<<tdata>tm_year+1900<<''
<<setw(2)<<tdata>tm_hour<<':'
<<setw(2)<<tdata>tm_min<<':'
<<setw(2)<<tdata>tm_sec
os.fill('')//Padonleftwith''
streamsizeprec=os.precision(4)
os<<"Lat:"<<setw(9)<<d.latitude.toString()
<<",Long:"<<setw(9)<<d.longitude.toString()
<<",depth:"<<setw(9)<<d.depth
<<",temp:"<<setw(9)<<d.temperature
os.fill(fillc)
os.precision(prec)
returnos
}///:~
TheCoord::toString()functionisnecessarybecausetheDataPointinsertercallssetw()beforeit
printsthelatitudeandlongitude.IfweusedthestreaminserterforCoordinstead,thewidthwouldonly
applytothefirstinsertion(thatis,toCoord::deg),sincewidthchangesarealwaysresetimmediately.The
calltosetf()causesthefloatingpointoutputtobefixedprecision,andprecision()setsthenumberof
decimalplacestofour.Noticehowwerestorethefillcharacterandprecisiontowhatevertheywerebefore
theinserterwascalled.
TogetthevaluesfromthetimeencodingstoredinDataPoint::timestamp,wecallthefunction
std::localtime(),whichreturnsastaticpointertoatmobject.Thetmstructhasthefollowinglayout:
structtm{
inttm_sec//059seconds
inttm_min//059minutes
inttm_hour//023hours
inttm_mday//Dayofmonth
inttm_mon//011months
inttm_year//Yearssince1900
inttm_wday//Sunday==0,etc.
inttm_yday//0365dayofyear
inttm_isdst//Daylightsavings?
}
Generatingtestdata
Heresaprogramthatcreatesafileoftestdatainbinaryform(usingwrite())andasecondfileinASCII
formusingtheDataPointinserter.Youcanalsoprintitouttothescreen,butitseasiertoinspectinfile
form.
//:C04:Datagen.cpp
//Testdatagenerator.
//{L}DataLogger
#include<cstdlib>
#include<ctime>
#include<cstring>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
124/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<fstream>
#include"DataLogger.h"
#include"../require.h"
usingnamespacestd
intmain(){
time_ttimer
srand(time(&timer))//Seedtherandomnumbergenerator
ofstreamdata("data.txt")
assure(data,"data.txt")
ofstreambindata("data.bin",ios::binary)
assure(bindata,"data.bin")
for(inti=0i<100i++,timer+=55){
//Zeroto199meters:
doublenewdepth=rand()%200
doublefraction=rand()%100+1
newdepth+=1.0/fraction
doublenewtemp=150+rand()%200//Kelvin
fraction=rand()%100+1
newtemp+=1.0/fraction
constDataPointd(timer,Coord(45,20,31),
Coord(22,34,18),newdepth,
newtemp)
data<<d<<endl
bindata.write(reinterpret_cast<constchar*>(&d),
sizeof(d))
}
}///:~
Thefiledata.txtiscreatedintheordinarywayasanASCIIfile,butdata.binhastheflagios::binaryto
telltheconstructortosetitupasabinaryfile.Toillustratetheformattingusedforthetextfile,hereisthe
firstlineofdata.txt(thelinewrapsbecauseitslongerthanthispagewillallow):
07\28\200312:54:40Lat:45*20'31",Long:22*34'18",depth:16.0164,temp:242.0122
TheStandardClibraryfunctiontime()updatesthetime_tvalueitsargumentpointstowithanencoding
ofthecurrenttime,whichonmostplatformsisthenumberofsecondselapsedsince00:00:00GMT,
January11970(thedawningoftheageofAquarius?).Thecurrenttimeisalsoaconvenientwaytoseed
therandomnumbergeneratorwiththeStandardClibraryfunctionsrand(),asisdonehere.
Afterthis,thetimerisincrementedby55secondstogiveaninterestingintervalbetweenreadingsinthis
simulation.
Thelatitudeandlongitudeusedarefixedvaluestoindicateasetofreadingsatasinglelocation.Boththe
depthandthetemperaturearegeneratedwiththeStandardClibraryrand()function,whichreturnsa
pseudorandomnumberbetweenzeroandaplatformdependentconstant,RAND_MAX,definedin
<cstdlib>(usuallythevalueoftheplatformslargestunsignedinteger).Toputthisinadesiredrange,
usetheremainderoperator%andtheupperendoftherange.Thesenumbersareintegraltoadda
fractionalpart,asecondcalltorand()ismade,andthevalueisinvertedafteraddingone(toprevent
dividebyzeroerrors).
Ineffect,thedata.binfileisbeingusedasacontainerforthedataintheprogram,eventhoughthe
containerexistsondiskandnotinRAM.write()sendsthedataouttothediskinbinaryform.Thefirst
argumentisthestartingaddressofthesourceblocknoticeitmustbecasttoachar*becausethatswhat
write()expectsfornarrowstreams.Thesecondargumentisthenumberofcharacterstowrite,whichin
thiscaseisthesizeoftheDataPointobject(again,becausewereusingnarrowstreams).Becauseno
pointersarecontainedinDataPoint,thereisnoprobleminwritingtheobjecttodisk.Iftheobjectismore
sophisticated,youmustimplementaschemeforserialization,whichwritesthedatareferredtobypointers
anddefinesnewpointerswhenreadbackinlater.(Wedonttalkaboutserializationinthisvolumemost
vendorclasslibrarieshavesomesortofserializationstructurebuiltintothem.)
Verifyingandviewingthedata
Tocheckthevalidityofthedatastoredinbinaryformat,youcanreaditintomemorywiththeread()
memberfunctionforinputstreams,andcompareittothetextfilecreatedearlierbyDatagen.cpp.The
followingexamplejustwritestheformattedresultstocout,butyoucanredirectthistoafileandthenuse
afilecomparisonutilitytoverifythatitisidenticaltotheoriginal:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
125/458
5/3/2015
ThinkinginC++2ndedVolume2
//:C04:Datascan.cpp
//{L}DataLogger
#include<fstream>
#include<iostream>
#include"DataLogger.h"
#include"../require.h"
usingnamespacestd
intmain(){
ifstreambindata("data.bin",ios::binary)
assure(bindata,"data.bin")
DataPointd
while(bindata.read(reinterpret_cast<char*>(&d),
sizeofd))
cout<<d<<endl
}///:~
Internationalization
Thesoftwareindustryisnowahealthy,worldwideeconomicmarket,withdemandforapplicationsthatcan
runinvariouslanguagesandcultures.Asearlyasthelate1980s,theCStandardsCommitteeadded
supportfornonU.S.formattingconventionswiththeirlocalemechanism.Alocaleisasetofpreferences
fordisplayingcertainentitiessuchasdatesandmonetaryquantities.Inthe1990s,theCStandards
CommitteeapprovedanaddendumtoStandardCthatspecifiedfunctionstohandlewide
characters(denotedbythetypewchar_t),whichallowsupportforcharactersetsotherthanASCIIandits
commonlyusedWesternEuropeanextensions.Althoughthesizeofawidecharacterisnotspecified,some
platformsimplementthemas32bitquantities,sotheycanholdtheencodingsspecifiedbythe
UnicodeConsortium,aswellasmappingstomultibytecharacterssetsdefinedbyAsianstandardsbodies.
C++hasintegratedsupportforbothwidecharactersandlocalesintotheiostreamslibrary.
WideStreams
Awidestreamisastreamclassthathandleswidecharacters.Alltheexamplessofar(exceptforthelast
traitsexampleinChapter3)haveusednarrowstreamsthatholdinstancesofchar.Sincestream
operationsareessentiallythesamenomattertheunderlyingcharactertype,theyareencapsulated
genericallyastemplates.Soallinputstreams,forexample,areconnectedtothebasic_istreamclass
template:
template<classcharT,classtraits=char_traits<charT>>
classbasic_istream{...}
Infact,allinputstreamtypesarespecializationsofthistemplate,accordingtothefollowingtype
definitions:
typedefbasic_istream<char>istream
typedefbasic_istream<wchar_t>wistream
typedefbasic_ifstream<char>ifstream
typedefbasic_ifstream<wchar_t>wifstream
typedefbasic_istringstream<char>istringstream
typedefbasic_istringstream<wchar_t>wistringstream
Allotherstreamtypesaredefinedinsimilarfashion.
Inaperfectworld,thisisallyoudneedtocreatestreamsofdifferentcharactertypes.Butthingsarent
thatsimple.Thereasonisthatthecharacterprocessingfunctionsprovidedforcharandwchar_tdont
havethesamenames.Tocomparetwonarrowstrings,forexample,youusethestrcmp()function.For
widecharacters,thatfunctionisnamedwcscmp().(RemembertheseoriginatedinC,whichdoesnot
havefunctionoverloading,henceuniquenamesarerequired.)Forthisreason,agenericstreamcantjust
callstrcmp()inresponsetoacomparisonoperator.Thereneedstobeawayforthecorrectlowlevel
functionstobecalledautomatically.
Thesolutionistofactoroutthedifferencesintoanewabstraction.Theoperationsyoucanperformon
charactershavebeenabstractedintothechar_traitstemplate,whichhaspredefinedspecializationsfor
charandwchar_t,aswediscussedattheendofthepreviouschapter.Tocomparetwostrings,then,
basic_stringjustcallstraits::compare()(rememberthattraitsisthesecondtemplateparameter),
whichinturncallseitherstrcmp()orwcscmp(),dependingonwhichspecializationisbeingused
(transparenttobasic_string).
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
126/458
5/3/2015
ThinkinginC++2ndedVolume2
Youonlyneedtobeconcernedaboutchar_traitsifyouaccessthelowlevelcharacterprocessing
functionsmostofthetimeyoudontcare.Consider,however,makingyourinsertersandextractorsmore
robustbydefiningthemastemplates,justincasesomeonewantstousethemonawidestream.
Toillustrate,recallagaintheDateclassinserterfromthebeginningofthischapter.Weoriginallydeclared
itas:
ostream&operator<<(ostream&,constDate&)
Thisaccommodatesonlynarrowstreams.Tomakeitgeneric,wesimplymakeitatemplatebasedon
basic_ostream:
template<classcharT,classtraits>
std::basic_ostream<charT,traits>&
operator<<(std::basic_ostream<charT,traits>&os,
constDate&d){
charTfillc=os.fill(os.widen('0'))
charTdash=os.widen('')
os<<setw(2)<<d.month<<dash
<<setw(2)<<d.day<<dash
<<setw(4)<<d.year
os.fill(fillc)
returnos
}
NoticethatwealsohavetoreplacecharwiththetemplateparametercharTinthedeclarationoffillc,
sinceitcouldbeeithercharorwchar_t,dependingonthetemplateinstantiationbeingused.
Sinceyoudontknowwhenyourewritingthetemplatewhichtypeofstreamyouhave,youneedawayto
automaticallyconvertcharacterliteralstothecorrectsizeforthestream.Thisisthejobofthe
widen()memberfunction.Theexpressionwiden(''),forexample,convertsitsargumenttoL(the
literalsyntaxequivalenttotheconversionwchar_t())ifthestreamisawidestreamandleavesitalone
otherwise.Thereisalsoanarrow()functionthatconvertstoacharifneeded.
Wecanusewiden()towriteagenericversionofthenlmanipulatorwepresentedearlierinthechapter.
template<classcharT,classtraits>
basic_ostream<charT,traits>&
nl(basic_ostream<charT,traits>&os){
returnos<<charT(os.widen('\n'))
}
Locales
Perhapsthemostnotabledifferenceintypicalnumericcomputeroutputfromcountrytocountryisthe
punctuatorusedtoseparatetheintegerandfractionalpartsofarealnumber.IntheUnitedStates,a
perioddenotesadecimalpoint,butinmuchoftheworld,acommaisexpectedinstead.Itwouldbequite
inconvenienttodoallyourownformattingforlocaledependentdisplays.Onceagain,creatingan
abstractionthathandlesthesedifferencessolvestheproblem.
Thatabstractionisthelocale.Allstreamshaveanassociatedlocaleobjectthattheyuseforguidanceon
howtodisplaycertainquantitiesfordifferentculturalenvironments.Alocalemanagesthecategoriesof
culturedependentdisplayrules,whicharedefinedasfollows:
Category
Effect
collate
Allowscomparingstringsaccordingtodifferent,supported
collatingsequences.
ctype
Abstractsthecharacterclassificationandconversion
facilitiesfoundin<cctype>.
monetary
Supportsdifferentdisplaysofmonetaryquantities.
numeric
Supportsdifferentdisplayformatsofrealnumbers,
includingradix(decimalpoint)andgrouping(thousands)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
127/458
5/3/2015
ThinkinginC++2ndedVolume2
separators.
time
Supportsvariousinternationalformatsfordisplayofdate
andtime.
messages
Scaffoldingtoimplementcontextdependentmessage
catalogs(suchasforerrormessagesindifferent
languages).
Thefollowingprogramillustratesbasiclocalebehavior:
//:C04:Locale.cpp{g++}{bor}{edg}{RunByHand}
//Illustrateseffectsoflocales.
#include<iostream>
#include<locale>
usingnamespacestd
intmain(){
localedef
cout<<def.name()<<endl
localecurrent=cout.getloc()
cout<<current.name()<<endl
floatval=1234.56
cout<<val<<endl
//ChangetoFrench/France
cout.imbue(locale("french"))
current=cout.getloc()
cout<<current.name()<<endl
cout<<val<<endl
cout<<"Entertheliteral7890,12:"
cin.imbue(cout.getloc())
cin>>val
cout<<val<<endl
cout.imbue(def)
cout<<val<<endl
}///:~
Herestheoutput:
C
C
1234.56
French_France.1252
1234,56
Entertheliteral7890,12:7890,12
7890,12
7890.12
ThedefaultlocaleistheClocale,whichiswhatCandC++programmershavebeenusedtoallthese
years(basically,EnglishlanguageandAmericanculture).AllstreamsareinitiallyimbuedwiththeC
locale.Theimbue()memberfunctionchangesthelocalethatastreamuses.NoticethatthefullISO
namefortheFrenchlocaleisdisplayed(thatis,FrenchusedinFrancevs.Frenchusedinanother
country).Thisexampleshowsthatthislocaleusesacommaforaradixpointinnumericdisplay.Wehave
tochangecintothesamelocaleifwewanttodoinputaccordingtotherulesofthislocale.
Eachlocalecategoryisdividedintonumberoffacets,whichareclassesencapsulatingthefunctionalitythat
pertainstothatcategory.Forexample,thetimecategoryhasthefacetstime_putandtime_get,which
containfunctionsfordoingtimeanddateinputandoutputrespectively.Themonetarycategoryhas
facetsmoney_get,money_put,andmoneypunct.(Thelatterfacetdeterminesthecurrencysymbol.)
Thefollowingprogramillustratesthemoneypunctfacet.(Thetimefacetrequiresasophisticateduseof
iteratorswhichisbeyondthescopeofthischapter.)
//:C04:Facets.cpp{bor}{g++}{mwcc}{edg}
#include<iostream>
#include<locale>
#include<string>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
128/458
5/3/2015
ThinkinginC++2ndedVolume2
usingnamespacestd
intmain(){
//ChangetoFrench/France
localeloc("french")
cout.imbue(loc)
stringcurrency=
use_facet<moneypunct<char>>(loc).curr_symbol()
charpoint=
use_facet<moneypunct<char>>(loc).decimal_point()
cout<<"Imade"<<currency<<12.34<<"today!"
<<endl
}///:~
TheoutputshowstheFrenchcurrencysymbolanddecimalseparator:
Imade12,34today!
Youcanalsodefineyourownfacetstoconstructcustomizedlocales.[49]Beawarethattheoverheadfor
localesisconsiderable.Infact,somelibraryvendorsprovidedifferentflavorsoftheStandardC++
librarytoaccommodateenvironmentsthathavelimitedspace.[50]
Summary
Thischapterhasgivenyouafairlythoroughintroductiontotheiostreamclasslibrary.Whatyouveseen
hereislikelytobeallyouneedtocreateprogramsusingiostreams.However,beawarethatsome
additionalfeaturesiniostreamsarenotusedoften,butyoucandiscoverthembylookingattheiostream
headerfilesandbyreadingyourcompilersdocumentationoniostreamsorthereferencesmentionedin
thischapterandintheappendices.
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.Openafilebycreatinganifstreamobject.Makeanostringstreamobjectandreadtheentire
contentsintotheostringstreamusingtherdbuf()memberfunction.Extractastringcopyof
theunderlyingbufferandcapitalizeeverycharacterinthefileusingtheStandardCtoupper()
macrodefinedin<cctype>.Writetheresultouttoanewfile.
2.Createaprogramthatopensafile(thefirstargumentonthecommandline)andsearchesitfor
anyoneofasetofwords(theremainingargumentsonthecommandline).Readtheinputaline
atatime,andwriteoutthelines(withlinenumbers)thatmatchtothenewfile.
3.Writeaprogramthataddsacopyrightnoticetothebeginningofallsourcecodefilesindicatedby
theprogramscommandlinearguments.
4.Useyourfavoritetextsearchingprogram(grep,forexample)tooutputthenames(only)ofall
thefilesthatcontainaparticularpattern.Redirecttheoutputintoafile.Writeaprogramthat
usesthecontentsofthatfiletogenerateabatchfilethatinvokesyoureditoroneachofthefiles
foundbythesearchprogram.
5.Weknowthatsetw()allowsforaminimumofcharactersreadin,butwhatifyouwantedto
readamaximum?Writeaneffectorthatallowstheusertospecifyamaximumnumberof
characterstoextract.Haveyoureffectoralsoworkforoutput,insuchawaythatoutputfields
aretruncated,ifnecessary,tostaywithinwidthlimits.
6.Demonstratetoyourselfthatifthefailorbadbitisset,andyousubsequentlyturnonstream
exceptions,thatthestreamwillimmediatelythrowanexception.
7.Stringstreamsaccommodateeasyconversions,buttheycomewithaprice.Writeaprogramthat
racesatoi()againstthestringstreamconversionsystemtoseetheeffectoftheoverhead
involvedwithstringstream.
8.MakeaPersonstructwithfieldssuchasname,age,address,etc.Makethestringfieldsfixed
sizearrays.Thesocialsecuritynumberwillbethekeyforeachrecord.Implementthefollowing
Databaseclass:
classDataBase{
public:
//Findwherearecordisondisk
size_tquery(size_tssn)
//Returnthepersonatrn(recordnumber)
Personretrieve(size_trn)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
129/458
5/3/2015
ThinkinginC++2ndedVolume2
//Recordarecordondisk
voidadd(constPerson&p)
}
WritesomePersonrecordstodisk(donotkeepthemallinmemory).Whentheuserrequestsa
record,readitoffthediskandreturnit.TheI/OoperationsintheDataBaseclassuseread()
andwrite()toprocessallPersonrecords.
9.Writeanoperator<<inserterforthePersonstructthatcanbeusedtodisplayrecordsina
formatforeasyreading.Demonstrateitbywritingdataouttoafile.
10.SupposethedatabaseforyourPersonstructswaslostbutthatyouhavethefileyouwrotefrom
thepreviousexercise.Recreateyourdatabaseusingthisfile.Besuretouseerrorchecking.
11.Writesize_t(1)(thelargestunsignedintonyourplatform)toatextfile1,000,000times.
Repeat,butwritetoabinaryfile.Comparethesizeofthetwofiles,andseehowmuchroomis
savedusingthebinaryformat.(Youmayfirstwanttocalculatehowmuchwillbesavedonyour
platform.)
12.Discoverthemaximumnumberofdigitsofprecisionyourimplementationofiostreamswillprint
byrepeatedlyincreasingthevalueoftheargumenttoprecision()whenprintinga
transcendentalnumbersuchassqrt(2.0).
13.Writeaprogramthatreadsrealnumbersfromafileandprintstheirsum,average,minimum,
andmaximum.
14.Determinetheoutputofthefollowingprogrambeforeitisexecuted:
//:C04:Exercise14.cpp
#include<fstream>
#include<iostream>
#include<sstream>
#include"../require.h"
usingnamespacestd
#defined(a)cout<<#a"==\t"<<a<<endl
voidtellPointers(fstream&s){
d(s.tellp())
d(s.tellg())
cout<<endl
}
voidtellPointers(stringstream&s){
d(s.tellp())
d(s.tellg())
cout<<endl
}
intmain(){
fstreamin("Exercise14.cpp")
assure(in,"Exercise14.cpp")
in.seekg(10)
tellPointers(in)
in.seekp(20)
tellPointers(in)
stringstreammemStream("Hereisasentence.")
memStream.seekg(10)
tellPointers(memStream)
memStream.seekp(5)
tellPointers(memStream)
}///:~
15.Supposeyouaregivenlineorienteddatainafileformattedasfollows:
//:C04:Exercise15.txt
Australia
5E56,7667230284,Langler,Tyson,31.2147,0.00042117361
2B97,7586701,Oneill,Zeke,553.429,0.0074673053156065
4D75,7907252710,Nickerson,Kelly,761.612,0.010276276
9F2,6882945012,Hartenbach,Neil,47.9637,0.0006471644
Austria
480F,7187262472,Oneill,Dee,264.012,0.00356226040013
1B65,4754732628,Haney,Kim,7.33843,0.000099015948475
DA1,1954960784,Pascente,Lester,56.5452,0.0007629529
3F18,1839715659,Elsea,Chelsy,801.901,0.010819887645
Belgium
BDF,5993489554,Oneill,Meredith,283.404,0.0038239127
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
130/458
5/3/2015
ThinkinginC++2ndedVolume2
5AC6,6612945602,Parisienne,Biff,557.74,0.0075254727
6AD,6477082,Pennington,Lizanne,31.0807,0.0004193544
4D0E,7861652688,Sisca,Francis,704.751,0.00950906238
Bahamas
37D8,6837424208,Parisienne,Samson,396.104,0.0053445
5E98,6384069,Willis,Pam,90.4257,0.00122009564059246
1462,1288616408,Stover,Hazal,583.939,0.007878970561
5FF3,8028775718,Stromstedt,Bunk,39.8712,0.000537974
1095,3737212,Stover,Denny,3.05387,0.000041205248883
7428,2019381883,Parisienne,Shane,363.272,0.00490155
///:~
Theheadingofeachsectionisaregion,andeverylineunderthatheadingisasellerinthatregion.
Eachcommaseparatedfieldrepresentsthedataabouteachseller.Thefirstfieldinalineisthe
SELLER_IDwhichunfortunatelywaswrittenoutinhexadecimalformat.Thesecondisthe
PHONE_NUMBER(noticethatsomearemissingareacodes).LAST_NAMEandFIRST_NAMEthen
follow.TOTAL_SALESisthesecondtothelastcolumn.Thelastcolumnisthedecimalamountofthe
totalsalesthatthesellerrepresentsforthecompany.Youaretoformatthedataontheterminal
windowsothatanexecutivecaneasilyinterpretthetrends.Sampleoutputisgivenbelow.
Australia
*LastName**FirstName**ID**Phone**Sales**Percent*
LanglerTyson24150766723028431.244.21E02
OneillZeke11159XXX7586701553.437.47E01
(etc.)
5:TemplatesinDepth
TheC++templatefacilitygoesfarbeyondsimplecontainersofT.Althoughthe
originalmotivationwastoenabletypesafe,genericcontainers,inmodernC++,
templatesarealsousedtogeneratecustomcodeandtooptimizeprogram
executionthroughcompiletimeprogrammingconstructs.
Inthischapterweofferapracticallookatthepower(andpitfalls)ofprogrammingwithtemplatesin
modernC++.Foramorecompleteanalysisoftemplaterelatedlanguageissuesandpitfalls,we
recommendthesuperbbookbyDaveedVandevoordeandNicoJosuttis.[51]
Templateparameters
AsillustratedinVolume1,templatescomeintwoflavors:functiontemplatesandclasstemplates.Bothare
whollycharacterizedbytheirparameters.Eachtemplateparametercanrepresentoneofthefollowing:
1.Types(eitherbuiltinoruserdefined).
2.Compiletimeconstantvalues(forexample,integers,andpointersandreferencestostaticentities
oftenreferredtoasnontypeparameters).
3.Othertemplates.
TheexamplesinVolume1allfallintothefirstcategoryandarethemostcommon.Thecanonicalexample
forsimplecontainerliketemplatesnowadaysseemstobeaStackclass.Beingacontainer,aStack
objectisnotconcernedwiththetypeofobjectitstoresthelogicofholdingobjectsisindependentofthe
typeofobjectsbeingheld.Forthisreasonyoucanuseatypeparametertorepresentthecontainedtype:
template<classT>classStack{
T*data
size_tcount
public:
voidpush(constT&t)
//Etc.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
131/458
5/3/2015
ThinkinginC++2ndedVolume2
TheactualtypetobeusedforaparticularStackinstanceisdeterminedbytheargumentforthe
parameterT:
Stack<int>myStack//AStackofints
ThecompilerthenprovidesanintversionofStackbysubstitutingintforTandgeneratingthe
correspondingcode.Thenameoftheclassinstancegeneratedfromthetemplateinthiscaseis
Stack<int>.
Nontypetemplateparameters
Anontypetemplateparametermustbeanintegralvaluethatisknownatcompiletime.Youcanmakea
fixedsizeStack,forinstance,byspecifyinganontypeparametertobeusedasthedimensionforthe
underlyingarray,asfollows.
template<classT,size_tN>classStack{
Tdata[N]//FixedcapacityisN
size_tcount
public:
voidpush(constT&t)
//Etc.
}
YoumustprovideacompiletimeconstantvaluefortheparameterNwhenyourequestaninstanceofthis
template,suchas
Stack<int,100>myFixedStack
BecausethevalueofNisknownatcompiletime,theunderlyingarray(data)canbeplacedonthe
runtimestackinsteadofonthefreestore.Thiscanimproveruntimeperformancebyavoidingthe
overheadassociatedwithdynamicmemoryallocation.Followingthepatternmentionedearlier,thename
oftheclassaboveisStack<int,100>.ThismeansthateachdistinctvalueofNresultsinauniqueclass
type.Forexample,Stack<int,99>isadistinctclassfromStack<int,100>.
Thebitsetclasstemplate,discussedindetailinChapter7,istheonlyclassintheStandardC++library
thatusesanontypetemplateparameter(whichspecifiesthenumberofbitsthebitsetobjectcanhold).
Thefollowingrandomnumbergeneratorexampleusesabitsettotracknumberssoallthenumbersinits
rangearereturnedinrandomorderwithoutrepetitionbeforestartingover.Thisexamplealsooverloads
operator()toproduceafamiliarfunctioncallsyntax.
//:C05:Urand.h{bor}
//Uniquerandomizer.
#ifndefURAND_H
#defineURAND_H
#include<bitset>
#include<cstddef>
#include<cstdlib>
#include<ctime>
usingstd::size_t
usingstd::bitset
template<size_tUpperBound>classUrand{
bitset<UpperBound>used
public:
Urand(){srand(time(0))}//Randomize
size_toperator()()//The"generator"function
}
template<size_tUpperBound>
inlinesize_tUrand<UpperBound>::operator()(){
if(used.count()==UpperBound)
used.reset()//Startover(clearbitset)
size_tnewval
while(used[newval=rand()%UpperBound])
//Untiluniquevalueisfound
used[newval]=true
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
132/458
5/3/2015
ThinkinginC++2ndedVolume2
returnnewval
}
#endif//URAND_H///:~
ThenumbersgeneratedbyUrandareuniquebecausethebitsetusedtracksallthepossiblenumbersin
therandomspace(theupperboundissetwiththetemplateargument)andrecordseachusednumberby
settingthecorrespondingpositionbit.Whenthenumbersareallusedup,thebitsetisclearedtostart
over.HeresasimpledriverthatillustrateshowtouseaUrandobject:
//:C05:UrandTest.cpp{bor}
#include<iostream>
#include"Urand.h"
usingnamespacestd
intmain(){
Urand<10>u
for(inti=0i<20++i)
cout<<u()<<''
}///:~
Asweexplainlaterinthischapter,nontypetemplateargumentsarealsoimportantintheoptimizationof
numericcomputations.
Defaulttemplatearguments
Youcanprovidedefaultargumentsfortemplateparametersinclasstemplates,butnotfunctiontemplates.
Aswithdefaultfunctionarguments,theyshouldonlybedefinedonce,thefirsttimeatemplatedeclaration
ordefinitionisseenbythecompiler.Onceyouintroduceadefaultargument,allthesubsequenttemplate
parametersmustalsohavedefaults.TomakethefixedsizeStacktemplateshownearlieralittle
friendlier,forexample,youcanaddadefaultargumentlikethis:
template<classT,size_tN=100>classStack{
Tdata[N]//FixedcapacityisN
size_tcount
public:
voidpush(constT&t)
//Etc.
}
Now,ifyouomitthesecondtemplateargumentwhendeclaringaStackobject,thevalueforNwilldefault
to100.
Youcanchoosetoprovidedefaultsforallarguments,butyoumustuseanemptysetofbracketswhen
declaringaninstancesothatthecompilerknowsthataclasstemplateisinvolved:
template<classT=int,size_tN=100>//Bothdefaulted
classStack{
Tdata[N]//FixedcapacityisN
size_tcount
public:
voidpush(constT&t)
//Etc.
}
Stack<>myStack//SameasStack<int,100>
DefaultargumentsareusedheavilyintheStandardC++library.Thevectorclasstemplate,forinstance,
isdeclaredasfollows:
template<classT,classAllocator=allocator<T>>
classvector
Notethespacebetweenthelasttworightanglebracketcharacters.Thispreventsthecompilerfrom
interpretingthosetwocharacters(>>)astherightshiftoperator.
Thisdeclarationrevealsthatvectortakestwoarguments:thetypeofthecontainedobjectsitholds,anda
typethatrepresentstheallocatorusedbythevector.Wheneveryouomitthesecondargument,the
standardallocatortemplateisused,parameterizedbythefirsttemplateparameter.Thisdeclarationalso
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
133/458
5/3/2015
ThinkinginC++2ndedVolume2
showsthatyoucanusetemplateparametersinsubsequenttemplateparameters,asTisusedhere.
Althoughyoucannotusedefaulttemplateargumentsinfunctiontemplates,youcanusetemplate
parametersasdefaultargumentstonormalfunctions.Thefollowingfunctiontemplateaddstheelementsin
asequence:
//:C05:FuncDef.cpp
#include<iostream>
usingnamespacestd
template<classT>Tsum(T*b,T*e,Tinit=T()){
while(b!=e)
init+=*b++
returninit
}
intmain(){
inta[]={1,2,3}
cout<<sum(a,a+sizeofa/sizeofa[0])<<endl//6
}///:~
Thethirdargumenttosum()istheinitialvaluefortheaccumulationoftheelements.Sinceweomittedit,
thisargumentdefaultstoT(),whichinthecaseofintandotherbuiltintypesinvokesapseudo
constructorthatperformszeroinitialization.
Templatetemplateparameters
Thethirdtypeofparameteratemplatecanacceptisanotherclasstemplate.Ifyouaregoingtousea
templatetypeparameteritselfasatemplateinyourcode,thecompilerneedstoknowthattheparameter
isatemplateinthefirstplace.Thefollowingexampleillustratesatemplatetemplateparameter:
//:C05:TempTemp.cpp
//Illustratesatemplatetemplateparameter.
#include<cstddef>
#include<iostream>
usingnamespacestd
template<classT>
classArray{//Asimple,expandablesequence
enum{INIT=10}
T*data
size_tcapacity
size_tcount
public:
Array(){
count=0
data=newT[capacity=INIT]
}
~Array(){delete[]data}
voidpush_back(constT&t){
if(count==capacity){
//Growunderlyingarray
size_tnewCap=2*capacity
T*newData=newT[newCap]
for(size_ti=0i<count++i)
newData[i]=data[i]
delete[]data
data=newData
capacity=newCap
}
data[count++]=t
}
voidpop_back(){
if(count>0)
count
}
T*begin(){returndata}
T*end(){returndata+count}
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
134/458
5/3/2015
ThinkinginC++2ndedVolume2
template<classT,template<class>classSeq>
classContainer{
Seq<T>seq
public:
voidappend(constT&t){seq.push_back(t)}
T*begin(){returnseq.begin()}
T*end(){returnseq.end()}
}
intmain(){
Container<int,Array>container
container.append(1)
container.append(2)
int*p=container.begin()
while(p!=container.end())
cout<<*p++<<endl
}///:~
TheArrayclasstemplateisatrivialsequencecontainer.TheContainertemplatetakestwoparameters:
thetypethatitholds,andasequencedatastructuretodotheholding.Thefollowinglineinthe
implementationoftheContainerclassrequiresthatweinformthecompilerthatSeqisatemplate:
Seq<T>seq
IfwehadntdeclaredSeqtobeatemplatetemplateparameter,thecompilerwouldcomplainherethat
Seqisnotatemplate,sincewereusingitassuch.Inmain()aContainerisinstantiatedtousean
Arraytoholdintegers,soSeqstandsforArrayinthisexample.
NotethatitisnotnecessaryinthiscasetonametheparameterforSeqinsideContainersdeclaration.
Thelineinquestionis:
template<classT,template<class>classSeq>
Althoughwecouldhavewritten
template<classT,template<classU>classSeq>
theparameterUisnotneededanywhere.AllthatmattersisthatSeqisaclasstemplatethattakesa
singletypeparameter.Thisisanalogoustoomittingthenamesoffunctionparameterswhentheyrenot
needed,suchaswhenyouoverloadthepostincrementoperator:
Toperator++(int)
Theinthereismerelyaplaceholderandsoneedsnoname.
Thefollowingprogramusesafixedsizearray,whichhasanextratemplateparameterrepresentingthe
arraylength:
//:C05:TempTemp2.cpp
//Amultivariatetemplatetemplateparameter.
#include<cstddef>
#include<iostream>
usingnamespacestd
template<classT,size_tN>classArray{
Tdata[N]
size_tcount
public:
Array(){count=0}
voidpush_back(constT&t){
if(count<N)
data[count++]=t
}
voidpop_back(){
if(count>0)
count
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
135/458
5/3/2015
ThinkinginC++2ndedVolume2
T*begin(){returndata}
T*end(){returndata+count}
}
template<classT,size_tN,template<class,size_t>classSeq>
classContainer{
Seq<T,N>seq
public:
voidappend(constT&t){seq.push_back(t)}
T*begin(){returnseq.begin()}
T*end(){returnseq.end()}
}
intmain(){
constsize_tN=10
Container<int,N,Array>container
container.append(1)
container.append(2)
int*p=container.begin()
while(p!=container.end())
cout<<*p++<<endl
}///:~
Onceagain,parameternamesarenotneededinthedeclarationofSeqinsideContainersdeclaration,but
weneedtwoparameterstodeclarethedatamemberseq,hencetheappearanceofthenontype
parameterNatthetoplevel.
Combiningdefaultargumentswithtemplatetemplateparametersisslightlymoreproblematic.Whenthe
compilerlooksattheinnerparametersofatemplatetemplateparameter,defaultargumentsarenot
considered,soyouhavetorepeatthedefaultsinordertogetanexactmatch.Thefollowingexampleuses
adefaultargumentforthefixedsizeArraytemplateandshowshowtoaccommodatethisquirkinthe
language:
//:C05:TempTemp3.cpp{bor}{msc}
//Templatetemplateparametersanddefaultarguments.
#include<cstddef>
#include<iostream>
usingnamespacestd
template<classT,size_tN=10>//Adefaultargument
classArray{
Tdata[N]
size_tcount
public:
Array(){count=0}
voidpush_back(constT&t){
if(count<N)
data[count++]=t
}
voidpop_back(){
if(count>0)
count
}
T*begin(){returndata}
T*end(){returndata+count}
}
template<classT,template<class,size_t=10>classSeq>
classContainer{
Seq<T>seq//Defaultused
public:
voidappend(constT&t){seq.push_back(t)}
T*begin(){returnseq.begin()}
T*end(){returnseq.end()}
}
intmain(){
Container<int,Array>container
container.append(1)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
136/458
5/3/2015
ThinkinginC++2ndedVolume2
container.append(2)
int*p=container.begin()
while(p!=container.end())
cout<<*p++<<endl
}///:~
Thedefaultdimensionof10isrequiredintheline:
template<classT,template<class,size_t=10>classSeq>
BoththedefinitionofseqinContainerandcontainerinmain()usethedefault.Theonlywaytouse
somethingotherthanthedefaultvaluewasshowninTempTemp2.cpp.Thisistheonlyexceptiontothe
rulestatedearlierthatdefaultargumentsshouldappearonlyonceinacompilationunit.
Sincethestandardsequencecontainers(vector,list,anddeque,discussedindepthinChapter7)havea
defaultallocatorargument,thetechniqueshownaboveishelpfulshouldyoueverwanttopassoneofthese
sequencesasatemplateparameter.Thefollowingprogrampassesavectorandthenalisttotwo
instancesofContainer:
//:C05:TempTemp4.cpp{bor}{msc}
//Passesstandardsequencesastemplatearguments.
#include<iostream>
#include<list>
#include<memory>//Declaresallocator<T>
#include<vector>
usingnamespacestd
template<classT,template<classU,class=allocator<U>>
classSeq>
classContainer{
Seq<T>seq//Defaultofallocator<T>appliedimplicitly
public:
voidpush_back(constT&t){seq.push_back(t)}
typenameSeq<T>::iteratorbegin(){returnseq.begin()}
typenameSeq<T>::iteratorend(){returnseq.end()}
}
intmain(){
//Useavector
Container<int,vector>vContainer
vContainer.push_back(1)
vContainer.push_back(2)
for(vector<int>::iteratorp=vContainer.begin()
p!=vContainer.end()++p){
cout<<*p<<endl
}
//Usealist
Container<int,list>lContainer
lContainer.push_back(3)
lContainer.push_back(4)
for(list<int>::iteratorp2=lContainer.begin()
p2!=lContainer.end()++p2){
cout<<*p2<<endl
}
}///:~
HerewenamethefirstparameteroftheinnertemplateSeq(withthenameU)becausetheallocatorsin
thestandardsequencesmustthemselvesbeparameterizedwiththesametypeasthecontainedobjectsin
thesequence.Also,sincethedefaultallocatorparameterisknown,wecanomititinthesubsequent
referencestoSeq<T>,aswedidinthepreviousprogram.Tofullyexplainthisexample,however,we
havetodiscussthesemanticsofthetypenamekeyword.
Thetypenamekeyword
Considerthefollowing:
//:C05:TypenamedID.cpp{bor}
//Uses'typename'asaprefixfornestedtypes.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
137/458
5/3/2015
ThinkinginC++2ndedVolume2
template<classT>classX{
//Withouttypename,youshouldgetanerror:
typenameT::idi
public:
voidf(){i.g()}
}
classY{
public:
classid{
public:
voidg(){}
}
}
intmain(){
X<Y>xy
xy.f()
}///:~
ThetemplatedefinitionassumesthattheclassTthatyouhanditmusthaveanestedidentifierofsome
kindcalledid.YetidcouldalsobeastaticdatamemberofT,inwhichcaseyoucanperformoperationson
iddirectly,butyoucantcreateanobjectofthetypeid.Inthisexample,theidentifieridisbeing
treatedasifitwereanestedtypeinsideT.InthecaseofclassY,idisinfactanestedtype,but(without
thetypenamekeyword)thecompilercantknowthatwhenitscompilingX.
Ifthecompilerhastheoptionoftreatinganidentifierasatypeorassomethingotherthanatypewhenit
seesanidentifierinatemplate,itwillassumethattheidentifierreferstosomethingotherthanatype.
Thatis,itwillassumethattheidentifierreferstoanobject(includingvariablesofprimitivetypes),an
enumeration,orsomethingsimilar.However,itwillnotcannotjustassumethatitisatype.
Becausethedefaultbehaviorofthecompileristoassumethatanamethatfitstheabovetwopointsisnot
atype,youmustusetypenamefornestednames(exceptinconstructorinitializerlists,whereitisneither
needednorallowed).Intheaboveexample,whenthecompilerseestypenameT::id,itknows(because
ofthetypenamekeyword)thatidreferstoanestedtypeandthusitcancreateanobjectofthattype.
Theshortversionoftheruleis:ifatypereferredtoinsidetemplatecodeisqualifiedbyatemplatetype
parameter,youmustusethetypenamekeywordasaprefix,unlessitappearsinabaseclass
specificationorinitializerlistinthesamescope(inwhichcaseyoumustnot).
TheaboveexplainstheuseofthetypenamekeywordintheprogramTempTemp4.cpp.Withoutit,the
compilerwouldassumethattheexpressionSeq<T>::iteratorisnotatype,butwewereusingittodefine
thereturntypeofthebegin()andend()memberfunctions.
Thefollowingexample,whichdefinesafunctiontemplatethatcanprintanyStandardC++sequence,
showsasimilaruseoftypename:
//:C05:PrintSeq.cpp{msc}{mwcc}
//AprintfunctionforStandardC++sequences.
#include<iostream>
#include<list>
#include<memory>
#include<vector>
usingnamespacestd
template<classT,template<classU,class=allocator<U>>
classSeq>
voidprintSeq(Seq<T>&seq){
for(typenameSeq<T>::iteratorb=seq.begin()
b!=seq.end())
cout<<*b++<<endl
}
intmain(){
//Processavector
vector<int>v
v.push_back(1)
v.push_back(2)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
138/458
5/3/2015
ThinkinginC++2ndedVolume2
printSeq(v)
//Processalist
list<int>lst
lst.push_back(3)
lst.push_back(4)
printSeq(lst)
}///:~
Onceagain,withoutthetypenamekeywordthecompilerwillinterpretiteratorasastaticdatamember
ofSeq<T>,whichisasyntaxerror,sinceatypeisrequired.
Typedefingatypename
Itsimportantnottoassumethatthetypenamekeywordcreatesanewtypename.Itdoesnt.Itspurpose
istoinformthecompilerthatthequalifiedidentifieristobeinterpretedasatype.Alinethatreads:
typenameSeq<T>::iteratorIt
causesavariablenamedIttobedeclaredoftypeSeq<T>::iterator.Ifyoumeantocreateanewtype
name,youshouldusetypedef,asusual,asin:
typedeftypenameSeq<It>::iteratorIt
Usingtypenameinsteadofclass
Anotherroleofthetypenamekeywordistoprovideyoutheoptionofusingtypenameinsteadofclassin
thetemplateargumentlistofatemplatedefinition:
//:C05:UsingTypename.cpp
//Using'typename'inthetemplateargumentlist.
template<typenameT>classX{}
intmain(){
X<int>x
}///:~
Tosome,thisproducesclearercode.
Usingthetemplatekeywordasahint
Justasthetypenamekeywordhelpsthecompilerwhenatypeidentifierisnotexpected,thereisalsoa
potentialdifficultywithtokensthatarenotidentifiers,suchasthe<and>characters.Sometimesthese
representthelessthanorgreaterthansymbols,andsometimestheydelimittemplateparameterlists.As
anexample,welloncemoreusethebitsetclass:
//:C05:DotTemplate.cpp
//Illustratethe.templateconstruct.
#include<bitset>
#include<cstddef>
#include<iostream>
#include<string>
usingnamespacestd
template<classcharT,size_tN>
basic_string<charT>bitsetToString(constbitset<N>&bs){
returnbs.templateto_string<charT,char_traits<charT>,
allocator<charT>>()
}
intmain(){
bitset<10>bs
bs.set(1)
bs.set(5)
cout<<bs<<endl//0000100010
strings=bitsetToString<char>(bs)
cout<<s<<endl//0000100010
}///:~
Thebitsetclasssupportsconversiontostringobjectviaitsto_stringmemberfunction.Tosupport
multiplestringclasses,to_stringisitselfatemplate,followingthepatternestablishedbythe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
139/458
5/3/2015
ThinkinginC++2ndedVolume2
basic_stringtemplatediscussedinChapter3.Thedeclarationofto_stringinsideofbitsetlookslike
this:
template<classcharT,classtraits,classAllocator>
basic_string<charT,traits,Allocator>to_string()const
OurbitsetToString()functiontemplateaboverequestsdifferenttypesofstringrepresentationsofa
bitset.Togetawidestring,forinstance,youchangethecalltothefollowing:
wstrings=bitsetToString<wchar_t>(bs)
Notethatbasic_stringusesdefaulttemplatearguments,sowedontneedtorepeatthechar_traitsand
allocatorargumentsinthereturnvalue.Unfortunately,bitset::to_stringdoesnotusedefaultarguments.
UsingbitsetToString<char>(bs)ismoreconvenientthantypingafullyqualifiedcalltobs.template
to_string<char,char_traits,allocator<char>>()everytime.
ThereturnstatementinbitsetToString()containsthetemplatekeywordinanoddplacerightafterthe
dotoperatorappliedtothebitsetobjectbs.Thisisbecausewhenthetemplateisparsed,the<character
aftertheto_stringtokenwouldbeinterpretedasalessthanoperationinsteadofthebeginningofa
templateargumentlist.Thetemplatekeywordusedinthiscontexttellsthecompilerthatwhatfollowsis
thenameofatemplate,causingthe<charactertobeinterpretedcorrectly.Thesamereasoningappliesto
the>and::operatorswhenappliedtotemplates.Aswiththetypenamekeyword,thistemplate
disambiguationtechniquecanonlybeusedwithintemplatecode.[52]
MemberTemplates
Thebitset::to_string()functiontemplateisanexampleofamembertemplate:atemplatedeclared
withinanotherclassorclasstemplate.Thisallowsmanycombinationsofindependenttemplatearguments
tobecombined.AusefulexampleisfoundinthecomplexclasstemplateintheStandardC++library.The
complextemplatehasatypeparametermeanttorepresentanunderlyingfloatingpointtypetoholdthe
realandimaginarypartsofacomplexnumber.Thefollowingcodesnippetfromthestandardlibraryshows
amembertemplateconstructorinthecomplexclasstemplate:
template<typenameT>classcomplex{
public:
template<classX>complex(constcomplex<X>&)
Thestandardcomplextemplatecomesreadymadewithspecializationsthatusefloat,double,andlong
doublefortheparameterT.Themembertemplateconstructorabovecreatesanewcomplexnumberthat
usesadifferentfloatingpointtypeasitsbasetype,asseeninthecodebelow:
complex<float>z(1,2)
complex<double>w(z)
Inthedeclarationofw,thecomplextemplateparameterTisdoubleandXisfloat.Membertemplates
makethiskindofflexibleconversioneasy.
Definingatemplatewithinatemplateisanestingoperation,sotheprefixesthatintroducethetemplates
mustreflectthisnestingifyoudefinethemembertemplateoutsidetheouterclassdefinition.Forexample,
ifyouimplementthecomplexclasstemplate,andifyoudefinethemembertemplateconstructoroutside
thecomplextemplateclassdefinition,youdoitlikethis:
template<typenameT>
template<typenameX>
complex<T>::complex(constcomplex<X>&c){/*Bodyhere*/}
Anotheruseofmemberfunctiontemplatesinthestandardlibraryisintheinitializationofcontainers.
Supposewehaveavectorofintsandwewanttoinitializeanewvectorofdoubleswithit,likethis:
intdata[5]={1,2,3,4,5}
vector<int>v1(data,data+5)
vector<double>v2(v1.begin(),v1.end())
Aslongastheelementsinv1areassignmentcompatiblewiththeelementsinv2(asdoubleandintare
here),alliswell.Thevectorclasstemplatehasthefollowingmembertemplateconstructor:
template<classInputIterator>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
140/458
5/3/2015
ThinkinginC++2ndedVolume2
vector(InputIteratorfirst,InputIteratorlast,
constAllocator&=Allocator())
Thisconstructorisusedtwiceinthevectordeclarationsabove.Whenv1isinitializedfromthearrayof
ints,thetypeInputIteratorisint*.Whenv2isinitializedfromv1,aninstanceofthemembertemplate
constructorisusedwithInputIteratorrepresentingvector<int>::iterator.
Membertemplatescanalsobeclasses.(Theydontneedtobefunctions.)Thefollowingexampleshowsa
memberclasstemplateinsideanouterclasstemplate:
//:C05:MemberClass.cpp
//Amemberclasstemplate.
#include<iostream>
#include<typeinfo>
usingnamespacestd
template<classT>classOuter{
public:
template<classR>classInner{
public:
voidf()
}
}
template<classT>template<classR>
voidOuter<T>::Inner<R>::f(){
cout<<"Outer=="<<typeid(T).name()<<endl
cout<<"Inner=="<<typeid(R).name()<<endl
cout<<"FullInner=="<<typeid(*this).name()<<endl
}
intmain(){
Outer<int>::Inner<bool>inner
inner.f()
}///:~
Thetypeidoperator,coveredinChapter8,takesasingleargumentandreturnsatype_infoobjectwhose
name()functionyieldsastringrepresentingtheargumenttype.Forexample,typeid(int).name()
mightreturnthestringint.(Theactualstringisplatformdependent.)Thetypeidoperatorcanalsotake
anexpressionandreturnatype_infoobjectrepresentingthetypeofthatexpression.Forexample,with
inti,typeid(i).name()returnssomethinglikeint,andtypeid(&i).name()returnssomethinglike
int*.
Theoutputoftheprogramaboveshouldbesomethinglikethis:
Outer==int
Inner==bool
FullInner==Outer<int>::Inner<bool>
ThedeclarationofthevariableinnerinthemainprograminstantiatesbothInner<bool>and
Outer<int>.
Membertemplatefunctionscannotbedeclaredvirtual.Currentcompilertechnologyexpectstobeableto
determinethesizeofaclasssvirtualfunctiontablewhentheclassisparsed.Allowingvirtualmember
templatefunctionswouldrequireknowingallcallstosuchmemberfunctionseverywhereintheprogram
aheadoftime.Thisisnotfeasible,especiallyformultifileprojects.
Functiontemplateissues
Justasaclasstemplatedescribesafamilyofclasses,afunctiontemplatedescribesafamilyoffunctions.
Thesyntaxforcreatingeithertypeoftemplateisvirtuallyidentical,buttheydiffersomewhatinhowthey
areused.Youmustalwaysuseanglebracketswheninstantiatingclasstemplatesandyoumustsupplyall
nondefaulttemplatearguments.However,withfunctiontemplatesyoucanoftenomitthetemplate
arguments,anddefaulttemplateargumentsarenotevenallowed.Consideratypicalimplementationofthe
min()functiontemplatedeclaredinthe<algorithm>header,whichlookssomethinglikethis:
template<typenameT>constT&min(constT&a,constT&b){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
141/458
5/3/2015
ThinkinginC++2ndedVolume2
return(a<b)?a:b
}
Youcouldinvokethistemplatebyprovidingthetypeoftheargumentsinanglebrackets,justlikeyoudo
withclasstemplates,asin:
intz=min<int>(i,j)
Thissyntaxtellsthecompilerthataspecializationofthemin()templateisneededwithintusedinplace
oftheparameterT,whereuponthecompilergeneratesthecorrespondingcode.Followingthepatternof
namingtheclassesgeneratedfromclasstemplates,youcanthinkofthenameoftheinstantiatedfunction
asmin<int>().
Typedeductionoffunctiontemplatearguments
Youcanalwaysusesuchexplicitfunctiontemplatespecificationasintheexampleabove,butitisoften
convenienttoleaveoffthetemplateargumentsandletthecompilerdeducethemfromthefunction
arguments,likethis:
intz=min(i,j)
Ifbothiandjareints,thecompilerknowsthatyouneedmin<int>(),whichittheninstantiates
automatically.Thetypesmustbeidenticalbecausethetemplatewasoriginallyspecifiedwithonlyone
templatetypeargumentusedforbothfunctionparameters.Nostandardconversionsareappliedfor
functionargumentswhosetypeisspecifiedbyatemplateparameter.Forexample,ifyouwantedtofind
theminimumofanintandadouble,thefollowingattemptatacalltomin()wouldfail:
intz=min(x,j)//xisadouble
Sincexandjaredistincttypes,nosingleparametermatchesthetemplateparameterTinthedefinitionof
min(),sothecalldoesnotmatchthetemplatedeclaration.Youcanworkaroundthisdifficultybycasting
oneargumenttotheotherstypeorbyrevertingtothefullyspecifiedcallsyntax,asin:
intz=min<double>(x,j)
Thistellsthecompilertogeneratethedoubleversionofmin(),afterwhichjcanbepromotedtoa
doublebynormalstandardconversionrules(becausethefunctionmin<double>(constdouble&,const
double&)wouldthenexist).
Youmightbetemptedtorequiretwoparametersformin(),allowingthetypesoftheargumentstobe
independent,likethis:
template<typenameT,typenameU>
constT&min(constT&a,constU&b){
return(a<b)?a:b
}
Thisisoftenagoodstrategy,buthereitisproblematicbecausemin()mustreturnavalue,andthereis
nosatisfactorywaytodeterminewhichtypeitshouldbe(TorU?).
Ifthereturntypeofafunctiontemplateisanindependenttemplateparameter,youmustalwaysspecify
itstypeexplicitlywhenyoucallit,sincethereisnoargumentfromwhichtodeduceit.Suchisthecase
withthefromStringtemplatebelow.
//:C05:StringConv.h
//Functiontemplatestoconverttoandfromstrings.
#ifndefSTRINGCONV_H
#defineSTRINGCONV_H
#include<string>
#include<sstream>
template<typenameT>TfromString(conststd::string&s){
std::istringstreamis(s)
Tt
is>>t
returnt
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
142/458
5/3/2015
ThinkinginC++2ndedVolume2
template<typenameT>std::stringtoString(constT&t){
std::ostringstreams
s<<t
returns.str()
}
#endif//STRINGCONV_H///:~
Thesefunctiontemplatesprovideconversionstoandfromstd::stringforanytypesthatprovideastream
inserterorextractor,respectively.Heresatestprogramthatincludestheuseofthestandardlibrary
complexnumbertype:
//:C05:StringConvTest.cpp
#include<complex>
#include<iostream>
#include"StringConv.h"
usingnamespacestd
intmain(){
inti=1234
cout<<"i==\""<<toString(i)<<"\""<<endl
floatx=567.89
cout<<"x==\""<<toString(x)<<"\""<<endl
complex<float>c(1.0,2.0)
cout<<"c==\""<<toString(c)<<"\""<<endl
cout<<endl
i=fromString<int>(string("1234"))
cout<<"i=="<<i<<endl
x=fromString<float>(string("567.89"))
cout<<"x=="<<x<<endl
c=fromString<complex<float>>(string("(1.0,2.0)"))
cout<<"c=="<<c<<endl
}///:~
Theoutputiswhatyoudexpect:
i=="1234"
x=="567.89"
c=="(1,2)"
i==1234
x==567.89
c==(1,2)
NoticethatineachoftheinstantiationsoffromString(),thetemplateparameterisspecifiedinthecall.
Ifyouhaveafunctiontemplatewithtemplateparametersfortheparametertypesaswellasthereturn
types,itisimportanttodeclarethereturntypeparameterfirst,otherwiseyouwontbeabletoomitthe
typeparametersforthefunctionparameters.Asanillustration,considerthefollowingwellknownfunction
template:[53]
//:C05:ImplicitCast.cpp
template<typenameR,typenameP>
Rimplicit_cast(constP&p){
returnp
}
intmain(){
inti=1
floatx=implicit_cast<float>(i)
intj=implicit_cast<int>(x)
//!char*p=implicit_cast<char*>(i)
}///:~
IfyouinterchangeRandPinthetemplateparameterlistnearthetopofthefile,itwillbeimpossibleto
compilethisprogrambecausethereturntypewillremainunspecified(thefirsttemplateparameterwould
bethefunctionsparametertype).Thelastline(whichiscommentedout)isillegalbecausethereisno
standardconversionfrominttochar*.implicit_castisforrevealingconversionsinyourcodethatare
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
143/458
5/3/2015
ThinkinginC++2ndedVolume2
allowednaturally.
Withalittlecareyoucanevendeducearraydimensions.Thisexamplehasanarrayinitializationfunction
template(init2)thatperformssuchadeduction:
//:C05:ArraySize.cpp
#include<cstddef>
usingstd::size_t
template<size_tR,size_tC,typenameT>
voidinit1(Ta[R][C]){
for(size_ti=0i<R++i)
for(size_tj=0j<C++j)
a[i][j]=T()
}
template<size_tR,size_tC,classT>
voidinit2(T(&a)[R][C]){//Referenceparameter
for(size_ti=0i<R++i)
for(size_tj=0j<C++j)
a[i][j]=T()
}
intmain(){
inta[10][20]
init1<10,20>(a)//Mustspecify
init2(a)//Sizesdeduced
}///:~
Arraydimensionsarenotpassedaspartofafunctionparameterstypeunlessthatparameterispassedby
pointerorreference.Thefunctiontemplateinit2declaresatobeareferencetoatwodimensionalarray,
soitsdimensionsRandCarededucedbythetemplatefacility,makinginit2ahandywaytoinitializea
twodimensionalarrayofanysize.Thetemplateinit1doesnotpassthearraybyreference,sothesizes
mustbeexplicitlyspecified,althoughthetypeparametercanstillbededuced.
Functiontemplateoverloading
Aswithordinaryfunctions,youcanoverloadfunctiontemplatesthathavethesamename.Whenthe
compilerprocessesafunctioncallinaprogram,ithastodecidewhichtemplateorordinaryfunctionisthe
bestfitforthecall.Alongwiththemin()functiontemplateintroducedearlier,letsaddsomeordinary
functionstothemix:
//:C05:MinTest.cpp
#include<cstring>
#include<iostream>
usingstd::strcmp
usingstd::cout
usingstd::endl
template<typenameT>constT&min(constT&a,constT&b){
return(a<b)?a:b
}
constchar*min(constchar*a,constchar*b){
return(strcmp(a,b)<0)?a:b
}
doublemin(doublex,doubley){
return(x<y)?x:y
}
intmain(){
constchar*s2="say\"Ni!\"",*s1="knightswho"
cout<<min(1,2)<<endl//1:1(template)
cout<<min(1.0,2.0)<<endl//2:1(double)
cout<<min(1,2.0)<<endl//3:1(double)
cout<<min(s1,s2)<<endl//4:knightswho(const
//char*)
cout<<min<>(s1,s2)<<endl//5:say"Ni!"
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
144/458
5/3/2015
ThinkinginC++2ndedVolume2
//(template)
}///:~
Inadditiontothefunctiontemplate,thisprogramdefinestwonontemplatefunctions:aCstylestring
versionofmin()andadoubleversion.Ifthetemplatedoesntexist,thecallinline1aboveinvokesthe
doubleversionofmin()becauseofthestandardconversionfrominttodouble.Thetemplatecan
generateanintversionwhichisconsideredabettermatch,sothatswhathappens.Thecallinline2isan
exactmatchforthedoubleversion,andthecallinline3alsoinvokesthesamefunction,implicitly
converting1to1.0.Inline4theconstchar*versionofmin()iscalleddirectly.Inline5weforcethe
compilertousethetemplatefacilitybyappendingemptyanglebracketstothefunctionname,whereupon
itgeneratesaconstchar*versionfromthetemplateandusesit(whichisverifiedbythewronganswer
itsjustcomparingaddresses![54]).Ifyourewonderingwhywehaveusingdeclarationsinlieuofthe
usingnamespacestddirective,itsbecausesomecompilersincludeheadersbehindthescenesthatbring
instd::min(),whichwouldconflictwithourdeclarationsofthenamemin().
Asstatedabove,youcanoverloadtemplatesofthesamename,aslongastheycanbedistinguishedby
thecompiler.Youcould,forexample,declareamin()functiontemplatethatprocessesthreearguments:
template<typenameT>
constT&min(constT&a,constT&b,constT&c)
Versionsofthistemplatewillbegeneratedonlyforcallstomin()thathavethreeargumentsofthesame
type.
Takingtheaddressofageneratedfunctiontemplate
Insomesituationsyouneedtotaketheaddressofafunction.Forexample,youmayhaveafunctionthat
takesanargumentofapointertoanotherfunction.Itspossiblethatthisotherfunctionmightbegenerated
fromatemplatefunction,soyouneedsomewaytotakethatkindofaddress:[55]
//:C05:TemplateFunctionAddress.cpp{mwcc}
//Takingtheaddressofafunctiongenerated
//fromatemplate.
template<typenameT>voidf(T*){}
voidh(void(*pf)(int*)){}
template<typenameT>voidg(void(*pf)(T*)){}
intmain(){
h(&f<int>)//Fulltypespecification
h(&f)//Typededuction
g<int>(&f<int>)//Fulltypespecification
g(&f<int>)//Typededuction
g<int>(&f)//Partial(butsufficient)specification
}///:~
Thisexampledemonstratesanumberofissues.First,eventhoughyoureusingtemplates,thesignatures
mustmatch.Thefunctionh()takesapointertoafunctionthattakesanint*andreturnsvoid,andthats
whatthetemplatef()produces.Second,thefunctionthatwantsthefunctionpointerasanargumentcan
itselfbeatemplate,asinthecaseofthetemplateg().
Inmain()youcanseethattypedeductionworkshere,too.Thefirstcalltoh()explicitlygivesthe
templateargumentforf(),butsinceh()saysthatitwillonlytaketheaddressofafunctionthattakesan
int*,thatpartcanbededucedbythecompiler.Withg()thesituationisevenmoreinterestingbecause
twotemplatesareinvolved.Thecompilercannotdeducethetypewithnothingtogoon,butifeitherf()or
g()isgivenint,therestcanbededuced.
Anobscureissueariseswhentryingtopassthefunctionstolowerortoupper,declaredin<cctype>,as
parameters.Itispossibletousethese,forexample,withthetransformalgorithm(whichiscoveredin
detailinthenextchapter)toconvertastringtoloweroruppercase.Youmustbecarefulbecausethere
aremultipledeclarationsforthesefunctions.Anaiveapproachwouldbesomethinglikethis:
//Thevariablesisastd::string
transform(s.begin(),s.end(),s.begin(),tolower)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
145/458
5/3/2015
ThinkinginC++2ndedVolume2
Thetransformalgorithmappliesitsfourthparameter(tolower()inthiscase)toeachcharacterinthe
stringsandplacestheresultinsitself,thusoverwritingeachcharacterinswithitslowercase
equivalent.Asitiswritten,thisstatementmayormaynotwork!Itfailsinthefollowingcontext:
//:C05:FailedTransform.cpp{xo}
#include<algorithm>
#include<cctype>
#include<iostream>
#include<string>
usingnamespacestd
intmain(){
strings("LOWER")
transform(s.begin(),s.end(),s.begin(),tolower)
cout<<s<<endl
}///:~
Evenifyourcompilerletsyougetawaywiththis,itisillegal.Thereasonisthatthe<iostream>header
alsomakesavailableatwoargumentversionoftolower()andtoupper():
template<classcharT>charTtoupper(charTc,
constlocale&loc)
template<classcharT>charTtolower(charTc,
constlocale&loc)
Thesefunctiontemplatestakeasecondargumentoftypelocale.Thecompilerhasnowayofknowing
whetheritshouldusetheoneargumentversionoftolower()definedin<cctype>ortheonementioned
above.Youcansolvethisproblem(almost!)withacastinthecalltotransform,asfollows:
transform(s.begin(),s.end(),s.begin()
static_cast<int(*)(int)>(tolower))
(Recallthattolower()andtoupper()workwithintinsteadofchar.)Thecastabovemakesclearthat
thesingleargumentversionoftolower()isdesired.Thisworkswithsomecompilers,butitisnot
requiredto.Thereason,albeitobscure,isthatalibraryimplementationisallowedtogiveClinkage
(meaningthatthefunctionnamedoesnotcontainalltheauxiliaryinformation[56]thatnormalC++
functionsdo)tofunctionsinheritedfromtheClanguage.Ifthisisthecase,thecastfailsbecause
transformisaC++functiontemplateandexpectsitsfourthargumenttohaveC++linkageandacastis
notallowedtochangethelinkage.Whatapredicament!
Thesolutionistoplacetolower()callsinanunambiguouscontext.Forexample,youcouldwritea
functionnamedstrTolower()andplaceitinitsownfilewithoutincluding<iostream>,likethis:
//:C05:StrTolower.cpp{O}{mwcc}
#include<algorithm>
#include<cctype>
#include<string>
usingnamespacestd
stringstrTolower(strings){
transform(s.begin(),s.end(),s.begin(),tolower)
returns
}///:~
Theheader<iostream>isnotinvolvedhere,andthecompilersweusedonotintroducethetwo
argumentversionoftolower()inthiscontext,[57]sotheresnoproblem.Youcanthenusethisfunction
normally:
//:C05:Tolower.cpp{mwcc}
//{L}StrTolower
#include<algorithm>
#include<cctype>
#include<iostream>
#include<string>
usingnamespacestd
stringstrTolower(string)
intmain(){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
146/458
5/3/2015
ThinkinginC++2ndedVolume2
strings("LOWER")
cout<<strTolower(s)<<endl
}///:~
Anothersolutionistowriteawrapperfunctiontemplatethatcallsthecorrectversionoftolower()
explicitly:
//:C05:ToLower2.cpp{mwcc}
#include<algorithm>
#include<cctype>
#include<iostream>
#include<string>
usingnamespacestd
template<classcharT>charTstrTolower(charTc){
returntolower(c)//Oneargversioncalled
}
intmain(){
strings("LOWER")
transform(s.begin(),s.end(),s.begin(),&strTolower<char>)
cout<<s<<endl
}///:~
Thisversionhastheadvantagethatitcanprocessbothwideandnarrowstringssincetheunderlying
charactertypeisatemplateparameter.TheC++StandardsCommitteeisworkingonmodifyingthe
languagesothatthefirstexample(withoutthecast)willwork,andsomedaytheseworkaroundscanbe
ignored.[58]
ApplyingafunctiontoanSTLsequence
SupposeyouwanttotakeanSTLsequencecontainer(whichyoulllearnmoreaboutinsubsequent
chaptersfornowwecanjustusethefamiliarvector)andapplyamemberfunctiontoalltheobjectsit
contains.Becauseavectorcancontainanytypeofobject,youneedafunctionthatworkswithanytypeof
vector:
//:C05:ApplySequence.h
//ApplyafunctiontoanSTLsequencecontainer.
//const,0arguments,anytypeofreturnvalue:
template<classSeq,classT,classR>
voidapply(Seq&sq,R(T::*f)()const){
typenameSeq::iteratorit=sq.begin()
while(it!=sq.end())
((*it++)>*f)()
}
//const,1argument,anytypeofreturnvalue:
template<classSeq,classT,classR,classA>
voidapply(Seq&sq,R(T::*f)(A)const,Aa){
typenameSeq::iteratorit=sq.begin()
while(it!=sq.end())
((*it++)>*f)(a)
}
//const,2arguments,anytypeofreturnvalue:
template<classSeq,classT,classR,
classA1,classA2>
voidapply(Seq&sq,R(T::*f)(A1,A2)const,
A1a1,A2a2){
typenameSeq::iteratorit=sq.begin()
while(it!=sq.end())
((*it++)>*f)(a1,a2)
}
//Nonconst,0arguments,anytypeofreturnvalue:
template<classSeq,classT,classR>
voidapply(Seq&sq,R(T::*f)()){
typenameSeq::iteratorit=sq.begin()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
147/458
5/3/2015
ThinkinginC++2ndedVolume2
while(it!=sq.end())
((*it++)>*f)()
}
//Nonconst,1argument,anytypeofreturnvalue:
template<classSeq,classT,classR,classA>
voidapply(Seq&sq,R(T::*f)(A),Aa){
typenameSeq::iteratorit=sq.begin()
while(it!=sq.end())
((*it++)>*f)(a)
}
//Nonconst,2arguments,anytypeofreturnvalue:
template<classSeq,classT,classR,
classA1,classA2>
voidapply(Seq&sq,R(T::*f)(A1,A2),
A1a1,A2a2){
typenameSeq::iteratorit=sq.begin()
while(it!=sq.end())
((*it++)>*f)(a1,a2)
}
//Etc.,tohandlemaximumlikelyarguments///:~
Theapply()functiontemplateabovetakesareferencetothecontainerclassandapointertomemberfor
amemberfunctionoftheobjectscontainedintheclass.Itusesaniteratortomovethroughthesequence
andapplythefunctiontoeveryobject.Wehaveoverloadedontheconstnessofthefunctionsoyoucan
useitwithbothconstandnonconstfunctions.
NoticethattherearenoSTLheaderfiles(oranyheaderfiles,forthatmatter)includedin
applySequence.h,soitisnotlimitedtousewithanSTLcontainer.However,itdoesmakeassumptions
(primarily,thenameandbehavioroftheiterator)thatapplytoSTLsequences,anditalsoassumesthat
theelementsofthecontainerbeofpointertype.
Youcanseethereismorethanoneversionofapply(),furtherillustratingoverloadingoffunction
templates.Althoughthesetemplatesallowanytypeofreturnvalue(whichisignored,butthetype
informationisrequiredtomatchthepointertomember),eachversiontakesadifferentnumberof
arguments,andbecauseitsatemplate,thoseargumentscanbeofanytype.Theonlylimitationhereis
thattheresnosupertemplatetocreatetemplatesforyouyoumustdecidehowmanyargumentswill
everberequiredandmaketheappropriatedefinitions.
Totestthevariousoverloadedversionsofapply(),theclassGromit[59]iscreatedcontainingfunctions
withdifferentnumbersofarguments,andbothconstandnonconstmemberfunctions:
//:C05:Gromit.h
//Thetechnodog.Hasmemberfunctions
//withvariousnumbersofarguments.
#include<iostream>
classGromit{
intarf
inttotalBarks
public:
Gromit(intarf=1):arf(arf+1),totalBarks(0){}
voidspeak(int){
for(inti=0i<arfi++){
std::cout<<"arf!"
++totalBarks
}
std::cout<<std::endl
}
chareat(float)const{
std::cout<<"chomp!"<<std::endl
return'z'
}
intsleep(char,double)const{
std::cout<<"zzz..."<<std::endl
return0
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
148/458
5/3/2015
ThinkinginC++2ndedVolume2
voidsit()const{
std::cout<<"Sitting..."<<std::endl
}
}///:~
Nowyoucanusetheapply()templatefunctionstoapplytheGromitmemberfunctionstoa
vector<Gromit*>,likethis:
//:C05:ApplyGromit.cpp
//TestApplySequence.h.
#include<cstddef>
#include<iostream>
#include<vector>
#include"ApplySequence.h"
#include"Gromit.h"
#include"../purge.h"
usingnamespacestd
intmain(){
vector<Gromit*>dogs
for(size_ti=0i<5i++)
dogs.push_back(newGromit(i))
apply(dogs,&Gromit::speak,1)
apply(dogs,&Gromit::eat,2.0f)
apply(dogs,&Gromit::sleep,'z',3.0)
apply(dogs,&Gromit::sit)
purge(dogs)
}///:~
Thepurge()functionisasmallutilitythatcallsdeleteoneveryelementofsequence.Youllfindit
definedinChapter7,andusedvariousplacesinthisbook.
Althoughthedefinitionofapply()issomewhatcomplexandnotsomethingyoudeverexpectanoviceto
understand,itsuseisremarkablycleanandsimple,andanovicecoulduseitknowingonlywhatitis
intendedtoaccomplish,nothow.Thisisthetypeofdivisionyoushouldstriveforinallyourprogram
components.Thetoughdetailsareallisolatedonthedesignerssideofthewall.Usersareconcernedonly
withaccomplishingtheirgoalsanddontsee,knowabout,ordependondetailsoftheunderlying
implementation.Wellexploreevenmoreflexiblewaystoapplyfunctionstosequencesinthenextchapter.
Partialorderingoffunctiontemplates
Wementionedearlierthatanordinaryfunctionoverloadofmin()ispreferabletousingthetemplate.Ifa
functionalreadyexiststomatchafunctioncall,whygenerateanother?Intheabsenceofordinary
functions,however,itispossiblethatoverloadedfunctiontemplatescanleadtoambiguities.Tominimize
thechancesofthis,anorderingisdefinedforfunctiontemplates,whichchoosesthemostspecialized
template,ifsuchexists.Afunctiontemplateisconsideredmorespecializedthananotherifeverypossible
listofargumentsthatmatchesitalsomatchestheother,butnottheotherwayaround.Considerthe
followingfunctiontemplatedeclarations,takenfromanexampleintheC++Standarddocument:
template<classT>voidf(T)
template<classT>voidf(T*)
template<classT>voidf(constT*)
Thefirsttemplatecanbematchedwithanytype.Thesecondtemplateismorespecializedthanthefirst
becauseonlypointertypesmatchit.Inotherwords,youcanlookuponthesetofpossiblecallsthatmatch
thesecondtemplateasasubsetofthefirst.Asimilarrelationshipexistsbetweenthesecondandthird
templatedeclarationsabove:thethirdcanonlybecalledforpointerstoconst,butthesecond
accommodatesanypointertype.Thefollowingprogramillustratestheserules:
//:C05:PartialOrder.cpp
//Revealsorderingoffunctiontemplates.
#include<iostream>
usingnamespacestd
template<classT>voidf(T){
cout<<"T"<<endl
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
149/458
5/3/2015
ThinkinginC++2ndedVolume2
template<classT>voidf(T*){
cout<<"T*"<<endl
}
template<classT>voidf(constT*){
cout<<"constT*"<<endl
}
intmain(){
f(0)//T
inti=0
f(&i)//T*
constintj=0
f(&j)//constT*
}///:~
Thecallf(&i)certainlymatchesthefirsttemplate,butsincethesecondismorespecialized,itiscalled.
Thethirdcantbecalledheresincethepointerisnotapointertoconst.Thecallf(&j)matchesallthree
templates(forexample,Twouldbeconstintinthesecondtemplate),butagain,thethirdtemplateis
morespecialized,soitisusedinstead.
Ifthereisnomostspecializedtemplateamongasetofoverloadedfunctiontemplates,anambiguity
remains,andthecompilerwillreportanerror.Thatiswhythisfeatureiscalledapartialorderingit
maynotbeabletoresolveallpossibilities.Similarrulesexistforclasstemplates(seethesectionPartial
specializationbelow).
Templatespecialization
Thetermspecializationhasaspecific,templaterelatedmeaninginC++.Atemplatedefinitionis,byits
verynature,ageneralization,becauseitdescribesafamilyoffunctionsorclassesingeneralterms.When
templateargumentsaresupplied,theresultisaspecializationofthetemplatebecauseitdeterminesa
uniqueinstanceoutofthemanypossibleinstancesofthefamilyoffunctionsorclasses.Themin()
functiontemplateseenatthebeginningofthischapterisageneralizationofaminimumfindingfunction
becausethetypeofitsparametersisnotspecified.Whenyousupplythetypeforthetemplateparameter,
whetherexplicitlyorimplicitlyviaargumentdeduction,theresultantcodegeneratedbythecompiler(for
example,min<int>())isaspecializationofthetemplate.Thecodegeneratedisalsoconsideredan
instantiationofthetemplate,asareallcodebodiesgeneratedbythetemplatefacility.
Explicitspecialization
Youcanalsoprovidethecodeyourselfforagiventemplatespecialization,shouldtheneedarise.Providing
yourowntemplatespecializationsisoftenneededwithclasstemplates,butwewillbeginwiththemin()
functiontemplatetointroducethesyntax.
RecallthatinMinTest.cppearlierinthischapterweintroducedthefollowingordinaryfunction:
constchar*min(constchar*a,constchar*b){
return(strcmp(a,b)<0)?a:b
}
Thiswassothatacalltomin()wouldcomparestringsandnotaddresses.Althoughitwouldprovideno
advantagehere,wecouldinsteaddefineaconstchar*specializationformin(),asinthefollowing
program:
//:C05:MinTest2.cpp
#include<cstring>
#include<iostream>
usingstd::strcmp
usingstd::cout
usingstd::endl
template<classT>constT&min(constT&a,constT&b){
return(a<b)?a:b
}
//Anexplicitspecializationofthemintemplate
template<>
constchar*const&min<constchar*>(constchar*const&a,
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
150/458
5/3/2015
ThinkinginC++2ndedVolume2
constchar*const&b){
return(strcmp(a,b)<0)?a:b
}
intmain(){
constchar*s2="say\"Ni!\"",*s1="knightswho"
cout<<min(s1,s2)<<endl
cout<<min<>(s1,s2)<<endl
}///:~
Thetemplate<>prefixtellsthecompilerthatwhatfollowsisaspecializationofatemplate.Thetype
forthespecializationmustappearinanglebracketsimmediatelyfollowingthefunctionname,asit
normallywouldinanexplicitlyspecifiedcall.Notethatwecarefullysubstituteconstchar*forTinthe
explicitspecialization.WhenevertheoriginaltemplatespecifiesconstT,thatconstmodifiesthewhole
typeT.Itisthepointertoaconstchar*thatisconst.Sowemustwriteconstchar*constinplaceof
constTinthespecialization.Whenthecompilerseesacalltomin()withconstchar*argumentsinthe
program,itwillinstantiateourconstchar*versionofmin()soitcanbecalled.Thetwocallstomin()
inthisprogramcallthesamespecializationofmin().
Explicitspecializationstendtobemoreusefulforclasstemplatesthanforfunctiontemplates.Whenyou
provideafullspecializationforaclasstemplate,though,youmayneedtoimplementallthemember
functions.Thisisbecauseyouareprovidingaseparateclass,andclientcodemayexpectthecomplete
interfacetobeimplemented.
Thestandardlibraryhasanexplicitspecializationforvectorwhenitholdsobjectsoftypebool.The
purposeforvector<bool>istoallowlibraryimplementationstosavespacebypackingbitsintointegers.
[60]
Asyousawearlierinthischapter,thedeclarationfortheprimaryvectorclasstemplateis:
template<classT,classAllocator=allocator<T>>
classvector{...}
Tospecializeforobjectsoftypebool,youcoulddeclareanexplicitspecializationasfollows:
template<>classvector<bool,allocator<bool>>{...}
Again,thisisquicklyrecognizedasafull,explicitspecializationbecauseofthetemplate<>prefixand
becausealltheprimarytemplatesparametersaresatisfiedbytheargumentlistappendedtotheclass
name.
Itturnsoutthatvector<bool>isalittlemoreflexiblethanwehavedescribed,asseeninthenext
section.
PartialSpecialization
Classtemplatescanalsobepartiallyspecialized,meaningthatatleastoneofthetemplateparametersis
leftopeninsomewayinthespecialization.vector<bool>specifiestheobjecttype(bool),butleaves
theallocatortypeunspecified.Hereistheactualdeclarationofvector<bool>:
template<classAllocator>classvector<bool,Allocator>
Youcanrecognizeapartialspecializationbecausenonemptyparameterlistsappearinanglebracketsboth
afterthetemplatekeyword(theunspecifiedparameters)andaftertheclass(thespecifiedarguments).
Becauseofthewayvector<bool>isdefined,ausercanprovideacustomallocatortype,eventhoughthe
containedtypeofboolisfixed.Inotherwords,specialization,andpartialspecializationinparticular,
constituteasortofoverloadingforclasstemplates.
Partialorderingofclasstemplates
Therulesthatdeterminewhichtemplateisselectedforinstantiationaresimilartothepartialorderingfor
functiontemplatesthemostspecializedtemplateisselected.Thestringineachf()memberfunctionin
theillustrationbelowexplainstheroleofeachtemplatedefinition:
//:C05:PartialOrder2.cpp
//Revealspartialorderingofclasstemplates.
#include<iostream>
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
151/458
5/3/2015
ThinkinginC++2ndedVolume2
template<classT,classU>classC{
public:
voidf(){cout<<"PrimaryTemplate\n"}
}
template<classU>classC<int,U>{
public:
voidf(){cout<<"T==int\n"}
}
template<classT>classC<T,double>{
public:
voidf(){cout<<"U==double\n}
}
template<classT,classU>classC<T*,U>{
public:
voidf(){cout<<"T*used\n}
}
template<classT,classU>classC<T,U*>{
public:
voidf(){cout<<"U*used\n}
}
template<classT,classU>classC<T*,U*>{
public:
voidf(){cout<<"T*andU*used\n}
}
template<classT>classC<T,T>{
public:
voidf(){cout<<"T==U\n}
}
intmain(){
C<float,int>().f()//1:Primarytemplate
C<int,float>().f()//2:T==int
C<float,double>().f()//3:U==double
C<float,float>().f()//4:T==U
C<float*,float>().f()//5:T*used[Tisfloat]
C<float,float*>().f()//6:U*used[Uisfloat]
C<float*,int*>().f()//7:T*andU*used[float,int]
//Thefollowingareambiguous:
//8:C<int,int>().f()
//9:C<double,double>().f()
//10:C<float*,float*>().f()
//11:C<int,int*>().f()
//12:C<int*,int*>().f()
}///:~
Asyoucansee,youcanpartiallyspecifytemplateparametersaccordingtowhethertheyarepointertypes,
orwhethertheyareequal.WhentheT*specializationisused,suchasisthecaseinline5,Titselfisnot
thetoplevelpointertypethatwaspasseditisthetypethatthepointerrefersto(float,inthiscase).The
T*specificationisapatterntoallowmatchingagainstpointertypes.Ifyouuseint**asthefirsttemplate
argument,Tbecomesint*.Line8isambiguousbecausehavingthefirstparameterasanintversus
havingthetwoparametersequalareindependentissuesoneisnotmorespecializedthantheother.
Similarlogicappliestolines9through12.
Apracticalexample
Youcaneasilyderivefromaclasstemplate,andyoucancreateanewtemplatethatinstantiatesand
inheritsfromanexistingtemplate.Ifthevectortemplatedoesmosteverythingyouwant,forexample,
butinacertainapplicationyoudalsolikeaversionthatcansortitself,youcaneasilyreusethevector
code.Thefollowingexamplederivesfromvector<T>andaddssorting.Notethatderivingfromvector,
whichdoesnthaveavirtualdestructor,wouldbedangerousifweneededtoperformcleanupinour
destructor.
//:C05:Sortable.h
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
152/458
5/3/2015
ThinkinginC++2ndedVolume2
//Templatespecialization.
#ifndefSORTABLE_H
#defineSORTABLE_H
#include<cstring>
#include<cstddef>
#include<string>
#include<vector>
usingstd::size_t
template<classT>
classSortable:publicstd::vector<T>{
public:
voidsort()
}
template<classT>
voidSortable<T>::sort(){//Asimplesort
for(size_ti=this>size()i>0i)
for(size_tj=1j<i++j)
if(this>at(j1)>this>at(j)){
Tt=this>at(j1)
this>at(j1)=this>at(j)
this>at(j)=t
}
}
//Partialspecializationforpointers:
template<classT>
classSortable<T*>:publicstd::vector<T*>{
public:
voidsort()
}
template<classT>
voidSortable<T*>::sort(){
for(size_ti=this>size()i>0i)
for(size_tj=1j<i++j)
if(*this>at(j1)>*this>at(j)){
T*t=this>at(j1)
this>at(j1)=this>at(j)
this>at(j)=t
}
}
//Fullspecializationforchar*
//(Madeinlinehereforconveniencenormallyyouwould
//placethefunctionbodyinaseparatefileandonly
//leavethedeclarationhere).
template<>inlinevoidSortable<char*>::sort(){
for(size_ti=this>size()i>0i)
for(size_tj=1j<i++j)
if(std::strcmp(this>at(j1),this>at(j))>0){
char*t=this>at(j1)
this>at(j1)=this>at(j)
this>at(j)=t
}
}
#endif//SORTABLE_H///:~
TheSortabletemplateimposesarestrictiononallbutoneoftheclassesforwhichitisinstantiated:they
mustcontaina>operator.Itworkscorrectlyonlywithnonpointerobjects(includingobjectsofbuiltin
types).Thefullspecializationcomparestheelementsusingstrcmp()tosortvectorsofchar*according
tothenullterminatedstringstowhichtheyrefer.Theuseofthis>aboveismandatory[61]andis
explainedinthesectionentitledNamelookupissueslaterinthischapter.[62]
HeresadriverforSortable.hthatusestherandomizerintroducedearlierinthechapter:
//:C05:Sortable.cpp
//{bor}(BecauseofbitsetinUrand.h)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
153/458
5/3/2015
ThinkinginC++2ndedVolume2
//Testingtemplatespecialization.
#include<cstddef>
#include<iostream>
#include"Sortable.h"
#include"Urand.h"
usingnamespacestd
#defineasz(a)(sizeofa/sizeofa[0])
char*words[]={"is","running","big","dog","a",}
char*words2[]={"this","that","theother",}
intmain(){
Sortable<int>is
Urand<47>rnd
for(size_ti=0i<15++i)
is.push_back(rnd())
for(size_ti=0i<is.size()++i)
cout<<is[i]<<''
cout<<endl
is.sort()
for(size_ti=0i<is.size()++i)
cout<<is[i]<<''
cout<<endl
//Usesthetemplatepartialspecialization:
Sortable<string*>ss
for(size_ti=0i<asz(words)++i)
ss.push_back(newstring(words[i]))
for(size_ti=0i<ss.size()++i)
cout<<*ss[i]<<''
cout<<endl
ss.sort()
for(size_ti=0i<ss.size()++i){
cout<<*ss[i]<<''
deletess[i]
}
cout<<endl
//Usesthefullchar*specialization:
Sortable<char*>scp
for(size_ti=0i<asz(words2)++i)
scp.push_back(words2[i])
for(size_ti=0i<scp.size()++i)
cout<<scp[i]<<''
cout<<endl
scp.sort()
for(size_ti=0i<scp.size()++i)
cout<<scp[i]<<''
cout<<endl
}///:~
Eachofthetemplateinstantiationsaboveusesadifferentversionofthetemplate.Sortable<int>uses
theprimarytemplate.Sortable<string*>usesthepartialspecializationforpointers.Last,
Sortable<char*>usesthefullspecializationforchar*.Withoutthisfullspecialization,youcouldbe
fooledintothinkingthatthingswereworkingcorrectlybecausethewordsarraywouldstillsortouttoa
bigdogisrunningsincethepartialspecializationwouldendupcomparingthefirstcharacterofeach
array.However,words2wouldnotsortcorrectly.
Preventingtemplatecodebloat
Wheneveraclasstemplateisinstantiated,thecodefromtheclassdefinitionfortheparticular
specializationisgenerated,alongwithallthememberfunctionsthatarecalledintheprogram.Onlythe
memberfunctionsthatarecalledaregenerated.Thisisgood,asyoucanseeinthefollowingprogram:
//:C05:DelayedInstantiation.cpp
//Memberfunctionsofclasstemplatesarenot
//instantiateduntilthey'reneeded.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
154/458
5/3/2015
ThinkinginC++2ndedVolume2
classX{
public:
voidf(){}
}
classY{
public:
voidg(){}
}
template<typenameT>classZ{
Tt
public:
voida(){t.f()}
voidb(){t.g()}
}
intmain(){
Z<X>zx
zx.a()//Doesn'tcreateZ<X>::b()
Z<Y>zy
zy.b()//Doesn'tcreateZ<Y>::a()
}///:~
Here,eventhoughthetemplateZpurportstousebothf()andg()memberfunctionsofT,thefactthat
theprogramcompilesshowsyouthatitonlygeneratesZ<X>::a()whenitisexplicitlycalledforzx.(If
Z<X>::b()werealsogeneratedatthesametime,acompiletimeerrormessagewouldbegenerated
becauseitwouldattempttocallX::g(),whichdoesntexist.)Similarly,thecalltozy.b()doesnt
generateZ<Y>::a().Asaresult,theZtemplatecanbeusedwithXandY,whereasifallthemember
functionsweregeneratedwhentheclasswasfirstinstantiatedtheuseofmanytemplateswouldbecome
significantlylimited.
SupposeyouhaveatemplatizedStackcontainerandyouusespecializationsforint,int*,andchar*.
ThreeversionsofStackcodewillbegeneratedandlinkedaspartofyourprogram.Oneofthereasonsfor
usingatemplateinthefirstplaceissoyoudontneedtoreplicatecodebyhandbutcodestillgets
replicateditsjustthecompilerthatdoesitinsteadofyou.Youcanfactorthebulkoftheimplementation
forstoringpointertypesintoasingleclassbyusingacombinationoffullandpartialspecialization.Thekey
istofullyspecializeforvoid*andthenderiveallotherpointertypesfromthevoid*implementationso
thecommoncodecanbeshared.Theprogrambelowillustratesthistechnique:
//:C05:Nobloat.h
//SharescodeforstoringpointersinaStack.
#ifndefNOBLOAT_H
#defineNOBLOAT_H
#include<cassert>
#include<cstddef>
#include<cstring>
//Theprimarytemplate
template<classT>classStack{
T*data
std::size_tcount
std::size_tcapacity
enum{INIT=5}
public:
Stack(){
count=0
capacity=INIT
data=newT[INIT]
}
voidpush(constT&t){
if(count==capacity){
//Growarraystore
std::size_tnewCapacity=2*capacity
T*newData=newT[newCapacity]
for(size_ti=0i<count++i)
newData[i]=data[i]
delete[]data
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
155/458
5/3/2015
ThinkinginC++2ndedVolume2
data=newData
capacity=newCapacity
}
assert(count<capacity)
data[count++]=t
}
voidpop(){
assert(count>0)
count
}
Ttop()const{
assert(count>0)
returndata[count1]
}
std::size_tsize()const{returncount}
}
//Fullspecializationforvoid*
template<>classStack<void*>{
void**data
std::size_tcount
std::size_tcapacity
enum{INIT=5}
public:
Stack(){
count=0
capacity=INIT
data=newvoid*[INIT]
}
voidpush(void*const&t){
if(count==capacity){
std::size_tnewCapacity=2*capacity
void**newData=newvoid*[newCapacity]
std::memcpy(newData,data,count*sizeof(void*))
delete[]data
data=newData
capacity=newCapacity
}
assert(count<capacity)
data[count++]=t
}
voidpop(){
assert(count>0)
count
}
void*top()const{
assert(count>0)
returndata[count1]
}
std::size_tsize()const{returncount}
}
//Partialspecializationforotherpointertypes
template<classT>classStack<T*>:privateStack<void*>{
typedefStack<void*>Base
public:
voidpush(T*const&t){Base::push(t)}
voidpop(){Base::pop()}
T*top()const{returnstatic_cast<T*>(Base::top())}
std::size_tsize(){returnBase::size()}
}
#endif//NOBLOAT_H///:~
Thissimplestackexpandsasitfillsitscapacity.Thevoid*specializationstandsoutasafullspecialization
byvirtueofthetemplate<>prefix(thatis,thetemplateparameterlistisempty).Asmentionedearlier,
itisnecessarytoimplementallmemberfunctionsinaclasstemplatespecialization.Thesavingsoccurs
withallotherpointertypes.ThepartialspecializationforotherpointertypesderivesfromStack<void*>
privately,sincewearemerelyusingStack<void*>forimplementationpurposes,anddonotwishto
exposeanyofitsinterfacedirectlytotheuser.Thememberfunctionsforeachpointerinstantiationare
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
156/458
5/3/2015
ThinkinginC++2ndedVolume2
smallforwardingfunctionstothecorrespondingfunctionsinStack<void*>.Hence,wheneverapointer
typeotherthanvoid*isinstantiated,itisafractionofthesizeitwouldhavebeenhadtheprimary
templatealonebeenused.[63]Hereisadriverprogram:
//:C05:NobloatTest.cpp
#include<iostream>
#include<string>
#include"Nobloat.h"
usingnamespacestd
template<classStackType>
voidemptyTheStack(StackType&stk){
while(stk.size()>0){
cout<<stk.top()<<endl
stk.pop()
}
}
//AnoverloadforemptyTheStack(notaspecialization!)
template<classT>
voidemptyTheStack(Stack<T*>&stk){
while(stk.size()>0){
cout<<*stk.top()<<endl
stk.pop()
}
}
intmain(){
Stack<int>s1
s1.push(1)
s1.push(2)
emptyTheStack(s1)
Stack<int*>s2
inti=3
intj=4
s2.push(&i)
s2.push(&j)
emptyTheStack(s2)
}///:~
ForconvenienceweincludetwoemptyStackfunctiontemplates.Sincefunctiontemplatesdontsupport
partialspecialization,weprovideoverloadedtemplates.ThesecondversionofemptyStackismore
specializedthanthefirst,soitischosenwheneverpointertypesareused.Threeclasstemplatesare
instantiatedinthisprogram:Stack<int>,Stack<void*>,andStack<int*>.Stack<void*>is
implicitlyinstantiatedbecauseStack<int*>derivesfromit.Aprogramusinginstantiationsformany
pointertypescanproducesubstantialsavingsincodesizeoverjustusingasingleStacktemplate.
Namelookupissues
Whenthecompilerencountersanidentifieritmustdeterminethetypeandscope(andinthecaseof
variables,thelifetime)oftheentitytheidentifierrepresents.Templatesaddcomplexitytothesituation.
Becausethecompilerdoesntknoweverythingaboutatemplatewhenitfirstseesthedefinition,itcant
tellwhetherthetemplateisbeingusedproperlyuntilitseesthetemplateinstantiation.Thispredicament
leadstoatwophaseprocessfortemplatecompilation.
Namesintemplates
Inthefirstphase,thecompilerparsesthetemplatedefinitionlookingforobvioussyntaxerrorsand
resolvingallthenamesitcan.Itcanresolvenamesthatdonotdependontemplateparametersusing
normalnamelookup,andifnecessarythroughargumentdependentlookup(discussedbelow).Thenames
itcantresolvearethesocalleddependentnames,whichdependontemplateparametersinsomeway.
Thesecantberesolveduntilthetemplateisinstantiatedwithitsactualarguments.Soinstantiationisthe
secondphaseoftemplatecompilation.Here,thecompilerdetermineswhethertouseanexplicit
specializationofthetemplateinsteadoftheprimarytemplate.
Beforeyouseeanexample,youmustunderstandtwomoreterms.Aqualifiednameisanamewitha
classnameprefix,anamewithanobjectnameandadotoperator,oranamewithapointertoanobject
andanarrowoperator.Examplesofqualifiednamesare:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
157/458
5/3/2015
ThinkinginC++2ndedVolume2
MyClass::f()
x.f()
p>f()
Weusequalifiednamesmanytimesinthisbook,andmostrecentlyinconnectionwiththetypename
keyword.Thesearecalledqualifiednamesbecausethetargetnames(likefabove)areexplicitly
associatedwithaclassornamespace,whichtellsthecompilerwheretolookforthedeclarationsofthose
names.
Theothertermisargumentdependentlookup[64](ADL),amechanismoriginallydesignedtosimplifynon
memberfunctioncalls(includingoperators)declaredinnamespaces.Considerthefollowing:
#include<iostream>
#include<string>
//...
std::strings("hello")
std::cout<<s<<std::endl
Notethat,followingthetypicalpracticeinheaderfiles,thereisnousingnamespacestddirective.
Withoutsuchadirective,youmustusethestd::qualifierontheitemsthatareinthestdnamespace.We
have,however,notqualifiedeverythingfromstdthatweareusing.Canyouseewhatisunqualified?
Wehavenotspecifiedwhichoperatorfunctionstouse.Wewantthefollowingtohappen,butwedontwant
tohavetotypeit!
std::operator<<(std::operator<<(std::cout,s),std::endl)
Tomaketheoriginaloutputstatementworkasdesired,ADLspecifiesthatwhenanunqualifiedfunctioncall
appearsanditsdeclarationisnotin(normal)scope,thenamespacesofeachofitsargumentsaresearched
foramatchingfunctiondeclaration.Intheoriginalstatement,thefirstfunctioncallis:
operator<<(std::cout,s)
Sincethereisnosuchfunctioninscopeinouroriginalexcerpt,thecompilernotesthatthisfunctionsfirst
argument(std::cout)isinthenamespacestdsoitaddsthatnamespacetothelistofscopestosearchfor
auniquefunctionthatbestmatchesthesignatureoperator<<(std::ostream&,std::string).Itfindsthis
functiondeclaredinthestdnamespaceviathe<string>header.
NamespaceswouldbeveryinconvenientwithoutADL.NotethatADLgenerallybringsinalldeclarationsof
thenameinquestionfromalleligiblenamespacesifthereisnosinglebestmatch,anambiguitywill
result.
ToturnoffADL,youcanenclosethefunctionnameinparentheses:
(f)(x,y)//ADLsuppressed
Nowconsiderthefollowingprogram:[65]
//:C05:Lookup.cpp
//OnlyproducescorrectbehaviorwithEDG,
//andMetrowerksusingaspecialoption.
#include<iostream>
usingstd::cout
usingstd::endl
voidf(double){cout<<"f(double)"<<endl}
template<classT>classX{
public:
voidg(){f(1)}
}
voidf(int){cout<<"f(int)"<<endl}
intmain(){
X<int>().g()
}///:~
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
158/458
5/3/2015
ThinkinginC++2ndedVolume2
TheonlycompilerwehavethatproducescorrectbehaviorwithoutmodificationistheEdisonDesignGroup
frontend,[66]althoughsomecompilers,suchasMetrowerks,haveanoptiontoenablethecorrectlookup
behavior.Theoutputshouldbe:
f(double)
becausefisanondependentnamethatcanberesolvedearlybylookinginthecontextwherethe
templateisdefined,whenonlyf(double)isinscope.Unfortunately,thereisalotofexistingcodeinthe
industrythatdependsonthenonstandardbehaviorofbindingthecalltof(1)insideg()tothelatter
f(int),socompilerwritershavebeenreluctanttomakethechange.
Hereisamoredetailedexample:[67]
//:C05:Lookup2.cpp{bor}{g++}{dmc}
//Microsoft:useoptionZa(ANSImode)
#include<algorithm>
#include<iostream>
#include<typeinfo>
usingstd::cout
usingstd::endl
voidg(){cout<<"globalg()<<endl}
template<classT>classY{
public:
voidg(){
cout<<"Y<"<<typeid(T).name()<<">::g()<<endl
}
voidh(){
cout<<"Y<"<<typeid(T).name()<<">::h()<<endl
}
typedefintE
}
typedefdoubleE
template<classT>voidswap(T&t1,T&t2){
cout<<"globalswap<<endl
Ttemp=t1
t1=t2
t2=temp
}
template<classT>classX:publicY<T>{
public:
Ef(){
g()
this>h()
Tt1=T(),t2=T(1)
cout<<t1<<endl
swap(t1,t2)
std::swap(t1,t2)
cout<<typeid(E).name()<<endl
returnE(t2)
}
}
intmain(){
X<int>x
cout<<x.f()<<endl
}///:~
Theoutputfromthisprogramshouldbe:
globalg()
Y<int>::h()
0
globalswap
double
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
159/458
5/3/2015
ThinkinginC++2ndedVolume2
LookingatthedeclarationsinsideofX::f():
E,thereturntypeofX::f(),isnotadependentname,soitislookedupwhenthetemplateis
parsed,andthetypedefnamingEasadoubleisfound.Thismayseemstrange,sincewithnon
templateclassesthedeclarationofEinthebaseclasswouldbefoundfirst,butthosearetherules.
(Thebaseclass,Y,isadependentbaseclass,soitcantbesearchedattemplatedefinitiontime).
Thecalltog()isalsonondependent,sincethereisnomentionofT.Ifghadparametersthat
wereofclasstypeofdefinedinanothernamespace,ADLwouldtakeover,sincethereisnogwith
parametersinscope.Asitis,thiscallmatchestheglobaldeclarationofg().
Thecallthis>h()isaqualifiedname,andtheobjectthatqualifiesit(this)referstothecurrent
object,whichisoftypeX,whichinturndependsonthenameY<T>byinheritance.Thereisno
functionh()insideofX,sothelookupwillsearchthescopeofXsbaseclass,Y<T>.Sincethisis
adependentname,itislookedupatinstantiationtime,whenY<T>arereliablyknown(including
anypotentialspecializationsthatmighthavebeenwrittenafterthedefinitionofX),soitcalls
Y<int>::h().
Thedeclarationsoft1andt2aredependent.
Thecalltooperator<<(cout,t1)isdependent,sincet1isoftypeT.Thisislookeduplaterwhen
Tisint,andtheinserterforintisfoundinstd.
Theunqualifiedcalltoswap()isdependentbecauseitsargumentsareoftypeT.Thisultimately
causesaglobalswap(int&,int&)tobeinstantiated.
Thequalifiedcalltostd::swap()isnotdependent,becausestdisafixednamespace.The
compilerknowstolookinstdfortheproperdeclaration.(Thequalifierontheleftofthe::must
mentionatemplateparameterforaqualifiednametobeconsidereddependent.)Thestd::swap()
functiontemplatelatergeneratesstd::swap(int&,int&),atinstantiationtime.Nomoredependent
namesremaininX<T>::f().
Toclarifyandsummarize:namelookupisdoneatthepointofinstantiationifthenameisdependent,
exceptthatforunqualifieddependentnamesthenormalnamelookupisalsoattemptedearly,atthepoint
ofdefinition.Allnondependentnamesintemplatesarelookedupearly,atthetimethetemplatedefinition
isparsed.(Ifnecessary,anotherlookupoccursatinstantiationtime,whenthetypeoftheactualargument
isknown.)
Ifyouhavestudiedthisexampletothepointthatyouunderstandit,prepareyourselfforyetanother
surpriseinthefollowingsectiononfrienddeclarations.
Templatesandfriends
Afriendfunctiondeclarationinsideaclassallowsanonmemberfunctiontoaccessnonpublicmembersof
thatclass.Ifthefriendfunctionnameisqualified,itwillbefoundinthenamespaceorclassthatqualifies
it.Ifitisunqualified,however,thecompilermustmakeanassumptionaboutwherethedefinitionofthe
friendfunctionwillbe,sinceallidentifiersmusthaveauniquescope.Theexpectationisthatthefunction
willbedefinedinthenearestenclosingnamespace(nonclass)scopethatcontainstheclassgranting
friendship.Oftenthisisjusttheglobalscope.Thefollowingnontemplateexampleclarifiesthisissue:
//:C05:FriendScope.cpp
#include<iostream>
usingnamespacestd
classFriendly{
inti
public:
Friendly(inttheInt){i=theInt}
friendvoidf(constFriendly&)//Needsglobaldef.
voidg(){f(*this)}
}
voidh(){
f(Friendly(1))//UsesADL
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
160/458
5/3/2015
ThinkinginC++2ndedVolume2
voidf(constFriendly&fo){//Definitionoffriend
cout<<fo.i<<endl
}
intmain(){
h()//Prints1
Friendly(2).g()//Prints2
}///:~
Thedeclarationoff()insidetheFriendlyclassisunqualified,sothecompilerwillexpecttobeableto
eventuallylinkthatdeclarationtoadefinitionatfilescope(thenamespacescopethatcontainsFriendlyin
thiscase).Thatdefinitionappearsafterthedefinitionofthefunctionh().Thelinkingofthecalltof()
insideh()tothesamefunctionisaseparatematter,however.ThisisresolvedbyADL.Sincethe
argumentoff()insideh()isaFriendlyobject,theFriendlyclassissearchedforadeclarationoff(),
whichsucceeds.Ifthecallweref(1)instead(whichmakessomesensesince1canbeimplicitlyconverted
toFriendly(1)),thecallshouldfail,sincethereisnohintofwherethecompilershouldlookforthe
declarationoff().TheEDGcompilercorrectlycomplainsthatfisundefinedinthatcase.
NowsupposethatFriendlyandfarebothtemplates,asinthefollowingprogram:
//:C05:FriendScope2.cpp
#include<iostream>
usingnamespacestd
//Necessaryforwarddeclarations:
template<classT>classFriendly
template<classT>voidf(constFriendly<T>&)
template<classT>classFriendly{
Tt
public:
Friendly(constT&theT):t(theT){}
friendvoidf<>(constFriendly<T>&)
voidg(){f(*this)}
}
voidh(){
f(Friendly<int>(1))
}
template<classT>voidf(constFriendly<T>&fo){
cout<<fo.t<<endl
}
intmain(){
h()
Friendly<int>(2).g()
}///:~
FirstnoticethatanglebracketsinthedeclarationoffinsideFriendly.Thisisnecessarytotellthe
compilerthatfisatemplate.Otherwise,thecompilerwilllookforanordinaryfunctionnamedfandnot
findit.Wecouldhaveinsertedthetemplateparameter(<T>)inthebrackets,butitiseasilydeduced
fromthedeclaration.
Theforwarddeclarationofthefunctiontemplatefbeforetheclassdefinitionisnecessary,eventhoughit
wasntinthepreviousexamplewhenfwasanotatemplatethelanguagespecifiesthatfriendfunction
templatesmustbepreviouslydeclared.Toproperlydeclaref,Friendlymustalsohavebeendeclared,
sinceftakesaFriendlyargument,hencetheforwarddeclarationofFriendlyinthebeginning.Wecould
haveplacedthefulldefinitionoffrightaftertheinitialdeclarationofFriendlyinsteadofseparatingits
definitionanddeclaration,butwechoseinsteadtoleaveitinaformthatmorecloselyresemblesthe
previousexample.
Onelastoptionremainsforusingfriendsinsidetemplates:fullydefinetheminsidethehostclasstemplate
definitionitself.Hereishowthepreviousexamplewouldappearwiththatchange:
//:C05:FriendScope3.cpp{bor}
//Microsoft:usetheZa(ANSIcompliant)option
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
161/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<iostream>
usingnamespacestd
template<classT>classFriendly{
Tt
public:
Friendly(constT&theT):t(theT){}
friendvoidf(constFriendly<T>&fo){
cout<<fo.t<<endl
}
voidg(){f(*this)}
}
voidh(){
f(Friendly<int>(1))
}
intmain(){
h()
Friendly<int>(2).g()
}///:~
Thereisanimportantdifferencebetweenthisandthepreviousexample:fisnotatemplatehere,butisan
ordinaryfunction.(Rememberthatanglebracketswerenecessarybeforetoimplythatf()wasa
template.)EverytimetheFriendlyclasstemplateisinstantiated,anew,ordinaryfunctionoverloadis
createdthattakesanargumentofthecurrentFriendlyspecialization.ThisiswhatDanSakshascalled
makingnewfriends.[68]Thisisthemostconvenientwaytodefinefriendfunctionsfortemplates.
Toclarify,supposeyouwanttoaddnonmemberfriendoperatorstoaclasstemplate.Hereisaclass
templatethatsimplyholdsagenericvalue:
template<classT>classBox{
Tt
public:
Box(constT&theT):t(theT){}
}
Withoutunderstandingthepreviousexamplesinthissection,novicesfindthemselvesfrustratedbecause
theycantgetasimplestreamoutputinsertertowork.Ifyoudontdefineyouroperatorsinsidethe
definitionofBox,youmustprovidetheforwarddeclarationsweshowedearlier:
//:C05:Box1.cpp
//Definestemplateoperators.
#include<iostream>
usingnamespacestd
//Forwarddeclarations
template<classT>classBox
template<classT>
Box<T>operator+(constBox<T>&,constBox<T>&)
template<classT>
ostream&operator<<(ostream&,constBox<T>&)
template<classT>classBox{
Tt
public:
Box(constT&theT):t(theT){}
friendBoxoperator+<>(constBox<T>&,constBox<T>&)
friendostream&operator<<<>(ostream&,constBox<T>&)
}
template<classT>
Box<T>operator+(constBox<T>&b1,constBox<T>&b2){
returnBox<T>(b1.t+b2.t)
}
template<classT>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
162/458
5/3/2015
ThinkinginC++2ndedVolume2
ostream&operator<<(ostream&os,constBox<T>&b){
returnos<<'['<<b.t<<']'
}
intmain(){
Box<int>b1(1),b2(2)
cout<<b1+b2<<endl//[3]
//cout<<b1+2<<endl//Noimplicitconversions!
}///:~
Herewearedefiningbothanadditionoperatorandanoutputstreamoperator.Themainprogramreveals
adisadvantageofthisapproach:youcantdependonimplicitconversions(theexpressionb1+2)because
templatesdonotprovidethem.Usingtheinclass,nontemplateapproachisshorterandmorerobust:
//:C05:Box2.cpp
//Definesnontemplateoperators.
#include<iostream>
usingnamespacestd
template<classT>classBox{
Tt
public:
Box(constT&theT):t(theT){}
friendBox<T>operator+(constBox<T>&b1,
constBox<T>&b2){
returnBox<T>(b1.t+b2.t)
}
friendostream&
operator<<(ostream&os,constBox<T>&b){
returnos<<'['<<b.t<<']'
}
}
intmain(){
Box<int>b1(1),b2(2)
cout<<b1+b2<<endl//[3]
cout<<b1+2<<endl//[3]
}///:~
Becausetheoperatorsarenormalfunctions(overloadedforeachspecializationofBoxjustintinthis
case),implicitconversionsareappliedasnormalsotheexpressionb1+2isvalid.
NotethattheresonetypeinparticularthatcannotbemadeafriendofBox,oranyotherclasstemplate
forthatmatter,andthattypeisTorrather,thetypethattheclasstemplateisparameterizedupon.To
thebestofourknowledge,therearereallynogoodreasonswhythisshouldntbeallowed,butasis,the
declarationfriendclassTisillegal,andshouldnotcompile.
Friendtemplates
Youcanbepreciseastowhichspecializationsofatemplatearefriendsofaclass.Intheexamplesinthe
previoussection,onlythespecializationofthefunctiontemplatefwiththesametypethatspecialized
Friendlywasafriend.Forexample,onlythespecializationf<int>(constFriendly<int>&)isafriendof
theclassFriendly<int>.ThiswasaccomplishedbyusingthetemplateparameterforFriendlyto
specializefinitsfrienddeclaration.Ifwehadwantedto,wecouldhavemadeaparticular,fixed
specializationoffafriendtoallinstancesofFriendly,likethis:
//InsideFriendly:
friendvoidf<>(constFriendly<double>&)
ByusingdoubleinsteadofT,thedoublespecializationoffhasaccesstothenonpublicmembersofany
Friendlyspecialization.Thespecializationf<double>()stillisntinstantiatedunlessitisexplicitlycalled.
Likewise,ifyoudeclareanontemplatefunctionwithnoparametersdependentonT,thatsinglefunctionis
afriendtoallinstancesofFriendly:
//InsideFriendly:
friendvoidg(int)//g(int)befriendsallFriendlys
Asalways,sinceg(int)isunqualified,itmustbedefinedatfilescope(thenamespacescopecontaining
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
163/458
5/3/2015
ThinkinginC++2ndedVolume2
Friendly).
ItisalsopossibletoarrangeforallspecializationsofftobefriendsforallspecializationsofFriendly,
withasocalledfriendtemplate,asfollows:
template<classT>classFriendly{
template<classU>friendvoidf<>(constFriendly<U>&)
SincethetemplateargumentforthefrienddeclarationisindependentofT,anycombinationofTandUis
allowed,achievingthefriendshipobjective.Likemembertemplates,friendtemplatescanappearwithin
nontemplateclassesaswell.
Templateprogrammingidioms
Sincelanguageisatoolofthought,newlanguagefeaturestendtospawnnewprogrammingtechniques.In
thissectionwecoversomecommonlyusedtemplateprogrammingidiomsthathaveemergedintheyears
sincetemplateswereaddedtotheC++language.[69]
Traits
Thetraitstemplatetechnique,pioneeredbyNathanMyers,isameansofbundlingtypedependent
declarationstogether.Inessence,usingtraitsyoucanmixandmatchcertaintypesandvalueswith
contextsthatusetheminaflexiblemanner,whilekeepingyourcodereadableandmaintainable.
Thesimplestexampleofatraitstemplateisthenumeric_limitsclasstemplatedefinedin<limits>.The
primarytemplateisdefinedasfollows:
template<classT>classnumeric_limits{
public:
staticconstboolis_specialized=false
staticTmin()throw()
staticTmax()throw()
staticconstintdigits=0
staticconstintdigits10=0
staticconstboolis_signed=false
staticconstboolis_integer=false
staticconstboolis_exact=false
staticconstintradix=0
staticTepsilon()throw()
staticTround_error()throw()
staticconstintmin_exponent=0
staticconstintmin_exponent10=0
staticconstintmax_exponent=0
staticconstintmax_exponent10=0
staticconstboolhas_infinity=false
staticconstboolhas_quiet_NaN=false
staticconstboolhas_signaling_NaN=false
staticconstfloat_denorm_stylehas_denorm=
denorm_absent
staticconstboolhas_denorm_loss=false
staticTinfinity()throw()
staticTquiet_NaN()throw()
staticTsignaling_NaN()throw()
staticTdenorm_min()throw()
staticconstboolis_iec559=false
staticconstboolis_bounded=false
staticconstboolis_modulo=false
staticconstbooltraps=false
staticconstbooltinyness_before=false
staticconstfloat_round_styleround_style=
round_toward_zero
}
The<limits>headerdefinesspecializationsforallfundamental,numerictypes(whenthemember
is_specializedissettotrue).Toobtainthebaseforthedoubleversionofyourfloatingpointnumber
system,forexample,youcanusetheexpressionnumeric_limits<double>::radix.Tofindthesmallest
integervalueavailable,youcanusenumeric_limits<int>::min().Notallmembersofnumeric_limits
applytoallfundamentaltypes.(Forexample,epsilon()isonlymeaningfulforfloatingpointtypes.)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
164/458
5/3/2015
ThinkinginC++2ndedVolume2
Thevaluesthatwillalwaysbeintegralarestaticdatamembersofnumeric_limits.Thosethatmaynotbe
integral,suchastheminimumvalueforfloat,areimplementedasstaticinlinememberfunctions.Thisis
becauseC++allowsonlyintegralstaticdatamemberconstantstobeinitializedinsideaclassdefinition.
InChapter3yousawhowtraitsareusedtocontrolthecharacterprocessingfunctionalityusedbythe
stringclasses.Theclassesstd::stringandstd::wstringarespecializationsofthestd::basic_string
template,whichisdefinedasfollows:
template<classcharT,
classtraits=char_traits<charT>,
classallocator=allocator<charT>>
classbasic_string
ThetemplateparametercharTrepresentstheunderlyingcharactertype,whichisusuallyeithercharor
wchar_t.Theprimarychar_traitstemplateistypicallyempty,andspecializationsforcharandwchar_t
areprovidedbythestandardlibrary.Hereisthespecificationofthespecializationchar_traits<char>
accordingtotheC++Standard:
template<>structchar_traits<char>{
typedefcharchar_type
typedefintint_type
typedefstreamoffoff_type
typedefstreampospos_type
typedefmbstate_tstate_type
staticvoidassign(char_type&c1,constchar_type&c2)
staticbooleq(constchar_type&c1,constchar_type&c2)
staticboollt(constchar_type&c1,constchar_type&c2)
staticintcompare(constchar_type*s1,
constchar_type*s2,size_tn)
staticsize_tlength(constchar_type*s)
staticconstchar_type*find(constchar_type*s,
size_tn,
constchar_type&a)
staticchar_type*move(char_type*s1,
constchar_type*s2,size_tn)
staticchar_type*copy(char_type*s1,
constchar_type*s2,size_tn)
staticchar_type*assign(char_type*s,size_tn,
char_typea)
staticint_typenot_eof(constint_type&c)
staticchar_typeto_char_type(constint_type&c)
staticint_typeto_int_type(constchar_type&c)
staticbooleq_int_type(constint_type&c1,
constint_type&c2)
staticint_typeeof()
}
Thesefunctionsareusedbythebasic_stringclasstemplateforcharacterbasedoperationscommonto
stringprocessing.Whenyoudeclareastringvariable,suchas:
std::strings
youareactuallydeclaringsasfollows(becauseofthedefaulttemplateargumentsinthespecificationof
basic_string):
std::basic_string<char,std::char_traits<char>,
std::allocator<char>>s
Becausethecharactertraitshavebeenseparatedfromthebasic_stringclasstemplate,youcansupplya
customtraitsclasstoreplacestd::char_traits.Thefollowingexampleillustratesthisflexibility:
//:C05:BearCorner.h
#ifndefBEARCORNER_H
#defineBEARCORNER_H
#include<iostream>
usingstd::ostream
//Itemclasses(traitsofguests):
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
165/458
5/3/2015
ThinkinginC++2ndedVolume2
classMilk{
public:
friendostream&operator<<(ostream&os,constMilk&){
returnos<<"Milk"
}
}
classCondensedMilk{
public:
friendostream&
operator<<(ostream&os,constCondensedMilk&){
returnos<<"CondensedMilk"
}
}
classHoney{
public:
friendostream&operator<<(ostream&os,constHoney&){
returnos<<"Honey"
}
}
classCookies{
public:
friendostream&operator<<(ostream&os,constCookies&){
returnos<<"Cookies"
}
}
//Guestclasses:
classBear{
public:
friendostream&operator<<(ostream&os,constBear&){
returnos<<"Theodore"
}
}
classBoy{
public:
friendostream&operator<<(ostream&os,constBoy&){
returnos<<"Patrick"
}
}
//Primarytraitstemplate(emptycouldholdcommontypes)
template<classGuest>classGuestTraits
//TraitsspecializationsforGuesttypes
template<>classGuestTraits<Bear>{
public:
typedefCondensedMilkbeverage_type
typedefHoneysnack_type
}
template<>classGuestTraits<Boy>{
public:
typedefMilkbeverage_type
typedefCookiessnack_type
}
#endif//BEARCORNER_H///:~
//:C05:BearCorner.cpp
//Illustratestraitsclasses.
#include<iostream>
#include"BearCorner.h"
usingnamespacestd
//Acustomtraitsclass
classMixedUpTraits{
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
166/458
5/3/2015
ThinkinginC++2ndedVolume2
public:
typedefMilkbeverage_type
typedefHoneysnack_type
}
//TheGuesttemplate(usesatraitsclass)
template<classGuest,classtraits=GuestTraits<Guest>>
classBearCorner{
GuesttheGuest
typedeftypenametraits::beverage_typebeverage_type
typedeftypenametraits::snack_typesnack_type
beverage_typebev
snack_typesnack
public:
BearCorner(constGuest&g):theGuest(g){}
voidentertain(){
cout<<"Entertaining"<<theGuest
<<"serving"<<bev
<<"and"<<snack<<endl
}
}
intmain(){
Boycr
BearCorner<Boy>pc1(cr)
pc1.entertain()
Bearpb
BearCorner<Bear>pc2(pb)
pc2.entertain()
BearCorner<Bear,MixedUpTraits>pc3(pb)
pc3.entertain()
}///:~
Inthisprogram,instancesoftheguestclassesBoyandBearareserveditemsappropriatetotheirtastes.
Boyslikemilkandcookies,andBearslikecondensedmilkandhoney.Thisassociationofgueststoitems
isdoneviaspecializationsofaprimary(empty)traitsclasstemplate.Thedefaultargumentsto
BearCornerensurethatguestsgettheirproperitems,butyoucanoverridethisbysimplyprovidinga
classthatmeetstherequirementsofthetraitsclass,aswedowiththeMixedUpTraitsclassabove.The
outputofthisprogramis:
EntertainingPatrickservingMilkandCookies
EntertainingTheodoreservingCondensedMilkandHoney
EntertainingTheodoreservingMilkandHoney
Usingtraitsprovidestwokeyadvantages:(1)itallowsflexibilityandextensibilityinpairingobjectswith
associatedattributesorfunctionality,and(2)itkeepstemplateparameterlistssmallandreadable.If30
typeswereassociatedwithaguest,itwouldbeinconvenienttohavetospecifyall30argumentsdirectlyin
eachBearCornerdeclaration.Factoringthetypesintoaseparatetraitsclasssimplifiesthings
considerably.
Thetraitstechniqueisalsousedinimplementingstreamsandlocales,asweshowedinChapter4.An
exampleofiteratortraitsisfoundintheheaderfilePrintSequence.hinChapter6.
Policies
Ifyouinspectthechar_traitsspecializationforwchar_t,youllseethatitispracticallyidenticaltoits
charcounterpart:
template<>structchar_traits<wchar_t>{
typedefwchar_tchar_type
typedefwint_tint_type
typedefstreamoffoff_type
typedefwstreampospos_type
typedefmbstate_tstate_type
staticvoidassign(char_type&c1,constchar_type&c2)
staticbooleq(constchar_type&c1,constchar_type&c2)
staticboollt(constchar_type&c1,constchar_type&c2)
staticintcompare(constchar_type*s1,
constchar_type*s2,size_tn)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
167/458
5/3/2015
ThinkinginC++2ndedVolume2
staticsize_tlength(constchar_type*s)
staticconstchar_type*find(constchar_type*s,
size_tn,
constchar_type&a)
staticchar_type*move(char_type*s1,
constchar_type*s2,size_tn)
staticchar_type*copy(char_type*s1,
constchar_type*s2,size_tn)
staticchar_type*assign(char_type*s,size_tn,
char_typea)
staticint_typenot_eof(constint_type&c)
staticchar_typeto_char_type(constint_type&c)
staticint_typeto_int_type(constchar_type&c)
staticbooleq_int_type(constint_type&c1,
constint_type&c2)
staticint_typeeof()
}
Theonlyrealdifferencebetweenthetwoversionsisthesetoftypesinvolved(charandintvs.wchar_t
andwint_t).Thefunctionalityprovidedisthesame.[70]Thishighlightsthefactthattraitsclassesare
indeedfortraits,andthethingsthatchangebetweenrelatedtraitsclassesareusuallytypesandconstant
values,orfixedalgorithmsthatusetyperelatedtemplateparameters.Traitsclassestendtobetemplates
themselves,sincethetypesandconstantstheycontainareseenascharacteristicsoftheprimarytemplate
parameter(s)(forexample,charandwchar_t).
Itisalsousefultobeabletoassociatefunctionalitywithtemplatearguments,sothatclientprogrammers
caneasilycustomizebehaviorwhentheycode.ThefollowingversionoftheBearCornerprogram,for
instance,supportsdifferenttypesofentertainment:
//:C05:BearCorner2.cpp
//Illustratespolicyclasses.
#include<iostream>
#include"BearCorner.h"
usingnamespacestd
//Policyclasses(requireastaticdoAction()function):
classFeed{
public:
staticconstchar*doAction(){return"Feeding"}
}
classStuff{
public:
staticconstchar*doAction(){return"Stuffing"}
}
//TheGuesttemplate(usesapolicyandatraitsclass)
template<classGuest,classAction,
classtraits=GuestTraits<Guest>>
classBearCorner{
GuesttheGuest
typedeftypenametraits::beverage_typebeverage_type
typedeftypenametraits::snack_typesnack_type
beverage_typebev
snack_typesnack
public:
BearCorner(constGuest&g):theGuest(g){}
voidentertain(){
cout<<Action::doAction()<<""<<theGuest
<<"with"<<bev
<<"and"<<snack<<endl
}
}
intmain(){
Boycr
BearCorner<Boy,Feed>pc1(cr)
pc1.entertain()
Bearpb
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
168/458
5/3/2015
ThinkinginC++2ndedVolume2
BearCorner<Bear,Stuff>pc2(pb)
pc2.entertain()
}///:~
TheActiontemplateparameterintheBearCornerclassexpectstohaveastaticmemberfunctionnamed
doAction(),whichisusedinBearCorner<>::entertain().UserscanchooseFeedorStuffatwill,both
ofwhichprovidetherequiredfunction.Classesthatencapsulatefunctionalityinthiswayarereferredtoas
policyclasses.TheentertainmentpoliciesareprovidedabovethroughFeed::doAction()and
Stuff::doAction().Thesepolicyclasseshappentobeordinaryclasses,buttheycanbetemplates,and
canbecombinedwithinheritancetogreatadvantage.Formoreindepthinformationonpolicybased
design,seeAndreiAlexandrescusbook,[71]thedefinitivesourceonthesubject.
Thecuriouslyrecurringtemplatepattern
AnynoviceC++programmercanfigureouthowtomodifyaclasstokeeptrackofthenumberofobjects
ofthatclassthatcurrentlyexist.Allyouhavetodoistoaddstaticmembers,andmodifyconstructorand
destructorlogic,asfollows:
//:C05:CountedClass.cpp
//Objectcountingviastaticmembers.
#include<iostream>
usingnamespacestd
classCountedClass{
staticintcount
public:
CountedClass(){++count}
CountedClass(constCountedClass&){++count}
~CountedClass(){count}
staticintgetCount(){returncount}
}
intCountedClass::count=0
intmain(){
CountedClassa
cout<<CountedClass::getCount()<<endl//1
CountedClassb
cout<<CountedClass::getCount()<<endl//2
{//Anarbitraryscope:
CountedClassc(b)
cout<<CountedClass::getCount()<<endl//3
a=c
cout<<CountedClass::getCount()<<endl//3
}
cout<<CountedClass::getCount()<<endl//2
}///:~
AllconstructorsofCountedClassincrementthestaticdatamembercount,andthedestructordecrements
it.ThestaticmemberfunctiongetCount()yieldsthecurrentnumberofobjects.
Itwouldbetedioustomanuallyaddthesememberseverytimeyouwantedtoaddobjectcountingtoa
class.Theusualobjectorienteddeviceusedtorepeatorsharecodeisinheritance,whichisonlyhalfa
solutioninthiscase.Observewhathappenswhenwecollectthecountinglogicintoabaseclass.
//:C05:CountedClass2.cpp
//Erroneousattempttocountobjects.
#include<iostream>
usingnamespacestd
classCounted{
staticintcount
public:
Counted(){++count}
Counted(constCounted&){++count}
~Counted(){count}
staticintgetCount(){returncount}
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
169/458
5/3/2015
ThinkinginC++2ndedVolume2
intCounted::count=0
classCountedClass:publicCounted{}
classCountedClass2:publicCounted{}
intmain(){
CountedClassa
cout<<CountedClass::getCount()<<endl//1
CountedClassb
cout<<CountedClass::getCount()<<endl//2
CountedClass2c
cout<<CountedClass2::getCount()<<endl//3(Error)
}///:~
AllclassesthatderivefromCountedsharethesame,singlestaticdatamember,sothenumberofobjects
istrackedcollectivelyacrossallclassesintheCountedhierarchy.Whatisneededisawayto
automaticallygenerateadifferentbaseclassforeachderivedclass.Thisisaccomplishedbythecurious
templateconstructillustratedbelow:
//:C05:CountedClass3.cpp
#include<iostream>
usingnamespacestd
template<classT>classCounted{
staticintcount
public:
Counted(){++count}
Counted(constCounted<T>&){++count}
~Counted(){count}
staticintgetCount(){returncount}
}
template<classT>intCounted<T>::count=0
//Curiousclassdefinitions
classCountedClass:publicCounted<CountedClass>{}
classCountedClass2:publicCounted<CountedClass2>{}
intmain(){
CountedClassa
cout<<CountedClass::getCount()<<endl//1
CountedClassb
cout<<CountedClass::getCount()<<endl//2
CountedClass2c
cout<<CountedClass2::getCount()<<endl//1(!)
}///:~
Eachderivedclassderivesfromauniquebaseclassthatisdeterminedbyusingitself(thederivedclass)
asatemplateparameter!Thismayseemlikeacirculardefinition,anditwouldbe,hadanybaseclass
membersusedthetemplateargumentinacomputation.SincenodatamembersofCountedare
dependentonT,thesizeofCounted(whichiszero!)isknownwhenthetemplateisparsed.Soitdoesnt
matterwhichargumentisusedtoinstantiateCountedbecausethesizeisalwaysthesame.Anyderivation
fromaninstanceofCountedcanbecompletedwhenitisparsed,andthereisnorecursion.Sinceeach
baseclassisunique,ithasitsownstaticdata,thusconstitutingahandytechniqueforaddingcountingto
anyclasswhatsoever.JimCoplienwasthefirsttomentionthisinterestingderivationidiominprint,which
hecitedinanarticle,entitledCuriouslyRecurringTemplatePatterns. [72]
Templatemetaprogramming
In1993compilerswerebeginningtosupportsimpletemplateconstructssothatuserscoulddefinegeneric
containersandfunctions.AboutthesametimethattheSTLwasbeingconsideredforadoptioninto
StandardC++,cleverandsurprisingexamplessuchasthefollowingwerepassedaroundamongmembers
oftheC++StandardsCommittee:[73]
//:C05:Factorial.cpp
//Compiletimecomputationusingtemplates.
#include<iostream>
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
170/458
5/3/2015
ThinkinginC++2ndedVolume2
template<intn>structFactorial{
enum{val=Factorial<n1>::val*n}
}
template<>structFactorial<0>{
enum{val=1}
}
intmain(){
cout<<Factorial<12>::val<<endl//479001600
}///:~
Thatthisprogramprintsthecorrectvalueof12!isnotalarming.Whatisalarmingisthatthecomputation
iscompletebeforetheprogramevenruns!
WhenthecompilerattemptstoinstantiateFactorial<12>,itfindsitmustalsoinstantiate
Factorial<11>,whichrequiresFactorial<10>,andsoon.Eventuallytherecursionendswiththe
specializationFactorial<1>,andthecomputationunwinds.Eventually,Factorial<12>::valisreplaced
bytheintegralconstant479001600,andcompilationends.Sinceallthecomputationisdonebythe
compiler,thevaluesinvolvedmustbecompiletimeconstants,hencetheuseofenum.Whentheprogram
runs,theonlyworklefttodoisprintthatconstantfollowedbyanewline.Toconvinceyourselfthata
specializationofFactorialresultsinthecorrectcompiletimevalue,youcoulduseitasanarray
dimension,suchas:
doublenums[Factorial<5>::val]
assert(sizeofnums==sizeof(double)*120)
Compiletimeprogramming
Sowhatwasmeanttobeaconvenientwaytoperformtypeparametersubstitutionturnedouttobea
mechanismtosupportcompiletimeprogramming.Suchaprogramiscalledatemplatemetaprogram
(sinceyoureineffectprogrammingaprogram),anditturnsoutthatyoucandoquitealotwithit.In
fact,templatemetaprogrammingisTuringcompletebecauseitsupportsselection(ifelse)andlooping
(throughrecursion).Theoretically,then,youcanperformanycomputationwithit.[74]Thefactorialexample
aboveshowshowtoimplementrepetition:writearecursivetemplateandprovideastoppingcriterionviaa
specialization.ThefollowingexampleshowshowtocomputeFibonaccinumbersatcompiletimebythe
sametechnique:
//:C05:Fibonacci.cpp
#include<iostream>
usingnamespacestd
template<intn>structFib{
enum{val=Fib<n1>::val+Fib<n2>::val}
}
template<>structFib<1>{enum{val=1}}
template<>structFib<0>{enum{val=0}}
intmain(){
cout<<Fib<5>::val<<endl//6
cout<<Fib<20>::val<<endl//6765
}///:~
Fibonaccinumbersaredefinedmathematicallyas:
Thefirsttwocasesleadtothetemplatespecializationsabove,andtheruleinthethirdlinebecomesthe
primarytemplate.
Compiletimelooping
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
171/458
5/3/2015
ThinkinginC++2ndedVolume2
Tocomputeanyloopinatemplatemetaprogram,itmustfirstbereformulatedrecursively.Forexample,
toraisetheintegerntothepowerp,insteadofusingaloopsuchasinthefollowinglines:
intval=1
while(p)
val*=n
youcastitasarecursiveprocedure:
intpower(intn,intp){
return(p==0)?1:n*power(n,p1)
}
Thiscannowbeeasilyrenderedasatemplatemetaprogram:
//:C05:Power.cpp
#include<iostream>
usingnamespacestd
template<intN,intP>structPower{
enum{val=N*Power<N,P1>::val}
}
template<intN>structPower<N,0>{
enum{val=1}
}
intmain(){
cout<<Power<2,5>::val<<endl//32
}///:~
Weneedtouseapartialspecializationforthestoppingcondition,sincethevalueNisstillafreetemplate
parameter.Notethatthisprogramonlyworksfornonnegativepowers.
ThefollowingmetaprogramadaptedfromCzarneckiandEisenecker[75]isinterestinginthatitusesa
templatetemplateparameter,andsimulatespassingafunctionasaparametertoanotherfunction,which
loopsthroughthenumbers0..n:
//:C05:Accumulate.cpp
//Passesa"function"asaparameteratcompiletime.
#include<iostream>
usingnamespacestd
//AccumulatestheresultsofF(0)..F(n)
template<intn,template<int>classF>structAccumulate{
enum{val=Accumulate<n1,F>::val+F<n>::val}
}
//Thestoppingcriterion(returnsthevalueF(0))
template<template<int>classF>structAccumulate<0,F>{
enum{val=F<0>::val}
}
//Various"functions":
template<intn>structIdentity{
enum{val=n}
}
template<intn>structSquare{
enum{val=n*n}
}
template<intn>structCube{
enum{val=n*n*n}
}
intmain(){
cout<<Accumulate<4,Identity>::val<<endl//10
cout<<Accumulate<4,Square>::val<<endl//30
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
172/458
5/3/2015
ThinkinginC++2ndedVolume2
cout<<Accumulate<4,Cube>::val<<endl//100
}///:~
TheprimaryAccumulatetemplateattemptstocomputethesumF(n)+F(n1)F(0).Thestopping
criterionisobtainedbyapartialspecialization,whichreturnsF(0).TheparameterFisitselfatemplate,
andactslikeafunctionasinthepreviousexamplesinthissection.ThetemplatesIdentity,Square,and
Cubecomputethecorrespondingfunctionsoftheirtemplateparameterthattheirnamessuggest.Thefirst
instantiationofAccumulateinmain()computesthesum4+3+2+1+0,becausetheIdentityfunction
simplyreturnsitstemplateparameter.Thesecondlineinmain()addsthesquaresofthosenumbers
(16+9+4+1+0),andthelastcomputesthesumofthecubes(64+27+8+1+0).
Loopunrolling
Algorithmdesignershavealwaysendeavoredtooptimizetheirprograms.Onetimehonoredoptimization,
especiallyfornumericprogramming,isloopunrolling,atechniquethatminimizesloopoverhead.The
quintessentialloopunrollingexampleismatrixmultiplication.Thefollowingfunctionmultipliesamatrix
andavector.(AssumethattheconstantsROWSandCOLShavebeenpreviouslydefined.):
voidmult(inta[ROWS][COLS],intx[COLS],inty[COLS]){
for(inti=0i<ROWS++i){
y[i]=0
for(intj=0j<COLS++j)
y[i]+=a[i][j]*x[j]
}
}
IfCOLSisanevennumber,theoverheadofincrementingandcomparingtheloopcontrolvariablejcanbe
cutinhalfbyunrollingthecomputationintopairsintheinnerloop:
voidmult(inta[ROWS][COLS],intx[COLS],inty[COLS]){
for(inti=0i<ROWS++i){
y[i]=0
for(intj=0j<COLSj+=2)
y[i]+=a[i][j]*x[j]+a[i][j+1]*x[j+1]
}
}
Ingeneral,ifCOLSisafactorofk,koperationscanbeperformedeachtimetheinnerloopiterates,
greatlyreducingtheoverhead.Thesavingsisonlynoticeableonlargearrays,butthatispreciselythecase
withindustrialstrengthmathematicalcomputations.
Functioninliningalsoconstitutesaformofloopunrolling.Considerthefollowingapproachtocomputing
powersofintegers:
//:C05:Unroll.cpp
//Unrollsanimplicitloopviainlining.
#include<iostream>
usingnamespacestd
template<intn>inlineintpower(intm){
returnpower<n1>(m)*m
}
template<>inlineintpower<1>(intm){
returnm
}
template<>inlineintpower<0>(intm){
return1
}
intmain(){
intm=4
cout<<power<3>(m)<<endl
}///:~
Conceptually,thecompilermustgeneratethreespecializationsofpower<>,oneeachforthetemplate
parameters3,2,and1.Becausethecodeforeachofthesefunctionscanbeinlined,theactualcodethatis
insertedintomain()isthesingleexpressionm*m*m.Thus,asimpletemplatespecializationcoupled
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
[76]
173/458
5/3/2015
ThinkinginC++2ndedVolume2
withinliningprovidesawaytototallyavoidloopcontroloverhead.[76]Thisapproachtoloopunrollingis
limitedbyyourcompilersinliningdepth.
Compiletimeselection
Tosimulateconditionalsatcompiletime,youcanusetheconditionalternaryoperatorinanenum
declaration.Thefollowingprogramusesthistechniquetocalculatethemaximumoftwointegersat
compiletime:
//:C05:Max.cpp
#include<iostream>
usingnamespacestd
template<intn1,intn2>structMax{
enum{val=n1>n2?n1:n2}
}
intmain(){
cout<<Max<10,20>::val<<endl//20
}///:~
Ifyouwanttousecompiletimeconditionstogoverncustomcodegeneration,youcanusespecializations
ofthevaluestrueandfalse:
//:C05:Conditionals.cpp
//Usescompiletimeconditionstochoosecode.
#include<iostream>
usingnamespacestd
template<boolcond>structSelect{}
template<>classSelect<true>{
staticvoidstatement1(){
cout<<"Thisisstatement1executing\n"
}
public:
staticvoidf(){statement1()}
}
template<>classSelect<false>{
staticvoidstatement2(){
cout<<"Thisisstatement2executing\n"
}
public:
staticvoidf(){statement2()}
}
template<boolcond>voidexecute(){
Select<cond>::f()
}
intmain(){
execute<sizeof(int)==4>()
}///:~
Thisprogramisequivalenttotheexpression:
if(cond)
statement1()
else
statement2()
exceptthattheconditioncondisevaluatedatcompiletime,andtheappropriateversionsofexecute<>
()andSelect<>areinstantiatedbythecompiler.ThefunctionSelect<>::f()executesatruntime.A
switchstatementcanbeemulatedinsimilarfashion,butspecializingoneachcasevalueinsteadofthe
valuestrueandfalse.
Compiletimeassertions
InChapter2wetoutedthevirtuesofusingassertionsaspartofanoveralldefensiveprogramming
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
174/458
5/3/2015
ThinkinginC++2ndedVolume2
strategy.AnassertionisbasicallyanevaluationofaBooleanexpressionfollowedbyasuitableaction:do
nothingiftheconditionistrue,orhaltwithadiagnosticmessageotherwise.Itsbesttodiscoverassertion
failuresassoonaspossible.Ifyoucanevaluateanexpressionatcompiletime,useacompiletime
assertion.ThefollowingexampleusesatechniquethatmapsaBooleanexpressiontoanarraydeclaration:
//:C05:StaticAssert1.cpp{xo}
//Asimple,compiletimeassertionfacility
#defineSTATIC_ASSERT(x)\
do{typedefinta[(x)?1:1]}while(0)
intmain(){
STATIC_ASSERT(sizeof(int)<=sizeof(long))//Passes
STATIC_ASSERT(sizeof(double)<=sizeof(int))//Fails
}///:~
Thedoloopcreatesatemporaryscopeforthedefinitionofanarray,a,whosesizeisdeterminedbythe
conditioninquestion.Itisillegaltodefineanarrayofsize1,sowhentheconditionisfalsethestatement
shouldfail.
TheprevioussectionshowedhowtoevaluatecompiletimeBooleanexpressions.Theremainingchallenge
inemulatingassertionsatcompiletimeistoprintameaningfulerrormessageandhalt.Allthatisrequired
tohaltthecompilerisacompileerrorthetrickistoinserthelpfultextintheerrormessage.Thefollowing
examplefromAlexandrescu[77]usestemplatespecialization,alocalclass,andalittlemacromagictodo
thejob:
//:C05:StaticAssert2.cpp{g++}
#include<iostream>
usingnamespacestd
//Atemplateandaspecialization
template<bool>structStaticCheck{
StaticCheck(...)
}
template<>structStaticCheck<false>{}
//Themacro(generatesalocalclass)
#defineSTATIC_CHECK(expr,msg){\
classError_##msg{}\
sizeof((StaticCheck<expr>(Error_##msg())))\
}
//Detectsnarrowingconversions
template<classTo,classFrom>Tosafe_cast(Fromfrom){
STATIC_CHECK(sizeof(From)<=sizeof(To),
NarrowingConversion)
returnreinterpret_cast<To>(from)
}
intmain(){
void*p=0
inti=safe_cast<int>(p)
cout<<"intcastokay<<endl
//!charc=safe_cast<char>(p)
}///:~
Thisexampledefinesafunctiontemplate,safe_cast<>(),thatcheckstoseeiftheobjectitiscasting
fromisnolargerthanthetypeofobjectitcaststo.Ifthesizeofthetargetobjecttypeissmaller,thenthe
userwillbenotifiedatcompiletimethatanarrowingconversionwasattempted.Noticethatthe
StaticCheckclasstemplatehasthecuriousfeaturethatanythingcanbeconvertedtoaninstanceof
StaticCheck<true>(becauseoftheellipsisinitsconstructor[78]),andnothingcanbeconvertedtoa
StaticCheck<false>becausenoconversionsaresuppliedforthatspecialization.Theideaistoattemptto
createaninstanceofanewclassandattempttoconvertittoStaticCheck<true>atcompiletime
whenevertheconditionofinterestistrue,ortoaStaticCheck<false>objectwhentheconditionbeing
testedisfalse.Sincethesizeofoperatordoesitsworkatcompiletime,itisusedtoattemptthe
conversion.Iftheconditionisfalse,thecompilerwillcomplainthatitdoesntknowhowtoconvertfrom
thenewclasstypetoStaticCheck<false>.(Theextraparenthesesinsidethesizeofinvocationin
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
175/458
5/3/2015
ThinkinginC++2ndedVolume2
STATIC_CHECK()aretopreventthecompilerfromthinkingthatweretryingtoinvokesizeofona
function,whichisillegal.)Togetsomemeaningfulinformationinsertedintotheerrormessage,thenew
classnamecarrieskeytextinitsname.
Thebestwaytounderstandthistechniqueistowalkthroughaspecificcase.Considerthelineinmain()
abovewhichreads:
inti=safe_cast<int>(p)
Thecalltosafe_cast<int>(p)containsthefollowingmacroexpansionreplacingitsfirstlineofcode:
{\
classError_NarrowingConversion{}\
sizeof(StaticCheck<sizeof(void*)<=sizeof(int)>\
(Error_NarrowingConversion()))\
}
(Recallthatthetokenpastingpreprocessingoperator,##,concatenatesitsoperandintoasingletoken,so
Error_##NarrowingConversionbecomesthetokenError_NarrowingConversionafter
preprocessing).TheclassError_NarrowingConversionisalocalclass(meaningthatitisdeclaredinside
anonnamespacescope)becauseitisnotneededelsewhereintheprogram.Theapplicationofthesizeof
operatorhereattemptstodeterminethesizeofaninstanceofStaticCheck<true>(because
sizeof(void*)<=sizeof(int)istrueonourplatforms),createdimplicitlyfromthetemporaryobject
returnedbythecallError_NarrowingConversion().Thecompilerknowsthesizeofthenewclass
Error_NarrowingConversion(itsempty),andsothecompiletimeuseofsizeofattheouterlevelin
STATIC_CHECK()isvalid.SincetheconversionfromtheError_NarrowingConversiontemporaryto
StaticCheck<true>succeeds,sodoesthisouterapplicationofsizeof,andexecutioncontinues.
Nowconsiderwhatwouldhappenifthecommentwereremovedfromthelastlineofmain():
charc=safe_cast<char>(p)
HeretheSTATIC_CHECK()macroinsidesafe_cast<char>(p)expandsto:
{\
classError_NarrowingConversion{}\
sizeof(StaticCheck<sizeof(void*)<=sizeof(char)>\
(Error_NarrowingConversion()))\
}
Sincetheexpressionsizeof(void*)<=sizeof(char)isfalse,aconversionfroman
Error_NarrowingConversiontemporarytoStaticCheck<false>isattempted,asfollows:
sizeof(StaticCheck<false>(Error_NarrowingConversion()))
whichfails,sothecompilerhaltswithamessagesomethinglikethefollowing:
Cannotcastfrom'Error_NarrowingConversion'to'StaticCheck<0>'infunction
charsafe_cast<char,void*>(void*)
TheclassnameError_NarrowingConversionisthemeaningfulmessagejudiciouslyarrangedbythe
coder.Ingeneral,toperformastaticassertionwiththistechnique,youjustinvoketheSTATIC_CHECK
macrowiththecompiletimeconditiontocheckandwithameaningfulnametodescribetheerror.
Expressiontemplates
Perhapsthemostpowerfulapplicationoftemplatesisatechniquediscoveredindependentlyin1994by
ToddVeldhuizen[79]andDaveedVandevoorde:[80]expressiontemplates.Expressiontemplatesenable
extensivecompiletimeoptimizationofcertaincomputationsthatresultincodethatisatleastasfastas
handoptimizedFortran,andyetpreservesthenaturalnotationofmathematicsviaoperatoroverloading.
Althoughyouwouldntbelikelytousethistechniqueineverydayprogramming,itisthebasisforanumber
ofsophisticated,highperformancemathematicallibrarieswritteninC++.[81]
Tomotivatetheneedforexpressiontemplates,considertypicalnumericallinearalgebraoperations,such
asaddingtogethertwomatricesorvectors,[82]suchasinthefollowing:
D=A+B+C
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
176/458
5/3/2015
ThinkinginC++2ndedVolume2
Innaiveimplementations,thisexpressionwouldresultinanumberoftemporariesoneforA+B,andone
for(A+B)+C.Whenthesevariablesrepresentimmensematricesorvectors,thecoincidentdrainon
resourcesisunacceptable.Expressiontemplatesallowyoutousethesameexpressionwithout
temporaries.
ThefollowingprogramdefinesaMyVectorclasstosimulatemathematicalvectorsofanysize.Weusea
nontypetemplateargumentforthelengthofthevector.WealsodefineaMyVectorSumclasstoactasa
proxyclassforasumofMyVectorobjects.Thisallowsustouselazyevaluation,sotheadditionofvector
componentsisperformedondemandwithouttheneedfortemporaries.
//:C05:MyVector.cpp
//Optimizesawaytemporariesviatemplates.
#include<cstddef>
#include<cstdlib>
#include<ctime>
#include<iostream>
usingnamespacestd
//Aproxyclassforsumsofvectors
template<class,size_t>classMyVectorSum
template<classT,size_tN>classMyVector{
Tdata[N]
public:
MyVector<T,N>&operator=(constMyVector<T,N>&right){
for(size_ti=0i<N++i)
data[i]=right.data[i]
return*this
}
MyVector<T,N>&operator=(constMyVectorSum<T,N>&right)
constT&operator[](size_ti)const{returndata[i]}
T&operator[](size_ti){returndata[i]}
}
//Proxyclassholdreferencesuseslazyaddition
template<classT,size_tN>classMyVectorSum{
constMyVector<T,N>&left
constMyVector<T,N>&right
public:
MyVectorSum(constMyVector<T,N>&lhs,
constMyVector<T,N>&rhs)
:left(lhs),right(rhs){}
Toperator[](size_ti)const{
returnleft[i]+right[i]
}
}
//Operatortosupportv3=v1+v2
template<classT,size_tN>MyVector<T,N>&
MyVector<T,N>::operator=(constMyVectorSum<T,N>&right){
for(size_ti=0i<N++i)
data[i]=right[i]
return*this
}
//operator+juststoresreferences
template<classT,size_tN>inlineMyVectorSum<T,N>
operator+(constMyVector<T,N>&left,
constMyVector<T,N>&right){
returnMyVectorSum<T,N>(left,right)
}
//Conveniencefunctionsforthetestprogrambelow
template<classT,size_tN>voidinit(MyVector<T,N>&v){
for(size_ti=0i<N++i)
v[i]=rand()%100
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
177/458
5/3/2015
ThinkinginC++2ndedVolume2
template<classT,size_tN>voidprint(MyVector<T,N>&v){
for(size_ti=0i<N++i)
cout<<v[i]<<''
cout<<endl
}
intmain(){
srand(time(0))
MyVector<int,5>v1
init(v1)
print(v1)
MyVector<int,5>v2
init(v2)
print(v2)
MyVector<int,5>v3
v3=v1+v2
print(v3)
MyVector<int,5>v4
//Notyetsupported:
//!v4=v1+v2+v3
}///:~
TheMyVectorSumclassdoesnocomputationwhenitiscreateditmerelyholdsreferencestothetwo
vectorstobeadded.Calculationshappenonlywhenyouaccessacomponentofavectorsum(seeits
operator[]()).TheoverloadoftheassignmentoperatorforMyVectorthattakesaMyVectorSum
argumentisforanexpressionsuchas:
v1=v2+v3//Addtwovectors
Whentheexpressionv1+v2isevaluated,aMyVectorSumobjectisreturned(oractually,insertedinline,
sincethatoperator+()isdeclaredinline).Thisisasmall,fixedsizeobject(itholdsonlytwo
references).Thentheassignmentoperatormentionedaboveisinvoked:
v3.operator=<int,5>(MyVectorSum<int,5>(v2,v3))
Thisassignstoeachelementofv3thesumofthecorrespondingelementsofv1andv2,computedinreal
time.NotemporaryMyVectorobjectsarecreated.
Thisprogramdoesnotsupportanexpressionthathasmorethantwooperands,however,suchas
v4=v1+v2+v3
Thereasonisthat,afterthefirstaddition,asecondadditionisattempted:
(v1+v2)+v3
whichwouldrequireanoperator+()withafirstargumentofMyVectorSumandasecondargumentof
typeMyVector.Wecouldattempttoprovideanumberofoverloadstomeetallsituations,butitisbetter
tolettemplatesdothework,asinthefollowingversionoftheprogram:
//:C05:MyVector2.cpp
//Handlessumsofanylengthwithexpressiontemplates.
#include<cstddef>
#include<cstdlib>
#include<ctime>
#include<iostream>
usingnamespacestd
//Aproxyclassforsumsofvectors
template<class,size_t,class,class>classMyVectorSum
template<classT,size_tN>classMyVector{
Tdata[N]
public:
MyVector<T,N>&operator=(constMyVector<T,N>&right){
for(size_ti=0i<N++i)
data[i]=right.data[i]
return*this
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
178/458
5/3/2015
ThinkinginC++2ndedVolume2
}
template<classLeft,classRight>MyVector<T,N>&
operator=(constMyVectorSum<T,N,Left,Right>&right)
constT&operator[](size_ti)const{
returndata[i]
}
T&operator[](size_ti){
returndata[i]
}
}
//AllowsmixingMyVectorandMyVectorSum
template<classT,size_tN,classLeft,classRight>
classMyVectorSum{
constLeft&left
constRight&right
public:
MyVectorSum(constLeft&lhs,constRight&rhs)
:left(lhs),right(rhs){}
Toperator[](size_ti)const{
returnleft[i]+right[i]
}
}
template<classT,size_tN>
template<classLeft,classRight>
MyVector<T,N>&
MyVector<T,N>::
operator=(constMyVectorSum<T,N,Left,Right>&right){
for(size_ti=0i<N++i)
data[i]=right[i]
return*this
}
//operator+juststoresreferences
template<classT,size_tN>
inlineMyVectorSum<T,N,MyVector<T,N>,MyVector<T,N>>
operator+(constMyVector<T,N>&left,
constMyVector<T,N>&right){
returnMyVectorSum<T,N,MyVector<T,N>,MyVector<T,N>>
(left,right)
}
template<classT,size_tN,classLeft,classRight>
inlineMyVectorSum<T,N,MyVectorSum<T,N,Left,Right>,
MyVector<T,N>>
operator+(constMyVectorSum<T,N,Left,Right>&left,
constMyVector<T,N>&right){
returnMyVectorSum<T,N,MyVectorSum<T,N,Left,Right>,
MyVector<T,N>>
(left,right)
}
//Conveniencefunctionsforthetestprogrambelow
template<classT,size_tN>voidinit(MyVector<T,N>&v){
for(size_ti=0i<N++i)
v[i]=rand()%100
}
template<classT,size_tN>voidprint(MyVector<T,N>&v){
for(size_ti=0i<N++i)
cout<<v[i]<<''
cout<<endl
}
intmain(){
srand(time(0))
MyVector<int,5>v1
init(v1)
print(v1)
MyVector<int,5>v2
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
179/458
5/3/2015
ThinkinginC++2ndedVolume2
init(v2)
print(v2)
MyVector<int,5>v3
v3=v1+v2
print(v3)
//Nowsupported:
MyVector<int,5>v4
v4=v1+v2+v3
print(v4)
MyVector<int,5>v5
v5=v1+v2+v3+v4
print(v5)
}///:~
Thetemplatefacilitydeducestheargumenttypesofasumusingthetemplatearguments,LeftandRight,
insteadofcommittingtothosetypesaheadoftime.TheMyVectorSumtemplatetakestheseextratwo
parameterssoitcanrepresentasumofanycombinationofpairsofMyVectorandMyVectorSum.
Theassignmentoperatorisnowamemberfunctiontemplate.Thisallowsany<T,N>pairtobecoupled
withany<Left,Right>pair,soaMyVectorobjectcanbeassignedfromaMyVectorSumholding
referencestoanypossiblepairofthetypesMyVectorandMyVectorSum.
Aswedidbefore,letstracethroughasampleassignmenttounderstandexactlywhattakesplace,
beginningwiththeexpression
v4=v1+v2+v3
Sincetheresultingexpressionsbecomequiteunwieldy,intheexplanationthatfollows,wewilluseMVSas
shorthandforMyVectorSum,andwillomitthetemplatearguments.
Thefirstoperationisv1+v2,whichinvokestheinlineoperator+(),whichinturninsertsMVS(v1,v2)
intothecompilationstream.Thisisthenaddedtov3,whichresultsinatemporaryobjectaccordingtothe
expressionMVS(MVS(v1,v2),v3).Thefinalrepresentationoftheentirestatementis
v4.operator+(MVS(MVS(v1,v2),v3))
Thistransformationisallarrangedbythecompilerandexplainswhythistechniquecarriesthemoniker
expressiontemplates.ThetemplateMyVectorSumrepresentsanexpression(anaddition,inthiscase),
andthenestedcallsabovearereminiscentoftheparsetreeoftheleftassociativeexpressionv1+v2+v3.
AnexcellentarticlebyAngelikaLangerandKlausKreftexplainshowthistechniquecanbeextendedto
morecomplexcomputations.[83]
Templatecompilationmodels
Youmayhavenoticedthatallourtemplateexamplesplacefullydefinedtemplateswithineachcompilation
unit.(Forexample,weplacethemcompletelywithinsinglefileprograms,orinheaderfilesformultifile
programs.)Thisrunscountertotheconventionalpracticeofseparatingordinaryfunctiondefinitionsfrom
theirdeclarationsbyplacingthelatterinheaderfilesandthefunctionimplementationsinseparate(thatis,
.cpp)files.
Thereasonsforthistraditionalseparationare:
Noninlinefunctionbodiesinheaderfilesleadtomultiplefunctiondefinitions,resultinginlinker
errors.
Hidingtheimplementationfromclientshelpsreducecompiletimecoupling.
Vendorscandistributeprecompiledcode(foraparticularcompiler)alongwithheaderssothat
userscannotseethefunctionimplementations.
Compiletimesareshortersinceheaderfilesaresmaller.
Theinclusionmodel
Templates,ontheotherhand,arenotcodeperse,butinstructionsforcodegeneration.Onlytemplate
instantiationsarerealcode.Whenacompilerhasseenacompletetemplatedefinitionduringacompilation
andthenencountersapointofinstantiationforthattemplateinthesametranslationunit,itmustdealwith
thefactthatanequivalentpointofinstantiationmaybepresentinanothertranslationunit.Themost
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
180/458
5/3/2015
ThinkinginC++2ndedVolume2
commonapproachconsistsofgeneratingthecodefortheinstantiationineverytranslationunitandletting
thelinkerweedoutduplicates.Thatparticularapproachalsoworkswellwithinlinefunctionsthatcannotbe
inlinedandwithvirtualfunctiontables,whichisoneofthereasonsforitspopularity.Nonetheless,several
compilerspreferinsteadtorelyonmorecomplexschemestoavoidgeneratingaparticularinstantiation
morethanonce.Eitherway,itistheresponsibilityoftheC++translationsystemtoavoiderrorsdueto
multipleequivalentpointsofinstantiation.
Adrawbackofthisapproachisthatalltemplatesourcecodeisvisibletotheclient,sothereislittle
opportunityforlibraryvendorstohidetheirimplementationstrategies.Anotherdisadvantageofthe
inclusionmodelisthatheaderfilesaremuchlargerthantheywouldbeiffunctionbodieswerecompiled
separately.Thiscanincreasecompiletimesdramaticallyovertraditionalcompilationmodels.
Tohelpreducethelargeheadersrequiredbytheinclusionmodel,C++offerstwo(nonexclusive)
alternativecodeorganizationmechanisms:youcanmanuallyinstantiateeachspecializationusingexplicit
instantiationoryoucanuseexportedtemplates,whichsupportalargedegreeofseparatecompilation.
Explicitinstantiation
Youcanmanuallydirectthecompilertoinstantiateanytemplatespecializationsofyourchoice.Whenyou
usethistechnique,theremustbeoneandonlyonesuchdirectiveforeachsuchspecializationotherwise
youmightgetmultipledefinitionerrors,justasyouwouldwithordinary,noninlinefunctionswithidentical
signatures.Toillustrate,wefirst(erroneously)separatethedeclarationofthemin()templatefrom
earlierinthischapterfromitsdefinition,followingthenormalpatternforordinary,noninlinefunctions.
Thefollowingexampleconsistsoffivefiles:
OurMin.h:containsthedeclarationofthemin()functiontemplate.
OurMin.cpp:containsthedefinitionofthemin()functiontemplate.
UseMin1.cpp:attemptstouseanintinstantiationofmin().
UseMin2.cpp:attemptstouseadoubleinstantiationofmin().
MinMain.cpp:callsusemin1()andusemin2().
//:C05:OurMin.h
#ifndefOURMIN_H
#defineOURMIN_H
//Thedeclarationofmin()
template<typenameT>constT&min(constT&,constT&)
#endif//OURMIN_H///:~
//OurMin.cpp
#include"OurMin.h"
//Thedefinitionofmin()
template<typenameT>constT&min(constT&a,constT&b){
return(a<b)?a:b
}
//:C05:UseMin1.cpp{O}
#include<iostream>
#include"OurMin.h"
voidusemin1(){
std::cout<<min(1,2)<<std::endl
}///:~
//:C05:UseMin2.cpp{O}
#include<iostream>
#include"OurMin.h"
voidusemin2(){
std::cout<<min(3.1,4.2)<<std::endl
}///:~
//:C05:MinMain.cpp
//{L}UseMin1UseMin2MinInstances
voidusemin1()
voidusemin2()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
181/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
usemin1()
usemin2()
}///:~
Whenweattempttobuildthisprogram,thelinkerreportsunresolvedexternalreferencesformin<int>()
andmin<double>().Thereasonisthatwhenthecompilerencountersthecallstospecializationsof
min()inUseMin1andUseMin2,onlythedeclarationofmin()isvisible.Sincethedefinitionisnot
available,thecompilerassumesitwillcomefromsomeothertranslationunit,andtheneeded
specializationsarethusnotinstantiatedatthatpoint,leavingthelinkertoeventuallycomplainthatit
cannotfindthem.
Tosolvethisproblem,wewillintroduceanewfile,MinInstances.cpp,thatexplicitlyinstantiatesthe
neededspecializationsofmin():
//:C05:MinInstances.cpp{O}
#include"OurMin.cpp"
//ExplicitInstantiationsforintanddouble
templateconstint&min<int>(constint&,constint&)
templateconstdouble&min<double>(constdouble&,
constdouble&)
///:~
Tomanuallyinstantiateaparticulartemplatespecialization,youprecedethespecializationsdeclaration
withthetemplatekeyword.NotethatwemustincludeOurMin.cpp,notOurMin.h,here,becausethe
compilerneedsthetemplatedefinitiontoperformtheinstantiation.Thisistheonlyplacewherewehaveto
dothisinthisprogram,[84]however,sinceitgivesustheuniqueinstantiationsofmin()thatweneedthe
declarationsalonesufficefortheotherfiles.SinceweareincludingOurMin.cppwiththemacro
preprocessor,weaddincludeguards:
//:C05:OurMin.cpp{O}
#ifndefOURMIN_CPP
#defineOURMIN_CPP
#include"OurMin.h"
template<typenameT>constT&min(constT&a,constT&b){
return(a<b)?a:b
}
#endif//OURMIN_CPP///:~
Nowwhenwecompileallthefilestogetherintoacompleteprogram,theuniqueinstancesofmin()are
found,andtheprogramexecutescorrectly,givingtheoutput:
1
3.1
Youcanalsomanuallyinstantiateclassesandstaticdatamembers.Whenexplicitlyinstantiatingaclass,all
memberfunctionsfortherequestedspecializationareinstantiated,exceptanythatmayhavebeen
explicitlyinstantiatedpreviously.Thisisimportant,asitwillrendermanytemplatesuselesswhenusing
thismechanismspecifically,templatesthatimplementdifferentfunctionalitydependingontheir
parameterizationtype.Implicitinstantiationhastheadvantagehere:onlymemberfunctionsthatgetcalled
areinstantiated.
Explicitinstantiationisintendedforlargeprojectswhereaheftychunkofcompilationtimecanbeavoided.
Whetheryouuseimplicitorexplicitinstantiationisindependentofwhichtemplatecompilationyouuse.You
canusemanualinstantiationwitheithertheinclusionmodelortheseparationmodel(discussedinthenext
section).
Theseparationmodel
Theseparationmodeloftemplatecompilationseparatesfunctiontemplatedefinitionsorstaticdata
memberdefinitionsfromtheirdeclarationsacrosstranslationunits,justlikeyoudowithordinaryfunctions
anddata,byexportingtemplates.Afterreadingtheprecedingtwosections,thismustsoundstrange.Why
bothertohavetheinclusionmodelinthefirstplaceifyoucanjustadheretothestatusquo?Thereasons
arebothhistoricalandtechnical.
Historically,theinclusionmodelwasthefirsttoexperiencewidespreadcommercialuseallC++compilers
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
182/458
5/3/2015
ThinkinginC++2ndedVolume2
supporttheinclusionmodel.Partofthereasonforthatwasthattheseparationmodelwasnotwell
specifieduntillateinthestandardizationprocess,butalsothattheinclusionmodeliseasiertoimplement.
Alotofworkingcodewasinexistencelongbeforethesemanticsoftheseparationmodelwerefinalized.
Theseparationmodelissodifficulttoimplementthat,asofSummer2003,onlyonecompilerfrontend
(EDG)supportstheseparationmodel,andatthemomentitstillrequiresthattemplatesourcecodebe
availableatcompiletimetoperforminstantiationondemand.Plansareinplacetousesomeformof
intermediatecodeinsteadofrequiringthattheoriginalsourcebeathand,atwhichpointyouwillbeableto
shipprecompiledtemplateswithoutshippingsourcecode.Becauseofthelookupcomplexitiesexplained
earlierinthischapter(aboutdependentnamesbeinglookedupinthetemplatedefinitioncontext),afull
templatedefinitionstillhastobeavailableinsomeformwhenyoucompileaprogramthatinstantiatesit.
Thesyntaxtoseparatethesourcecodeofatemplatedefinitionfromitsdeclarationiseasyenough.You
usetheexportkeyword:
//C05:OurMin2.h
//Declaresminasanexportedtemplate
//(OnlyworkswithEDGbasedcompilers)
#ifndefOURMIN2_H
#defineOURMIN2_H
exporttemplate<typenameT>constT&min(constT&,
constT&)
#endif//OURMIN2_H///:~
Similartoinlineorvirtual,theexportkeywordneedonlybementionedonceinacompilationstream,
whereanexportedtemplateisintroduced.Forthisreason,weneednotrepeatitintheimplementation
file,butitisconsideredgoodpracticetodoso:
//C05:OurMin2.cpp
//Thedefinitionoftheexportedmintemplate
//(OnlyworkswithEDGbasedcompilers)
#include"OurMin2.h"
export
template<typenameT>constT&min(constT&a,constT&b){
return(a<b)?a:b
}///:~
TheUseMinfilesusedpreviouslyonlyneedtoincludethecorrectheaderfile(OurMin2.h),andthemain
programdoesntchange.Althoughthisappearstogivetrueseparation,thefilewiththetemplatedefinition
(OurMin2.cpp)muststillbeshippedtousers(becauseitmustbeprocessedforeachinstantiationof
min())untilsuchtimeassomeformofintermediatecoderepresentationoftemplatedefinitionsis
supported.Sowhilethestandarddoesprovideforatrueseparationmodel,notallofitsbenefitscanbe
reapedtoday.Onlyonefamilyofcompilerscurrentlysupportsexport(thosebasedontheEDGfrontend),
andthesecompilerscurrentlydonotexploitthepotentialabilitytodistributetemplatedefinitionsin
compiledform.
Summary
Templatesgofarbeyondsimpletypeparameterization.Whenyoucombineargumenttypededuction,
customspecialization,andtemplatemetaprogramming,C++templatesemergeasapowerfulcode
generationmechanism.
OneoftheweaknessesofC++templatesthatwedidnotmentionisthedifficultyininterpretingcompile
timeerrormessages.Thequantityofinscrutabletextspewedoutbythecompilercanbequite
overwhelming.C++compilershaveimprovedtheirtemplateerrormessages,andLeorZolmanhaswritten
atoolcalledSTLFiltthatrenderstheseerrormessagesmuchmorereadablebyextractingtheuseful
informationandthrowingawaytherest.[85]
Anotherimportantideatotakeawayfromthischapteristhatatemplateimpliesaninterface.Thatis,even
thoughthetemplatekeywordsaysIlltakeanytype,thecodeinatemplatedefinitionrequiresthat
certainoperatorsandmemberfunctionsbesupportedthatstheinterface.Soinreality,atemplate
definitionissaying,Illtakeanytypethatsupportsthisinterface.Thingswouldbemuchnicerifthe
compilercouldsimplysay,Hey,thistypethatyouretryingtoinstantiatethetemplatewithdoesnt
supportthatinterfacecantdoit.Usingtemplatesconstitutesasortoflatenttypecheckingthatismore
flexiblethanthepureobjectorientedpracticeofrequiringalltypestoderivefromcertainbaseclasses.
InChapters6and7weexploreindepththemostfamousapplicationoftemplates,thesubsetofthe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
183/458
5/3/2015
ThinkinginC++2ndedVolume2
StandardC++librarycommonlyknownastheStandardTemplateLibrary(STL).Chapters9and10also
usetemplatetechniquesnotfoundinthischapter.
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.Writeaunaryfunctiontemplatethattakesasingletypetemplateparameter.Createafull
specializationforthetypeint.Alsocreateanontemplateoverloadforthisfunctionthattakesa
singleintparameter.Haveyourmainprograminvokethreefunctionvariations.
2.Writeaclasstemplatethatusesavectortoimplementastackdatastructure.
3.Modifyyoursolutiontothepreviousexercisesothatthetypeofthecontainerusedtoimplement
thestackisatemplatetemplateparameter.
4.Inthefollowingcode,theclassNonComparabledoesnothaveanoperator=().Whywould
thepresenceoftheclassHardLogiccauseacompileerror,butSoftLogicwouldnot?
//:C05:Exercise4.cpp{xo}
classNoncomparable{}
structHardLogic{
Noncomparablenc1,nc2
voidcompare(){
returnnc1==nc2//Compilererror
}
}
template<classT>structSoftLogic{
Noncomparablenc1,nc2
voidnoOp(){}
voidcompare(){
nc1==nc2
}
}
intmain(){
SoftLogic<Noncomparable>l
l.noOp()
}///:~
5.Writeafunctiontemplatethattakesasingletypeparameter(T)andacceptsfourfunction
arguments:anarrayofT,astartindex,astopindex(inclusive),andanoptionalinitialvalue.
Thefunctionreturnsthesumofallthearrayelementsinthespecifiedrangeandtheinitialvalue.
UsethedefaultconstructorofTforthedefaultinitialvalue.
6.Repeatthepreviousexercisebutuseexplicitinstantiationtomanuallycreatespecializationsfor
intanddouble,followingthetechniqueexplainedinthischapter.
7.Whydoesthefollowingcodenotcompile?(Hint:whatdoclassmemberfunctionshaveaccess
to?)
//:C05:Exercise7.cpp{xo}
classBuddy{}
template<classT>classMy{
inti
public:
voidplay(My<Buddy>&s){
s.i=3
}
}
intmain(){
My<int>h
My<Buddy>me,bud
h.play(bud)
me.play(bud)
}///:~
8.Whydoesthefollowingcodenotcompile?
//:C05:Exercise8.cpp{xo}
template<classT>doublepythag(Ta,Tb,Tc){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
184/458
5/3/2015
ThinkinginC++2ndedVolume2
return(b+sqrt(double(b*b4*a*c)))/2*a
}
intmain(){
pythag(1,2,3)
pythag(1.0,2.0,3.0)
pythag(1,2.0,3.0)
pythag<double>(1,2.0,3.0)
}///:~
9.Writetemplatesthattakenontypeparametersofthefollowingvariety:anint,apointertoan
int,apointertoastaticclassmemberoftypeint,andapointertoastaticmemberfunction.
10.Writeaclasstemplatethattakestwotypeparameters.Defineapartialspecializationforthefirst
parameter,andanotherpartialspecializationthatspecifiesthesecondparameter.Ineach
specialization,introducemembersthatarenotintheprimarytemplate.
11.DefineaclasstemplatenamedBobthattakesasingletypeparameter.MakeBobafriendofall
instancesofatemplateclassnamedFriendly,andafriendofaclasstemplatenamedPicky
onlywhenthetypeparameterofBobandPickyareidentical.GiveBobmemberfunctionsthat
demonstrateitsfriendship.
6:GenericAlgorithms
Algorithmsareatthecoreofcomputing.Tobeabletowriteanalgorithmthat
workswithanytypeofsequencemakesyourprogramsbothsimplerandsafer.
Theabilitytocustomizealgorithmsatruntimehasrevolutionizedsoftware
development.
ThesubsetoftheStandardC++libraryknownastheStandardTemplateLibrary(STL)wasoriginally
designedaroundgenericalgorithmscodethatprocessessequencesofanytypeofvaluesinatypesafe
manner.Thegoalwastousepredefinedalgorithmsforalmosteverytask,insteadofhandcodingloops
everytimeyouneedtoprocessacollectionofdata.Thispowercomeswithabitofalearningcurve,
however.Bythetimeyougettotheendofthischapter,youshouldbeabletodecideforyourselfwhether
youfindthealgorithmsaddictiveortooconfusingtoremember.Ifyourelikemostpeople,youllresist
thematfirstbutthentendtousethemmoreandmoreastimegoeson.
Afirstlook
Amongotherthings,thegenericalgorithmsinthestandardlibraryprovideavocabularywithwhichto
describesolutions.Onceyoubecomefamiliarwiththealgorithms,youllhaveanewsetofwordswith
whichtodiscusswhatyouredoing,andthesewordsareatahigherlevelthanwhatyouhadbefore.You
dontneedtosay,Thisloopmovesthroughandassignsfromheretothereoh,Isee,itscopying!
Instead,youjustsaycopy().Thisiswhatwevebeendoingincomputerprogrammingfromthebeginning
creatinghighlevelabstractionstoexpresswhatyouredoingandspendinglesstimesayinghowyoure
doingit.Thehowhasbeensolvedonceandforallandishiddeninthealgorithmscode,readytobe
reusedondemand.
Heresanexampleofhowtousethecopyalgorithm:
//:C06:CopyInts.cpp
//Copiesintswithoutanexplicitloop.
#include<algorithm>
#include<cassert>
#include<cstddef>//Forsize_t
usingnamespacestd
intmain(){
inta[]={10,20,30}
constsize_tSIZE=sizeofa/sizeofa[0]
intb[SIZE]
copy(a,a+SIZE,b)
for(size_ti=0i<SIZE++i)
assert(a[i]==b[i])
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
185/458
5/3/2015
ThinkinginC++2ndedVolume2
}///:~
Thecopy()algorithmsfirsttwoparametersrepresenttherangeoftheinputsequenceinthiscasethe
arraya.Rangesaredenotedbyapairofpointers.Thefirstpointstothefirstelementofthesequence,and
thesecondpointsonepositionpasttheendofthearray(rightafterthelastelement).Thismayseem
strangeatfirst,butitisanoldCidiomthatcomesinquitehandy.Forexample,thedifferenceofthesetwo
pointersyieldsthenumberofelementsinthesequence.Moreimportant,inimplementingcopy,thesecond
pointercanactasasentineltostoptheiterationthroughthesequence.Thethirdargumentreferstothe
beginningoftheoutputsequence,whichisthearraybinthisexample.Itisassumedthatthearraythatb
representshasenoughspacetoreceivethecopiedelements.
Thecopy()algorithmwouldntbeveryexcitingifitcouldonlyprocessintegers.Itcancopyanykindof
sequence.Thefollowingexamplecopiesstringobjects:
//:C06:CopyStrings.cpp
//Copiesstrings.
#include<algorithm>
#include<cassert>
#include<cstddef>
#include<string>
usingnamespacestd
intmain(){
stringa[]={"read","my","lips"}
constsize_tSIZE=sizeofa/sizeofa[0]
stringb[SIZE]
copy(a,a+SIZE,b)
assert(equal(a,a+SIZE,b))
}///:~
Thisexampleintroducesanotheralgorithm,equal(),whichreturnstrueonlyifeachelementinthefirst
sequenceisequal(usingitsoperator==())tothecorrespondingelementinthesecondsequence.This
exampletraverseseachsequencetwice,onceforthecopy,andonceforthecomparison,withoutasingle
explicitloop!
Genericalgorithmsachievethisflexibilitybecausetheyarefunctiontemplates.Ifyouthinkthatthe
implementationofcopy()lookslikethefollowing,yourealmostright:
template<typenameT>voidcopy(T*begin,T*end,T*dest){
while(begin!=end)
*dest++=*begin++
}
Wesayalmostbecausecopy()canprocesssequencesdelimitedbyanythingthatactslikeapointer,
suchasaniterator.Inthisway,copy()canbeusedtoduplicateavector:
//:C06:CopyVector.cpp
//Copiesthecontentsofavector.
#include<algorithm>
#include<cassert>
#include<cstddef>
#include<vector>
usingnamespacestd
intmain(){
inta[]={10,20,30}
constsize_tSIZE=sizeofa/sizeofa[0]
vector<int>v1(a,a+SIZE)
vector<int>v2(SIZE)
copy(v1.begin(),v1.end(),v2.begin())
assert(equal(v1.begin(),v1.end(),v2.begin()))
}///:~
Thefirstvector,v1,isinitializedfromthesequenceofintegersinthearraya.Thedefinitionofthe
vectorv2usesadifferentvectorconstructorthatmakesroomforSIZEelements,initializedtozero(the
defaultvalueforintegers).
Aswiththearrayexampleearlier,itsimportantthatv2haveenoughspacetoreceiveacopyofthe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
186/458
5/3/2015
ThinkinginC++2ndedVolume2
contentsofv1.Forconvenience,aspeciallibraryfunction,back_inserter(),returnsaspecialtypeof
iteratorthatinsertselementsinsteadofoverwritingthem,somemoryisexpandedautomaticallybythe
containerasneeded.Thefollowingexampleusesback_inserter(),soitdoesnthavetoestablishthesize
oftheoutputvector,v2,aheadoftime:
//:C06:InsertVector.cpp
//Appendsthecontentsofavectortoanother.
#include<algorithm>
#include<cassert>
#include<cstddef>
#include<iterator>
#include<vector>
usingnamespacestd
intmain(){
inta[]={10,20,30}
constsize_tSIZE=sizeofa/sizeofa[0]
vector<int>v1(a,a+SIZE)
vector<int>v2//v2isemptyhere
copy(v1.begin(),v1.end(),back_inserter(v2))
assert(equal(v1.begin(),v1.end(),v2.begin()))
}///:~
Theback_inserter()functionisdefinedinthe<iterator>header.Wellexplainhowinsertiterators
workindepthinthenextchapter.
Sinceiteratorsareidenticaltopointersinallessentialways,youcanwritethealgorithmsinthestandard
libraryinsuchawayastoallowbothpointeranditeratorarguments.Forthisreason,theimplementation
ofcopy()looksmorelikethefollowingcode:
template<typenameIterator>
voidcopy(Iteratorbegin,Iteratorend,Iteratordest){
while(begin!=end)
*begin++=*dest++
}
Whicheverargumenttypeyouuseinthecall,copy()assumesitproperlyimplementstheindirectionand
incrementoperators.Ifitdoesnt,youllgetacompiletimeerror.
Predicates
Attimes,youmightwanttocopyonlyawelldefinedsubsetofonesequencetoanother,suchasonlythose
elementsthatsatisfyaparticularcondition.Toachievethisflexibility,manyalgorithmshavealternate
callingsequencesthatallowyoutosupplyapredicate,whichissimplyafunctionthatreturnsaBoolean
valuebasedonsomecriterion.Suppose,forexample,thatyouonlywanttoextractfromasequenceof
integersthosenumbersthatarelessthanorequalto15.Aversionofcopy()called
remove_copy_if()candothejob,likethis:
//:C06:CopyInts2.cpp
//Ignoresintsthatsatisfyapredicate.
#include<algorithm>
#include<cstddef>
#include<iostream>
usingnamespacestd
//Yousupplythispredicate
boolgt15(intx){return15<x}
intmain(){
inta[]={10,20,30}
constsize_tSIZE=sizeofa/sizeofa[0]
intb[SIZE]
int*endb=remove_copy_if(a,a+SIZE,b,gt15)
int*beginb=b
while(beginb!=endb)
cout<<*beginb++<<endl//Prints10only
}///:~
Theremove_copy_if()functiontemplatetakestheusualrangedelimitingpointers,followedbya
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
[86]
187/458
5/3/2015
ThinkinginC++2ndedVolume2
predicateofyourchoosing.Thepredicatemustbeapointertoafunction[86]thattakesasingleargument
ofthesametypeastheelementsinthesequence,anditmustreturnabool.Here,thefunctiongt15
returnstrueifitsargumentisgreaterthan15.Theremove_copy_if()algorithmappliesgt15()toeach
elementintheinputsequenceandignoresthoseelementswherethepredicateyieldstruewhenwritingto
theoutputsequence.
Thefollowingprogramillustratesyetanothervariationofthecopyalgorithm:
//:C06:CopyStrings2.cpp
//Replacesstringsthatsatisfyapredicate.
#include<algorithm>
#include<cstddef>
#include<iostream>
#include<string>
usingnamespacestd
//Thepredicate
boolcontains_e(conststring&s){
returns.find('e')!=string::npos
}
intmain(){
stringa[]={"read","my","lips"}
constsize_tSIZE=sizeofa/sizeofa[0]
stringb[SIZE]
string*endb=replace_copy_if(a,a+SIZE,b,
contains_e,string("kiss"))
string*beginb=b
while(beginb!=endb)
cout<<*beginb++<<endl
}///:~
Insteadofjustignoringelementsthatdontsatisfythepredicate,replace_copy_if()substitutesafixed
valueforsuchelementswhenpopulatingtheoutputsequence.Theoutputis:
kiss
my
lips
becausetheoriginaloccurrenceofread,theonlyinputstringcontainingthelettere,isreplacedbythe
wordkiss,asspecifiedinthelastargumentinthecalltoreplace_copy_if().
Thereplace_if()algorithmchangestheoriginalsequenceinplace,insteadofwritingtoaseparateoutput
sequence,asthefollowingprogramshows:
//:C06:ReplaceStrings.cpp
//Replacesstringsinplace.
#include<algorithm>
#include<cstddef>
#include<iostream>
#include<string>
usingnamespacestd
boolcontains_e(conststring&s){
returns.find('e')!=string::npos
}
intmain(){
stringa[]={"read","my","lips"}
constsize_tSIZE=sizeofa/sizeofa[0]
replace_if(a,a+SIZE,contains_e,string("kiss"))
string*p=a
while(p!=a+SIZE)
cout<<*p++<<endl
}///:~
Streamiterators
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
188/458
5/3/2015
ThinkinginC++2ndedVolume2
Likeanygoodsoftwarelibrary,theStandardC++Libraryattemptstoprovideconvenientwaysto
automatecommontasks.Wementionedinthebeginningofthischapterthatyoucanusegeneric
algorithmsinplaceofloopingconstructs.Sofar,however,ourexampleshavestillusedanexplicitloopto
printtheiroutput.Sinceprintingoutputisoneofthemostcommontasks,youwouldhopeforawayto
automatethattoo.
Thatswherestreamiteratorscomein.Astreamiteratorusesastreamaseitheraninputoranoutput
sequence.ToeliminatetheoutputloopintheCopyInts2.cppprogram,forinstance,youcandosomething
likethefollowing:
//:C06:CopyInts3.cpp
//Usesanoutputstreamiterator.
#include<algorithm>
#include<cstddef>
#include<iostream>
#include<iterator>
usingnamespacestd
boolgt15(intx){return15<x}
intmain(){
inta[]={10,20,30}
constsize_tSIZE=sizeofa/sizeofa[0]
remove_copy_if(a,a+SIZE,
ostream_iterator<int>(cout,"\n"),gt15)
}///:~
Inthisexamplewevereplacedtheoutputsequencebinthethirdargumentofremove_copy_if()with
anoutputstreamiterator,whichisaninstanceoftheostream_iteratorclasstemplatedeclaredinthe
<iterator>header.Outputstreamiteratorsoverloadtheircopyassignmentoperatorstowritetotheir
stream.Thisparticularinstanceofostream_iteratorisattachedtotheoutputstreamcout.Everytime
remove_copy_if()assignsanintegerfromthesequenceatocoutthroughthisiterator,theiterator
writestheintegertocoutandalsoautomaticallywritesaninstanceoftheseparatorstringfoundinits
secondargument,whichinthiscasecontainsthenewlinecharacter.
Itisjustaseasytowritetoafilebyprovidinganoutputfilestream,insteadofcout:
//:C06:CopyIntsToFile.cpp
//Usesanoutputfilestreamiterator.
#include<algorithm>
#include<cstddef>
#include<fstream>
#include<iterator>
usingnamespacestd
boolgt15(intx){return15<x}
intmain(){
inta[]={10,20,30}
constsize_tSIZE=sizeofa/sizeofa[0]
ofstreamoutf("ints.out")
remove_copy_if(a,a+SIZE,
ostream_iterator<int>(outf,"\n"),gt15)
}///:~
Aninputstreamiteratorallowsanalgorithmtogetitsinputsequencefromaninputstream.Thisis
accomplishedbyhavingboththeconstructorandoperator++()readthenextelementfromthe
underlyingstreamandbyoverloadingoperator*()toyieldthevaluepreviouslyread.Sincealgorithms
requiretwopointerstodelimitaninputsequence,youcanconstructanistream_iteratorintwoways,as
youcanseeintheprogramthatfollows.
//:C06:CopyIntsFromFile.cpp
//Usesaninputstreamiterator.
#include<algorithm>
#include<fstream>
#include<iostream>
#include<iterator>
#include"../require.h"
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
189/458
5/3/2015
ThinkinginC++2ndedVolume2
usingnamespacestd
boolgt15(intx){return15<x}
intmain(){
ofstreamints("someInts.dat")
ints<<"13475849"
ints.close()
ifstreaminf("someInts.dat")
assure(inf,"someInts.dat")
remove_copy_if(istream_iterator<int>(inf),
istream_iterator<int>(),
ostream_iterator<int>(cout,"\n"),gt15)
}///:~
Thefirstargumenttoreplace_copy_if()inthisprogramattachesanistream_iteratorobjecttothe
inputfilestreamcontainingints.Thesecondargumentusesthedefaultconstructorofthe
istream_iteratorclass.Thiscallconstructsaspecialvalueofistream_iteratorthatindicatesendof
file,sothatwhenthefirstiteratorfinallyencounterstheendofthephysicalfile,itcomparesequaltothe
valueistream_iterator<int>(),allowingthealgorithmtoterminatecorrectly.Notethatthisexample
avoidsusinganexplicitarrayaltogether.
Algorithmcomplexity
Usingasoftwarelibraryisamatteroftrust.Youtrusttheimplementerstonotonlyprovidecorrect
functionality,butyoualsohopethatthefunctionsexecuteasefficientlyaspossible.Itsbettertowrite
yourownloopsthantousealgorithmsthatdegradeperformance.
Toguaranteequalitylibraryimplementations,theC++Standardnotonlyspecifieswhatanalgorithm
shoulddo,buthowfastitshoulddoitandsometimeshowmuchspaceitshoulduse.Anyalgorithmthat
doesnotmeettheperformancerequirementsdoesnotconformtothestandard.Themeasureofan
algorithmsoperationalefficiencyiscalleditscomplexity.
Whenpossible,thestandardspecifiestheexactnumberofoperationcountsanalgorithmshoulduse.The
count_if()algorithm,forexample,returnsthenumberofelementsinasequencesatisfyingagiven
predicate.Thefollowingcalltocount_if(),ifappliedtoasequenceofintegerssimilartotheexamples
earlierinthischapter,yieldsthenumberofintegerelementsthataregreaterthan15:
size_tn=count_if(a,a+SIZE,gt15)
Sincecount_if()mustlookateveryelementexactlyonce,itisspecifiedtomakeanumberof
comparisonsexactlyequaltothenumberofelementsinthesequence.Thecopy()algorithmhasthe
samespecification.
Otheralgorithmscanbespecifiedtotakeatmostacertainnumberofoperations.Thefind()algorithm
searchesthroughasequenceinorderuntilitencountersanelementequaltoitsthirdargument:
int*p=find(a,a+SIZE,20)
Itstopsassoonastheelementisfoundandreturnsapointertothatfirstoccurrence.Ifitdoesntfind
one,itreturnsapointeronepositionpasttheendofthesequence(a+SIZEinthisexample).Sofind()
makesatmostanumberofcomparisonsequaltothenumberofelementsinthesequence.
Sometimesthenumberofoperationsanalgorithmtakescannotbemeasuredwithsuchprecision.Insuch
cases,thestandardspecifiesthealgorithmsasymptoticcomplexity,whichisameasureofhowthe
algorithmbehaveswithlargesequencescomparedtowellknownformulas.Agoodexampleisthesort()
algorithm,whichthestandardsaystakesapproximatelynlogncomparisonsonaverage(nisthe
numberofelementsinthesequence).[87]Suchcomplexitymeasuresgiveafeelforthecostofan
algorithmandatleastgiveameaningfulbasisforcomparingalgorithms.Asyoullseeinthenextchapter,
thefind()memberfunctionforthesetcontainerhaslogarithmiccomplexity,whichmeansthatthecost
ofsearchingforanelementinasetwill,forlargesets,beproportionaltothelogarithmofthenumberof
elements.Thisismuchsmallerthanthenumberofelementsforlargen,soitisalwaysbettertosearcha
setbyusingitsfind()memberfunctionratherthanbyusingthegenericfind()algorithm.
Functionobjects
Asyoustudysomeoftheexamplesearlierinthischapter,youwillprobablynoticethelimitedutilityofthe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
190/458
5/3/2015
ThinkinginC++2ndedVolume2
functiongt15().Whatifyouwanttouseanumberotherthan15asacomparisonthreshold?Youmay
needagt20()orgt25()orothersaswell.Havingtowriteaseparatefunctionistimeconsuming,but
alsounreasonablebecauseyoumustknowallrequiredvalueswhenyouwriteyourapplicationcode.
Thelatterlimitationmeansthatyoucantuseruntimevalues[88]togovernyoursearches,whichis
unacceptable.Overcomingthisdifficultyrequiresawaytopassinformationtopredicatesatruntime.For
example,youwouldneedagreaterthanfunctionthatyoucaninitializewithanarbitrarycomparisonvalue.
Unfortunately,youcantpassthatvalueasafunctionparameterbecauseunarypredicates,suchasour
gt15(),areappliedtoeachvalueinasequenceindividuallyandmustthereforetakeonlyoneparameter.
Thewayoutofthisdilemmais,asalways,tocreateanabstraction.Here,weneedanabstractionthatcan
actlikeafunctionaswellasstorestate,withoutdisturbingthenumberoffunctionparametersitaccepts
whenused.Thisabstractioniscalledafunctionobject.[89]
Afunctionobjectisaninstanceofaclassthatoverloadsoperator(),thefunctioncalloperator.This
operatorallowsanobjecttobeusedwithfunctioncallsyntax.Aswithanyotherobject,youcaninitializeit
viaitsconstructors.Hereisafunctionobjectthatcanbeusedinplaceofgt15():
//:C06:GreaterThanN.cpp
#include<iostream>
usingnamespacestd
classgt_n{
intvalue
public:
gt_n(intval):value(val){}
booloperator()(intn){returnn>value}
}
intmain(){
gt_nf(4)
cout<<f(3)<<endl//Prints0(forfalse)
cout<<f(5)<<endl//Prints1(fortrue)
}///:~
Thefixedvaluetocompareagainst(4)ispassedwhenthefunctionobjectfiscreated.Theexpressionf(3)
isthenevaluatedbythecompilerasthefollowingfunctioncall:
f.operator()(3)
whichreturnsthevalueoftheexpression3>value,whichisfalsewhenvalueis4,asitisinthis
example.
Sincesuchcomparisonsapplytotypesotherthanint,itwouldmakesensetodefinegt_n()asaclass
template.Itturnsoutyoudontneedtodoityourself,thoughthestandardlibraryhasalreadydoneitfor
you.Thefollowingdescriptionsoffunctionobjectsshouldnotonlymakethattopicclear,butalsogiveyou
abetterunderstandingofhowthegenericalgorithmswork.
Classificationoffunctionobjects
TheStandardC++libraryclassifiesafunctionobjectbasedonthenumberofargumentsitsoperator()
takesandthekindofvalueitreturns.Thisclassificationisbasedonwhetherafunctionobjects
operator()takeszero,one,ortwoarguments:
Generator:Atypeoffunctionobjectthattakesnoargumentsandreturnsavalueofanarbitrarytype.A
randomnumbergeneratorisanexampleofagenerator.Thestandardlibraryprovidesonegenerator,the
functionrand()declaredin<cstdlib>,andhassomealgorithms,suchasgenerate_n(),whichapply
generatorstoasequence.
UnaryFunction:Atypeoffunctionobjectthattakesasingleargumentofanytypeandreturnsavalue
thatmaybeofadifferenttype(whichmaybevoid).
BinaryFunction:Atypeoffunctionobjectthattakestwoargumentsofanytwo(possiblydistinct)types
andreturnsavalueofanytype(includingvoid).
UnaryPredicate:AUnaryFunctionthatreturnsabool.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
191/458
5/3/2015
ThinkinginC++2ndedVolume2
BinaryPredicate:ABinaryFunctionthatreturnsabool.
StrictWeakOrdering:Abinarypredicatethatallowsforamoregeneralinterpretationofequality.
Someofthestandardcontainersconsidertwoelementsequivalentifneitherislessthantheother(using
operator<()).Thisisimportantwhencomparingfloatingpointvalues,andobjectsofothertypeswhere
operator==()isunreliableorunavailable.Thisnotionalsoappliesifyouwanttosortasequenceofdata
records(structs)onasubsetofthestructsfields.Thatcomparisonschemeisconsideredastrictweak
orderingbecausetworecordswithequalkeysarenotreallyequalastotalobjects,buttheyareequalas
farasthecomparisonyoureusingisconcerned.Theimportanceofthisconceptwillbecomeclearerinthe
nextchapter.
Inaddition,certainalgorithmsmakeassumptionsabouttheoperationsavailableforthetypesofobjects
theyprocess.Wewillusethefollowingtermstoindicatetheseassumptions:
LessThanComparable:Aclassthathasalessthanoperator<.
Assignable:Aclassthathasacopyassignmentoperator=foritsowntype.
EqualityComparable:Aclassthathasanequivalenceoperator==foritsowntype.
Wewillusethesetermslaterinthischaptertodescribethegenericalgorithmsinthestandardlibrary.
Automaticcreationoffunctionobjects
The<functional>headerdefinesanumberofusefulgenericfunctionobjects.Theyareadmittedly
simple,butyoucanusethemtocomposemorecomplicatedfunctionobjects.Consequently,inmany
instances,youcanconstructcomplicatedpredicateswithoutwritingasinglefunction.Youdosobyusing
functionobjectadaptors[90]totakethesimplefunctionobjectsandadaptthemforusewithotherfunction
objectsinachainofoperations.
Toillustrate,letsuseonlystandardfunctionobjectstoaccomplishwhatgt15()didearlier.Thestandard
functionobject,greater,isabinaryfunctionobjectthatreturnstrueifitsfirstargumentisgreaterthan
itssecondargument.Wecannotapplythisdirectlytoasequenceofintegersthroughanalgorithmsuchas
remove_copy_if()becauseremove_copy_if()expectsaunarypredicate.Wecanconstructaunary
predicateontheflythatusesgreatertocompareitsfirstargumenttoafixedvalue.Wefixthevalueof
thesecondparameterat15usingthefunctionobjectadaptorbind2nd,likethis:
//:C06:CopyInts4.cpp
//Usesastandardfunctionobjectandadaptor.
#include<algorithm>
#include<cstddef>
#include<functional>
#include<iostream>
#include<iterator>
usingnamespacestd
intmain(){
inta[]={10,20,30}
constsize_tSIZE=sizeofa/sizeofa[0]
remove_copy_if(a,a+SIZE,
ostream_iterator<int>(cout,"\n"),
bind2nd(greater<int>(),15))
}///:~
ThisprogramproducesthesameresultasCopyInts3.cpp,butwithoutwritingourownpredicatefunction
gt15().Thefunctionobjectadaptorbind2nd()isatemplatefunctionthatcreatesafunctionobjectof
typebinder2nd,whichsimplystoresthetwoargumentspassedtobind2nd(),thefirstofwhichmustbe
abinaryfunctionorfunctionobject(thatis,anythingthatcanbecalledwithtwoarguments).The
operator()functioninbinder2nd,whichisitselfaunaryfunction,callsthebinaryfunctionitstored,
passingititsincomingparameterandthefixedvalueitstored.
Tomaketheexplanationconcreteforthisexample,letscalltheinstanceofbinder2ndcreatedby
bind2nd()bythenameb.Whenbiscreated,itreceivestwoparameters(greater<int>()and15)and
storesthem.Letscalltheinstanceofgreater<int>bythenameg,andcalltheinstanceoftheoutput
streamiteratorbythenameo.Thenthecalltoremove_copy_if()earlierconceptuallybecomesthe
following:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
192/458
5/3/2015
ThinkinginC++2ndedVolume2
remove_copy_if(a,a+SIZE,o,b(g,15).operator())
Asremove_copy_if()iteratesthroughthesequence,itcallsboneachelement,todeterminewhetherto
ignoretheelementwhencopyingtothedestination.Ifwedenotethecurrentelementbythenamee,that
callinsideremove_copy_if()isequivalentto
if(b(e))
butbinder2ndsfunctioncalloperatorjustturnsaroundandcallsg(e,15),sotheearliercallisthesame
as
if(greater<int>(e,15))
whichisthecomparisonwewereseeking.Thereisalsoabind1st()adaptorthatcreatesa
binder1stobject,whichfixesthefirstargumentoftheassociatedinputbinaryfunction.
Asanotherexample,letscountthenumberofelementsinthesequencenotequalto20.Thistimewell
usethealgorithmcount_if(),introducedearlier.Thereisastandardbinaryfunctionobject,equal_to,
andalsoafunctionobjectadaptor,not1(),thattakesaunaryfunctionobjectasaparameterandinvert
itstruthvalue.Thefollowingprogramwilldothejob:
//:C06:CountNotEqual.cpp
//Countelementsnotequalto20.
#include<algorithm>
#include<cstddef>
#include<functional>
#include<iostream>
usingnamespacestd
intmain(){
inta[]={10,20,30}
constsize_tSIZE=sizeofa/sizeofa[0]
cout<<count_if(a,a+SIZE,
not1(bind1st(equal_to<int>(),20)))//2
}///:~
Asremove_copy_if()didinthepreviousexample,count_if()callsthepredicateinitsthirdargument
(letscallitn)foreachelementofitssequenceandincrementsitsinternalcountereachtimetrueis
returned.If,asbefore,wecallthecurrentelementofthesequencebythenamee,thestatement
if(n(e))
intheimplementationofcount_ifisinterpretedas
if(!bind1st(equal_to<int>,20)(e))
whichendsupas
if(!equal_to<int>(20,e))
becausenot1()returnsthelogicalnegationoftheresultofcallingitsunaryfunctionargument.Thefirst
argumenttoequal_tois20becauseweusedbind1st()insteadofbind2nd().Sincetestingforequality
issymmetricinitsarguments,wecouldhaveusedeitherbind1st()orbind2nd()inthisexample.
Thefollowingtableshowsthetemplatesthatgeneratethestandardfunctionobjects,alongwiththekinds
ofexpressionstowhichtheyapply:
Name
Type
Resultproduced
plus
BinaryFunction
arg1+arg2
minus
BinaryFunction
arg1arg2
multiplies
BinaryFunction
arg1*arg2
divides
BinaryFunction
arg1/arg2
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
193/458
5/3/2015
ThinkinginC++2ndedVolume2
modulus
BinaryFunction
arg1%arg2
negate
UnaryFunction
arg1
equal_to
BinaryPredicate
arg1==arg2
not_equal_to
BinaryPredicate
arg1!=arg2
greater
BinaryPredicate
arg1>arg2
less
BinaryPredicate
arg1<arg2
greater_equal
BinaryPredicate
arg1>=arg2
less_equal
BinaryPredicate
arg1<=arg2
logical_and
BinaryPredicate
arg1&&arg2
Logical_or
BinaryPredicate
arg1||arg2
logical_not
UnaryPredicate
!arg1
unary_negate
UnaryLogical
!(UnaryPredicate(arg1))
binary_negate
BinaryLogical
!(BinaryPredicate(arg1,arg2))
Adaptablefunctionobjects
Standardfunctionadaptorssuchasbind1st()andbind2nd()makesomeassumptionsaboutthefunction
objectstheyprocess.Considerthefollowingexpressionfromthelastlineoftheearlier
CountNotEqual.cppprogram:
not1(bind1st(equal_to<int>(),20))
Thebind1st()adaptorcreatesaunaryfunctionobjectoftypebinder1st,whichsimplystoresaninstance
ofequal_to<int>andthevalue20.Thebinder1st::operator()functionneedstoknowitsargument
typeanditsreturntypeotherwise,itwillnothaveavaliddeclaration.Theconventiontosolvethis
problemistoexpectallfunctionobjectstoprovidenestedtypedefinitionsforthesetypes.Forunary
functions,thetypenamesareargument_typeandresult_typeforbinaryfunctionobjectstheyare
first_argument_type,second_argument_type,andresult_type.Lookingattheimplementationof
bind1st()andbinder1stinthe<functional>headerrevealstheseexpectations.Firstinspect
bind1st(),asitmightappearinatypicallibraryimplementation:
template<classOp,classT>
binder1st<Op>bind1st(constOp&f,constT&val){
typedeftypenameOp::first_argument_typeArg1_t
returnbinder1st<Op>(f,Arg1_t(val))
}
Notethatthetemplateparameter,Op,whichrepresentsthetypeofthebinaryfunctionbeingadaptedby
bind1st(),musthaveanestedtypenamedfirst_argument_type.(Notealsotheuseoftypenameto
informthecompilerthatitisamembertypename,asexplainedinChapter5.)Nowseehowbinder1st
usesthetypenamesinOpinitsdeclarationofitsfunctioncalloperator:
//Insidetheimplementationforbinder1st<Op>
typenameOp::result_type
operator()(consttypenameOp::second_argument_type&x)
const
Functionobjectswhoseclassesprovidethesetypenamesarecalledadaptablefunctionobjects.
Sincethesenamesareexpectedofallstandardfunctionobjectsaswellasofanyfunctionobjectsyou
createtousewithfunctionobjectadaptors,the<functional>headerprovidestwotemplatesthatdefine
thesetypesforyou:unary_functionandbinary_function.Yousimplyderivefromtheseclasseswhile
fillingintheargumenttypesastemplateparameters.Suppose,forexample,thatwewanttomakethe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
194/458
5/3/2015
ThinkinginC++2ndedVolume2
functionobjectgt_n,definedearlierinthischapter,adaptable.Allweneedtodoisthefollowing:
classgt_n:publicunary_function<int,bool>{
intvalue
public:
gt_n(intval):value(val){}
booloperator()(intn){
returnn>value
}
}
Allunary_functiondoesistoprovidetheappropriatetypedefinitions,whichitinfersfromitstemplate
parametersasyoucanseeinitsdefinition:
template<classArg,classResult>structunary_function{
typedefArgargument_type
typedefResultresult_type
}
Thesetypesbecomeaccessiblethroughgt_nbecauseitderivespubliclyfromunary_function.The
binary_functiontemplatebehavesinasimilarmanner.
Morefunctionobjectexamples
ThefollowingFunctionObjects.cppexampleprovidessimpletestsformostofthebuiltinbasicfunction
objecttemplates.Thisway,youcanseehowtouseeachtemplate,alongwiththeresultingbehavior.This
exampleusesoneofthefollowinggeneratorsforconvenience:
//:C06:Generators.h
//Differentwaystofillsequences.
#ifndefGENERATORS_H
#defineGENERATORS_H
#include<cstring>
#include<set>
#include<cstdlib>
//Ageneratorthatcanskipovernumbers:
classSkipGen{
inti
intskp
public:
SkipGen(intstart=0,intskip=1)
:i(start),skp(skip){}
intoperator()(){
intr=i
i+=skp
returnr
}
}
//Generateuniquerandomnumbersfrom0tomod:
classURandGen{
std::set<int>used
intlimit
public:
URandGen(intlim):limit(lim){}
intoperator()(){
while(true){
inti=int(std::rand())%limit
if(used.find(i)==used.end()){
used.insert(i)
returni
}
}
}
}
//Producesrandomcharacters:
classCharGen{
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
195/458
5/3/2015
ThinkinginC++2ndedVolume2
staticconstchar*source
staticconstintlen
public:
charoperator()(){
returnsource[std::rand()%len]
}
}
#endif//GENERATORS_H///:~
//:C06:Generators.cpp{O}
#include"Generators.h"
constchar*CharGen::source="ABCDEFGHIJK"
"LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
constintCharGen::len=std::strlen(source)
///:~
Wellbeusingthesegeneratingfunctionsinvariousexamplesthroughoutthischapter.TheSkipGen
functionobjectreturnsthenextnumberofanarithmeticsequencewhosecommondifferenceisheldinits
skpdatamember.AURandGenobjectgeneratesauniquerandomnumberinaspecifiedrange.(Itusesa
setcontainer,whichwelldiscussinthenextchapter.)ACharGenobjectreturnsarandomalphabetic
character.HereisasampleprogramusingUrandGen:
//:C06:FunctionObjects.cpp{bor}
//Illustratesselectedpredefinedfunctionobject
//templatesfromtheStandardC++library.
//{L}Generators
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<iostream>
#include<iterator>
#include<vector>
#include"Generators.h"
#include"PrintSequence.h"
usingnamespacestd
template<typenameContain,typenameUnaryFunc>
voidtestUnary(Contain&source,Contain&dest,
UnaryFuncf){
transform(source.begin(),source.end(),dest.begin(),f)
}
template<typenameContain1,typenameContain2,
typenameBinaryFunc>
voidtestBinary(Contain1&src1,Contain1&src2,
Contain2&dest,BinaryFuncf){
transform(src1.begin(),src1.end(),
src2.begin(),dest.begin(),f)
}
//Executestheexpression,thenstringizesthe
//expressionintotheprintstatement:
#defineT(EXPR)EXPRprint(r.begin(),r.end(),\
"After"#EXPR)
//ForBooleantests:
#defineB(EXPR)EXPRprint(br.begin(),br.end(),\
"After"#EXPR)
//Booleanrandomgenerator:
structBRand{
booloperator()(){returnrand()%2==0}
}
intmain(){
constintSZ=10
constintMAX=50
vector<int>x(SZ),y(SZ),r(SZ)
//Anintegerrandomnumbergenerator:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
196/458
5/3/2015
ThinkinginC++2ndedVolume2
URandGenurg(MAX)
srand(time(0))//Randomize
generate_n(x.begin(),SZ,urg)
generate_n(y.begin(),SZ,urg)
//Addonetoeachtoguaranteenonzerodivide:
transform(y.begin(),y.end(),y.begin(),
bind2nd(plus<int>(),1))
//Guaranteeonepairofelementsis==:
x[0]=y[0]
print(x.begin(),x.end(),"x")
print(y.begin(),y.end(),"y")
//Operateoneachelementpairofx&y,
//puttingtheresultintor:
T(testBinary(x,y,r,plus<int>()))
T(testBinary(x,y,r,minus<int>()))
T(testBinary(x,y,r,multiplies<int>()))
T(testBinary(x,y,r,divides<int>()))
T(testBinary(x,y,r,modulus<int>()))
T(testUnary(x,r,negate<int>()))
vector<bool>br(SZ)//ForBooleanresults
B(testBinary(x,y,br,equal_to<int>()))
B(testBinary(x,y,br,not_equal_to<int>()))
B(testBinary(x,y,br,greater<int>()))
B(testBinary(x,y,br,less<int>()))
B(testBinary(x,y,br,greater_equal<int>()))
B(testBinary(x,y,br,less_equal<int>()))
B(testBinary(x,y,br,not2(greater_equal<int>())))
B(testBinary(x,y,br,not2(less_equal<int>())))
vector<bool>b1(SZ),b2(SZ)
generate_n(b1.begin(),SZ,BRand())
generate_n(b2.begin(),SZ,BRand())
print(b1.begin(),b1.end(),"b1")
print(b2.begin(),b2.end(),"b2")
B(testBinary(b1,b2,br,logical_and<int>()))
B(testBinary(b1,b2,br,logical_or<int>()))
B(testUnary(b1,br,logical_not<int>()))
B(testUnary(b1,br,not1(logical_not<int>())))
}///:~
Thisexampleusesahandyfunctiontemplate,print(),whichiscapableofprintingasequenceofanytype
alongwithanoptionalmessage.ThistemplateappearsintheheaderfilePrintSequence.h,andis
explainedlaterinthischapter.
Thetwotemplatefunctionsautomatetheprocessoftestingthevariousfunctionobjecttemplates.There
aretwobecausethefunctionobjectsareeitherunaryorbinary.ThetestUnary()functiontakesasource
vector,adestinationvector,andaunaryfunctionobjecttoapplytothesourcevectortoproducethe
destinationvector.IntestBinary(),twosourcevectorsarefedtoabinaryfunctiontoproducethe
destinationvector.Inbothcases,thetemplatefunctionssimplyturnaroundandcallthetransform()
algorithm,whichappliestheunaryfunctionorfunctionobjectfoundinitsfourthparametertoeach
sequenceelement,writingtheresulttothesequenceindicatedbyitsthirdparameter,whichinthiscaseis
thesameastheinputsequence.
Foreachtest,youwanttoseeastringdescribingthetest,followedbytheresultsofthetest.Toautomate
this,thepreprocessorcomesinhandytheT()andB()macroseachtaketheexpressionyouwantto
execute.Afterevaluatingtheexpression,theypasstheappropriaterangetoprint().Toproducethe
messagetheexpressionisstringizedusingthepreprocessor.Thatwayyouseethecodeofthe
expressionthatisexecutedfollowedbytheresultvector.
Thelastlittletool,BRand,isageneratorobjectthatcreatesrandomboolvalues.Todothis,itgetsa
randomnumberfromrand()andteststoseeifitsgreaterthan(RAND_MAX+1)/2.Iftherandom
numbersareevenlydistributed,thisshouldhappenhalfthetime.
Inmain(),threevectorsofintarecreated:xandyforsourcevalues,andrforresults.Toinitializex
andywithrandomvaluesnogreaterthan50,ageneratoroftypeURandGenfromGenerators.hisused.
Thestandardgenerate_n()algorithmpopulatesthesequencespecifiedinitsfirstargumentbyinvoking
itsthirdargument(whichmustbeagenerator)agivennumberoftimes(specifiedinitssecondargument).
Sincethereisoneoperationwhereelementsofxaredividedbyelementsofy,wemustensurethatthere
arenozerovaluesofy.Thisisaccomplishedbyonceagainusingthetransform()algorithm,takingthe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
197/458
5/3/2015
ThinkinginC++2ndedVolume2
sourcevaluesfromyandputtingtheresultsbackintoy.Thefunctionobjectforthisiscreatedwiththe
expression:
bind2nd(plus<int>(),1)
Thisexpressionusestheplusfunctionobjecttoadd1toitsfirstargument.Aswedidearlierinthis
chapter,weuseabinderadaptortomakethisaunaryfunctionsoitcanappliedtothesequencebya
singlecalltotransform().
Anothertestintheprogramcomparestheelementsinthetwovectorsforequality,soitisinterestingto
guaranteethatatleastonepairofelementsisequivalenthereelementzeroischosen.
Oncethetwovectorsareprinted,T()testseachofthefunctionobjectsthatproducesanumericvalue,
andthenB()testseachfunctionobjectthatproducesaBooleanresult.Theresultisplacedintoa
vector<bool>,andwhenthisvectorisprinted,itproducesa1foratruevalueanda0forafalse
value.HereistheoutputfromanexecutionofFunctionObjects.cpp:
x:
48183622629192547
y:
414239113213154430
AftertestBinary(x,y,r,plus<int>()):
8224145333842346977
AftertestBinary(x,y,r,minus<int>()):
0652711261641917
AftertestBinary(x,y,r,multiplies<int>()):
1611241432424219237728511001410
AftertestBinary(x,y,r,divides<int>()):
1004202101
AftertestBinary(x,y,r,limit<int>()):
0818006342517
AftertestUnary(x,r,negate<int>()):
48183622629192547
AftertestBinary(x,y,br,equal_to<int>()):
1000000000
AftertestBinary(x,y,br,not_equal_to<int>()):
0111111111
AftertestBinary(x,y,br,greater<int>()):
0001101101
AftertestBinary(x,y,br,less<int>()):
0110010010
AftertestBinary(x,y,br,greater_equal<int>()):
1001101101
AftertestBinary(x,y,br,less_equal<int>()):
1110010010
AftertestBinary(x,y,br,not2(greater_equal<int>())):
0110010010
AftertestBinary(x,y,br,not2(less_equal<int>())):
0001101101
b1:
0110001011
b2:
0110001011
AftertestBinary(b1,b2,br,logical_and<int>()):
0110001011
AftertestBinary(b1,b2,br,logical_or<int>()):
0110001011
AftertestUnary(b1,br,logical_not<int>()):
1001110100
AftertestUnary(b1,br,not1(logical_not<int>())):
0110001011
IfyouwanttheBooleanvaluestodisplayastrueandfalseinsteadof1and0,call
cout.setf(ios::boolalpha).
Abinderdoesnthavetoproduceaunarypredicateitcanalsocreateanyunaryfunction(thatis,a
functionthatreturnssomethingotherthanbool).Forexample,youcantomultiplyeveryelementina
vectorby10usingabinderwiththetransform()algorithm:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
198/458
5/3/2015
ThinkinginC++2ndedVolume2
//:C06:FBinder.cpp
//Bindersaren'tlimitedtoproducingpredicates.
//{L}Generators
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<iostream>
#include<iterator>
#include<vector>
#include"Generators.h"
usingnamespacestd
intmain(){
ostream_iterator<int>out(cout,"")
vector<int>v(15)
srand(time(0))//Randomize
generate(v.begin(),v.end(),URandGen(20))
copy(v.begin(),v.end(),out)
transform(v.begin(),v.end(),v.begin(),
bind2nd(multiplies<int>(),10))
copy(v.begin(),v.end(),out)
}///:~
Sincethethirdargumenttotransform()isthesameasthefirst,theresultingelementsarecopiedback
intothesourcevector.Thefunctionobjectcreatedbybind2nd()inthiscaseproducesanintresult.
Theboundargumenttoabindercannotbeafunctionobject,butitdoesnothavetobeacompiletime
constant.Forexample:
//:C06:BinderValue.cpp
//Theboundargumentcanvary.
#include<algorithm>
#include<functional>
#include<iostream>
#include<iterator>
#include<cstdlib>
usingnamespacestd
intboundedRand(){returnrand()%100}
intmain(){
constintSZ=20
inta[SZ],b[SZ]={0}
generate(a,a+SZ,boundedRand)
intval=boundedRand()
int*end=remove_copy_if(a,a+SZ,b,
bind2nd(greater<int>(),val))
//Sortforeasierviewing:
sort(a,a+SZ)
sort(b,end)
ostream_iterator<int>out(cout,"")
cout<<"OriginalSequence:<<endl
copy(a,a+SZ,out)cout<<endl
cout<<"Values<="<<val<<endl
copy(b,end,out)cout<<endl
}///:~
Here,anarrayisfilledwith20randomnumbersbetween0and100,andtheuserprovidesavalueonthe
commandline.Intheremove_copy_if()call,youcanseethattheboundargumenttobind2nd()is
randomnumberinthesamerangeasthesequence.Hereistheoutputfromonerun:
OriginalSequence:
412151719212630474856586063717982909295
Values<=41
412151719212630
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
199/458
5/3/2015
ThinkinginC++2ndedVolume2
Functionpointeradaptors
Whereverafunctionlikeentityisexpectedbyanalgorithm,youcansupplyeitherapointertoanordinary
functionorafunctionobject.Whenthealgorithmissuesacall,ifitisthroughafunctionpointer,thanthe
nativefunctioncallmechanismisused.Ifitisthroughafunctionobject,thenthatobjectsoperator()
memberexecutes.InCopyInts2.cpp,wepassedtherawfunctiongt15()asapredicateto
remove_copy_if().Wealsopassedpointerstofunctionsreturningrandomnumberstogenerate()and
generate_n().
Youcannotuserawfunctionswithfunctionobjectadaptorssuchasbind2nd()becausetheyassumethe
existenceoftypedefinitionsfortheargumentandresulttypes.Insteadofmanuallyconvertingyournative
functionsintofunctionobjectsyourself,thestandardlibraryprovidesafamilyofadaptorstodothework
foryou.Theptr_fun()adaptorstakeapointertoafunctionandturnitintoafunctionobject.Theyare
notdesignedforafunctionthattakesnoargumentstheymustonlybeusedwithunaryfunctionsorbinary
functions.
Thefollowingprogramusesptr_fun()towrapaunaryfunction:
//:C06:PtrFun1.cpp
//Usingptr_fun()withaunaryfunction.
#include<algorithm>
#include<cmath>
#include<functional>
#include<iostream>
#include<iterator>
#include<vector>
usingnamespacestd
intd[]={123,94,10,314,315}
constintDSZ=sizeofd/sizeof*d
boolisEven(intx){returnx%2==0}
intmain(){
vector<bool>vb
transform(d,d+DSZ,back_inserter(vb),
not1(ptr_fun(isEven)))
copy(vb.begin(),vb.end(),
ostream_iterator<bool>(cout,""))
cout<<endl
//Output:10001
}///:~
WecantsimplypassisEventonot1,becausenot1needstoknowtheactualargumenttypeandreturn
typeitsargumentuses.Theptr_fun()adaptordeducesthosetypesthroughtemplateargumentdeduction.
Thedefinitionoftheunaryversionofptr_fun()lookssomethinglikethis:
template<classArg,classResult>
pointer_to_unary_function<Arg,Result>
ptr_fun(Result(*fptr)(Arg)){
returnpointer_to_unary_function<Arg,Result>(fptr)
}
Asyoucansee,thisversionofptr_fun()deducestheargumentandresulttypesfromfptrandusesthem
toinitializeapointer_to_unary_functionobjectthatstoresfptr.Thefunctioncalloperatorfor
pointer_to_unary_functionjustcallsfptr,asyoucanseebythelastlineofitscode:
template<classArg,classResult>
classpointer_to_unary_function
:publicunary_function<Arg,Result>{
Result(*fptr)(Arg)//Storesthefptr
public:
pointer_to_unary_function(Result(*x)(Arg)):fptr(x){}
Resultoperator()(Argx)const{returnfptr(x)}
}
Sincepointer_to_unary_functionderivesfromunary_function,theappropriatetypedefinitionscome
alongfortherideandareavailabletonot1.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
200/458
5/3/2015
ThinkinginC++2ndedVolume2
Thereisalsoabinaryversionofptr_fun(),whichreturnsapointer_to_binary_functionobject(which
derivesfrombinary_function)thatbehavesanalogouslytotheunarycase.Thefollowingprogramuses
thebinaryversionofptr_fun()toraisenumbersinasequencetoapower.Italsorevealsapitfallwhen
passingoverloadedfunctionstoptr_fun().
//:C06:PtrFun2.cpp{edg}
//Usingptr_fun()forabinaryfunction.
#include<algorithm>
#include<cmath>
#include<functional>
#include<iostream>
#include<iterator>
#include<vector>
usingnamespacestd
doubled[]={01.23,91.370,56.661,
023.230,19.959,1.0,3.14159}
constintDSZ=sizeofd/sizeof*d
intmain(){
vector<double>vd
transform(d,d+DSZ,back_inserter(vd),
bind2nd(ptr_fun<double,double,double>(pow),2.0))
copy(vd.begin(),vd.end(),
ostream_iterator<double>(cout,""))
cout<<endl
}///:~
Thepow()functionisoverloadedintheStandardC++header<cmath>foreachofthefloatingpoint
datatypes,asfollows:
floatpow(float,int)//Efficientintpowerversions...
doublepow(double,int)
longdoublepow(longdouble,int)
floatpow(float,float)
doublepow(double,double)
longdoublepow(longdouble,longdouble)
Sincetherearemultipleversionsofpow(),thecompilerhasnowayofknowingwhichtochoose.Here,
wehavetohelpthecompilerbyusingexplicitfunctiontemplatespecialization,asexplainedintheprevious
chapter.[91]
Itseventrickiertoconvertamemberfunctionintoafunctionobjectsuitableforusingwiththegeneric
algorithms.Asasimpleexample,supposewehavetheclassicalshapeproblemandwanttoapplythe
draw()memberfunctiontoeachpointerinacontainerofShape:
//:C06:MemFun1.cpp
//Applyingpointerstomemberfunctions.
#include<algorithm>
#include<functional>
#include<iostream>
#include<vector>
#include"../purge.h"
usingnamespacestd
classShape{
public:
virtualvoiddraw()=0
virtual~Shape(){}
}
classCircle:publicShape{
public:
virtualvoiddraw(){cout<<"Circle::Draw()"<<endl}
~Circle(){cout<<"Circle::~Circle()"<<endl}
}
classSquare:publicShape{
public:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
201/458
5/3/2015
ThinkinginC++2ndedVolume2
virtualvoiddraw(){cout<<"Square::Draw()"<<endl}
~Square(){cout<<"Square::~Square()"<<endl}
}
intmain(){
vector<Shape*>vs
vs.push_back(newCircle)
vs.push_back(newSquare)
for_each(vs.begin(),vs.end(),mem_fun(&Shape::draw))
purge(vs)
}///:~
Thefor_each()algorithmpasseseachelementinasequencetothefunctionobjectdenotedbyitsthird
argument.Here,wewantthefunctionobjecttowraponeofthememberfunctionsoftheclassitself,and
sothefunctionobjectsargumentbecomesthepointertotheobjectforthememberfunctioncall.To
producesuchafunctionobject,themem_fun()templatetakesapointertoamemberasitsargument.
Themem_fun()functionsareforproducingfunctionobjectsthatarecalledusingapointertotheobject
thatthememberfunctioniscalledfor,whilemem_fun_ref()callsthememberfunctiondirectlyforan
object.Onesetofoverloadsofbothmem_fun()andmem_fun_ref()isformemberfunctionsthattake
zeroargumentsandoneargument,andthisismultipliedbytwotohandleconstvs.nonconstmember
functions.However,templatesandoverloadingtakecareofsortingallthatoutallyouneedtoremember
iswhentousemem_fun()vs.mem_fun_ref().
Supposeyouhaveacontainerofobjects(notpointers),andyouwanttocallamemberfunctionthattakes
anargument.Theargumentyoupassshouldcomefromasecondcontainerofobjects.Toaccomplishthis,
usethesecondoverloadedformofthetransform()algorithm:
//:C06:MemFun2.cpp
//Callingmemberfunctionsthroughanobjectreference.
#include<algorithm>
#include<functional>
#include<iostream>
#include<iterator>
#include<vector>
usingnamespacestd
classAngle{
intdegrees
public:
Angle(intdeg):degrees(deg){}
intmul(inttimes){returndegrees*=times}
}
intmain(){
vector<Angle>va
for(inti=0i<50i+=10)
va.push_back(Angle(i))
intx[]={1,2,3,4,5}
transform(va.begin(),va.end(),x,
ostream_iterator<int>(cout,""),
mem_fun_ref(&Angle::mul))
cout<<endl
//Output:02060120200
}///:~
Becausethecontainerisholdingobjects,mem_fun_ref()mustbeusedwiththepointertomember
function.Thisversionoftransform()takesthestartandendpointofthefirstrange(wheretheobjects
live)thestartingpointofthesecondrange,whichholdstheargumentstothememberfunctionthe
destinationiterator,whichinthiscaseisstandardoutputandthefunctionobjecttocallforeachobject.
Thisfunctionobjectiscreatedwithmem_fun_ref()andthedesiredpointertomember.Noticethatthe
transform()andfor_each()templatefunctionsareincompletetransform()requiresthatthefunction
itcallsreturnavalue,andthereisnofor_each()thatpassestwoargumentstothefunctionitcalls.
Thus,youcannotcallamemberfunctionthatreturnsvoidandtakesanargumentusingtransform()or
for_each().
Mostanymemberfunctionworkswithmem_fun_ref().Youcanalsousestandardlibrarymember
functions,ifyourcompilerdoesntaddanydefaultargumentsbeyondthenormalargumentsspecifiedin
[92]
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
202/458
5/3/2015
ThinkinginC++2ndedVolume2
[92]
thestandard.
Forexample,supposeyoudliketoreadafileandsearchforblanklines.Yourcompiler
mayallowyoutousethestring::empty()memberfunctionlikethis:
//:C06:FindBlanks.cpp
//Demonstratesmem_fun_ref()withstring::empty().
#include<algorithm>
#include<cassert>
#include<cstddef>
#include<fstream>
#include<functional>
#include<string>
#include<vector>
#include"../require.h"
usingnamespacestd
typedefvector<string>::iteratorLSI
intmain(intargc,char*argv[]){
char*fname="FindBlanks.cpp"
if(argc>1)fname=argv[1]
ifstreamin(fname)
assure(in,fname)
vector<string>vs
strings
while(getline(in,s))
vs.push_back(s)
vector<string>cpy=vs//Fortesting
LSIlsi=find_if(vs.begin(),vs.end(),
mem_fun_ref(&string::empty))
while(lsi!=vs.end()){
*lsi="ABLANKLINE"
lsi=find_if(vs.begin(),vs.end(),
mem_fun_ref(&string::empty))
}
for(size_ti=0i<cpy.size()i++)
if(cpy[i].size()==0)
assert(vs[i]=="ABLANKLINE")
else
assert(vs[i]!="ABLANKLINE")
}///:~
Thisexampleusesfind_if()tolocatethefirstblanklineinthegivenrangeusingmem_fun_ref()with
string::empty().Afterthefileisopenedandreadintothevector,theprocessisrepeatedtofindevery
blanklineinthefile.Eachtimeablanklineisfound,itisreplacedwiththecharactersABLANKLINE.All
youhavetodotoaccomplishthisisdereferencetheiteratortoselectthecurrentstring.
Writingyourownfunctionobjectadaptors
Considerhowtowriteaprogramthatconvertsstringsrepresentingfloatingpointnumberstotheiractual
numericvalues.Togetthingsstarted,heresageneratorthatcreatesthestrings:
//:C06:NumStringGen.h
//Arandomnumbergeneratorthatproduces
//stringsrepresentingfloatingpointnumbers.
#ifndefNUMSTRINGGEN_H
#defineNUMSTRINGGEN_H
#include<cstdlib>
#include<string>
classNumStringGen{
constintsz//Numberofdigitstomake
public:
NumStringGen(intssz=5):sz(ssz){}
std::stringoperator()(){
std::stringdigits("0123456789")
constintndigits=digits.size()
std::stringr(sz,'')
//Don'twantazeroasthefirstdigit
r[0]=digits[std::rand()%(ndigits1)]+1
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
203/458
5/3/2015
ThinkinginC++2ndedVolume2
//Nowassigntherest
for(inti=1i<sz++i)
if(sz>=3&&i==sz/2)
r[i]='.'//Insertadecimalpoint
else
r[i]=digits[std::rand()%ndigits]
returnr
}
}
#endif//NUMSTRINGGEN_H///:~
YoutellithowbigthestringsshouldbewhenyoucreatetheNumStringGenobject.Therandomnumber
generatorselectsdigits,andadecimalpointisplacedinthemiddle.
ThefollowingprogramusesNumStringGentofillavector<string>.However,tousethestandardC
libraryfunctionatof()toconvertthestringstofloatingpointnumbers,thestringobjectsmustfirstbe
turnedintocharpointers,sincethereisnoautomatictypeconversionfromstringtochar*.The
transform()algorithmcanbeusedwithmem_fun_ref()andstring::c_str()toconvertallthestrings
tochar*,andthenthesecanbetransformedusingatof.
//:C06:MemFun3.cpp
//Usingmem_fun().
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<functional>
#include<iostream>
#include<iterator>
#include<string>
#include<vector>
#include"NumStringGen.h"
usingnamespacestd
intmain(){
constintSZ=9
vector<string>vs(SZ)
//Fillitwithrandomnumberstrings:
srand(time(0))//Randomize
generate(vs.begin(),vs.end(),NumStringGen())
copy(vs.begin(),vs.end(),
ostream_iterator<string>(cout,"\t"))
cout<<endl
constchar*vcp[SZ]
transform(vs.begin(),vs.end(),vcp,
mem_fun_ref(&string::c_str))
vector<double>vd
transform(vcp,vcp+SZ,back_inserter(vd),
std::atof)
cout.precision(4)
cout.setf(ios::showpoint)
copy(vd.begin(),vd.end(),
ostream_iterator<double>(cout,"\t"))
cout<<endl
}///:~
Thisprogramdoestwotransformations:onetoconvertstringstoCstylestrings(arraysofcharacters),
andonetoconverttheCstylestringstonumbersviaatof().Itwouldbenicetocombinethesetwo
operationsintoone.Afterall,wecancomposefunctionsinmathematics,sowhynotC++?
Theobviousapproachtakesthetwofunctionsasargumentsandappliesthemintheproperorder:
//:C06:ComposeTry.cpp
//Afirstattemptatimplementingfunctioncomposition.
#include<cassert>
#include<cstdlib>
#include<functional>
#include<iostream>
#include<string>
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
204/458
5/3/2015
ThinkinginC++2ndedVolume2
template<typenameR,typenameE,typenameF1,typenameF2>
classunary_composer{
F1f1
F2f2
public:
unary_composer(F1fone,F2ftwo):f1(fone),f2(ftwo){}
Roperator()(Ex){returnf1(f2(x))}
}
template<typenameR,typenameE,typenameF1,typenameF2>
unary_composer<R,E,F1,F2>compose(F1f1,F2f2){
returnunary_composer<R,E,F1,F2>(f1,f2)
}
intmain(){
doublex=compose<double,conststring&>(
atof,mem_fun_ref(&string::c_str))("12.34")
assert(x==12.34)
}///:~
Theunary_composerobjectinthisexamplestoresthefunctionpointersatofandstring::c_strsuchthat
thelatterfunctionisappliedfirstwhenitsoperator()iscalled.Thecompose()functionadaptorisa
convenience,sowedontneedtosupplyallfourtemplateargumentsexplicitlyF1andF2arededuced
fromthecall.
Itwouldbemuchbetterifwedidntneedtosupplyanytemplatearguments.Thisisachievedbyadhering
totheconventionfortypedefinitionsforadaptablefunctionobjects.Inotherwords,wewillassumethat
thefunctionstobecomposedareadaptable.Thisrequiresthatweuseptr_fun()foratof().For
maximumflexibility,wealsomakeunary_composeradaptableincaseitgetspassedtoafunction
adaptor.Thefollowingprogramdoessoandeasilysolvestheoriginalproblem:
//:C06:ComposeFinal.cpp{edg}
//Anadaptablecomposer.
#include<algorithm>
#include<cassert>
#include<cstdlib>
#include<functional>
#include<iostream>
#include<iterator>
#include<string>
#include<vector>
#include"NumStringGen.h"
usingnamespacestd
template<typenameF1,typenameF2>classunary_composer
:publicunary_function<typenameF2::argument_type,
typenameF1::result_type>{
F1f1
F2f2
public:
unary_composer(F1f1,F2f2):f1(f1),f2(f2){}
typenameF1::result_type
operator()(typenameF2::argument_typex){
returnf1(f2(x))
}
}
template<typenameF1,typenameF2>
unary_composer<F1,F2>compose(F1f1,F2f2){
returnunary_composer<F1,F2>(f1,f2)
}
intmain(){
constintSZ=9
vector<string>vs(SZ)
//Fillitwithrandomnumberstrings:
generate(vs.begin(),vs.end(),NumStringGen())
copy(vs.begin(),vs.end(),
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
205/458
5/3/2015
ThinkinginC++2ndedVolume2
ostream_iterator<string>(cout,"\t"))
cout<<endl
vector<double>vd
transform(vs.begin(),vs.end(),back_inserter(vd),
compose(ptr_fun(atof),mem_fun_ref(&string::c_str)))
copy(vd.begin(),vd.end(),
ostream_iterator<double>(cout,"\t"))
cout<<endl
}///:~
Onceagainwemustusetypenametoletthecompilerknowthatthememberwearereferringtoisa
nestedtype.
Someimplementations[93]supportcompositionoffunctionobjectsasanextension,andtheC++Standards
CommitteeislikelytoaddthesecapabilitiestothenextversionofStandardC++.
AcatalogofSTLalgorithms
Thissectionprovidesaquickreferencewhenyouresearchingfortheappropriatealgorithm.Weleavethe
fullexplorationofalltheSTLalgorithmstootherreferences(seetheendofthischapter,andAppendixA),
alongwiththemoreintimatedetailsofissueslikeperformance.Ourgoalhereisforyoutorapidlybecome
comfortablewiththealgorithms,andwellassumeyouwilllookintothemorespecializedreferencesifyou
needmoredetail.
Althoughyouwilloftenseethealgorithmsdescribedusingtheirfulltemplatedeclarationsyntax,werenot
doingthatherebecauseyoualreadyknowtheyaretemplates,anditsquiteeasytoseewhatthetemplate
argumentsarefromthefunctiondeclarations.Thetypenamesfortheargumentsprovidedescriptionsfor
thetypesofiteratorsrequired.Wethinkyoullfindthisformiseasiertoread,andyoucanquicklyfindthe
fulldeclarationinthetemplateheaderfileifyouneedit.
Thereasonforallthefussaboutiteratorsistoaccommodateanytypeofcontainerthatmeetsthe
requirementsinthestandardlibrary.Sofarwehaveillustratedthegenericalgorithmswithonlyarraysand
vectorsassequences,butinthenextchapteryoullseeabroadrangeofdatastructuresthatsupportless
robustiteration.Forthisreason,thealgorithmsarecategorizedinpartbythetypesofiterationfacilities
theyrequire.
Thenamesoftheiteratorclassesdescribetheiteratortypetowhichtheymustconform.Thereareno
interfacebaseclassestoenforcetheseiterationoperationstheyarejustexpectedtobethere.Iftheyare
not,yourcompilerwillcomplain.Thevariousflavorsofiteratorsaredescribedbrieflyasfollows.
InputIterator.Aninputiteratoronlyallowsreadingelementsofitssequenceinasingle,forwardpass
usingoperator++andoperator*.Inputiteratorscanalsobetestedwithoperator==andoperator!=.
Thatstheextentoftheconstraints.
OutputIterator.Anoutputiteratoronlyallowswritingelementstoasequenceinasingle,forwardpass
usingoperator++andoperator*.OutputIteratorscannotbetestedwithoperator==and
operator!=,however,becauseyouassumethatyoucanjustkeepsendingelementstothedestinationand
thatyoudontneedtoseeifthedestinationsendmarkerwasreached.Thatis,thecontainerthatan
OutputIteratorreferencescantakeaninfinitenumberofobjects,sonoendcheckingisnecessary.This
requirementisimportantsothatanOutputIteratorcanbeusedwithostreams(viaostream_iterator),
butyoullalsocommonlyusetheinsertiteratorssuchasarethetypeofiteratorreturnedby
back_inserter()).
ThereisnowaytodeterminewhethermultipleInputIteratorsorOutputIteratorspointwithinthesame
range,sothereisnowaytousesuchiteratorstogether.Justthinkintermsofiteratorstosupport
istreamsandostreams,andInputIteratorandOutputIteratorwillmakeperfectsense.Alsonotethat
algorithmsthatuseInputIteratorsorOutputIteratorsputtheweakestrestrictionsonthetypesof
iteratorstheywillaccept,whichmeansthatyoucanuseanymoresophisticatedtypeofiteratorwhen
youseeInputIteratororOutputIteratorusedasSTLalgorithmtemplatearguments.
ForwardIterator.BecauseyoucanonlyreadfromanInputIteratorandwritetoanOutputIterator,
youcantuseeitherofthemtosimultaneouslyreadandmodifyarange,andyoucantdereferencesuchan
iteratormorethanonce.WithaForwardIteratortheserestrictionsarerelaxedyoucanstillonlymove
forwardusingoperator++,butyoucanbothwriteandread,andyoucancomparesuchiteratorsinthe
samerangeforequality.Sinceforwarditeratorscanbothreadandwrite,theycanbeusedinplaceofan
InputIteratororOutputIterator.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
206/458
5/3/2015
ThinkinginC++2ndedVolume2
BidirectionalIterator.Effectively,thisisaForwardIteratorthatcanalsogobackward.Thatis,a
BidirectionalIteratorsupportsalltheoperationsthataForwardIteratordoes,butinadditionithasan
operator.
RandomAccessIterator.Thistypeofiteratorsupportsalltheoperationsthataregularpointerdoes:you
canaddandsubtractintegralvaluestomoveitforwardandbackwardbyjumps(ratherthanjustone
elementatatime),youcansubscriptitwithoperator[],youcansubtractoneiteratorfromanother,and
youcancompareiteratorstoseewhichisgreaterusingoperator<,operator>,andsoon.Ifyoure
implementingasortingroutineorsomethingsimilar,randomaccessiteratorsarenecessarytobeableto
createanefficientalgorithm.
Thenamesusedforthetemplateparametertypesinthealgorithmdescriptionslaterinthischapterconsist
ofthelistediteratortypes(sometimeswitha1or2appendedtodistinguishdifferenttemplate
arguments)andcanalsoincludeotherarguments,oftenfunctionobjects.
Whendescribingthegroupofelementspassedtoanoperation,mathematicalrangenotationisoften
used.Inthis,thesquarebracketmeansincludestheendpoint,andtheparenthesismeansdoesnot
includetheendpoint.Whenusingiterators,arangeisdeterminedbytheiteratorpointingtotheinitial
elementandbythepasttheenditerator,pointingpastthelastelement.Sincethepasttheendelement
isneverused,therangedeterminedbyapairofiteratorscanbeexpressedas[first,last),wherefirstis
theiteratorpointingtotheinitialelement,andlastisthepasttheenditerator.
MostbooksanddiscussionsoftheSTLalgorithmsarrangethemaccordingtosideeffects:nonmutating
algorithmsdontchangetheelementsintherange,mutatingalgorithmsdochangetheelements,andso
on.Thesedescriptionsarebasedprimarilyontheunderlyingbehaviororimplementationofthealgorithm
thatis,onthedesignersperspective.Inpractice,wedontfindthisausefulcategorization,soinstead
wellorganizethemaccordingtotheproblemyouwanttosolve:Areyousearchingforanelementorsetof
elements,performinganoperationoneachelement,countingelements,replacingelements,andsoon?
Thisshouldhelpyoufindthealgorithmyouwantmoreeasily.
Ifyoudonotseeadifferentheadersuchas<utility>or<numeric>abovethefunctiondeclarations,it
appearsin<algorithm>.Also,allthealgorithmsareinthenamespacestd.
Supporttoolsforexamplecreation
Itsusefultocreatesomebasictoolstotestthealgorithms.Intheexamplesthatfollowwellusethe
generatorsmentionedearlierinGenerators.h,aswellaswhatappearsbelow.
Displayingarangeisafrequenttask,sohereisafunctiontemplatetoprintanysequence,regardlessof
thetypecontainedinthatsequence:
//:C06:PrintSequence.h
//Printsthecontentsofanysequence.
#ifndefPRINTSEQUENCE_H
#definePRINTSEQUENCE_H
#include<algorithm>
#include<iostream>
#include<iterator>
template<typenameIter>
voidprint(Iterfirst,Iterlast,constchar*nm="",
constchar*sep="\n",
std::ostream&os=std::cout){
if(nm!=0&&*nm!='\0')
os<<nm<<":"<<sep
typedeftypename
std::iterator_traits<Iter>::value_typeT
std::copy(first,last,
std::ostream_iterator<T>(std::cout,sep))
os<<std::endl
}
#endif//PRINTSEQUENCE_H///:~
Bydefaultthisfunctiontemplateprintstocoutwithnewlinesasseparators,butyoucanchangethatby
modifyingthedefaultargument.Youcanalsoprovideamessagetoprintattheheadoftheoutput.Since
print()usesthecopy()algorithmtosendobjectstocoutviaanostream_iterator,the
ostream_iteratormustknowthetypeofobjectitisprinting,whichweinferfromthevalue_type
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
207/458
5/3/2015
ThinkinginC++2ndedVolume2
memberoftheiteratorpassed.
Thestd::iterator_traitstemplateenablestheprint()functiontemplatetoprocesssequencesdelimited
byanytypeofiterator.Theiteratortypesreturnedbythestandardcontainerssuchasvectordefinea
nestedtype,value_type,whichrepresentstheelementtype,butwhenusingarrays,theiteratorsarejust
pointers,whichcanhavenonestedtypes.Tosupplytheconventionaltypesassociatedwithiteratorsinthe
standardlibrary,std::iterator_traitsprovidesthefollowingpartialspecializationforpointertypes:
template<classT>
structiterator_traits<T*>{
typedefrandom_access_iterator_tagiterator_category
typedefTvalue_type
typedefptrdiff_tdifference_type
typedefT*pointer
typedefT&reference
}
Thismakesthetypeoftheelementspointedat(namely,T)availableviathetypenamevalue_type.
Stablevs.unstablereordering
AnumberoftheSTLalgorithmsthatmoveelementsofasequencearounddistinguishbetweenstableand
unstablereorderingofasequence.Astablesortpreservestheoriginalrelativeorderoftheelementsthat
areequivalentasfarasthecomparisonfunctionisconcerned.Forexample,considerasequence{c(1),
b(1),c(2),a(1),b(2),a(2)}.Theseelementsaretestedforequivalencebasedontheirletters,buttheir
numbersindicatehowtheyfirstappearedinthesequence.Ifyousort(forexample)thissequenceusingan
unstablesort,theresnoguaranteeofanyparticularorderamongequivalentletters,soyoucouldendup
with{a(2),a(1),b(1),b(2),c(2),c(1)}.However,ifyouuseastablesort,youwillget{a(1),a(2),
b(1),b(2),c(1),c(2)}.TheSTLsort()algorithmusesavariationofquicksortandisthusunstable,but
astable_sort()isalsoprovided.[94]
Todemonstratethestabilityversusinstabilityofalgorithmsthatreorderasequence,weneedsomewayto
keeptrackofhowtheelementsoriginallyappeared.Thefollowingisakindofstringobjectthatkeeps
trackoftheorderinwhichthatparticularobjectoriginallyappeared,usingastaticmapthatmaps
NStringstoCounters.EachNStringthencontainsanoccurrencefieldthatindicatestheorderinwhich
thisNStringwasdiscovered.
//:C06:NString.h
//A"numberedstring"thatkeepstrackofthe
//numberofoccurrencesoftheworditcontains.
#ifndefNSTRING_H
#defineNSTRING_H
#include<algorithm>
#include<iostream>
#include<string>
#include<utility>
#include<vector>
typedefstd::pair<std::string,int>psi
//Onlycompareonthefirstelement
booloperator==(constpsi&l,constpsi&r){
returnl.first==r.first
}
classNString{
std::strings
intthisOccurrence
//Keeptrackofthenumberofoccurrences:
typedefstd::vector<psi>vp
typedefvp::iteratorvpit
staticvpwords
voidaddString(conststd::string&x){
psip(x,0)
vpitit=std::find(words.begin(),words.end(),p)
if(it!=words.end())
thisOccurrence=++it>second
else{
thisOccurrence=0
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
208/458
5/3/2015
ThinkinginC++2ndedVolume2
words.push_back(p)
}
}
public:
NString():thisOccurrence(0){}
NString(conststd::string&x):s(x){addString(x)}
NString(constchar*x):s(x){addString(x)}
//Implicitoperator=andcopyconstructorareOKhere.
friendstd::ostream&operator<<(
std::ostream&os,constNString&ns){
returnos<<ns.s<<"["<<ns.thisOccurrence<<"]"
}
//Needthisforsorting.Noticeitonly
//comparesstrings,notoccurrences:
friendbool
operator<(constNString&l,constNString&r){
returnl.s<r.s
}
friend
booloperator==(constNString&l,constNString&r){
returnl.s==r.s
}
//Forsortingwithgreater<NString>:
friendbool
operator>(constNString&l,constNString&r){
returnl.s>r.s
}
//Togetatthestringdirectly:
operatorconststd::string&()const{returns}
}
//BecauseNString::vpisatemplateandweareusingthe
//inclusionmodel,itmustbedefinedinthisheaderfile:
NString::vpNString::words
#endif//NSTRING_H///:~
Wewouldnormallyuseamapcontainertoassociateastringwithitsnumberofoccurrences,butmaps
dontappearuntilthenextchapter,soweuseavectorofpairsinstead.Youllseeplentyofsimilar
examplesinChapter7.
TheonlyoperatornecessarytoperformanordinaryascendingsortisNString::operator<().Tosortin
reverseorder,theoperator>()isalsoprovidedsothatthegreatertemplatecancallit.
Fillingandgenerating
Thesealgorithmsletyouautomaticallyfillarangewithaparticularvalueorgenerateasetofvaluesfora
particularrange.Thefillfunctionsinsertasinglevaluemultipletimesintothecontainer.Thegenerate
functionsusegeneratorssuchasthosedescribedearliertoproducevaluestoinsertintothecontainer.
voidfill(ForwardIteratorfirst,ForwardIteratorlast,
constT&value)
voidfill_n(OutputIteratorfirst,Sizen,constT&value)
fill()assignsvaluetoeveryelementintherange[first,last).fill_n()assignsvaluetonelements
startingatfirst.
voidgenerate(ForwardIteratorfirst,ForwardIteratorlast,
Generatorgen)
voidgenerate_n(OutputIteratorfirst,Sizen,Generator
gen)
generate()makesacalltogen()foreachelementintherange[first,last),presumablytoproducea
differentvalueforeachelement.generate_n()callsgen()ntimesandassignseachresultton
elementsstartingatfirst.
Example
Thefollowingexamplefillsandgeneratesintovectors.Italsoshowstheuseofprint():
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
209/458
5/3/2015
ThinkinginC++2ndedVolume2
//:C06:FillGenerateTest.cpp
//Demonstrates"fill"and"generate."
//{L}Generators
#include<vector>
#include<algorithm>
#include<string>
#include"Generators.h"
#include"PrintSequence.h"
usingnamespacestd
intmain(){
vector<string>v1(5)
fill(v1.begin(),v1.end(),"howdy")
print(v1.begin(),v1.end(),"v1","")
vector<string>v2
fill_n(back_inserter(v2),7,"bye")
print(v2.begin(),v2.end(),"v2")
vector<int>v3(10)
generate(v3.begin(),v3.end(),SkipGen(4,5))
print(v3.begin(),v3.end(),"v3","")
vector<int>v4
generate_n(back_inserter(v4),15,URandGen(30))
print(v4.begin(),v4.end(),"v4","")
}///:~
Avector<string>iscreatedwithapredefinedsize.Sincestoragehasalreadybeencreatedforallthe
stringobjectsinthevector,fill()canuseitsassignmentoperatortoassignacopyofhowdytoeach
spaceinthevector.Also,thedefaultnewlineseparatorisreplacedwithaspace.
Thesecondvector<string>v2isnotgivenaninitialsize,soback_inserter()mustbeusedtoforce
newelementsininsteadoftryingtoassigntoexistinglocations.
Thegenerate()andgenerate_n()functionshavethesameformasthefillfunctionsexceptthatthey
useageneratorinsteadofaconstantvalue.Here,bothgeneratorsaredemonstrated.
Counting
Allcontainershaveamemberfunctionsize()thattellsyouhowmanyelementstheyhold.Thereturn
typeofsize()istheiteratorsdifference_type[95](usuallyptrdiff_t),whichwedenoteby
IntegralValueinthefollowing.Thefollowingtwoalgorithmscountobjectsthatsatisfycertaincriteria.
IntegralValuecount(InputIteratorfirst,InputIterator
last,constEqualityComparable&value)
Producesthenumberofelementsin[first,last)thatareequivalenttovalue(whentestedusing
operator==).
IntegralValuecount_if(InputIteratorfirst,InputIterator
last,Predicatepred)
Producesthenumberofelementsin[first,last)thateachcausepredtoreturntrue.
Example
Here,avector<char>visfilledwithrandomcharacters(includingsomeduplicates).Aset<char>is
initializedfromv,soitholdsonlyoneofeachletterrepresentedinv.Thissetcountsalltheinstancesof
allthecharacters,whicharethendisplayed:
//:C06:Counting.cpp
//Thecountingalgorithms.
//{L}Generators
#include<algorithm>
#include<functional>
#include<iterator>
#include<set>
#include<vector>
#include"Generators.h"
#include"PrintSequence.h"
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
210/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
vector<char>v
generate_n(back_inserter(v),50,CharGen())
print(v.begin(),v.end(),"v","")
//Createasetofthecharactersinv:
set<char>cs(v.begin(),v.end())
typedefset<char>::iteratorsci
for(sciit=cs.begin()it!=cs.end()it++){
intn=count(v.begin(),v.end(),*it)
cout<<*it<<":"<<n<<","
}
intlc=count_if(v.begin(),v.end(),
bind2nd(greater<char>(),'a'))
cout<<"\nLowercaseletters:"<<lc<<endl
sort(v.begin(),v.end())
print(v.begin(),v.end(),"sorted","")
}///:~
Thecount_if()algorithmisdemonstratedbycountingallthelowercaselettersthepredicateiscreated
usingthebind2nd()andgreaterfunctionobjecttemplates.
Manipulatingsequences
Thesealgorithmsletyoumovesequencesaround.
OutputIteratorcopy(InputIteratorfirst,InputIterator
last,OutputIteratordestination)
Usingassignment,copiesfrom[first,last)todestination,incrementingdestinationaftereach
assignment.Thisisessentiallyashuffleleftoperation,andsothesourcesequencemustnotcontainthe
destination.Becauseassignmentisused,youcannotdirectlyinsertelementsintoanemptycontainerorat
theendofacontainer,butinsteadyoumustwrapthedestinationiteratorinan
insert_iterator(typicallybyusingback_inserter()orbyusinginserter()inthecaseofanassociative
container).
BidirectionalIterator2copy_backward(BidirectionalIterator1
first,BidirectionalIterator1last,
BidirectionalIterator2destinationEnd)
Likecopy(),butcopiestheelementsinreverseorder.Thisisessentiallyashufflerightoperation,and,
likecopy(),thesourcesequencemustnotcontainthedestination.Thesourcerange[first,last)is
copiedtothedestination,butthefirstdestinationelementisdestinationEnd1.Thisiteratoristhen
decrementedaftereachassignment.Thespaceinthedestinationrangemustalreadyexist(toallow
assignment),andthedestinationrangecannotbewithinthesourcerange.
voidreverse(BidirectionalIteratorfirst,
BidirectionalIteratorlast)
OutputIteratorreverse_copy(BidirectionalIteratorfirst,
BidirectionalIteratorlast,OutputIteratordestination)
Bothformsofthisfunctionreversetherange[first,last).reverse()reversestherangeinplace,and
reverse_copy()leavestheoriginalrangealoneandcopiesthereversedelementsintodestination,
returningthepasttheenditeratoroftheresultingrange.
ForwardIterator2swap_ranges(ForwardIterator1first1,
ForwardIterator1last1,ForwardIterator2first2)
Exchangesthecontentsoftworangesofequalsizebyswappingcorrespondingelements.
voidrotate(ForwardIteratorfirst,ForwardIteratormiddle,
ForwardIteratorlast)
OutputIteratorrotate_copy(ForwardIteratorfirst,
ForwardIteratormiddle,ForwardIteratorlast,
OutputIteratordestination)
Movesthecontentsof[first,middle)totheendofthesequence,andthecontentsof[middle,last)to
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
211/458
5/3/2015
ThinkinginC++2ndedVolume2
thebeginning.Withrotate(),theswapisperformedinplaceandwithrotate_copy()theoriginalrange
isuntouched,andtherotatedversioniscopiedintodestination,returningthepasttheenditeratorofthe
resultingrange.Notethatwhileswap_ranges()requiresthatthetworangesbeexactlythesamesize,
therotatefunctionsdonot.
boolnext_permutation(BidirectionalIteratorfirst,
BidirectionalIteratorlast)
boolnext_permutation(BidirectionalIteratorfirst,
BidirectionalIteratorlast,StrictWeakOrdering
binary_pred)
boolprev_permutation(BidirectionalIteratorfirst,
BidirectionalIteratorlast)
boolprev_permutation(BidirectionalIteratorfirst,
BidirectionalIteratorlast,StrictWeakOrdering
binary_pred)
Apermutationisoneuniqueorderingofasetofelements.Ifyouhavenuniqueelements,therearen!(n
factorial)distinctpossiblecombinationsofthoseelements.Allthesecombinationscanbeconceptually
sortedintoasequenceusingalexicographical(dictionarylike)orderingandthusproduceaconceptofa
nextandpreviouspermutation.Sowhateverthecurrentorderingofelementsintherange,thereisa
distinctnextandpreviouspermutationinthesequenceofpermutations.
Thenext_permutation()andprev_permutation()functionsrearrangetheelementsintotheirnextor
previouspermutationand,ifsuccessful,returntrue.Iftherearenomorenextpermutations,the
elementsareinsortedordersonext_permutation()returnsfalse.Iftherearenomoreprevious
permutations,theelementsareindescendingsortedordersoprevious_permutation()returnsfalse.
TheversionsofthefunctionsthathaveaStrictWeakOrderingargumentperformthecomparisonsusing
binary_predinsteadofoperator<.
voidrandom_shuffle(RandomAccessIteratorfirst,
RandomAccessIteratorlast)
voidrandom_shuffle(RandomAccessIteratorfirst,
RandomAccessIteratorlastRandomNumberGenerator&rand)
Thisfunctionrandomlyrearrangestheelementsintherange.Ityieldsuniformlydistributedresultsifthe
randomnumbergeneratordoes.Thefirstformusesaninternalrandomnumbergenerator,andthesecond
usesausersuppliedrandomnumbergenerator.Thegeneratormustreturnavalueintherange[0,n)for
somepositiven.
BidirectionalIteratorpartition(BidirectionalIterator
first,BidirectionalIteratorlast,Predicatepred)
BidirectionalIteratorstable_partition(BidirectionalIteratorfirst,
BidirectionalIteratorlast,Predicatepred)
Thepartitionfunctionsmoveelementsthatsatisfypredtothebeginningofthesequence.Aniterator
pointingonepastthelastofthoseelementsisreturned(whichis,ineffect,anenditeratorfortheinitial
subsequenceofelementsthatsatisfypred).Thislocationisoftencalledthepartitionpoint.
Withpartition(),theorderoftheelementsineachresultingsubsequenceafterthefunctioncallisnot
specified,butwithstable_partition(),therelativeorderoftheelementsbeforeandafterthepartition
pointwillbethesameasbeforethepartitioningprocess.
Example
Thisgivesabasicdemonstrationofsequencemanipulation:
//:C06:Manipulations.cpp
//Showsbasicmanipulations.
//{L}Generators
//NString
#include<vector>
#include<string>
#include<algorithm>
#include"PrintSequence.h"
#include"NString.h"
#include"Generators.h"
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
212/458
5/3/2015
ThinkinginC++2ndedVolume2
usingnamespacestd
intmain(){
vector<int>v1(10)
//Simplecounting:
generate(v1.begin(),v1.end(),SkipGen())
print(v1.begin(),v1.end(),"v1","")
vector<int>v2(v1.size())
copy_backward(v1.begin(),v1.end(),v2.end())
print(v2.begin(),v2.end(),"copy_backward","")
reverse_copy(v1.begin(),v1.end(),v2.begin())
print(v2.begin(),v2.end(),"reverse_copy","")
reverse(v1.begin(),v1.end())
print(v1.begin(),v1.end(),"reverse","")
inthalf=v1.size()/2
//Rangesmustbeexactlythesamesize:
swap_ranges(v1.begin(),v1.begin()+half,
v1.begin()+half)
print(v1.begin(),v1.end(),"swap_ranges","")
//Startwithafreshsequence:
generate(v1.begin(),v1.end(),SkipGen())
print(v1.begin(),v1.end(),"v1","")
intthird=v1.size()/3
for(inti=0i<10i++){
rotate(v1.begin(),v1.begin()+third,v1.end())
print(v1.begin(),v1.end(),"rotate","")
}
cout<<"Secondrotateexample:"<<endl
charc[]="aabbccddeeffgghhiijj"
constcharCSZ=strlen(c)
for(inti=0i<10i++){
rotate(c,c+2,c+CSZ)
print(c,c+CSZ,"","")
}
cout<<"Alln!permutationsofabcd:"<<endl
intnf=4*3*2*1
charp[]="abcd"
for(inti=0i<nfi++){
next_permutation(p,p+4)
print(p,p+4,"","")
}
cout<<"Usingprev_permutation:"<<endl
for(inti=0i<nfi++){
prev_permutation(p,p+4)
print(p,p+4,"","")
}
cout<<"random_shufflingaword:"<<endl
strings("hello")
cout<<s<<endl
for(inti=0i<5i++){
random_shuffle(s.begin(),s.end())
cout<<s<<endl
}
NStringsa[]={"a","b","c","d","a","b",
"c","d","a","b","c","d","a","b","c"}
constintSASZ=sizeofsa/sizeof*sa
vector<NString>ns(sa,sa+SASZ)
print(ns.begin(),ns.end(),"ns","")
vector<NString>::iteratorit=
partition(ns.begin(),ns.end(),
bind2nd(greater<NString>(),"b"))
cout<<"Partitionpoint:"<<*it<<endl
print(ns.begin(),ns.end(),"","")
//Reloadvector:
copy(sa,sa+SASZ,ns.begin())
it=stable_partition(ns.begin(),ns.end(),
bind2nd(greater<NString>(),"b"))
cout<<"Stablepartition"<<endl
cout<<"Partitionpoint:"<<*it<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
213/458
5/3/2015
ThinkinginC++2ndedVolume2
print(ns.begin(),ns.end(),"","")
}///:~
Thebestwaytoseetheresultsofthisprogramistorunit.(Youllprobablywanttoredirecttheoutputtoa
file.)
Thevector<int>v1isinitiallyloadedwithasimpleascendingsequenceandprinted.Youllseethatthe
effectofcopy_backward()(whichcopiesintov2,whichisthesamesizeasv1)isthesameasan
ordinarycopy.Again,copy_backward()doesthesamethingascopy()itjustperformstheoperations
inreverseorder.
reverse_copy()actuallydoescreateareversedcopy,andreverse()performsthereversalinplace.
Next,swap_ranges()swapstheupperhalfofthereversedsequencewiththelowerhalf.Theranges
couldbesmallersubsetsoftheentirevector,aslongastheyareofequivalentsize.
Afterrecreatingtheascendingsequence,rotate()isdemonstratedbyrotatingonethirdofv1multiple
times.Asecondrotate()exampleusescharactersandjustrotatestwocharactersatatime.Thisalso
demonstratestheflexibilityofboththeSTLalgorithmsandtheprint()template,sincetheycanbothbe
usedwitharraysofcharaseasilyaswithanythingelse.
Todemonstratenext_permutation()andprev_permutation(),asetoffourcharactersabcdis
permutedthroughalln!(nfactorial)possiblecombinations.Youllseefromtheoutputthatthe
permutationsmovethroughastrictlydefinedorder(thatis,permutingisadeterministicprocess).
Aquickanddirtydemonstrationofrandom_shuffle()istoapplyittoastringandseewhatwords
result.Becauseastringobjecthasbegin()andend()memberfunctionsthatreturntheappropriate
iterators,ittoocanbeeasilyusedwithmanyoftheSTLalgorithms.Anarrayofcharcouldalsohavebeen
used.
Finally,thepartition()andstable_partition()aredemonstrated,usinganarrayofNString.Youllnote
thattheaggregateinitializationexpressionuseschararrays,butNStringhasachar*constructorthatis
automaticallyused.
Youllseefromtheoutputthatwiththeunstablepartition,theobjectsarecorrectlyaboveandbelowthe
partitionpoint,butinnoparticularorderwhereaswiththestablepartition,theiroriginalorderis
maintained.
Searchingandreplacing
Allthesealgorithmsareusedforsearchingforoneormoreobjectswithinarangedefinedbythefirsttwo
iteratorarguments.
InputIteratorfind(InputIteratorfirst,InputIteratorlast,
constEqualityComparable&value)
Searchesforvaluewithinarangeofelements.Returnsaniteratorintherange[first,last)thatpointsto
thefirstoccurrenceofvalue.Ifvalueisntintherange,find()returnslast.Thisisalinearsearchthat
is,itstartsatthebeginningandlooksateachsequentialelementwithoutmakinganyassumptionsabout
thewaytheelementsareordered.Incontrast,abinary_search()(definedlater)worksonasorted
sequenceandcanthusbemuchfaster.
InputIteratorfind_if(InputIteratorfirst,InputIterator
last,Predicatepred)
Justlikefind(),find_if()performsalinearsearchthroughtherange.However,insteadofsearchingfor
value,find_if()looksforanelementsuchthatthePredicatepredreturnstruewhenappliedtothat
element.Returnslastifnosuchelementcanbefound.
ForwardIteratoradjacent_find(ForwardIteratorfirst,
ForwardIteratorlast)
ForwardIteratoradjacent_find(ForwardIteratorfirst,
ForwardIteratorlast,BinaryPredicatebinary_pred)
Likefind(),performsalinearsearchthroughtherange,butinsteadoflookingforonlyoneelement,it
searchesfortwoadjacentelementsthatareequivalent.Thefirstformofthefunctionlooksfortwo
elementsthatareequivalent(viaoperator==).Thesecondformlooksfortwoadjacentelementsthat,
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
214/458
5/3/2015
ThinkinginC++2ndedVolume2
whenpassedtogethertobinary_pred,produceatrueresult.Aniteratortothefirstofthetwoelements
isreturnedifapairisfoundotherwise,lastisreturned.
ForwardIterator1find_first_of(ForwardIterator1first1,
ForwardIterator1last1,ForwardIterator2first2,
ForwardIterator2last2)
ForwardIterator1find_first_of(ForwardIterator1first1,
ForwardIterator1last1,ForwardIterator2first2,
ForwardIterator2last2,BinaryPredicatebinary_pred)
Likefind(),performsalinearsearchthroughtherange.Bothformssearchforanelementinthesecond
rangethatsequivalenttooneinthefirst,thefirstformusingoperator==,andthesecondusingthe
suppliedpredicate.Inthesecondform,thecurrentelementfromthefirstrangebecomesthefirst
argumenttobinary_pred,andtheelementfromthesecondrangebecomesthesecondargument.
ForwardIterator1search(ForwardIterator1first1,
ForwardIterator1last1,ForwardIterator2first2,
ForwardIterator2last2)
ForwardIterator1search(ForwardIterator1first1,
ForwardIterator1last1,ForwardIterator2first2,
ForwardIterator2last2BinaryPredicatebinary_pred)
Checkstoseeifthesecondrangeoccurs(intheexactorderofthesecondrange)withinthefirstrange,
andifsoreturnsaniteratorpointingtotheplaceinthefirstrangewherethesecondrangebegins.Returns
last1ifnosubsetcanbefound.Thefirstformperformsitstestusingoperator==,andthesecondchecks
toseeifeachpairofobjectsbeingcomparedcausesbinary_predtoreturntrue.
ForwardIterator1find_end(ForwardIterator1first1,
ForwardIterator1last1,ForwardIterator2first2,
ForwardIterator2last2)
ForwardIterator1find_end(ForwardIterator1first1,
ForwardIterator1last1,ForwardIterator2first2,
ForwardIterator2last2,BinaryPredicatebinary_pred)
Theformsandargumentsarejustlikesearch()inthattheylookforthesecondrangeappearingasa
subsetofthefirstrange,butwhilesearch()looksforthefirstoccurrenceofthesubset,find_end()
looksforthelastoccurrenceandreturnsaniteratortoitsfirstelement.
ForwardIteratorsearch_n(ForwardIteratorfirst,
ForwardIteratorlast,Sizecount,constT&value)
ForwardIteratorsearch_n(ForwardIteratorfirst,
ForwardIteratorlast,Sizecount,constT&value,
BinaryPredicatebinary_pred)
Looksforagroupofcountconsecutivevaluesin[first,last)thatareallequaltovalue(inthefirstform)
orthatallcauseareturnvalueoftruewhenpassedintobinary_predalongwithvalue(inthesecond
form).Returnslastifsuchagroupcannotbefound.
ForwardIteratormin_element(ForwardIteratorfirst,
ForwardIteratorlast)
ForwardIteratormin_element(ForwardIteratorfirst,
ForwardIteratorlast,BinaryPredicatebinary_pred)
Returnsaniteratorpointingtothefirstoccurrenceofthesmallestvalueintherange(asexplainedbelow
theremaybemultipleoccurrencesofthisvalue.)Returnslastiftherangeisempty.Thefirstversion
performscomparisonswithoperator<,andthevaluerreturnedissuchthat*e<*risfalseforevery
elementeintherange[first,r).Thesecondversioncomparesusingbinary_pred,andthevaluer
returnedissuchthatbinary_pred(*e,*r)isfalseforeveryelementeintherange[first,r).
ForwardIteratormax_element(ForwardIteratorfirst,
ForwardIteratorlast)
ForwardIteratormax_element(ForwardIteratorfirst,
ForwardIteratorlast,BinaryPredicatebinary_pred)
Returnsaniteratorpointingtothefirstoccurrenceofthelargestvalueintherange.(Theremaybe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
215/458
5/3/2015
ThinkinginC++2ndedVolume2
multipleoccurrencesofthelargestvalue.)Returnslastiftherangeisempty.Thefirstversionperforms
comparisonswithoperator<,andthevaluerreturnedissuchthat*r<*eisfalseforeveryelementein
therange[first,r).Thesecondversioncomparesusingbinary_pred,andthevaluerreturnedissuch
thatbinary_pred(*r,*e)isfalseforeveryelementeintherange[first,r).
voidreplace(ForwardIteratorfirst,ForwardIteratorlast,
constT&old_value,constT&new_value)
voidreplace_if(ForwardIteratorfirst,ForwardIterator
last,Predicatepred,constT&new_value)
OutputIteratorreplace_copy(InputIteratorfirst,
InputIteratorlast,OutputIteratorresult,constT&
old_value,constT&new_value)
OutputIteratorreplace_copy_if(InputIteratorfirst,
InputIteratorlast,OutputIteratorresult,Predicate
pred,constT&new_value)
Eachofthereplaceformsmovesthroughtherange[first,last),findingvaluesthatmatchacriterion
andreplacingthemwithnew_value.Bothreplace()andreplace_copy()simplylookforold_valueto
replacereplace_if()andreplace_copy_if()lookforvaluesthatsatisfythepredicatepred.Thecopy
versionsofthefunctionsdonotmodifytheoriginalrangebutinsteadmakeacopywiththereplacements
intoresult(incrementingresultaftereachassignment).
Example
Toprovideeasyviewingoftheresults,thisexamplemanipulatesvectorsofint.Again,noteverypossible
versionofeachalgorithmisshown.(Somethatshouldbeobvioushavebeenomitted.)
//:C06:SearchReplace.cpp
//TheSTLsearchandreplacealgorithms.
#include<algorithm>
#include<functional>
#include<vector>
#include"PrintSequence.h"
usingnamespacestd
structPlusOne{
booloperator()(inti,intj){returnj==i+1}
}
classMulMoreThan{
intvalue
public:
MulMoreThan(intval):value(val){}
booloperator()(intv,intm){returnv*m>value}
}
intmain(){
inta[]={1,2,3,4,5,6,6,7,7,7,
8,8,8,8,11,11,11,11,11}
constintASZ=sizeofa/sizeof*a
vector<int>v(a,a+ASZ)
print(v.begin(),v.end(),"v","")
vector<int>::iteratorit=find(v.begin(),v.end(),4)
cout<<"find:"<<*it<<endl
it=find_if(v.begin(),v.end(),
bind2nd(greater<int>(),8))
cout<<"find_if:"<<*it<<endl
it=adjacent_find(v.begin(),v.end())
while(it!=v.end()){
cout<<"adjacent_find:"<<*it
<<","<<*(it+1)<<endl
it=adjacent_find(it+1,v.end())
}
it=adjacent_find(v.begin(),v.end(),PlusOne())
while(it!=v.end()){
cout<<"adjacent_findPlusOne:"<<*it
<<","<<*(it+1)<<endl
it=adjacent_find(it+1,v.end(),PlusOne())
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
216/458
5/3/2015
ThinkinginC++2ndedVolume2
intb[]={8,11}
constintBSZ=sizeofb/sizeof*b
print(b,b+BSZ,"b","")
it=find_first_of(v.begin(),v.end(),b,b+BSZ)
print(it,it+BSZ,"find_first_of","")
it=find_first_of(v.begin(),v.end(),
b,b+BSZ,PlusOne())
print(it,it+BSZ,"find_first_ofPlusOne","")
it=search(v.begin(),v.end(),b,b+BSZ)
print(it,it+BSZ,"search","")
intc[]={5,6,7}
constintCSZ=sizeofc/sizeof*c
print(c,c+CSZ,"c","")
it=search(v.begin(),v.end(),c,c+CSZ,PlusOne())
print(it,it+CSZ,"searchPlusOne","")
intd[]={11,11,11}
constintDSZ=sizeofd/sizeof*d
print(d,d+DSZ,"d","")
it=find_end(v.begin(),v.end(),d,d+DSZ)
print(it,v.end(),"find_end","")
inte[]={9,9}
print(e,e+2,"e","")
it=find_end(v.begin(),v.end(),e,e+2,PlusOne())
print(it,v.end(),"find_endPlusOne","")
it=search_n(v.begin(),v.end(),3,7)
print(it,it+3,"search_n3,7","")
it=search_n(v.begin(),v.end(),
6,15,MulMoreThan(100))
print(it,it+6,
"search_n6,15,MulMoreThan(100)","")
cout<<"min_element:"
<<*min_element(v.begin(),v.end())<<endl
cout<<"max_element:"
<<*max_element(v.begin(),v.end())<<endl
vector<int>v2
replace_copy(v.begin(),v.end(),
back_inserter(v2),8,47)
print(v2.begin(),v2.end(),"replace_copy8>47","")
replace_if(v.begin(),v.end(),
bind2nd(greater_equal<int>(),7),1)
print(v.begin(),v.end(),"replace_if>=7>1","")
}///:~
Theexamplebeginswithtwopredicates:PlusOne,whichisabinarypredicatethatreturnstrueifthe
secondargumentisequivalenttooneplusthefirstargumentandMulMoreThan,whichreturnstrueif
thefirstargumenttimesthesecondargumentisgreaterthanavaluestoredintheobject.Thesebinary
predicatesareusedastestsintheexample.
Inmain(),anarrayaiscreatedandfedtotheconstructorforvector<int>v.Thisvectoristhetarget
forthesearchandreplaceactivities,andyoullnotethatthereareduplicateelementstheseare
discoveredbysomeofthesearch/replaceroutines.
Thefirsttestdemonstratesfind(),discoveringthevalue4inv.Thereturnvalueistheiteratorpointingto
thefirstinstanceof4,ortheendoftheinputrange(v.end())ifthesearchvalueisnotfound.
Thefind_if()algorithmusesapredicatetodetermineifithasdiscoveredthecorrectelement.Inthis
example,thispredicateiscreatedontheflyusinggreater<int>(thatis,seeifthefirstintargumentis
greaterthanthesecond)andbind2nd()tofixthesecondargumentto8.Thus,itreturnstrueifthevalue
invisgreaterthan8.
Sincetwoidenticalobjectsappearnexttoeachotherinanumberofcasesinv,thetestof
adjacent_find()isdesignedtofindthemall.Itstartslookingfromthebeginningandthendropsintoa
whileloop,makingsurethattheiteratorithasnotreachedtheendoftheinputsequence(whichwould
meanthatnomorematchescanbefound).Foreachmatchitfinds,theloopprintsthematchesandthen
performsthenextadjacent_find(),thistimeusingit+1asthefirstargument(thisway,itwillstillfind
twopairsinatriple).
Youmightlookatthewhileloopandthinkthatyoucandoitabitmorecleverly,likethis:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
217/458
5/3/2015
ThinkinginC++2ndedVolume2
while(it!=v.end()){
cout<<"adjacent_find:"<<*it++
<<","<<*it++<<endl
it=adjacent_find(it,v.end())
}
Thisisexactlywhatwetriedfirst.However,wedidnotgettheoutputweexpected,onanycompiler.This
isbecausethereisnoguaranteeaboutwhentheincrementsoccurinthisexpression.
Thenexttestusesadjacent_find()withthePlusOnepredicate,whichdiscoversalltheplaceswherethe
nextnumberinthesequencevchangesfromthepreviousbyone.Thesamewhileapproachfindsallthe
cases.
Thefind_first_of()algorithmrequiresasecondrangeofobjectsforwhichtohuntthisisprovidedinthe
arrayb.Becausethefirstrangeandthesecondrangeinfind_first_of()arecontrolledbyseparate
templatearguments,thoserangescanrefertotwodifferenttypesofcontainers,asseenhere.Thesecond
formoffind_first_of()isalsotested,usingPlusOne.
Thesearch()algorithmfindsexactlythesecondrangeinsidethefirstone,withtheelementsinthesame
order.Thesecondformofsearch()usesapredicate,whichistypicallyjustsomethingthatdefines
equivalence,butitalsopresentssomeinterestingpossibilitieshere,thePlusOnepredicatecausesthe
range{4,5,6}tobefound.
Thefind_end()testdiscoversthelastoccurrenceoftheentiresequence{11,11,11}.Toshowthatit
hasinfactfoundthelastoccurrence,therestofvstartingfromitisprinted.
Thefirstsearch_n()testlooksfor3copiesofthevalue7,whichitfindsandprints.Whenusingthe
secondversionofsearch_n(),thepredicateisordinarilymeanttobeusedtodetermineequivalence
betweentwoelements,butwevetakensomelibertiesandusedafunctionobjectthatmultipliesthevalue
inthesequenceby(inthiscase)15andcheckstoseeifitsgreaterthan100.Thatis,thesearch_n()
testsaysfindme6consecutivevaluesthat,whenmultipliedby15,eachproduceanumbergreaterthan
100.Notexactlywhatyounormallyexpecttodo,butitmightgiveyousomeideasthenexttimeyouhave
anoddsearchingproblem.
Themin_element()andmax_element()algorithmsarestraightforward,buttheylookodd,asifthe
functionisbeingdereferencedwitha*.Actually,thereturnediteratorisbeingdereferencedtoproduce
thevalueforprinting.
Totestreplacements,replace_copy()isusedfirst(soitdoesntmodifytheoriginalvector)toreplace
allvaluesof8withthevalue47.Noticetheuseofback_inserter()withtheemptyvectorv2.To
demonstratereplace_if(),afunctionobjectiscreatedusingthestandardtemplategreater_equalalong
withbind2ndtoreplaceallthevaluesthataregreaterthanorequalto7withthevalue1.
Comparingranges
Thesealgorithmsprovidewaystocomparetworanges.Atfirstglance,theoperationstheyperformseem
similartothesearch()function.However,search()tellsyouwherethesecondsequenceappearswithin
thefirst,andequal()andlexicographical_compare()simplytellyouhowtwosequencescompare.On
theotherhand,mismatch()doestellyouwherethetwosequencesgooutofsync,butthosesequences
mustbeexactlythesamelength.
boolequal(InputIteratorfirst1,InputIteratorlast1,
InputIteratorfirst2)
boolequal(InputIteratorfirst1,InputIteratorlast1,
InputIteratorfirst2BinaryPredicatebinary_pred)
Inboththesefunctions,thefirstrangeisthetypicalone,[first1,last1).Thesecondrangestartsat
first2,butthereisnolast2becauseitslengthisdeterminedbythelengthofthefirstrange.The
equal()functionreturnstrueifbothrangesareexactlythesame(thesameelementsinthesameorder).
Inthefirstcase,theoperator==performsthecomparison,andinthesecondcasebinary_preddecides
iftwoelementsarethesame.
boollexicographical_compare(InputIterator1first1,
InputIterator1last1,InputIterator2first2,
InputIterator2last2)
boollexicographical_compare(InputIterator1first1,
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
218/458
5/3/2015
ThinkinginC++2ndedVolume2
InputIterator1last1,InputIterator2first2,
InputIterator2last2,BinaryPredicatebinary_pred)
Thesetwofunctionsdetermineifthefirstrangeislexicographicallylessthanthesecond.(Theyreturn
trueifrange1islessthanrange2,andfalseotherwise.)Lexicographicalcomparison,ordictionary
comparison,meansthatthecomparisonisdoneinthesamewaythatweestablishtheorderofstringsina
dictionary:oneelementatatime.Thefirstelementsdeterminetheresultiftheseelementsaredifferent,
butiftheyreequal,thealgorithmmovesontothenextelementsandlooksatthose,andsoonuntilit
findsamismatch.Atthatpoint,itlooksattheelements,andiftheelementfromrange1islessthanthe
elementfromrangetwo,lexicographical_compare()returnstrueotherwise,itreturnsfalse.Ifit
getsallthewaythroughonerangeortheother(therangesmaybedifferentlengthsforthisalgorithm)
withoutfindinganinequality,range1isnotlessthanrange2,sothefunctionreturnsfalse.
Ifthetworangesaredifferentlengths,amissingelementinonerangeactsasonethatprecedesan
elementthatexistsintheotherrange,soabcprecedesabcd.Ifthealgorithmreachestheendofoneof
therangeswithoutamismatch,thentheshorterrangecomesfirst.Inthatcase,iftheshorterrangeisthe
firstrange,theresultistrue,otherwiseitisfalse.
Inthefirstversionofthefunction,operator<performsthecomparisons,andinthesecondversion,
binary_predisused.
pair<InputIterator1,InputIterator2>
mismatch(InputIterator1first1,InputIterator1last1,
InputIterator2first2)
pair<InputIterator1,InputIterator2>
mismatch(InputIterator1first1,InputIterator1last1,
InputIterator2first2,BinaryPredicatebinary_pred)
Asinequal(),thelengthofbothrangesisexactlythesame,soonlythefirstiteratorinthesecondrange
isnecessary,andthelengthofthefirstrangeisusedasthelengthofthesecondrange.Whereasequal()
justtellsyouwhetherthetworangesarethesame,mismatch()tellsyouwheretheybegintodiffer.To
accomplishthis,youmustbetold(1)theelementinthefirstrangewherethemismatchoccurredand(2)
theelementinthesecondrangewherethemismatchoccurred.Thesetwoiteratorsarepackagedtogether
intoapairobjectandreturned.Ifnomismatchoccurs,thereturnvalueislast1combinedwiththepast
theenditeratorofthesecondrange.Thepairtemplateclassisastructwithtwoelementsdenotedbythe
membernamesfirstandsecondandisdefinedinthe<utility>header.
Asinequal(),thefirstfunctiontestsforequalityusingoperator==whilethesecondoneuses
binary_pred.
Example
BecausetheStandardC++stringclassisbuiltlikeacontainer(ithasbegin()andend()member
functionsthatproduceobjectsoftypestring::iterator),itcanbeusedtoconvenientlycreaterangesof
characterstotestwiththeSTLcomparisonalgorithms.However,notethatstringhasafairlycompleteset
ofnativeoperations,solookatthestringclassbeforeusingtheSTLalgorithmstoperformoperations.
//:C06:Comparison.cpp
//TheSTLrangecomparisonalgorithms.
#include<algorithm>
#include<functional>
#include<string>
#include<vector>
#include"PrintSequence.h"
usingnamespacestd
intmain(){
//Stringsprovideaconvenientwaytocreate
//rangesofcharacters,butyoushould
//normallylookfornativestringoperations:
strings1("Thisisatest")
strings2("ThisisaTest")
cout<<"s1:"<<s1<<endl<<"s2:"<<s2<<endl
cout<<"compares1&s1:"
<<equal(s1.begin(),s1.end(),s1.begin())<<endl
cout<<"compares1&s2:"
<<equal(s1.begin(),s1.end(),s2.begin())<<endl
cout<<"lexicographical_compares1&s1:"
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
219/458
5/3/2015
ThinkinginC++2ndedVolume2
<<lexicographical_compare(s1.begin(),s1.end(),
s1.begin(),s1.end())<<endl
cout<<"lexicographical_compares1&s2:"
<<lexicographical_compare(s1.begin(),s1.end(),
s2.begin(),s2.end())<<endl
cout<<"lexicographical_compares2&s1:"
<<lexicographical_compare(s2.begin(),s2.end(),
s1.begin(),s1.end())<<endl
cout<<"lexicographical_compareshortened"
"s1&fulllengths2:"<<endl
strings3(s1)
while(s3.length()!=0){
boolresult=lexicographical_compare(
s3.begin(),s3.end(),s2.begin(),s2.end())
cout<<s3<<endl<<s2<<",result="
<<result<<endl
if(result==true)break
s3=s3.substr(0,s3.length()1)
}
pair<string::iterator,string::iterator>p=
mismatch(s1.begin(),s1.end(),s2.begin())
print(p.first,s1.end(),"p.first","")
print(p.second,s2.end(),"p.second","")
}///:~
Notethattheonlydifferencebetweens1ands2isthecapitalTins2sTest.Comparings1ands1for
equalityyieldstrue,asexpected,whiles1ands2arenotequalbecauseofthecapitalT.
Tounderstandtheoutputofthelexicographical_compare()tests,remembertwothings:first,the
comparisonisperformedcharacterbycharacter,andsecond,onourplatformcapitallettersprecede
lowercaseletters.Inthefirsttest,s1iscomparedtos1.Theseareexactlyequivalent.Oneisnot
lexicographicallylessthantheother(whichiswhatthecomparisonislookingfor),andthustheresultis
false.Thesecondtestisaskingdoess1precedes2?Whenthecomparisongetstothetintest,it
discoversthatthelowercasetins1isgreaterthantheuppercaseTins2,sotheanswerisagain
false.However,ifwetesttoseewhethers2precedess1,theansweristrue.
Tofurtherexaminelexicographicalcomparison,thenexttestinthisexamplecomparess1withs2again
(whichreturnedfalsebefore).Butthistimeitrepeatsthecomparison,trimmingonecharacterofftheend
ofs1(whichisfirstcopiedintos3)eachtimethroughtheloopuntilthetestevaluatestotrue.Whatyoull
seeisthat,assoonastheuppercaseTistrimmedoffs3(thecopyofs1),thecharacters,whichare
exactlyequaluptothatpoint,nolongercount.Becauses3isshorterthans2,itlexicographicallyprecedes
s2.
Thefinaltestusesmismatch().Tocapturethereturnvalue,createtheappropriatepairp,constructing
thetemplateusingtheiteratortypefromthefirstrangeandtheiteratortypefromthesecondrange(in
thiscase,bothstring::iterators).Toprinttheresults,theiteratorforthemismatchinthefirstrangeis
p.first,andforthesecondrangeisp.second.Inbothcases,therangeisprintedfromthemismatch
iteratortotheendoftherangesoyoucanseeexactlywheretheiteratorpoints.
Removingelements
BecauseofthegenericityoftheSTL,theconceptofremovalisabitconstrained.Sinceelementscanonly
beremovedviaiterators,anditeratorscanpointtoarrays,vectors,lists,andsoon,itisnotsafeor
reasonabletotrytodestroytheelementsthatarebeingremovedandtochangethesizeoftheinputrange
[first,last).(Anarray,forexample,cannothaveitssizechanged.)Soinstead,whattheSTLremove
functionsdoisrearrangethesequencesothattheremovedelementsareattheendofthesequence,and
theunremovedelementsareatthebeginningofthesequence(inthesameorderthattheywerebefore,
minustheremovedelementsthatis,thisisastableoperation).Thenthefunctionwillreturnaniteratorto
thenewlastelementofthesequence,whichistheendofthesequencewithouttheremovedelements
andthebeginningofthesequenceoftheremovedelements.Inotherwords,ifnew_lastistheiterator
thatisreturnedfromtheremovefunction,[first,new_last)isthesequencewithoutanyoftheremoved
elements,and[new_last,last)isthesequenceofremovedelements.
Ifyouaresimplyusingyoursequence,includingtheremovedelements,withmoreSTLalgorithms,you
canjustusenew_lastasthenewpasttheenditerator.However,ifyoureusingaresizablecontainerc
(notanarray)andyouwanttoeliminatetheremovedelementsfromthecontainer,youcanuseerase()
todoso,forexample:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
220/458
5/3/2015
ThinkinginC++2ndedVolume2
c.erase(remove(c.begin(),c.end(),value),c.end())
Youcanalsousetheresize()memberfunctionthatbelongstoallstandardsequences(moreonthisin
thenextchapter).
Thereturnvalueofremove()isthenew_lastiterator,soerase()deletesalltheremovedelements
fromc.
Theiteratorsin[new_last,last)aredereferenceable,buttheelementvaluesareunspecifiedandshould
notbeused.
ForwardIteratorremove(ForwardIteratorfirst,
ForwardIteratorlast,constT&value)
ForwardIteratorremove_if(ForwardIteratorfirst,
ForwardIteratorlast,Predicatepred)
OutputIteratorremove_copy(InputIteratorfirst,
InputIteratorlast,OutputIteratorresult,constT&
value)
OutputIteratorremove_copy_if(InputIteratorfirst,
InputIteratorlast,OutputIteratorresult,Predicate
pred)
Eachoftheremoveformsmovesthroughtherange[first,last),findingvaluesthatmatcharemoval
criterionandcopyingtheunremovedelementsovertheremovedelements(thuseffectivelyremoving
them).Theoriginalorderoftheunremovedelementsismaintained.Thereturnvalueisaniteratorpointing
pasttheendoftherangethatcontainsnoneoftheremovedelements.Thevaluesthatthisiteratorpoints
toareunspecified.
Theifversionspasseachelementtopred()todeterminewhetheritshouldberemoved.(Ifpred()
returnstrue,theelementisremoved.)Thecopyversionsdonotmodifytheoriginalsequence,but
insteadcopytheunremovedvaluesintoarangebeginningatresultandreturnaniteratorindicatingthe
pasttheendvalueofthisnewrange.
ForwardIteratorunique(ForwardIteratorfirst,
ForwardIteratorlast)
ForwardIteratorunique(ForwardIteratorfirst,
ForwardIteratorlast,BinaryPredicatebinary_pred)
OutputIteratorunique_copy(InputIteratorfirst,
InputIteratorlast,OutputIteratorresult)
OutputIteratorunique_copy(InputIteratorfirst,
InputIteratorlast,OutputIteratorresult,
BinaryPredicatebinary_pred)
Eachoftheuniquefunctionsmovesthroughtherange[first,last),findingadjacentvaluesthatare
equivalent(thatis,duplicates)andremovingtheduplicateelementsbycopyingoverthem.Theoriginal
orderoftheunremovedelementsismaintained.Thereturnvalueisaniteratorpointingpasttheendofthe
rangethathastheadjacentduplicatesremoved.
Becauseonlyduplicatesthatareadjacentareremoved,itslikelythatyoullwanttocallsort()before
callingauniquealgorithm,sincethatwillguaranteethatalltheduplicatesareremoved.
Foreachiteratorvalueiintheinputrange,theversionscontainingbinary_predcall:
binary_pred(*i,*(i1))
andiftheresultistrue,*iisconsideredaduplicate.
Thecopyversionsdonotmodifytheoriginalsequence,butinsteadcopytheunremovedvaluesintoa
rangebeginningatresultandreturnaniteratorindicatingthepasttheendvalueofthisnewrange.
Example
Thisexamplegivesavisualdemonstrationofthewaytheremoveanduniquefunctionswork.
//:C06:Removing.cpp
//Theremovingalgorithms.
//{L}Generators
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
221/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<algorithm>
#include<cctype>
#include<string>
#include"Generators.h"
#include"PrintSequence.h"
usingnamespacestd
structIsUpper{
booloperator()(charc){returnisupper(c)}
}
intmain(){
stringv
v.resize(25)
generate(v.begin(),v.end(),CharGen())
print(v.begin(),v.end(),"voriginal","")
//Createasetofthecharactersinv:
stringus(v.begin(),v.end())
sort(us.begin(),us.end())
string::iteratorit=us.begin(),cit=v.end(),
uend=unique(us.begin(),us.end())
//Stepthroughandremoveeverything:
while(it!=uend){
cit=remove(v.begin(),cit,*it)
print(v.begin(),v.end(),"Completev","")
print(v.begin(),cit,"Pseudov","")
cout<<"Removedelement:\t"<<*it
<<"\nPsuedoLastElement:\t"
<<*cit<<endl<<endl
++it
}
generate(v.begin(),v.end(),CharGen())
print(v.begin(),v.end(),"v","")
cit=remove_if(v.begin(),v.end(),IsUpper())
print(v.begin(),cit,"vafterremove_ifIsUpper","")
//Copyingversionsarenotshownforremove()
//andremove_if().
sort(v.begin(),cit)
print(v.begin(),cit,"sorted","")
stringv2
v2.resize(citv.begin())
unique_copy(v.begin(),cit,v2.begin())
print(v2.begin(),v2.end(),"unique_copy","")
//Samebehavior:
cit=unique(v.begin(),cit,equal_to<char>())
print(v.begin(),cit,"uniqueequal_to<char>","")
}///:~
Thestringvisacontainerofcharactersfilledwithrandomlygeneratedcharacters.Eachcharacterisused
inaremovestatement,buttheentirestringvisdisplayedeachtimesoyoucanseewhathappenstothe
restoftherange,aftertheresultingendpoint(whichisstoredincit).
Todemonstrateremove_if(),thestandardClibraryfunctionisupper()(in<cctype>)iscalledinside
thefunctionobjectclassIsUpper,anobjectofwhichispassedasthepredicateforremove_if().This
returnstrueonlyifacharacterisuppercase,soonlylowercasecharacterswillremain.Here,theendof
therangeisusedinthecalltoprint()soonlytheremainingelementswillappear.Thecopyingversions
ofremove()andremove_if()arenotshownbecausetheyareasimplevariationonthenoncopying
versions,whichyoushouldbeabletousewithoutanexample.
Therangeoflowercaselettersissortedinpreparationfortestingtheuniquefunctions.(Theunique
functionsarenotundefinediftherangeisntsorted,butitsprobablynotwhatyouwant.)First,
unique_copy()putstheuniqueelementsintoanewvectorusingthedefaultelementcomparison,and
thenusestheformofunique()thattakesapredicate.Thepredicateisthebuiltinfunctionobject
equal_to(),whichproducesthesameresultsasthedefaultelementcomparison.
Sortingandoperationsonsortedranges
AsignificantcategoryofSTLalgorithmsmustoperateonasortedrange.STLprovidesanumberof
separatesortingalgorithms,dependingonwhetherthesortshouldbestable,partial,orjustregular(non
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
222/458
5/3/2015
ThinkinginC++2ndedVolume2
stable).Oddlyenough,onlythepartialsorthasacopyingversion.Ifyoureusinganothersortandyou
needtoworkonacopy,youllhavetomakeyourowncopybeforesorting.
Onceyoursequenceissorted,youcanperformmanyoperationsonthatsequence,fromsimplylocatingan
elementorgroupofelementstomergingwithanothersortedsequenceormanipulatingsequencesas
mathematicalsets.
Eachalgorithminvolvedwithsortingoroperationsonsortedsequenceshastwoversions.Thefirstusesthe
objectsownoperator<toperformthecomparison,andthesecondusesoperator()(a,b)todetermine
therelativeorderofaandb.Otherthanthis,therearenodifferences,sothisdistinctionwillnotbe
pointedoutinthedescriptionofeachalgorithm.
Sorting
Thesortalgorithmsrequirerangesdelimitedbyrandomaccessiterators,suchasavectorordeque.The
listcontainerhasitsownbuiltinsort()function,sinceitonlysupportsbidirectionaliteration.
voidsort(RandomAccessIteratorfirst,RandomAccessIterator
last)
voidsort(RandomAccessIteratorfirst,RandomAccessIterator
last,StrictWeakOrderingbinary_pred)
Sorts[first,last)intoascendingorder.Thefirstformusesoperator<andthesecondformusesthe
suppliedcomparatorobjecttodeterminetheorder.
voidstable_sort(RandomAccessIteratorfirst,
RandomAccessIteratorlast)
voidstable_sort(RandomAccessIteratorfirst,
RandomAccessIteratorlast,StrictWeakOrdering
binary_pred)
Sorts[first,last)intoascendingorder,preservingtheoriginalorderingofequivalentelements.(Thisis
importantifelementscanbeequivalentbutnotidentical.)
voidpartial_sort(RandomAccessIteratorfirst,
RandomAccessIteratormiddle,RandomAccessIteratorlast)
voidpartial_sort(RandomAccessIteratorfirst,
RandomAccessIteratormiddle,RandomAccessIteratorlast,
StrictWeakOrderingbinary_pred)
Sortsthenumberofelementsfrom[first,last)thatcanbeplacedintherange[first,middle).Therest
oftheelementsendupin[middle,last)andhavenoguaranteedorder.
RandomAccessIteratorpartial_sort_copy(InputIteratorfirst,
InputIteratorlast,RandomAccessIteratorresult_first,
RandomAccessIteratorresult_last)
RandomAccessIteratorpartial_sort_copy(InputIteratorfirst,
InputIteratorlast,RandomAccessIteratorresult_first,
RandomAccessIteratorresult_last,StrictWeakOrdering
binary_pred)
Sortsthenumberofelementsfrom[first,last)thatcanbeplacedintherange[result_first,
result_last)andcopiesthoseelementsinto[result_first,result_last).Iftherange[first,last)is
smallerthan[result_first,result_last),thesmallernumberofelementsisused.
voidnth_element(RandomAccessIteratorfirst,
RandomAccessIteratornth,RandomAccessIteratorlast)
voidnth_element(RandomAccessIteratorfirst,
RandomAccessIteratornth,RandomAccessIteratorlast,
StrictWeakOrderingbinary_pred)
Justlikepartial_sort(),nth_element()partiallyordersarangeofelements.However,itsmuchless
orderedthanpartial_sort().Theonlyguaranteefromnth_element()isthatwhateverlocationyou
choosewillbecomeadividingpoint.Alltheelementsintherange[first,nth)willpairwisesatisfythe
binarypredicate(operator<bydefault,asusual),andalltheelementsintherange(nth,last]willnot.
However,neithersubrangeisinanyparticularorder,unlikepartial_sort()whichhasthefirstrangein
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
223/458
5/3/2015
ThinkinginC++2ndedVolume2
sortedorder.
Ifallyouneedisthisveryweakordering(if,forexample,youredeterminingmedians,percentiles,andso
on),thisalgorithmisfasterthanpartial_sort().
Locatingelementsinsortedranges
Oncearangeissorted,youcanuseagroupofoperationstofindelementswithinthoseranges.Inthe
followingfunctions,therearealwaystwoforms.Oneassumesthattheintrinsicoperator<performsthe
sort,andthesecondoperatormustbeusedifsomeothercomparisonfunctionobjectperformsthesort.
Youmustusethesamecomparisonforlocatingelementsasyoudotoperformthesortotherwise,the
resultsareundefined.Inaddition,ifyoutrytousethesefunctionsonunsortedranges,theresultswillbe
undefined.
boolbinary_search(ForwardIteratorfirst,ForwardIterator
last,constT&value)
boolbinary_search(ForwardIteratorfirst,ForwardIterator
last,constT&value,StrictWeakOrderingbinary_pred)
Tellsyouwhethervalueappearsinthesortedrange[first,last).
ForwardIteratorlower_bound(ForwardIteratorfirst,
ForwardIteratorlast,constT&value)
ForwardIteratorlower_bound(ForwardIteratorfirst,
ForwardIteratorlast,constT&value,StrictWeakOrdering
binary_pred)
Returnsaniteratorindicatingthefirstoccurrenceofvalueinthesortedrange[first,last).Ifvalueisnot
present,aniteratortowhereitwouldfitinthesequenceisreturned.
ForwardIteratorupper_bound(ForwardIteratorfirst,
ForwardIteratorlast,constT&value)
ForwardIteratorupper_bound(ForwardIteratorfirst,
ForwardIteratorlast,constT&value,StrictWeakOrdering
binary_pred)
Returnsaniteratorindicatingonepastthelastoccurrenceofvalueinthesortedrange[first,last).If
valueisnotpresent,aniteratortowhereitwouldfitinthesequenceisreturned.
pair<ForwardIterator,ForwardIterator>equal_range(ForwardIteratorfirst,ForwardIterator
last,
constT&value)
pair<ForwardIterator,ForwardIterator>equal_range(ForwardIteratorfirst,ForwardIterator
last,
constT&value,StrictWeakOrderingbinary_pred)
Essentiallycombineslower_bound()andupper_bound()toreturnapairindicatingthefirstandone
pastthelastoccurrencesofvalueinthesortedrange[first,last).Bothiteratorsindicatethelocation
wherevaluewouldfitifitisnotfound.
Youmayfinditsurprisingthatthebinarysearchalgorithmstakeaforwarditeratorinsteadofarandom
accessiterator.(Mostexplanationsofbinarysearchuseindexing.)Rememberthatarandomaccess
iteratorisaforwarditerator,andcanbeusedwhereverthelatterisspecified.Iftheiteratorpassedto
oneofthesealgorithmsinfactsupportsrandomaccess,thentheefficientlogarithmictimeprocedureis
used,otherwisealinearsearchisperformed.[96]
Example
ThefollowingexampleturnseachinputwordintoanNStringandaddsittoavector<NString>.The
vectoristhenusedtodemonstratethevarioussortingandsearchingalgorithms.
//:C06:SortedSearchTest.cpp
//Testsearchinginsortedranges.
//NString
#include<algorithm>
#include<cassert>
#include<ctime>
#include<cstdlib>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
224/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<cstddef>
#include<fstream>
#include<iostream>
#include<iterator>
#include<vector>
#include"NString.h"
#include"PrintSequence.h"
#include"../require.h"
usingnamespacestd
intmain(intargc,char*argv[]){
typedefvector<NString>::iteratorsit
char*fname="Test.txt"
if(argc>1)fname=argv[1]
ifstreamin(fname)
assure(in,fname)
srand(time(0))
cout.setf(ios::boolalpha)
vector<NString>original
copy(istream_iterator<string>(in),
istream_iterator<string>(),back_inserter(original))
require(original.size()>=4,"Musthavefourelements")
vector<NString>v(original.begin(),original.end()),
w(original.size()/2)
sort(v.begin(),v.end())
print(v.begin(),v.end(),"sort")
v=original
stable_sort(v.begin(),v.end())
print(v.begin(),v.end(),"stable_sort")
v=original
sitit=v.begin(),it2
//Moveiteratortomiddle
for(size_ti=0i<v.size()/2i++)
++it
partial_sort(v.begin(),it,v.end())
cout<<"middle="<<*it<<endl
print(v.begin(),v.end(),"partial_sort")
v=original
//Moveiteratortoaquarterposition
it=v.begin()
for(size_ti=0i<v.size()/4i++)
++it
//Lesselementstocopyfromthantothedestination
partial_sort_copy(v.begin(),it,w.begin(),w.end())
print(w.begin(),w.end(),"partial_sort_copy")
//Notenoughroomindestination
partial_sort_copy(v.begin(),v.end(),w.begin(),w.end())
print(w.begin(),w.end(),"wpartial_sort_copy")
//vremainsthesamethroughallthisprocess
assert(v==original)
nth_element(v.begin(),it,v.end())
cout<<"Thenth_element="<<*it<<endl
print(v.begin(),v.end(),"nth_element")
stringf=original[rand()%original.size()]
cout<<"binarysearch:"
<<binary_search(v.begin(),v.end(),f)<<endl
sort(v.begin(),v.end())
it=lower_bound(v.begin(),v.end(),f)
it2=upper_bound(v.begin(),v.end(),f)
print(it,it2,"foundrange")
pair<sit,sit>ip=equal_range(v.begin(),v.end(),f)
print(ip.first,ip.second,"equal_range")
}///:~
ThisexampleusestheNStringclassseenearlier,whichstoresanoccurrencenumberwithcopiesofa
string.Thecalltostable_sort()showshowtheoriginalorderforobjectswithequalstringsispreserved.
Youcanalsoseewhathappensduringapartialsort(theremainingunsortedelementsareinnoparticular
order).Thereisnopartialstablesort.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
225/458
5/3/2015
ThinkinginC++2ndedVolume2
Noticeinthecalltonth_element()that,whateverthenthelementturnsouttobe(whichwillvaryfrom
oneruntoanotherbecauseofURandGen),theelementsbeforethatareless,andafterthataregreater,
buttheelementshavenoparticularorderotherthanthat.BecauseofURandGen,therearenoduplicates,
butifyouuseageneratorthatallowsduplicates,youllseethattheelementsbeforethenthelementwill
belessthanorequaltothenthelement.
Thisexamplealsoillustratesallthreebinarysearchalgorithms.Asadvertised,lower_bound()refersto
thefirstelementinthesequenceequaltoagivenkey,upper_bound()pointsonepastthelast,and
equal_range()returnsbothresultsasapair.
Mergingsortedranges
Asbefore,thefirstformofeachfunctionassumesthattheintrinsicoperator<performsthesort.The
secondformmustbeusedifsomeothercomparisonfunctionobjectperformsthesort.Youmustusethe
samecomparisonforlocatingelementsasyoudotoperformthesortotherwise,theresultsare
undefined.Inaddition,ifyoutrytousethesefunctionsonunsortedranges,theresultswillbeundefined.
OutputIteratormerge(InputIterator1first1,InputIterator1
last1,InputIterator2first2,InputIterator2last2,
OutputIteratorresult)
OutputIteratormerge(InputIterator1first1,InputIterator1
last1,InputIterator2first2,InputIterator2last2,
OutputIteratorresult,StrictWeakOrderingbinary_pred)
Copieselementsfrom[first1,last1)and[first2,last2)intoresult,suchthattheresultingrangeis
sortedinascendingorder.Thisisastableoperation.
voidinplace_merge(BidirectionalIteratorfirst,
BidirectionalIteratormiddle,BidirectionalIterator
last)
voidinplace_merge(BidirectionalIteratorfirst,
BidirectionalIteratormiddle,BidirectionalIteratorlast,
StrictWeakOrderingbinary_pred)
Thisassumesthat[first,middle)and[middle,last)areeachsortedrangesinthesamesequence.The
tworangesaremergedsothattheresultingrange[first,last)containsthecombinedrangesinsorted
order.
Example
Itseasiertoseewhatgoesonwithmergingifintsareused.Thefollowingexamplealsoemphasizeshow
thealgorithms(andourownprinttemplate)workwitharraysaswellascontainers:
//:C06:MergeTest.cpp
//Testmerginginsortedranges.
//{L}Generators
#include<algorithm>
#include"PrintSequence.h"
#include"Generators.h"
usingnamespacestd
intmain(){
constintSZ=15
inta[SZ*2]={0}
//Bothrangesgointhesamearray:
generate(a,a+SZ,SkipGen(0,2))
a[3]=4
a[4]=4
generate(a+SZ,a+SZ*2,SkipGen(1,3))
print(a,a+SZ,"range1","")
print(a+SZ,a+SZ*2,"range2","")
intb[SZ*2]={0}//Initializealltozero
merge(a,a+SZ,a+SZ,a+SZ*2,b)
print(b,b+SZ*2,"merge","")
//Resetb
for(inti=0i<SZ*2i++)
b[i]=0
inplace_merge(a,a+SZ,a+SZ*2)
print(a,a+SZ*2,"inplace_merge","")
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
226/458
5/3/2015
ThinkinginC++2ndedVolume2
int*end=set_union(a,a+SZ,a+SZ,a+SZ*2,b)
print(b,end,"set_union","")
}///:~
Inmain(),insteadofcreatingtwoseparatearrays,bothrangesarecreatedendtoendinthearraya.
(Thiswillcomeinhandyfortheinplace_merge.)Thefirstcalltomerge()placestheresultina
differentarray,b.Forcomparison,set_union()isalsocalled,whichhasthesamesignatureandsimilar
behavior,exceptthatitremovesduplicatesfromthesecondset.Finally,inplace_merge()combinesboth
partsofa.
Setoperationsonsortedranges
Oncerangeshavebeensorted,youcanperformmathematicalsetoperationsonthem.
boolincludes(InputIterator1first1,InputIterator1last1,
InputIterator2first2,InputIterator2last2)
boolincludes(InputIterator1first1,InputIterator1last1,
InputIterator2first2,InputIterator2last2,
StrictWeakOrderingbinary_pred)
Returnstrueif[first2,last2)isasubsetof[first1,last1).Neitherrangeisrequiredtoholdonlyunique
elements,butif[first2,last2)holdsnelementsofaparticularvalue,[first1,last1)mustalsoholdat
leastnelementsiftheresultistobetrue.
OutputIteratorset_union(InputIterator1first1,
InputIterator1last1,InputIterator2first2,
InputIterator2last2,OutputIteratorresult)
OutputIteratorset_union(InputIterator1first1,
InputIterator1last1,InputIterator2first2,
InputIterator2last2,OutputIteratorresult,
StrictWeakOrderingbinary_pred)
Createsthemathematicalunionoftwosortedrangesintheresultrange,returningtheendoftheoutput
range.Neitherinputrangeisrequiredtoholdonlyuniqueelements,butifaparticularvalueappears
multipletimesinbothinputsets,theresultingsetwillcontainthelargernumberofidenticalvalues.
OutputIteratorset_intersection(InputIterator1first1,
InputIterator1last1,InputIterator2first2,
InputIterator2last2,OutputIteratorresult)
OutputIteratorset_intersection(InputIterator1first1,
InputIterator1last1,InputIterator2first2,
InputIterator2last2,OutputIteratorresult,
StrictWeakOrderingbinary_pred)
Produces,inresult,theintersectionofthetwoinputsets,returningtheendoftheoutputrangethatis,
thesetofvaluesthatappearinbothinputsets.Neitherinputrangeisrequiredtoholdonlyunique
elements,butifaparticularvalueappearsmultipletimesinbothinputsets,theresultingsetwillcontain
thesmallernumberofidenticalvalues.
OutputIteratorset_difference(InputIterator1first1,
InputIterator1last1,InputIterator2first2,
InputIterator2last2,OutputIteratorresult)
OutputIteratorset_difference(InputIterator1first1,
InputIterator1last1,InputIterator2first2,
InputIterator2last2,OutputIteratorresult,
StrictWeakOrderingbinary_pred)
Produces,inresult,themathematicalsetdifference,returningtheendoftheoutputrange.Allthe
elementsthatarein[first1,last1)butnotin[first2,last2)areplacedintheresultset.Neitherinput
rangeisrequiredtoholdonlyuniqueelements,butifaparticularvalueappearsmultipletimesinboth
inputsets(ntimesinset1andmtimesinset2),theresultingsetwillcontainmax(nm,0)copiesof
thatvalue.
OutputIteratorset_symmetric_difference(InputIterator1
first1,InputIterator1last1,InputIterator2first2,
InputIterator2last2,OutputIteratorresult)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
227/458
5/3/2015
ThinkinginC++2ndedVolume2
OutputIteratorset_symmetric_difference(InputIterator1
first1,InputIterator1last1,InputIterator2first2,
InputIterator2last2,OutputIteratorresult,
StrictWeakOrderingbinary_pred)
Constructs,inresult,thesetcontaining:
1.Alltheelementsinset1thatarenotinset2.
2.Alltheelementsinset2thatarenotinset1.
Neitherinputrangeisrequiredtoholdonlyuniqueelements,butifaparticularvalueappearsmultiple
timesinbothinputsets(ntimesinset1andmtimesinset2),theresultingsetwillcontainabs(nm)
copiesofthatvalue,whereabs()istheabsolutevalue.Thereturnvalueistheendoftheoutputrange.
Example
Itseasiesttoseethesetoperationsdemonstratedusingsimplevectorsofcharacters.Thesecharacters
arerandomlygeneratedandthensorted,buttheduplicatesareretainedsothatyoucanseewhattheset
operationsdowhenthereareduplicates.
//:C06:SetOperations.cpp
//Setoperationsonsortedranges.
//{L}Generators
#include<algorithm>
#include<vector>
#include"Generators.h"
#include"PrintSequence.h"
usingnamespacestd
intmain(){
constintSZ=30
charv[SZ+1],v2[SZ+1]
CharGeng
generate(v,v+SZ,g)
generate(v2,v2+SZ,g)
sort(v,v+SZ)
sort(v2,v2+SZ)
print(v,v+SZ,"v","")
print(v2,v2+SZ,"v2","")
boolb=includes(v,v+SZ,v+SZ/2,v+SZ)
cout.setf(ios::boolalpha)
cout<<"includes:"<<b<<endl
charv3[SZ*2+1],*end
end=set_union(v,v+SZ,v2,v2+SZ,v3)
print(v3,end,"set_union","")
end=set_intersection(v,v+SZ,v2,v2+SZ,v3)
print(v3,end,"set_intersection","")
end=set_difference(v,v+SZ,v2,v2+SZ,v3)
print(v3,end,"set_difference","")
end=set_symmetric_difference(v,v+SZ,
v2,v2+SZ,v3)
print(v3,end,"set_symmetric_difference","")
}///:~
Aftervandv2aregenerated,sorted,andprinted,theincludes()algorithmistestedbyseeingifthe
entirerangeofvcontainsthelasthalfofv.Itdoes,sotheresultshouldalwaysbetrue.Thearrayv3
holdstheoutputofset_union(),set_intersection(),set_difference(),and
set_symmetric_difference(),andtheresultsofeacharedisplayedsoyoucanponderthemand
convinceyourselfthatthealgorithmsworkaspromised.
Heapoperations
Aheapisanarraylikedatastructureusedtoimplementapriorityqueue,whichisjustarangethatis
organizedinawaythataccommodatesretrievingelementsbypriorityaccordingtosomecomparison
function.Theheapoperationsinthestandardlibraryallowasequencetobetreatedasaheapdata
structure,whichalwaysefficientlyreturnstheelementofhighestpriority,withoutfullyorderingtheentire
sequence.
Aswiththesortoperations,therearetwoversionsofeachfunction.Thefirstusestheobjectsown
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
228/458
5/3/2015
ThinkinginC++2ndedVolume2
operator<toperformthecomparisonthesecondusesanadditionalStrictWeakOrderingobjects
operator()(a,b)tocomparetwoobjectsfora<b.
voidmake_heap(RandomAccessIteratorfirst,
RandomAccessIteratorlast)
voidmake_heap(RandomAccessIteratorfirst,
RandomAccessIteratorlast,
StrictWeakOrderingbinary_pred)
Turnsanarbitraryrangeintoaheap.
voidpush_heap(RandomAccessIteratorfirst,
RandomAccessIteratorlast)
voidpush_heap(RandomAccessIteratorfirst,
RandomAccessIteratorlast,
StrictWeakOrderingbinary_pred)
Addstheelement*(last1)totheheapdeterminedbytherange[first,last1).Inotherwords,itplaces
thelastelementinitsproperlocationintheheap.
voidpop_heap(RandomAccessIteratorfirst,
RandomAccessIteratorlast)
voidpop_heap(RandomAccessIteratorfirst,
RandomAccessIteratorlast,
StrictWeakOrderingbinary_pred)
Placesthelargestelement(whichisactuallyin*first,beforetheoperation,becauseofthewayheapsare
defined)intotheposition*(last1)andreorganizestheremainingrangesothatitsstillinheaporder.If
yousimplygrabbed*first,thenextelementwouldnotbethenextlargestelementsoyoumustuse
pop_heap()ifyouwanttomaintaintheheapinitsproperpriorityqueueorder.
voidsort_heap(RandomAccessIteratorfirst,
RandomAccessIteratorlast)
voidsort_heap(RandomAccessIteratorfirst,
RandomAccessIteratorlast,
StrictWeakOrderingbinary_pred)
Thiscouldbethoughtofasthecomplementtomake_heap().Ittakesarangethatisinheaporderand
turnsitintoordinarysortedorder,soitisnolongeraheap.Thatmeansthatifyoucallsort_heap(),you
cannolongerusepush_heap()orpop_heap()onthatrange.(Rather,youcanusethosefunctions,but
theywontdoanythingsensible.)Thisisnotastablesort.
Applyinganoperationtoeachelementinarange
Thesealgorithmsmovethroughtheentirerangeandperformanoperationoneachelement.Theydifferin
whattheydowiththeresultsofthatoperation:for_each()discardsthereturnvalueoftheoperation,
andtransform()placestheresultsofeachoperationintoadestinationsequence(whichcanbethe
originalsequence).
UnaryFunctionfor_each(InputIteratorfirst,InputIterator
last,UnaryFunctionf)
Appliesthefunctionobjectftoeachelementin[first,last),discardingthereturnvaluefromeach
individualapplicationoff.Iffisjustafunctionpointer,youaretypicallynotinterestedinthereturnvalue
butiffisanobjectthatmaintainssomeinternalstate,itcancapturethecombinedreturnvalueofbeing
appliedtotherange.Thefinalreturnvalueoffor_each()isf.
OutputIteratortransform(InputIteratorfirst,InputIterator
last,OutputIteratorresult,UnaryFunctionf)
OutputIteratortransform(InputIterator1first,
InputIterator1last,InputIterator2first2,
OutputIteratorresult,BinaryFunctionf)
Likefor_each(),transform()appliesafunctionobjectftoeachelementintherange[first,last).
However,insteadofdiscardingtheresultofeachfunctioncall,transform()copiestheresult(using
operator=)into*result,incrementingresultaftereachcopy.(Thesequencepointedtobyresultmust
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
229/458
5/3/2015
ThinkinginC++2ndedVolume2
haveenoughstorageotherwise,useaninsertertoforceinsertionsinsteadofassignments.)
Thefirstformoftransform()simplycallsf(*first),wherefirstrangesthroughtheinputsequence.
Similarly,thesecondformcallsf(*first1,*first2).(Notethatthelengthofthesecondinputrangeis
determinedbythelengthofthefirst.)Thereturnvalueinbothcasesisthepasttheenditeratorforthe
resultingoutputrange.
Examples
Sincemuchofwhatyoudowithobjectsinacontaineristoapplyanoperationtoallthoseobjects,these
arefairlyimportantalgorithmsandmeritseveralillustrations.
First,considerfor_each().Thissweepsthroughtherange,pullingouteachelementandpassingitasan
argumentasitcallswhateverfunctionobjectitsbeengiven.Thus,for_each()performsoperationsthat
youmightnormallywriteoutbyhand.Ifyoulookinyourcompilersheaderfileatthetemplatedefining
for_each(),youllseesomethinglikethis:
template<classInputIterator,classFunction>
Functionfor_each(InputIteratorfirst,InputIteratorlast,
Functionf){
while(first!=last)
f(*first++)
returnf
}
Thefollowingexampleshowsseveralwaysthistemplatecanbeexpanded.First,weneedaclassthat
keepstrackofitsobjectssowecanknowthatitsbeingproperlydestroyed:
//:C06:Counted.h
//Anobjectthatkeepstrackofitself.
#ifndefCOUNTED_H
#defineCOUNTED_H
#include<vector>
#include<iostream>
classCounted{
staticintcount
char*ident
public:
Counted(char*id):ident(id){++count}
~Counted(){
std::cout<<ident<<"count="
<<count<<std::endl
}
}
classCountedVector:publicstd::vector<Counted*>{
public:
CountedVector(char*id){
for(inti=0i<5i++)
push_back(newCounted(id))
}
}
#endif//COUNTED_H///:~
//:C06:Counted.cpp{O}
#include"Counted.h"
intCounted::count=0
///:~
TheclassCountedkeepsastaticcountofthenumberofCountedobjectsthathavebeencreated,and
notifiesyouastheyaredestroyed.[97]Inaddition,eachCountedkeepsachar*identifiertomaketracking
theoutputeasier.
TheCountedVectorisderivedfromvector<Counted*>,andintheconstructoritcreatessomeCounted
objects,handingeachoneyourdesiredchar*.TheCountedVectormakestestingquitesimple,asyou
canseehere:
//:C06:ForEach.cpp{mwcc}
//UseofSTLfor_each()algorithm.
//{L}Counted
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
230/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<algorithm>
#include<iostream>
#include"Counted.h"
usingnamespacestd
//Functionobject:
template<classT>classDeleteT{
public:
voidoperator()(T*x){deletex}
}
//Templatefunction:
template<classT>voidwipe(T*x){deletex}
intmain(){
CountedVectorB("two")
for_each(B.begin(),B.end(),DeleteT<Counted>())
CountedVectorC("three")
for_each(C.begin(),C.end(),wipe<Counted>)
}///:~
Sincethisisobviouslysomethingyoumightwanttodoalot,whynotcreateanalgorithmtodeleteallthe
pointersinacontainer?Youcouldusetransform().Thevalueoftransform()overfor_each()isthat
transform()assignstheresultofcallingthefunctionobjectintoaresultingrange,whichcanactuallybe
theinputrange.Thatcasemeansaliteraltransformationfortheinputrange,sinceeachelementwouldbe
amodificationofitspreviousvalue.Inthisexample,thisapproachwouldbeespeciallyusefulsinceits
moreappropriatetoassigntoeachpointerthesafevalueofzeroaftercallingdeleteforthatpointer.
Transform()caneasilydothis:
//:C06:Transform.cpp{mwcc}
//UseofSTLtransform()algorithm.
//{L}Counted
#include<iostream>
#include<vector>
#include<algorithm>
#include"Counted.h"
usingnamespacestd
template<classT>T*deleteP(T*x){deletexreturn0}
template<classT>structDeleter{
T*operator()(T*x){deletexreturn0}
}
intmain(){
CountedVectorcv("one")
transform(cv.begin(),cv.end(),cv.begin(),
deleteP<Counted>)
CountedVectorcv2("two")
transform(cv2.begin(),cv2.end(),cv2.begin(),
Deleter<Counted>())
}///:~
Thisshowsbothapproaches:usingatemplatefunctionoratemplatizedfunctionobject.Afterthecallto
transform(),thevectorcontainsfivenullpointers,whichissafersinceanyduplicatedeleteswillhave
noeffect.
Onethingyoucannotdoisdeleteeverypointerinacollectionwithoutwrappingthecalltodeleteinsidea
functionoranobject.Thatis,youdothefollowing:
for_each(a.begin(),a.end(),ptr_fun(operatordelete))
Thishasthesameproblemasthecalltodestroy()didearlier:operatordelete()takesavoid*,but
iteratorsarentpointers.Evenifyoucouldmakeitcompile,whatyoudgetisasequenceofcallstothe
functionthatreleasesthestorage.Youwillnotgettheeffectofcallingdeleteforeachpointerina,
howeverthedestructorwillnotbecalled.Thisistypicallynotwhatyouwant,soyouwillneedtowrap
yourcallstodelete.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
231/458
5/3/2015
ThinkinginC++2ndedVolume2
Inthepreviousexampleoffor_each(),thereturnvalueofthealgorithmwasignored.Thisreturnvalue
isthefunctionthatispassedintofor_each().Ifthefunctionisjustapointertoafunction,thereturn
valueisnotveryuseful,butifitisafunctionobject,thatfunctionobjectmayhaveinternalmemberdata
thatitusestoaccumulateinformationaboutalltheobjectsthatitseesduringfor_each().
Forexample,considerasimplemodelofinventory.EachInventoryobjecthasthetypeofproductit
represents(here,singlecharacterswillbeusedforproductnames),thequantityofthatproduct,andthe
priceofeachitem:
//:C06:Inventory.h
#ifndefINVENTORY_H
#defineINVENTORY_H
#include<iostream>
#include<cstdlib>
usingstd::rand
classInventory{
charitem
intquantity
intvalue
public:
Inventory(charit,intquant,intval)
:item(it),quantity(quant),value(val){}
//Synthesizedoperator=©constructorOK
chargetItem()const{returnitem}
intgetQuantity()const{returnquantity}
voidsetQuantity(intq){quantity=q}
intgetValue()const{returnvalue}
voidsetValue(intval){value=val}
friendstd::ostream&operator<<(
std::ostream&os,constInventory&inv){
returnos<<inv.item<<":"
<<"quantity"<<inv.quantity
<<",value"<<inv.value
}
}
//Agenerator:
structInvenGen{
Inventoryoperator()(){
staticcharc='a'
intq=rand()%100
intv=rand()%500
returnInventory(c++,q,v)
}
}
#endif//INVENTORY_H///:~
Memberfunctionsgettheitemnameandgetandsetquantityandvalue.Anoperator<<printsthe
Inventoryobjecttoanostream.Ageneratorcreatesobjectsthathavesequentiallylabeleditemsand
randomquantitiesandvalues.
Tofindoutthetotalnumberofitemsandtotalvalue,youcancreateafunctionobjecttousewith
for_each()thathasdatamemberstoholdthetotals:
//:C06:CalcInventory.cpp
//Moreuseoffor_each().
#include<algorithm>
#include<ctime>
#include<vector>
#include"Inventory.h"
#include"PrintSequence.h"
usingnamespacestd
//Tocalculateinventorytotals:
classInvAccum{
intquantity
intvalue
public:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
232/458
5/3/2015
ThinkinginC++2ndedVolume2
InvAccum():quantity(0),value(0){}
voidoperator()(constInventory&inv){
quantity+=inv.getQuantity()
value+=inv.getQuantity()*inv.getValue()
}
friendostream&
operator<<(ostream&os,constInvAccum&ia){
returnos<<"totalquantity:"<<ia.quantity
<<",totalvalue:"<<ia.value
}
}
intmain(){
vector<Inventory>vi
srand(time(0))//Randomize
generate_n(back_inserter(vi),15,InvenGen())
print(vi.begin(),vi.end(),"vi")
InvAccumia=for_each(vi.begin(),vi.end(),InvAccum())
cout<<ia<<endl
}///:~
InvAccumsoperator()takesasingleargument,asrequiredbyfor_each().Asfor_each()moves
throughitsrange,ittakeseachobjectinthatrangeandpassesittoInvAccum::operator(),which
performscalculationsandsavestheresult.Attheendofthisprocess,for_each()returnstheInvAccum
object,whichisprinted.
YoucandomostthingstotheInventoryobjectsusingfor_each().Forexample,for_each()can
handilyincreaseallthepricesby10%.ButyoullnoticethattheInventoryobjectshavenowaytochange
theitemvalue.TheprogrammerswhodesignedInventorythoughtthiswasagoodidea.Afterall,why
wouldyouwanttochangethenameofanitem?Butmarketinghasdecidedthattheywantanew,
improvedlookbychangingalltheitemnamestouppercase.Theyvedonestudiesanddeterminedthatthe
newnameswillboostsales(well,marketingneedstohavesomethingtodo).Sofor_each()willnot
workhere,buttransform()will:
//:C06:TransformNames.cpp
//Moreuseoftransform().
#include<algorithm>
#include<cctype>
#include<ctime>
#include<vector>
#include"Inventory.h"
#include"PrintSequence.h"
usingnamespacestd
structNewImproved{
Inventoryoperator()(constInventory&inv){
returnInventory(toupper(inv.getItem()),
inv.getQuantity(),inv.getValue())
}
}
intmain(){
vector<Inventory>vi
srand(time(0))//Randomize
generate_n(back_inserter(vi),15,InvenGen())
print(vi.begin(),vi.end(),"vi")
transform(vi.begin(),vi.end(),vi.begin(),NewImproved())
print(vi.begin(),vi.end(),"vi")
}///:~
Noticethattheresultingrangeisthesameastheinputrangethatis,thetransformationisperformedin
place.
Nowsupposethatthesalesdepartmentneedstogeneratespecialpricelistswithdifferentdiscountsfor
eachitem.Theoriginallistmuststaythesame,andanynumberofspeciallistsneedtobegenerated.
Saleswillgiveyouaseparatelistofdiscountsforeachnewlist.Tosolvethisproblem,wecanusethe
secondversionoftransform():
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
233/458
5/3/2015
ThinkinginC++2ndedVolume2
//:C06:SpecialList.cpp
//Usingthesecondversionoftransform().
#include<algorithm>
#include<ctime>
#include<vector>
#include"Inventory.h"
#include"PrintSequence.h"
usingnamespacestd
structDiscounter{
Inventoryoperator()(constInventory&inv,
floatdiscount){
returnInventory(inv.getItem(),inv.getQuantity(),
int(inv.getValue()*(1discount)))
}
}
structDiscGen{
floatoperator()(){
floatr=float(rand()%10)
returnr/100.0
}
}
intmain(){
vector<Inventory>vi
srand(time(0))//Randomize
generate_n(back_inserter(vi),15,InvenGen())
print(vi.begin(),vi.end(),"vi")
vector<float>disc
generate_n(back_inserter(disc),15,DiscGen())
print(disc.begin(),disc.end(),"Discounts:")
vector<Inventory>discounted
transform(vi.begin(),vi.end(),disc.begin(),
back_inserter(discounted),Discounter())
print(discounted.begin(),discounted.end(),"discounted")
}///:~
GivenanInventoryobjectandadiscountpercentage,theDiscounterfunctionobjectproducesanew
Inventorywiththediscountedprice.TheDiscGenfunctionobjectjustgeneratesrandomdiscountvalues
between1%and10%tousefortesting.Inmain(),twovectorsarecreated,oneforInventoryandone
fordiscounts.Thesearepassedtotransform()alongwithaDiscounterobject,andtransform()fillsa
newvector<Inventory>calleddiscounted.
Numericalgorithms
Thesealgorithmsarealltuckedintotheheader<numeric>,sincetheyareprimarilyusefulfor
performingnumericcalculations.
Taccumulate(InputIteratorfirst,InputIteratorlast,T
result)
Taccumulate(InputIteratorfirst,InputIteratorlast,T
result,BinaryFunctionf)
Thefirstformisageneralizedsummationforeachelementpointedtobyaniteratoriin[first,last),it
performstheoperationresult=result+*i,whereresultisoftypeT.However,thesecondformis
moregeneralitappliesthefunctionf(result,*i)oneachelement*iintherangefrombeginningtoend.
Notethesimilaritybetweenthesecondformoftransform()andthesecondformofaccumulate().
Tinner_product(InputIterator1first1,InputIterator1
last1,InputIterator2first2,Tinit)
Tinner_product(InputIterator1first1,InputIterator1
last1,InputIterator2first2,Tinit,BinaryFunction1
op1,BinaryFunction2op2)
Calculatesageneralizedinnerproductofthetworanges[first1,last1)and[first2,first2+(last1
first1)).Thereturnvalueisproducedbymultiplyingtheelementfromthefirstsequencebytheparallel
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
234/458
5/3/2015
ThinkinginC++2ndedVolume2
elementinthesecondsequenceandthenaddingittothesum.Thus,ifyouhavetwosequences{1,1,2,
2}and{1,2,3,4},theinnerproductbecomes
(1*1)+(1*2)+(2*3)+(2*4)
whichis17.Theinitargumentistheinitialvaluefortheinnerproductthisisprobablyzerobutmaybe
anythingandisespeciallyimportantforanemptyfirstsequence,becausethenitbecomesthedefault
returnvalue.Thesecondsequencemusthaveatleastasmanyelementsasthefirst.
Thesecondformsimplyappliesapairoffunctionstoitssequence.Theop1functionisusedinplaceof
additionandop2isusedinsteadofmultiplication.Thus,ifyouappliedthesecondversionof
inner_product()tothesequence,theresultwouldbethefollowingoperations:
init=op1(init,op2(1,1))
init=op1(init,op2(1,2))
init=op1(init,op2(2,3))
init=op1(init,op2(2,4))
Thus,itssimilartotransform(),buttwooperationsareperformedinsteadofone.
OutputIteratorpartial_sum(InputIteratorfirst,
InputIteratorlast,OutputIteratorresult)
OutputIteratorpartial_sum(InputIteratorfirst,
InputIteratorlast,OutputIteratorresult,
BinaryFunctionop)
Calculatesageneralizedpartialsum.Anewsequenceiscreated,beginningatresult.Eachelementisthe
sumofalltheelementsuptothecurrentlyselectedelementin[first,last).Forexample,iftheoriginal
sequenceis{1,1,2,2,3},thegeneratedsequenceis{1,1+1,1+1+2,1+1+2+2,1+1+2
+2+3},thatis,{1,2,4,6,9}.
Inthesecondversion,thebinaryfunctionopisusedinsteadofthe+operatortotakeallthesummation
uptothatpointandcombineitwiththenewvalue.Forexample,ifyouusemultiplies<int>()asthe
objectforthesequence,theoutputis{1,1,2,4,12}.Notethatthefirstoutputvalueisalwaysthesame
asthefirstinputvalue.
Thereturnvalueistheendoftheoutputrange[result,result+(lastfirst)).
OutputIteratoradjacent_difference(InputIteratorfirst,
InputIteratorlast,OutputIteratorresult)
OutputIteratoradjacent_difference(InputIteratorfirst,
InputIteratorlast,OutputIteratorresult,BinaryFunction
op)
Calculatesthedifferencesofadjacentelementsthroughouttherange[first,last).Thismeansthatinthe
newsequence,thevalueisthevalueofthedifferenceofthecurrentelementandthepreviouselementin
theoriginalsequence(thefirstvalueisunchanged).Forexample,iftheoriginalsequenceis{1,1,2,2,
3},theresultingsequenceis{1,11,21,22,32},thatis:{1,0,1,0,1}.
Thesecondformusesthebinaryfunctionopinsteadoftheoperatortoperformthedifferencing.For
example,ifyouusemultiplies<int>()asthefunctionobjectforthesequence,theoutputis{1,1,2,4,
6}.
Thereturnvalueistheendoftheoutputrange[result,result+(lastfirst)).
Example
Thisprogramtestsallthealgorithmsin<numeric>inbothforms,onintegerarrays.Youllnoticethatin
thetestoftheformwhereyousupplythefunctionorfunctions,thefunctionobjectsusedaretheonesthat
producethesameresultasformone,sotheresultswillbeexactlythesame.Thisshouldalsodemonstrate
abitmoreclearlytheoperationsthataregoingonandhowtosubstituteyourownoperations.
//:C06:NumericTest.cpp
#include<algorithm>
#include<iostream>
#include<iterator>
#include<functional>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
235/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<numeric>
#include"PrintSequence.h"
usingnamespacestd
intmain(){
inta[]={1,1,2,2,3,5,7,9,11,13}
constintASZ=sizeofa/sizeofa[0]
print(a,a+ASZ,"a","")
intr=accumulate(a,a+ASZ,0)
cout<<"accumulate1:"<<r<<endl
//Shouldproducethesameresult:
r=accumulate(a,a+ASZ,0,plus<int>())
cout<<"accumulate2:"<<r<<endl
intb[]={1,2,3,4,1,2,3,4,1,2}
print(b,b+sizeofb/sizeofb[0],"b","")
r=inner_product(a,a+ASZ,b,0)
cout<<"inner_product1:"<<r<<endl
//Shouldproducethesameresult:
r=inner_product(a,a+ASZ,b,0,
plus<int>(),multiplies<int>())
cout<<"inner_product2:"<<r<<endl
int*it=partial_sum(a,a+ASZ,b)
print(b,it,"partial_sum1","")
//Shouldproducethesameresult:
it=partial_sum(a,a+ASZ,b,plus<int>())
print(b,it,"partial_sum2","")
it=adjacent_difference(a,a+ASZ,b)
print(b,it,"adjacent_difference1","")
//Shouldproducethesameresult:
it=adjacent_difference(a,a+ASZ,b,minus<int>())
print(b,it,"adjacent_difference2","")
}///:~
Notethatthereturnvalueofinner_product()andpartial_sum()isthepasttheenditeratorforthe
resultingsequence,soitisusedastheseconditeratorintheprint()function.
Sincethesecondformofeachfunctionallowsyoutoprovideyourownfunctionobject,onlythefirstform
ofthefunctionispurelynumeric.Youcouldconceivablydothingsthatarenotintuitivelynumericwith
inner_product().
Generalutilities
Finally,herearesomebasictoolsthatareusedwiththeotheralgorithmsyoumayormaynotusethem
directlyyourself.
(Templatesinthe<utility>header)
template<classT1,classT2>structpair
template<classT1,classT2>pair<T1,T2>
make_pair(constT1&,constT2&)
Theseweredescribedandusedearlierinthischapter.Apairissimplyawaytopackagetwoobjects
(whichmaybeofdifferenttypes)intoasingleobject.Thisistypicallyusedwhenyouneedtoreturnmore
thanoneobjectfromafunction,butitcanalsobeusedtocreateacontainerthatholdspairobjectsorto
passmorethanoneobjectasasingleargument.Youaccesstheelementsbysayingp.firstandp.second,
wherepisthepairobject.Thefunctionequal_range(),describedinthischapter,returnsitsresultasa
pairofiterators,forexample.Youcaninsert()apairdirectlyintoamapormultimapapairisthe
value_typeforthosecontainers.
Ifyouwanttocreateapaironthefly,youtypicallyusethetemplatefunctionmake_pair()ratherthan
explicitlyconstructingapairobject.make_pair()deducesthetypesoftheargumentsitreceives,
relievingyouofthetypingaswellasincreasingrobustness.
(From<iterator>)
difference_typedistance(InputIteratorfirst,InputIteratorlast)
Tellsyouthenumberofelementsbetweenfirstandlast.Moreprecisely,itreturnsanintegralvaluethat
tellsyouthenumberoftimesfirstmustbeincrementedbeforeitisequaltolast.Nodereferencingofthe
iteratorsoccursduringthisprocess.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
236/458
5/3/2015
ThinkinginC++2ndedVolume2
(From<iterator>)
Movestheiteratoriforwardbythevalueofn.(Itcanalsobemovedbackwardfornegativevaluesofnif
theiteratorisbidirectional.)Thisalgorithmisawareofthedifferenttypesofiteratorsandwillusethe
mostefficientapproach.Forexample,randomiteratorscanbeincrementeddirectlyusingordinary
arithmetic(i+=n),whereasabidirectionaliteratormustbeincrementedntimes.
(From<iterator>)
back_insert_iterator<Container>
back_inserter(Container&x)
front_insert_iterator<Container>
front_inserter(Container&x)
insert_iterator<Container>
inserter(Container&x,Iteratori)
Thesefunctionsareusedtocreateiteratorsforthegivencontainersthatwillinsertelementsintothe
container,ratherthanoverwritetheexistingelementsinthecontainerusingoperator=(whichisthe
defaultbehavior).Eachtypeofiteratorusesadifferentoperationforinsertion:back_insert_iterator
usespush_back(),front_insert_iteratorusespush_front(),andinsert_iteratorusesinsert()
(andthusitcanbeusedwiththeassociativecontainers,whiletheothertwocanbeusedwithsequence
containers).Thesewillbeshowninsomedetailinthenextchapter.
constLessThanComparable&min(constLessThanComparable&a,
constLessThanComparable&b)
constT&min(constT&a,constT&b,
BinaryPredicatebinary_pred)
Returnsthelesserofitstwoarguments,orreturnsthefirstargumentifthetwoareequivalent.Thefirst
versionperformscomparisonsusingoperator<,andthesecondpassesbothargumentstobinary_pred
toperformthecomparison.
constLessThanComparable&max(constLessThanComparable&a,
constLessThanComparable&b)
constT&max(constT&a,constT&b,
BinaryPredicatebinary_pred)
Exactlylikemin(),butreturnsthegreaterofitstwoarguments.
voidswap(Assignable&a,Assignable&b)
voiditer_swap(ForwardIterator1a,ForwardIterator2b)
Exchangesthevaluesofaandbusingassignment.Notethatallcontainerclassesusespecializedversions
ofswap()thataretypicallymoreefficientthanthisgeneralversion.
Theiter_swap()functionswapsthevaluesthatitstwoargumentsreference.
CreatingyourownSTLstylealgorithms
OnceyoubecomecomfortablewiththestyleofSTLalgorithms,youcanbegintocreateyourowngeneric
algorithms.BecausethesewillconformtotheconventionsofalltheotheralgorithmsintheSTL,theyre
easytouseforprogrammerswhoarefamiliarwiththeSTL,andthustheybecomeawaytoextendthe
STLvocabulary.
Theeasiestwaytoapproachtheproblemistogotothe<algorithm>headerfile,findsomethingsimilar
towhatyouneed,andpatternyourcodeafterthat.[98](VirtuallyallSTLimplementationsprovidethecode
forthetemplatesdirectlyintheheaderfiles.)
IfyoutakeacloselookatthelistofalgorithmsintheStandardC++library,youmightnoticeaglaring
omission:thereisnocopy_if()algorithm.Althoughitstruethatyoucanaccomplishthesameeffectwith
remove_copy_if(),thisisnotquiteasconvenientbecauseyouhavetoinvertthecondition.(Remember,
remove_copy_if()onlycopiesthoseelementsthatdontmatchitspredicate,ineffectremovingthose
thatdo.)Youmightbetemptedtowriteafunctionobjectadaptorthatnegatesitspredicatebeforepassing
ittoremove_copy_if(),byincludingastatementsomethinglikethis:
//Assumespredistheincomingcondition
replace_copy_if(begin,end,not1(pred))
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
237/458
5/3/2015
ThinkinginC++2ndedVolume2
Thisseemsreasonable,butwhenyourememberthatyouwanttobeabletousepredicatesthatare
pointerstorawfunctions,youseewhythiswontworknot1expectsanadaptablefunctionobject.The
onlysolutionistowriteacopy_if()algorithmfromscratch.Sinceyouknowfrominspectingtheother
copyalgorithmsthatconceptuallyyouneedseparateiteratorsforinputandoutput,thefollowingexample
willdothejob:
//:C06:copy_if.h
//CreateyourownSTLstylealgorithm.
#ifndefCOPY_IF_H
#defineCOPY_IF_H
template<typenameForwardIter,
typenameOutputIter,typenameUnaryPred>
OutputItercopy_if(ForwardIterbegin,ForwardIterend,
OutputIterdest,UnaryPredf){
while(begin!=end){
if(f(*begin))
*dest++=*begin
++begin
}
returndest
}
#endif//COPY_IF_H///:~
Noticethattheincrementofbegincannotbeintegratedintothecopyexpression.
Summary
ThegoalofthischapteristogiveyouapracticalunderstandingofthealgorithmsintheStandardTemplate
Library.Thatis,tomakeyouawareofandcomfortableenoughwiththeSTLthatyoubegintouseitona
regularbasis(or,atleast,tothinkofusingitsoyoucancomebackhereandhuntfortheappropriate
solution).TheSTLispowerfulnotonlybecauseitsareasonablycompletelibraryoftools,butalsobecause
itprovidesavocabularyforthinkingaboutproblemsolutionsanditisaframeworkforcreatingadditional
tools.
Althoughthischapterdidshowsomeexamplesofcreatingyourowntools,wedidnotgointothefulldepth
ofthetheoryoftheSTLnecessarytocompletelyunderstandalltheSTLnooksandcrannies.Such
understandingwillallowyoutocreatetoolsmoresophisticatedthanthoseshownhere.Thisomissionwas
inpartbecauseofspacelimitations,butmostlybecauseitisbeyondthecharterofthisbookourgoal
hereistogiveyoupracticalunderstandingthatwillimproveyourdaytodayprogrammingskills.
AnumberofbooksarededicatedsolelytotheSTL(thesearelistedintheappendices),butweespecially
recommendScottMeyersEffectiveSTL(AddisonWesley,2002).
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.Createageneratorthatreturnsthecurrentvalueofclock()(in<ctime>).Createa
list<clock_t>,andfillitwithyourgeneratorusinggenerate_n().Removeanyduplicatesinthe
listandprintittocoutusingcopy().
2.Usingtransform()andtoupper()(in<cctype>),writeasinglefunctioncallthatwillconverta
stringtoalluppercaseletters.
3.CreateaSumfunctionobjecttemplatethatwillaccumulateallthevaluesinarangewhenusedwith
for_each().
4.Writeananagramgeneratorthattakesawordasacommandlineargumentandproducesall
possiblepermutationsoftheletters.
5.Writeasentenceanagramgeneratorthattakesasentenceasacommandlineargumentand
producesallpossiblepermutationsofthewordsinthesentence.(Itleavesthewordsaloneandjust
movesthemaround.)
6.CreateaclasshierarchywithabaseclassBandaderivedclassD.Putavirtualmemberfunction
voidf()inBsuchthatitwillprintamessageindicatingthatBsf()wascalled,andredefinethis
functionforDtoprintadifferentmessage.Createavector<B*>,andfillitwithBandDobjects.
Usefor_each()tocallf()foreachoftheobjectsinyourvector.
7.ModifyFunctionObjects.cppsothatitusesfloatinsteadofint.
8.ModifyFunctionObjects.cppsothatittemplatizesthemainbodyoftestssoyoucanchoosewhich
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
238/458
5/3/2015
ThinkinginC++2ndedVolume2
typeyouregoingtotest.(Youllhavetopullmostofmain()outintoaseparatetemplatefunction.)
9.Writeaprogramthattakesanintegerasacommandlineargumentandfindsallofitsfactors.
10.Writeaprogramthattakesasacommandlineargumentthenameofatextfile.Openthisfileand
readitawordatatime(hint:use>>).Storeeachwordintoavector<string>.Forceallthe
wordstolowercase,sortthem,removealltheduplicates,andprinttheresults.
11.Writeaprogramthatfindsallthewordsthatareincommonbetweentwoinputfiles,using
set_intersection().Changeittoshowthewordsthatarenotincommon,using
set_symmetric_difference().
12.Createaprogramthat,givenanintegeronthecommandline,createsafactorialtableofallthe
factorialsuptoandincludingthenumberonthecommandline.Todothis,writeageneratortofilla
vector<int>,andthenusepartial_sum()withastandardfunctionobject.
13.ModifyCalcInventory.cppsothatitwillfindalltheobjectsthathaveaquantitythatslessthana
certainamount.Providethisamountasacommandlineargument,andusecopy_if()and
bind2nd()tocreatethecollectionofvalueslessthanthetargetvalue.
14.UseUrandGen()togenerate100numbers.(Thesizeofthenumbersdoesnotmatter.)Findwhich
numbersinyourrangearecongruentmod23(meaningtheyhavethesameremainderwhendivided
by23).Manuallypickarandomnumberyourself,anddeterminewhetherthatnumberisinyour
rangebydividingeachnumberinthelistbyyournumberandcheckingiftheresultis1insteadof
justusingfind()withyourvalue.
15.Fillavector<double>withnumbersrepresentinganglesinradians.Usingfunctionobject
composition,takethesineofalltheelementsinyourvector(see<cmath>).
16.Testthespeedofyourcomputer.Callsrand(time(0)),thenmakeanarrayofrandomnumbers.
Callsrand(time(0))againandgeneratethesamenumberofrandomnumbersinasecondarray.
Useequal()toseeifthearraysarethesame.(Ifyourcomputerisfastenough,time(0)will
returnthesamevaluebothtimesitiscalled.)Ifthearraysarenotthesame,sortthemanduse
mismatch()toseewheretheydiffer.Iftheyarethesame,increasethelengthofyourarrayand
tryagain.
17.CreateanSTLstylealgorithmtransform_if()followingthefirstformoftransform()that
performstransformationsonlyonobjectsthatsatisfyaunarypredicate.Objectsthatdontsatisfy
thepredicateareomittedfromtheresult.Itneedstoreturnanewenditerator.
18.CreateanSTLstylealgorithmthatisanoverloadedversionoffor_each()whichfollowsthesecond
formoftransform()andtakestwoinputrangessoitcanpasstheobjectsofthesecondinput
rangeatoabinaryfunctionthatitappliestoeachobjectofthefirstrange.
19.CreateaMatrixclasstemplatethatismadefromavector<vector<T>>.Provideitwithafriend
ostream&operator<<(ostream&,constMatrix&)todisplaythematrix.Createthefollowing
binaryoperationsusingtheSTLfunctionobjectswherepossible:operator+(constMatrix&,const
Matrix&)formatrixaddition,operator*(constMatrix&,constvector<int>&)formultiplyinga
matrixbyavector,andoperator*(constMatrix&,constMatrix&)formatrixmultiplication.
(Youmightneedtolookupthemathematicalmeaningsofthematrixoperationsifyoudont
rememberthem.)TestyourMatrixclasstemplateusingintandfloat.
20.Usingthecharacters
"~`!@#$%^&*()_+=}{[]|\:"'<.>,?/",
generateacodebookusinganinputfilegivenonthecommandlineasadictionaryofwords.Dont
worryaboutstrippingoffthenonalphabeticcharactersnorworryaboutcaseofthewordsinthe
dictionaryfile.Mapeachpermutationofthecharacterstringtoawordsuchasthefollowing:
"=')/%[}]|{*@?!"`,>&^~_:$+.#(<\"apple
"|]\~>#.+%(/_[`':=}{*"$^!&?),@<"carrot
"@=~['].\/<`>#*)^%+,"&?!_{:|$}("Carrot
etc.
Makesurethatnoduplicatecodesorwordsexistinyourcodebook.Use
lexicographical_compare()toperformasortonthecodes.Useyourcodebooktoencodethe
dictionaryfile.Decodeyourencodingofthedictionaryfile,andmakesureyougetthesame
contentsback.
21.Usingthefollowingnames:
JonBrittle
JaneBrittle
MikeBrittle
SharonBrittle
GeorgeJensen
EvelynJensen
Findallthepossiblewaystoarrangethemforaweddingpicture.
22.Afterbeingseparatedforonepicture,thebrideandgroomdecidedtheywantedtobetogetherfor
allofthem.Findallthepossiblewaystoarrangethepeopleforthepictureifthebrideandgroom
(JonBrittleandJaneBrittle)aretobenexttoeachother.
23.Atravelcompanywantstofindouttheaveragenumberofdayspeopletaketotravelfromoneend
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
239/458
5/3/2015
ThinkinginC++2ndedVolume2
ofthecontinenttoanother.Theproblemisthatinthesurvey,somepeopledidnottakeadirect
routeandtookmuchlongerthanisneeded(suchunusualdatapointsarecalledoutliers).Usingthe
followinggenerator,generatetraveldaysintoavector.Useremove_if()toremoveallthe
outliersinyourvector.Taketheaverageofthedatainthevectortofindouthowlongpeople
generallytaketotravel.
inttravelTime(){
//The"outlier"
if(rand()%10==0)
returnrand()%100
//Regularroute
returnrand()%10+10
}
24.Determinehowmuchfasterbinary_search()istofind()whenitcomestosearchingsorted
ranges.
25.Thearmywantstorecruitpeoplefromitsselectiveservicelist.Theyhavedecidedtorecruitthose
thatsignedupfortheservicein1997startingfromtheoldestdowntotheyoungest.Generatean
arbitraryamountofpeople(givethemdatamemberssuchasageandyearEnrolled)intoa
vector.Partitionthevectorsothatthosewhoenrolledin1997areorderedatthebeginningofthe
list,startingfromtheyoungesttotheoldest,andleavetheremainingpartofthelistsorted
accordingtoage.
26.MakeaclasscalledTownwithpopulation,altitude,andweatherdatamembers.Maketheweather
anenumwith{RAINY,SNOWY,CLOUDY,CLEAR}.MakeaclassthatgeneratesTownobjects.
Generatetownnames(whethertheymakesenseornotitdoesntmatter)orpullthemoffthe
Internet.Ensurethatthewholetownnameislowercaseandtherearenoduplicatenames.For
simplicity,werecommendkeepingyourtownnamestooneword.Forthepopulation,altitudes,and
weatherfields,makeageneratorthatwillrandomlygenerateweatherconditions,populationswithin
therange[100to1,000,000)andaltitudesbetween[0,8000)feet.FillavectorwithyourTown
objects.RewritethevectorouttoanewfilecalledTowns.txt.
27.Therewasababyboom,resultingina10%populationincreaseineverytown.Updateyourtown
datausingtransform(),rewriteyourdatabackouttofile.
28.Findthetownswiththehighestandlowestpopulation.Forthisexercise,implementoperator<for
yourTownclass.Alsotryimplementingafunctionthatreturnstrueifitsfirstparameterisless
thanitssecond.Useitasapredicatetocallthealgorithmyouuse.
29.Findallthetownswithinthealtitudes25003500feetinclusive.Implementequalityoperatorsforthe
Townclassasneeded.
30.Weneedtoplaceanairportinacertainaltitude,butlocationisnotaproblem.Organizeyourlistof
townssothattherearenoduplicate(duplicatemeaningthatnotwoaltitudesarewithinthesame
100ftrange.Suchclasseswouldinclude[100,199),[200,199),etc.altitudes.Sortthislistin
ascendingorderinatleasttwodifferentwaysusingthefunctionobjectsin<functional>.Dothe
samefordescendingorder.ImplementrelationaloperatorsforTownasneeded.
31.Generateanarbitrarynumberofrandomnumbersinastackbasedarray.Usemax_element()to
findthelargestnumberinarray.Swapitwiththenumberattheendofyourarray.Findthenext
largestnumberandplaceitinthearrayinthepositionbeforethepreviousnumber.Continuedoing
thisuntilallelementshavebeenmoved.Whenthealgorithmiscomplete,youwillhaveasorted
array.(Thisisaselectionsort.)
32.Writeaprogramthatwilltakephonenumbersfromafile(thatalsocontainsnamesandother
suitableinformation)andchangethenumbersthatbeginwith222to863.Besuretosavetheold
numbers.Thefileformatisasfollows:
2228945
7563920
2228432
etc.
33.Writeaprogramthat,givenalastname,willfindeveryonewiththatlastnamewithhisorher
correspondingphonenumber.Usethealgorithmsthatdealwithranges(lower_bound,
upper_bound,equal_range,etc.).Sortwiththelastnameactingasaprimarykeyandthefirst
nameactingasasecondarykey.Assumethatyouwillreadthenamesandnumbersfromafile
wheretheformatwillbeasfollows.(Besuretoorderthemsothatthelastnamesareordered,and
thefirstnamesareorderedwithinthelastnames.):
JohnDoe3459483
NickBonham3492930
JaneDoe2832819
34.Givenafilewithdatasimilartothefollowing,pullallthestateacronymsfromthefileandputthem
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
240/458
5/3/2015
ThinkinginC++2ndedVolume2
inaseparatefile.(Notethatyoucantdependonthelinenumberforthetypeofdata.Thedatais
onrandomlines.)
ALABAMA
AL
AK
ALASKA
ARIZONA
AZ
ARKANSAS
AR
CA
CALIFORNIA
CO
COLORADO
etc.
Whencomplete,youshouldhaveafilewithallthestateacronymswhichare:
ALAKAZARCACOCTDEFLGAHIIDILINIAKSKYLAMEMDMAMIMNMSMOMTNENVNHNJ
NMNYNCNDOHOKORPARISCSDTNTXUTVTVAWAWVWIWY
35.MakeanEmployeeclasswithtwodatamembers:hoursandhourlyPay.Employeeshallalsohave
acalcSalary()functionwhichreturnsthepayforthatemployee.Generaterandomhourlypayand
hoursforanarbitraryamountofemployees.Keepavector<Employee*>.Findouthowmuch
moneythecompanyisgoingtospendforthispayperiod.
36.Racesort(),partial_sort(),andnth_element()againsteachotherandfindoutifitsreally
worththetimesavedtouseoneoftheweakersortsiftheyreallthatsneeded.
7:GenericContainers
Containerclassesarethesolutiontoaspecifickindofcodereuseproblem.They
arebuildingblocksusedtocreateobjectorientedprograms,andtheymakethe
internalsofaprogrammucheasiertoconstruct.
Acontainerclassdescribesanobjectthatholdsotherobjects.Containerclassesaresoimportantthatthey
wereconsideredfundamentaltoearlyobjectorientedlanguages.InSmalltalk,forexample,programmers
thinkofthelanguageastheprogramtranslatortogetherwiththeclasslibrary,andacriticalpartofthat
libraryisthesetofcontainerclasses.Itbecamenatural,therefore,forC++compilervendorstoalso
includeacontainerclasslibrary.Youllnotethatthevectorissousefulthatitwasintroducedinits
simplestformearlyinVolume1ofthisbook.
LikemanyotherearlyC++libraries,earlycontainerclasslibrariesfollowedSmalltalksobjectbased
hierarchy,whichworkedwellforSmalltalk,butturnedouttobeawkwardanddifficulttouseinC++.
Anotherapproachwasrequired.
TheC++approachtocontainersisbasedontemplates.ThecontainersintheStandardC++library
representabroadrangeofdatastructuresdesignedtoworkwellwiththestandardalgorithmsandtomeet
commonsoftwaredevelopmentneeds.
Containersanditerators
Ifyoudontknowhowmanyobjectsyouregoingtoneedtosolveaparticularproblem,orhowlongthey
willlast,youalsodontknowaheadoftimehowtostorethoseobjects.Howcanyouknowhowmuch
spacetocreate?Theanswerisyoudontuntilruntime.
Thesolutiontomostproblemsinobjectorienteddesignseemssimpleyoucreateanothertypeofobject.
Forthestorageproblem,thenewtypeofobjectholdsotherobjectsorpointerstoobjects.Thisnewtypeof
object,whichistypicallyreferredtoinC++asacontainer(alsocalledacollectioninsomelanguages),
expandsitselfwhenevernecessarytoaccommodateeverythingyouplaceinsideit.Youdontneedtoknow
aheadoftimehowmanyobjectsyouregoingtoplaceinacontaineryoujustcreateacontainerobject
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
241/458
5/3/2015
ThinkinginC++2ndedVolume2
andletittakecareofthedetails.
Fortunately,agoodobjectorientedprogramminglanguagecomeswithasetofcontainers.InC++,itsthe
StandardTemplateLibrary(STL).Insomelibraries,agenericcontainerisconsideredgoodenoughforall
needs,andinothers(C++inparticular)thelibraryhasdifferenttypesofcontainersfordifferentneeds:a
vectorforefficientaccesstoallelements,andalinkedlistforefficientinsertionatallpositions,and
severalmore,soyoucanchoosetheparticulartypethatfitsyourneeds.
Allcontainershavesomewaytoputthingsinandgetthingsout.Thewayyouplacesomethingintoa
containerisfairlyobvioustheresafunctioncalledpushoraddorasimilarname.Thewayyou
retrievethingsfromacontainerisnotalwaysasapparentifanentityisarraylike,suchasavector,you
mightbeabletouseanindexingoperatororfunction.Butinmanysituationsthisdoesntmakesense.
Also,asingleselectionfunctionisrestrictive.Whatifyouwanttomanipulateorcompareagroupof
elementsinthecontainer?
Thesolutionforflexibleelementaccessistheiterator,anobjectwhosejobistoselecttheelementswithin
acontainerandpresentthemtotheuseroftheiterator.Asaclass,aniteratoralsoprovidesalevelof
abstraction,whichyoucanusetoseparatethedetailsofthecontainerfromthecodethatsaccessingthat
container.Thecontainer,viatheiterator,isseenasasequence.Theiteratorletsyoutraversethe
sequencewithoutworryingabouttheunderlyingstructurethatis,whetheritsavector,alinkedlist,a
set,orsomethingelse.Thisgivesyoutheflexibilitytoeasilychangetheunderlyingdatastructurewithout
disturbingthecodeinyourprogramthattraversesthecontainer.Separatingiterationfromthecontainers
controlalsoallowsmultiplesimultaneousiterators.
Fromadesignstandpoint,allyoureallywantisasequencethatcanbemanipulatedtosolveyourproblem.
Ifasingletypeofsequencesatisfiedallyourneeds,therewouldbenoreasontohavedifferenttypes.You
needachoiceofcontainersfortworeasons.First,containersprovidedifferenttypesofinterfacesand
externalbehavior.Astackhasaninterfaceandabehaviorthatisdifferentfromthatofaqueue,whichis
differentfromthatofasetoralist.Oneofthesemightprovideamoreflexiblesolutiontoyourproblem
thantheother,oritmightprovideaclearerabstractionthatconveysyourdesignintent.Second,different
containershavedifferentefficienciesforcertainoperations.Compareavectortoalist,forexample.Both
aresimplesequencesthatcanhavenearlyidenticalinterfacesandexternalbehaviors.Butcertain
operationscanhaveradicallydifferentcosts.Randomlyaccessingelementsinavectorisaconstanttime
operationittakesthesameamountoftimeregardlessoftheelementyouselect.However,itisexpensive
tomovethroughalinkedlisttorandomlyaccessanelement,andittakeslongertofindanelementifitis
fartherdownthelist.Ontheotherhand,ifyouwanttoinsertanelementinthemiddleofasequence,its
cheaperwithalistthanwithavector.Theefficienciesoftheseandotheroperationsdependonthe
underlyingstructureofthesequence.Inthedesignphase,youmightstartwithalistand,whentuningfor
performance,changetoavector,orviceversa.Becauseofiterators,codethatmerelytraverses
sequencesisinsulatedfromchangesintheunderlyingsequenceimplementation.
Rememberthatacontainerisonlyastoragecabinetthatholdsobjects.Ifthatcabinetsolvesallyour
needs,itprobablydoesntreallymatterhowitisimplemented.Ifyoureworkinginaprogramming
environmentthathasbuiltinoverheadduetootherfactors,thecostdifferencebetweenavectoranda
linkedlistmightnotmatter.Youmightneedonlyonetypeofsequence.Youcanevenimaginethe
perfectcontainerabstraction,whichcanautomaticallychangeitsunderlyingimplementationaccordingto
thewayitisused.[99]
STLreferencedocumentation
Asinthepreviouschapter,youwillnoticethatthischapterdoesnotcontainexhaustivedocumentation
describingeachofthememberfunctionsineachSTLcontainer.Althoughwedescribethemember
functionsweuse,weveleftthefulldescriptionstoothers.Werecommendtheonlineresourcesavailable
fortheDinkumware,SiliconGraphics,andSTLPortSTLimplementations.[100]
Afirstlook
Heresanexampleusingthesetclasstemplate,acontainermodeledafteratraditionalmathematicalset
andwhichdoesnotacceptduplicatevalues.Thefollowingsetwascreatedtoworkwithints:
//:C07:Intset.cpp
//SimpleuseofSTLset.
#include<cassert>
#include<set>
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
242/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
set<int>intset
for(inti=0i<25i++)
for(intj=0j<10j++)
//Trytoinsertduplicates:
intset.insert(j)
assert(intset.size()==10)
}///:~
Theinsert()memberdoesallthework:itattemptstoinsertanelementandignoresitifitsalready
there.Oftentheonlyactivitiesinvolvedinusingasetaresimplyinsertionandtestingtoseewhetherit
containstheelement.Youcanalsoformaunion,anintersection,oradifferenceofsetsandtesttoseeif
onesetisasubsetofanother.Inthisexample,thevalues09areinsertedintotheset25times,butonly
the10uniqueinstancesareaccepted.
NowconsidertakingtheformofIntset.cppandmodifyingittodisplayalistofthewordsusedina
document.Thesolutionbecomesremarkablysimple.
//:C07:WordSet.cpp
#include<fstream>
#include<iostream>
#include<iterator>
#include<set>
#include<string>
#include"../require.h"
usingnamespacestd
voidwordSet(constchar*fileName){
ifstreamsource(fileName)
assure(source,fileName)
stringword
set<string>words
while(source>>word)
words.insert(word)
copy(words.begin(),words.end(),
ostream_iterator<string>(cout,"\n"))
cout<<"Numberofuniquewords:"
<<words.size()<<endl
}
intmain(intargc,char*argv[]){
if(argc>1)
wordSet(argv[1])
else
wordSet("WordSet.cpp")
}///:~
Theonlysubstantivedifferencehereisthatthesetholdsstringsinsteadofintegers.Thewordsarepulled
fromafile,buttheotheroperationsaresimilartothoseinIntset.cpp.Notonlydoestheoutputreveal
thatduplicateshavebeenignored,butbecauseofthewaysetisimplemented,thewordsareautomatically
sorted.
Asetisanexampleofanassociativecontainer,oneofthethreecategoriesofcontainersprovidedbythe
StandardC++library.Thecontainersandtheircategoriesaresummarizedinthefollowingtable:
Category
Containers
SequenceContainers
vector,list,deque
ContainerAdaptors
queue,stack,priority_queue
AssociativeContainers
set,map,multiset,multimap
Thesecategoriesrepresentdifferentmodelsthatareusedfordifferentneeds.TheSequenceContainers
simplyorganizetheirelementslinearly,andarethemostfundamentaltypeofcontainers.Forsome
problems,specialpropertiesneedtobeattachedtothesesequences,andthatsexactlywhattheContainer
Adaptorsdotheymodelabstractionssuchasaqueueorstack.Theassociativecontainersorganizetheir
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
243/458
5/3/2015
ThinkinginC++2ndedVolume2
databasedonkeys,allowingforfastretrievalofthatdata.
Allthecontainersinthestandardlibraryholdcopiesoftheobjectsyouplaceinthem,andexpandtheir
resourcesasneeded,soyourobjectsmustbecopyconstructible(haveanaccessiblecopyconstructor)and
assignable(haveanaccessibleassignmentoperator).Thekeydifferencebetweenonecontainerand
anotheristhewaytheobjectsarestoredinmemoryandwhatoperationsareavailabletotheuser.
Avector,asyoualreadyknow,isalinearsequencethatallowsrapidrandomaccesstoitselements.
However,itsexpensivetoinsertanelementinthemiddleofacolocatedsequencelikeavector,justas
itiswithanarray.Adeque(doubleendedqueue,pronounceddeck)alsoallowsrandomaccessthats
nearlyasfastasvector,butitssignificantlyfasterwhenitneedstoallocatenewstorage,andyoucan
easilyaddnewelementsatthefrontaswellasthebackofthesequence.Alistisadoublylinkedlist,so
itsexpensivetomovearoundrandomlybutcheaptoinsertanelementanywhere.Thuslist,dequeand
vectoraresimilarintheirbasicfunctionality(theyallholdlinearsequences),butdifferentinthecostof
theiractivities.Foryourfirstattemptataprogram,youcouldchooseanyoneandexperimentwiththe
othersonlyifyouretuningforefficiency.
Manyoftheproblemsyousetouttosolvewillonlyrequireasimplelinearsequencesuchasavector,
deque,orlist.Allthreehaveamemberfunctionpush_back()thatyouusetoinsertanewelementat
thebackofthesequence(dequeandlistalsohavepush_front(),whichinsertselementsatthe
beginningofthesequence).
Buthowdoyouretrievetheelementsstoredinasequencecontainer?Withavectorordeque,itis
possibletousetheindexingoperator[],butthatdoesntworkwithlist.Youcanuseiteratorsonallthree
sequencestoaccesselements.Eachcontainerprovidestheappropriatetypeofiteratorforaccessingits
elements.
Eventhoughthecontainersholdobjectsbyvalue(thatis,theyholdcopiesofwholeobjects),sometimes
youllwanttostorepointerssothatyoucanrefertoobjectsfromahierarchyandthustakeadvantageof
thepolymorphicbehavioroftheclassesrepresented.Considertheclassicshapeexamplewhereshapes
haveasetofcommonoperations,andyouhavedifferenttypesofshapes.Hereswhatitlookslikeusing
theSTLvectortoholdpointerstovariousShapetypescreatedontheheap:
//:C07:Stlshape.cpp
//SimpleshapesusingtheSTL.
#include<vector>
#include<iostream>
usingnamespacestd
classShape{
public:
virtualvoiddraw()=0
virtual~Shape(){}
}
classCircle:publicShape{
public:
voiddraw(){cout<<"Circle::draw<<endl}
~Circle(){cout<<"~Circle<<endl}
}
classTriangle:publicShape{
public:
voiddraw(){cout<<"Triangle::draw<<endl}
~Triangle(){cout<<"~Triangle<<endl}
}
classSquare:publicShape{
public:
voiddraw(){cout<<"Square::draw<<endl}
~Square(){cout<<"~Square<<endl}
}
intmain(){
typedefstd::vector<Shape*>Container
typedefContainer::iteratorIter
Containershapes
shapes.push_back(newCircle)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
244/458
5/3/2015
ThinkinginC++2ndedVolume2
shapes.push_back(newSquare)
shapes.push_back(newTriangle)
for(Iteri=shapes.begin()i!=shapes.end()i++)
(*i)>draw()
//...Sometimelater:
for(Iterj=shapes.begin()j!=shapes.end()j++)
delete*j
}///:~
ThecreationofShape,Circle,Square,andTriangleshouldbefairlyfamiliar.Shapeisanabstractbase
class(becauseofthepurespecifier=0)thatdefinestheinterfaceforalltypesofShapes.Thederived
classesoverridethevirtualfunctiondraw()toperformtheappropriateoperation.Nowwedliketo
createabunchofdifferenttypesofShapeobjects,andthenaturalplacetostorethemisinanSTL
container.Forconvenience,thistypedef:
typedefstd::vector<Shape*>Container
createsanaliasforavectorofShape*,andthistypedef:
typedefContainer::iteratorIter
usesthataliastocreateanotherone,forvector<Shape*>::iterator.Noticethatthecontainertypename
mustbeusedtoproducetheappropriateiterator,whichisdefinedasanestedclass.Althoughthereare
differenttypesofiterators(forward,bidirectional,random,andsoon),theyallhavethesamebasic
interface:youcanincrementthemwith++,youcandereferencethemtoproducetheobjecttheyre
currentlyselecting,andyoucantestthemtoseeiftheyreattheendofthesequence.Thatswhatyoull
wanttodo90percentofthetime.Andthatswhatisdoneinthepreviousexample:afteracontaineris
created,itsfilledwithdifferenttypesofShapepointers.NoticethattheupcasthappensastheCircle,
Square,orRectanglepointerisaddedtotheShapescontainer,whichdoesntknowaboutthosespecific
typesbutinsteadholdsonlyShape*.Assoonasthepointerisaddedtothecontainer,itlosesitsspecific
identityandbecomesananonymousShape*.Thisisexactlywhatwewant:tossthemallinandlet
polymorphismsortitout.
Thefirstforloopcreatesaniteratorandsetsittothebeginningofthesequencebycallingthebegin()
memberfunctionforthecontainer.Allcontainershavebegin()andend()memberfunctionsthat
produceaniteratorselecting,respectively,thebeginningofthesequenceandonepasttheendofthe
sequence.Totesttoseeifyouredone,youmakesuretheiteratorisnotequaltotheiteratorproducedby
end()dontuse<or<=.Theonlyteststhatworkare!=and==,soitscommontowritealooplike:
for(Iteri=shapes.begin()i!=shapes.end()i++)
Thissaystakemethrougheveryelementinthesequence.
Whatdoyoudowiththeiteratortoproducetheelementitsselecting?Youdereferenceitusing(what
else?)the*(whichisactuallyanoverloadedoperator).Whatyougetbackiswhateverthecontaineris
holding.ThiscontainerholdsShape*,sothatswhat*iproduces.IfyouwanttocallaShapemember
function,youmustdosowiththe>operator,soyouwritetheline:
(*i)>draw()
Thiscallsthedraw()functionfortheShape*theiteratoriscurrentlyselecting.Theparenthesesareugly
butnecessarytoproducethedesiredoperatorprecedence.
Astheyaredestroyedorinothercaseswherethepointersareremoved,theSTLcontainersdonot
automaticallycalldeleteforthepointerstheycontain.Ifyoucreateanobjectontheheapwithnewand
placeitspointerinacontainer,thecontainercanttellifthatpointerisalsoplacedinsideanother
container,norifitreferstoheapmemoryinthefirstplace.Asalways,youareresponsibleformanaging
yourownheapallocations.Thelastlinesintheprogrammovethroughanddeleteeveryobjectinthe
containersothatpropercleanupoccurs.Theeasiestandsafestwaytohandlepointersincontainersisto
usesmartpointers.Itshouldbenotedthatauto_ptrcantbeusedforthispurpose,soyouwillneedto
lookoutsideoftheC++StandardLibraryforasuitablesmartpointers.[101]
Youcanchangethetypeofcontainerthatthisprogramuseswithtwolines.Insteadofincluding
<vector>,youinclude<list>,andinthefirsttypedefyousay:
typedefstd::list<Shape*>Container
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
245/458
5/3/2015
ThinkinginC++2ndedVolume2
insteadofusingavector.Everythingelsegoesuntouched.Thisispossiblenotbecauseofaninterface
enforcedbyinheritance(thereislittleinheritanceintheSTL),butbecausetheinterfaceisenforcedbya
conventionadoptedbythedesignersoftheSTL,preciselysoyoucouldperformthiskindofinterchange.
Nowyoucaneasilyswitchbetweenvectorandlistoranyothercontainerthatsupportsthesame
interface(bothsyntacticallyandsemantically)andseewhichoneworksfastestforyourneeds.
Containersofstrings
Inthepreviousexample,attheendofmain()itwasnecessarytomovethroughthewholelistand
deletealltheShapepointers:
for(Iterj=shapes.begin()j!=shapes.end()j++)
delete*j
STLcontainersmakesurethateachobjecttheycontainhasitsdestructorcalledwhenthecontaineritselfis
destroyed.Pointers,however,havenodestructor,sowehavetodeletethemourselves.
ThishighlightswhatcouldbeseenasanoversightintheSTL:theresnofacilityinanyoftheSTL
containerstoautomaticallydeletethepointerstheycontain,soyoumustdoitmanually.Itsasifthe
assumptionoftheSTLdesignerswasthatcontainersofpointerswerentaninterestingproblem,butthats
notthecase.
Automaticallydeletingapointerturnsouttobeproblematicbecauseofthemultiplemembershipproblem.
Ifacontainerholdsapointertoanobject,itsnotunlikelythatpointercouldalsobeinanothercontainer.
ApointertoanAluminumobjectinalistofTrashpointerscouldalsoresideinalistofAluminum
pointers.Ifthathappens,whichlistisresponsibleforcleaningupthatobjectthatis,whichlistownsthe
object?
Thisquestionisvirtuallyeliminatediftheobjectratherthanapointerresidesinthelist.Thenitseems
clearthatwhenthelistisdestroyed,theobjectsitcontainsmustalsobedestroyed.Here,theSTLshines,
asyoucanseewhencreatingacontainerofstringobjects.Thefollowingexamplestoreseachincoming
lineasastringinavector<string>:
//:C07:StringVector.cpp
//Avectorofstrings.
#include<fstream>
#include<iostream>
#include<iterator>
#include<sstream>
#include<string>
#include<vector>
#include"../require.h"
usingnamespacestd
intmain(intargc,char*argv[]){
constchar*fname="StringVector.cpp"
if(argc>1)fname=argv[1]
ifstreamin(fname)
assure(in,fname)
vector<string>strings
stringline
while(getline(in,line))
strings.push_back(line)
//Dosomethingtothestrings...
inti=1
vector<string>::iteratorw
for(w=strings.begin()w!=strings.end()w++){
ostringstreamss
ss<<i++
*w=ss.str()+":"+*w
}
//Nowsendthemout:
copy(strings.begin(),strings.end(),
ostream_iterator<string>(cout,"\n"))
//Sincetheyaren'tpointers,string
//objectscleanthemselvesup!
}///:~
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
246/458
5/3/2015
ThinkinginC++2ndedVolume2
Oncethevector<string>calledstringsiscreated,eachlineinthefileisreadintoastringandputin
thevector:
while(getline(in,line))
strings.push_back(line)
Theoperationthatsbeingperformedonthisfileistoaddlinenumbers.Astringstreamprovideseasy
conversionfromaninttoastringofcharactersrepresentingthatint.
Assemblingstringobjectsisquiteeasy,sinceoperator+isoverloaded.Sensiblyenough,theiteratorw
canbedereferencedtoproduceastringthatcanbeusedasbothanrvalueandanlvalue:
*w=ss.str()+":"+*w
Youmaybesurprisedthatyoucanassignbackintothecontainerviatheiterator,butitsatributetothe
carefuldesignoftheSTL.
Becausethevector<string>containstheobjects,twothingsareworthyofnote.First,asexplained
before,youdontneedtoexplicitlycleanupthestringobjects.Evenifyouputaddressesofthestring
objectsaspointersintoothercontainers,itsclearthatstringsisthemasterlistandmaintains
ownershipoftheobjects.
Second,youareeffectivelyusingdynamicobjectcreation,andyetyouneverusenewordelete!Itsall
takencareofforyoubythevectorbecauseitstorescopiesoftheobjectsyougiveit.Thusyourcodingis
significantlycleanedup.
InheritingfromSTLcontainers
Thepowerofinstantlycreatingasequenceofelementsisamazing,anditmakesyourealizehowmuch
timeyoumayhavelostinthepastsolvingthisparticularproblem.Forexample,manyutilityprograms
involvereadingafileintomemory,modifyingthefile,andwritingitbackouttodisk.Youmightaswell
takethefunctionalityinStringVector.cppandpackageitintoaclassforlaterreuse.
Nowthequestionis:doyoucreateamemberobjectoftypevector,ordoyouinherit?Ageneralobject
orienteddesignguidelineistoprefercomposition(memberobjects)overinheritance,butthestandard
algorithmsexpectsequencesthatimplementaparticularinterface,soinheritanceisoftennecessary.
//:C07:FileEditor.h
//Afileeditortool.
#ifndefFILEEDITOR_H
#defineFILEEDITOR_H
#include<iostream>
#include<string>
#include<vector>
classFileEditor:publicstd::vector<std::string>{
public:
voidopen(constchar*filename)
FileEditor(constchar*filename){open(filename)}
FileEditor(){}
voidwrite(std::ostream&out=std::cout)
}
#endif//FILEEDITOR_H///:~
TheconstructoropensthefileandreadsitintotheFileEditor,andwrite()putsthevectorofstring
ontoanyostream.Noticeinwrite()thatyoucanhaveadefaultargumentforthereference.
Theimplementationisquitesimple:
//:C07:FileEditor.cpp{O}
#include"FileEditor.h"
#include<fstream>
#include"../require.h"
usingnamespacestd
voidFileEditor::open(constchar*filename){
ifstreamin(filename)
assure(in,filename)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
247/458
5/3/2015
ThinkinginC++2ndedVolume2
stringline
while(getline(in,line))
push_back(line)
}
//Couldalsousecopy()here:
voidFileEditor::write(ostream&out){
for(iteratorw=begin()w!=end()w++)
out<<*w<<endl
}///:~
ThefunctionsfromStringVector.cpparesimplyrepackaged.Oftenthisisthewayclassesevolveyou
startbycreatingaprogramtosolveaparticularapplicationandthendiscoversomecommonlyused
functionalitywithintheprogramthatcanbeturnedintoaclass.
ThelinenumberingprogramcannowberewrittenusingFileEditor:
//:C07:FEditTest.cpp
//{L}FileEditor
//TesttheFileEditortool.
#include<sstream>
#include"FileEditor.h"
#include"../require.h"
usingnamespacestd
intmain(intargc,char*argv[]){
FileEditorfile
if(argc>1){
file.open(argv[1])
}else{
file.open("FEditTest.cpp")
}
//Dosomethingtothelines...
inti=1
FileEditor::iteratorw=file.begin()
while(w!=file.end()){
ostringstreamss
ss<<i++
*w=ss.str()+":"+*w
++w
}
//Nowsendthemtocout:
file.write()
}///:~
Nowtheoperationofreadingthefileisintheconstructor:
FileEditorfile(argv[1])
(orintheopen()memberfunction),andwritinghappensinthesingleline(whichdefaultstosendingthe
outputtocout):
file.write()
Thebulkoftheprogramisinvolvedwithmodifyingthefileinmemory.
Aplethoraofiterators
Aniteratorisanabstractionforgenericity.Itworkswithdifferenttypesofcontainerswithoutknowingthe
underlyingstructureofthosecontainers.Mostcontainerssupportiterators,[102]soyoucansay:
<ContainerType>::iterator
<ContainerType>::const_iterator
toproducetheiteratortypesforacontainer.Everycontainerhasabegin()memberfunctionthat
producesaniteratorindicatingthebeginningoftheelementsinthecontainer,andanend()member
functionthatproducesaniteratorwhichisthepasttheendmarkerofthecontainer.Ifthecontaineris
constbegin()andend()produceconstiterators,whichdisallowchangingtheelementspointedto
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
248/458
5/3/2015
ThinkinginC++2ndedVolume2
(becausetheappropriateoperatorsareconst).
Alliteratorscanadvancewithintheirsequence(viaoperator++)andallow==and!=comparisons.
Thus,tomoveaniteratoritforwardwithoutrunningitofftheend,yousaysomethinglike:
while(it!=pastEnd){
//Dosomething
++it
}
wherepastEndisthepasttheendmarkerproducedbythecontainersend()memberfunction.
Aniteratorcanbeusedtoproducethecontainerelementthatitiscurrentlyselectingviathedereferencing
operator(operator*).Thiscantaketwoforms.Ifitisaniteratortraversingacontainer,andf()isa
memberfunctionofthetypeofobjectsheldinthecontainer,youcansayeither:
(*it).f()
or
it>f()
Knowingthis,youcancreateatemplatethatworkswithanycontainer.Here,theapply()function
templatecallsamemberfunctionforeveryobjectinthecontainer,usingapointertomemberthatis
passedasanargument:
//:C07:Apply.cpp
//Usingsimpleiteration.
#include<iostream>
#include<vector>
#include<iterator>
usingnamespacestd
template<classCont,classPtrMemFun>
voidapply(Cont&c,PtrMemFunf){
typenameCont::iteratorit=c.begin()
while(it!=c.end()){
((*it).*f)()//Alternateform
++it
}
}
classZ{
inti
public:
Z(intii):i(ii){}
voidg(){++i}
friendostream&operator<<(ostream&os,constZ&z){
returnos<<z.i
}
}
intmain(){
ostream_iterator<Z>out(cout,"")
vector<Z>vz
for(inti=0i<10i++)
vz.push_back(Z(i))
copy(vz.begin(),vz.end(),out)
cout<<endl
apply(vz,&Z::g)
copy(vz.begin(),vz.end(),out)
}///:~
Youcantuseoperator>herebecausetheresultingstatementwouldbe:
(it>*f)()
whichattemptstousetheiteratorsoperator>*,whichisnotprovidedbytheiteratorclasses.[103]
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
249/458
5/3/2015
ThinkinginC++2ndedVolume2
Itismucheasiertouseeitherfor_each()ortransform()toapplyfunctionstosequences,asyousaw
inthepreviouschapter.
Iteratorsinreversiblecontainers
Acontainermayalsobereversible,whichmeansthatitcanproduceiteratorsthatmovebackwardfrom
theend,aswellasiteratorsthatmoveforwardfromthebeginning.Allstandardcontainerssupportsuch
bidirectionaliteration.
Areversiblecontainerhasthememberfunctionsrbegin()(toproduceareverse_iteratorselectingthe
end)andrend()(toproduceareverse_iteratorindicatingonepastthebeginning).Ifthecontaineris
const,rbegin()andrend()willproduceconst_reverse_iterators.
Thefollowingexampleusesvectorbutwillworkwithallcontainersthatsupportiteration:
//:C07:Reversible.cpp
//Usingreversiblecontainers.
#include<fstream>
#include<iostream>
#include<string>
#include<vector>
#include"../require.h"
usingnamespacestd
intmain(){
ifstreamin("Reversible.cpp")
assure(in,"Reversible.cpp")
stringline
vector<string>lines
while(getline(in,line))
lines.push_back(line)
for(vector<string>::reverse_iteratorr=lines.rbegin()
r!=lines.rend()r++)
cout<<*r<<endl
}///:~
Youmovebackwardthroughthecontainerusingthesamesyntaxasyoudowhenmovingforwardthrough
acontainerwithanordinaryiterator.
Iteratorcategories
TheiteratorsintheStandardC++libraryareclassifiedintocategoriesthatdescribetheircapabilities.
Theorderinwhichtheyaregenerallydescribedmovesfromthecategorieswiththemostrestricted
behaviortothosewiththemostpowerfulbehavior.
Input:readonly,onepass
Theonlypredefinedimplementationsofinputiteratorsareistream_iteratorandistreambuf_iterator,
toreadfromanistream.Asyoucanimagine,aninputiteratorcanonlybedereferencedonceforeach
elementthatsselected,justasyoucanonlyreadaparticularportionofaninputstreamonce.Theycan
onlymoveforward.Aspecialconstructordefinesthepasttheendvalue.Insummary,youcandereference
itforreading(onceonlyforeachvalue)andmoveitforward.
Output:writeonly,onepass
Thisisthecomplementofaninputiterator,butforwritingratherthanreading.Theonlypredefined
implementationsofoutputiteratorsareostream_iteratorandostreambuf_iterator,towritetoan
ostream,andthelesscommonlyusedraw_storage_iterator.Again,thesecanonlybedereferenced
onceforeachwrittenvalue,andtheycanonlymoveforward.Thereisnoconceptofaterminalpastthe
endvalueforanoutputiterator.Summarizing,youcandereferenceitforwriting(onceonlyforeach
value)andmoveitforward.
Forward:multipleread/write
Theforwarditeratorcontainsallthefunctionalityofboththeinputiteratorandtheoutputiterator,plusyou
candereferenceaniteratorlocationmultipletimes,soyoucanreadandwritetoavaluemultipletimes.As
thenameimplies,youcanonlymoveforward.Therearenopredefinediteratorsthatareonlyforward
iterators.
Bidirectional:operator
Thebidirectionaliteratorhasallthefunctionalityoftheforwarditerator,andinadditionitcanbemoved
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
250/458
5/3/2015
ThinkinginC++2ndedVolume2
backwardonelocationatatimeusing
operator.Theiteratorsreturnedbythelistcontainerarebidirectional.
Randomaccess:likeapointer
Finally,therandomaccessiteratorhasallthefunctionalityofthebidirectionaliteratorplusallthe
functionalityofapointer(apointerisarandomaccessiterator),exceptthatthereisnonulliterator
analoguetoanullpointer.Basically,anythingyoucandowithapointeryoucandowitharandomaccess
iterator,includingindexingwithoperator[],addingintegralvaluestoapointertomoveitforwardor
backwardbyanumberoflocations,orcomparingoneiteratortoanotherwithcomparisonoperators.
Isthisreallyimportant?
Whydoyoucareaboutthiscategorization?Whenyourejustusingcontainersinastraightforwardway(for
example,justhandcodingalltheoperationsyouwanttoperformontheobjectsinthecontainer),it
usuallydoesntmatter.Thingseitherworkortheydont.Theiteratorcategoriesbecomeimportantwhen:
1.Youusesomeofthefancierbuiltiniteratortypesthatwillbedemonstratedshortly,oryou
graduatetocreatingyourowniterators(demonstratedlaterinthischapter).
2.YouusetheSTLalgorithms(thesubjectofthepreviouschapter).Eachofthealgorithmsplaces
requirementsonitsiterators.Knowledgeoftheiteratorcategoriesisevenmoreimportantwhenyou
createyourownreusablealgorithmtemplates,becausetheiteratorcategoryrequiredbyyour
algorithmdetermineshowflexiblethealgorithmwillbe.Ifyourequireonlythemostprimitive
iteratorcategory(inputoroutput),youralgorithmwillworkwitheverything(copy()isanexample
ofthis).
Aniteratorscategoryisidentifiedbyahierarchyofiteratortagclasses.Theclassnamescorrespondtothe
iteratorcategories,andtheirderivationreflectstherelationshipbetweenthem:
structinput_iterator_tag{}
structoutput_iterator_tag{}
structforward_iterator_tag:
publicinput_iterator_tag{}
structbidirectional_iterator_tag:
publicforward_iterator_tag{}
structrandom_access_iterator_tag:
publicbidirectional_iterator_tag{}
Theclassforward_iterator_tagderivesonlyfrominput_iterator_tag,notfromoutput_iterator_tag,
becauseweneedtohavepasttheenditeratorvaluesinalgorithmsthatuseforwarditerators,but
algorithmsthatuseoutputiteratorsalwaysassumethatoperator*canbedereferenced.Forthisreason,
itisimportanttomakesurethatapasttheendvalueisneverpassedtoanalgorithmthatexpectsan
outputiterator.
Forefficiency,certainalgorithmsprovidedifferentimplementationsfordifferentiteratortypes,whichthey
inferfromtheiteratortagdefinedbytheiterator.Wewillusesomeofthesetagclasseslaterinthis
chapterwhenwedefineourowniteratortypes.
Predefinediterators
TheSTLhasapredefinedsetofiteratorsthatcanbequitehandy.Forexample,youvealreadyseenthe
reverse_iteratorobjectsproducedbycallingrbegin()andrend()forallthebasiccontainers.
TheinsertioniteratorsarenecessarybecausesomeoftheSTLalgorithmscopy(),forexampleusethe
assignmentoperator=toplaceobjectsinthedestinationcontainer.Thisisaproblemwhenyoureusing
thealgorithmtofillthecontainerratherthantooverwriteitemsthatarealreadyinthedestination
containerthatis,whenthespaceisntalreadythere.Whattheinsertiteratorsdoischangethe
implementationofoperator=sothatinsteadofdoinganassignment,itcallsapushorinsertfunction
forthatcontainer,thuscausingittoallocatenewspace.Theconstructorsforboth
back_insert_iteratorandfront_insert_iteratortakeabasicsequencecontainerobject(vector,deque
orlist)astheirargumentandproduceaniteratorthatcallspush_back()orpush_front(),respectively,
toperformassignment.Thehelperfunctionsback_inserter()andfront_inserter()producethese
insertiteratorobjectswithalittlelesstyping.Sinceallthebasicsequencecontainerssupport
push_back(),youwillprobablyfindyourselfusingback_inserter()withsomeregularity.
Aninsert_iteratorletsyouinsertelementsinthemiddleofthesequence,againreplacingthemeaningof
operator=,butthistimebyautomaticallycallinginsert()insteadofoneofthepushfunctions.The
insert()memberfunctionrequiresaniteratorindicatingtheplacetoinsertbefore,sothe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
251/458
5/3/2015
ThinkinginC++2ndedVolume2
insert_iteratorrequiresthisiteratorinadditiontothecontainerobject.Theshorthandfunction
inserter()producesthesameobject.
Thefollowingexampleshowstheuseofthedifferenttypesofinserters:
//:C07:Inserters.cpp
//Differenttypesofiteratorinserters.
#include<iostream>
#include<vector>
#include<deque>
#include<list>
#include<iterator>
usingnamespacestd
inta[]={1,3,5,7,11,13,17,19,23}
template<classCont>voidfrontInsertion(Cont&ci){
copy(a,a+sizeof(a)/sizeof(Cont::value_type),
front_inserter(ci))
copy(ci.begin(),ci.end(),
ostream_iterator<typenameCont::value_type>(
cout,""))
cout<<endl
}
template<classCont>voidbackInsertion(Cont&ci){
copy(a,a+sizeof(a)/sizeof(Cont::value_type),
back_inserter(ci))
copy(ci.begin(),ci.end(),
ostream_iterator<typenameCont::value_type>(
cout,""))
cout<<endl
}
template<classCont>voidmidInsertion(Cont&ci){
typenameCont::iteratorit=ci.begin()
++it++it++it
copy(a,a+sizeof(a)/(sizeof(Cont::value_type)*2),
inserter(ci,it))
copy(ci.begin(),ci.end(),
ostream_iterator<typenameCont::value_type>(
cout,""))
cout<<endl
}
intmain(){
deque<int>di
list<int>li
vector<int>vi
//Can'tuseafront_inserter()withvector
frontInsertion(di)
frontInsertion(li)
di.clear()
li.clear()
backInsertion(vi)
backInsertion(di)
backInsertion(li)
midInsertion(vi)
midInsertion(di)
midInsertion(li)
}///:~
Sincevectordoesnotsupportpush_front(),itcannotproduceafront_insert_iterator.However,you
canseethatvectordoessupporttheothertwotypesofinsertions(eventhough,asyoushallseelater,
insert()isnotanefficientoperationforvector).Notetheuseofthenestedtype
Cont::value_typeinsteadofhardcodingint.
Moreonstreamiterators
Weintroducedtheuseofthestreamiteratorsostream_iterator(anoutputiterator)and
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
252/458
5/3/2015
ThinkinginC++2ndedVolume2
istream_iterator(aninputiterator)inconjunctionwithcopy()inthepreviouschapter.Rememberthat
anoutputstreamdoesnthaveanyconceptofanend,sinceyoucanalwaysjustkeepwritingmore
elements.However,aninputstreameventuallyterminates(forexample,whenyoureachtheendofa
file),soyouneedawaytorepresentthat.Anistream_iteratorhastwoconstructors,onethattakesan
istreamandproducestheiteratoryouactuallyreadfrom,andtheotherwhichisthedefaultconstructor
andproducesanobjectthatisthepasttheendsentinel.Inthefollowingprogramthisobjectisnamed
end:
//:C07:StreamIt.cpp
//Iteratorsforistreamsandostreams.
#include<fstream>
#include<iostream>
#include<iterator>
#include<string>
#include<vector>
#include"../require.h"
usingnamespacestd
intmain(){
ifstreamin("StreamIt.cpp")
assure(in,"StreamIt.cpp")
istream_iterator<string>begin(in),end
ostream_iterator<string>out(cout,"\n")
vector<string>vs
copy(begin,end,back_inserter(vs))
copy(vs.begin(),vs.end(),out)
*out++=vs[0]
*out++="That'sall,folks!"
}///:~
Wheninrunsoutofinput(inthiscasewhentheendofthefileisreached),initbecomesequivalentto
end,andthecopy()terminates.
Becauseoutisanostream_iterator<string>,youcansimplyassignanystringobjecttothe
dereferencediteratorusingoperator=,andthatstringwillbeplacedontheoutputstream,asseeninthe
twoassignmentstoout.Becauseoutisdefinedwithanewlineasitssecondargument,theseassignments
alsoinsertanewlinealongwitheachassignment.
Althoughitispossibletocreateanistream_iterator<char>andostream_iterator<char>,these
actuallyparsetheinputandthuswill,forexample,automaticallyeatwhitespace(spaces,tabs,and
newlines),whichisnotdesirableifyouwanttomanipulateanexactrepresentationofanistream.
Instead,youcanusethespecialiteratorsistreambuf_iteratorandostreambuf_iterator,whichare
designedstrictlytomovecharacters.[104]Althoughthesearetemplates,theyaremeanttobeusedwith
templateargumentsofeithercharorwchar_t.[105]Thefollowingexampleletsyoucomparethebehavior
ofthestreamiteratorswiththestreambufiterators:
//:C07:StreambufIterator.cpp
//istreambuf_iterator&ostreambuf_iterator.
#include<algorithm>
#include<fstream>
#include<iostream>
#include<iterator>
#include"../require.h"
usingnamespacestd
intmain(){
ifstreamin("StreambufIterator.cpp")
assure(in,"StreambufIterator.cpp")
//Exactrepresentationofstream:
istreambuf_iterator<char>isb(in),end
ostreambuf_iterator<char>osb(cout)
while(isb!=end)
*osb++=*isb++//Copy'in'tocout
cout<<endl
ifstreamin2("StreambufIterator.cpp")
//Stripswhitespace:
istream_iterator<char>is(in2),end2
ostream_iterator<char>os(cout)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
253/458
5/3/2015
ThinkinginC++2ndedVolume2
while(is!=end2)
*os++=*is++
cout<<endl
}///:~
Thestreamiteratorsusetheparsingdefinedbyistream::operator>>,whichisprobablynotwhatyou
wantifyouareparsingcharactersdirectlyitsfairlyrarethatyouwantallthewhitespacestrippedoutof
yourcharacterstream.Youllvirtuallyalwayswanttouseastreambufiteratorwhenusingcharactersand
streams,ratherthanastreamiterator.Inaddition,istream::operator>>addssignificantoverheadfor
eachoperation,soitisonlyappropriateforhigherleveloperationssuchasparsingnumbers.[106]
Manipulatingrawstorage
Theraw_storage_iteratorisdefinedin<memory>andisanoutputiterator.Itisprovidedtoenable
algorithmstostoretheirresultsinuninitializedmemory.Theinterfaceisquitesimple:theconstructor
takesanoutputiteratorthatispointingtotherawmemory(typicallyapointer),andtheoperator=
assignsanobjectintothatrawmemory.Thetemplateparametersarethetypeoftheoutputiterator
pointingtotherawstorageandthetypeofobjectthatwillbestored.Heresanexamplethatcreates
Noisyobjects,whichprinttracestatementsfortheirconstruction,assignment,anddestruction(wellshow
theNoisyclassdefinitionlater):
//:C07:RawStorageIterator.cpp{bor}
//Demonstratetheraw_storage_iterator.
//{L}Noisy
#include<iostream>
#include<iterator>
#include<algorithm>
#include"Noisy.h"
usingnamespacestd
intmain(){
constintQUANTITY=10
//Createrawstorageandcasttodesiredtype:
Noisy*np=reinterpret_cast<Noisy*>(
newchar[QUANTITY*sizeof(Noisy)])
raw_storage_iterator<Noisy*,Noisy>rsi(np)
for(inti=0i<QUANTITYi++)
*rsi++=Noisy()//Placeobjectsinstorage
cout<<endl
copy(np,np+QUANTITY,
ostream_iterator<Noisy>(cout,""))
cout<<endl
//Explicitdestructorcallforcleanup:
for(intj=0j<QUANTITYj++)
(&np[j])>~Noisy()
//Releaserawstorage:
deletereinterpret_cast<char*>(np)
}///:~
Tomaketheraw_storage_iteratortemplatehappy,therawstoragemustbeofthesametypeasthe
objectsyourecreating.ThatswhythepointerfromthenewarrayofchariscasttoaNoisy*.The
assignmentoperatorforcestheobjectsintotherawstorageusingthecopyconstructor.Notethatthe
explicitdestructorcallmustbemadeforpropercleanup,andthisalsoallowstheobjectstobedeletedone
atatimeduringcontainermanipulation.Theexpressiondeletenpwouldbeinvalidanywaysincethe
statictypeofapointerinadeleteexpressionmustbethesameasthetypeassignedtointhenew
expression.
Thebasicsequences:
vector,list,deque
Sequenceskeepobjectsinwhateverorderyoustorethem.Theydifferintheefficiencyoftheiroperations,
however,soifyouaregoingtomanipulateasequenceinaparticularfashion,choosetheappropriate
containerforthosetypesofmanipulations.Sofarinthisbookwevebeenusingvectorasthecontainerof
choice.Thisisquiteoftenthecaseinapplications.Whenyoustartmakingmoresophisticatedusesof
containers,however,itbecomesimportanttoknowmoreabouttheirunderlyingimplementationsand
behaviorsothatyoucanmaketherightchoices.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
254/458
5/3/2015
ThinkinginC++2ndedVolume2
Basicsequenceoperations
Usingatemplate,thefollowingexampleshowstheoperationssupportedbyallthebasicsequences:
vector,deque,andlist:
//:C07:BasicSequenceOperations.cpp
//Theoperationsavailableforallthe
//basicsequenceContainers.
#include<deque>
#include<iostream>
#include<list>
#include<vector>
usingnamespacestd
template<typenameContainer>
voidprint(Container&c,char*title=""){
cout<<title<<':'<<endl
if(c.empty()){
cout<<"(empty)"<<endl
return
}
typenameContainer::iteratorit
for(it=c.begin()it!=c.end()it++)
cout<<*it<<""
cout<<endl
cout<<"size()"<<c.size()
<<"max_size()"<<c.max_size()
<<"front()"<<c.front()
<<"back()"<<c.back()
<<endl
}
template<typenameContainerOfInt>voidbasicOps(char*s){
cout<<""<<s<<""<<endl
typedefContainerOfIntCi
Cic
print(c,"cafterdefaultconstructor")
Cic2(10,1)//10elements,valuesall1
print(c2,"c2afterconstructor(10,1)")
intia[]={1,3,5,7,9}
constintIASZ=sizeof(ia)/sizeof(*ia)
//Initializewithbegin&enditerators:
Cic3(ia,ia+IASZ)
print(c3,"c3afterconstructor(iter,iter)")
Cic4(c2)//Copyconstructor
print(c4,"c4aftercopyconstructor(c2)")
c=c2//Assignmentoperator
print(c,"cafteroperator=c2")
c.assign(10,2)//10elements,valuesall2
print(c,"cafterassign(10,2)")
//Assignwithbegin&enditerators:
c.assign(ia,ia+IASZ)
print(c,"cafterassign(iter,iter)")
cout<<"cusingreverseiterators:"<<endl
typenameCi::reverse_iteratorrit=c.rbegin()
while(rit!=c.rend())
cout<<*rit++<<""
cout<<endl
c.resize(4)
print(c,"cafterresize(4)")
c.push_back(47)
print(c,"cafterpush_back(47)")
c.pop_back()
print(c,"cafterpop_back()")
typenameCi::iteratorit=c.begin()
++it++it
c.insert(it,74)
print(c,"cafterinsert(it,74)")
it=c.begin()
++it
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
255/458
5/3/2015
ThinkinginC++2ndedVolume2
c.insert(it,3,96)
print(c,"cafterinsert(it,3,96)")
it=c.begin()
++it
c.insert(it,c3.begin(),c3.end())
print(c,"cafterinsert("
"it,c3.begin(),c3.end())")
it=c.begin()
++it
c.erase(it)
print(c,"caftererase(it)")
typenameCi::iteratorit2=it=c.begin()
++it
++it2++it2++it2++it2++it2
c.erase(it,it2)
print(c,"caftererase(it,it2)")
c.swap(c2)
print(c,"cafterswap(c2)")
c.clear()
print(c,"cafterclear()")
}
intmain(){
basicOps<vector<int>>("vector")
basicOps<deque<int>>("deque")
basicOps<list<int>>("list")
}///:~
Thefirstfunctiontemplate,print(),demonstratesthebasicinformationyoucangetfromanysequence
container:whetheritsempty,itscurrentsize,thesizeofthelargestpossiblecontainer,theelementatthe
beginning,andtheelementattheend.Youcanalsoseethateverycontainerhasbegin()andend()
memberfunctionsthatreturniterators.
ThebasicOps()functiontestseverythingelse(andinturncallsprint()),includingavarietyof
constructors:default,copyconstructor,quantityandinitialvalue,andbeginningandendingiterators.
Thereareanassignmentoperator=andtwokindsofassign()memberfunctions.Onetakesaquantity
andaninitialvalue,andtheothertakesabeginningandendingiterator.
Allthebasicsequencecontainersarereversiblecontainers,asshownbytheuseoftherbegin()and
rend()memberfunctions.Asequencecontainercanberesized,andtheentirecontentsofthecontainer
canberemovedwithclear().Whenyoucallresize()toexpandasequence,thenewelementsusethe
defaultconstructorofthetypeofelementinthesequence,oriftheyarebuiltintypes,theyarezero
initialized.
Usinganiteratortoindicatewhereyouwanttostartinsertingintoanysequencecontainer,youcan
insert()asingleelement,anumberofelementsthatallhavethesamevalue,andagroupofelements
fromanothercontainerusingthebeginningandendingiteratorsofthatgroup.
Toerase()asingleelementfromthemiddle,useaniteratortoerase()arangeofelements,useapair
ofiterators.Noticethatsincealistsupportsonlybidirectionaliterators,alltheiteratormotionmustbe
performedwithincrementsanddecrements.(Ifthecontainerswerelimitedtovectoranddeque,which
producerandomaccessiterators,operator+andoperatorcouldhavebeenusedtomovetheiterators
inbiggerjumps.)
Althoughbothlistanddequesupportpush_front()andpop_front(),vectordoesnot,but
push_back()andpop_back()workwithallthree.
Thenamingofthememberfunctionswap()isalittleconfusing,sincetheresalsoanonmemberswap()
algorithmthatinterchangesthevaluesofanytwoobjectsofsametype.Thememberswap()swaps
everythinginonecontainerforanother(ifthecontainersholdthesametype),effectivelyswappingthe
containersthemselves.Itdoesthisefficientlybyswappingthecontentsofeachcontainer,whichconsists
mostlyofpointers.Thenonmemberswap()algorithmnormallyusesassignmenttointerchangeits
arguments(anexpensiveoperationforanentirecontainer),butitiscustomizedthroughtemplate
specializationtocallthememberswap()forthestandardcontainers.Thereisalsoaniter_swap
algorithmthatusesiteratorstointerchangetwoelementsinthesamecontainer.
Thefollowingsectionsdiscusstheparticularsofeachtypeofsequencecontainer.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
256/458
5/3/2015
ThinkinginC++2ndedVolume2
vector
Thevectorclasstemplateisintentionallymadetolooklikeasoupeduparray,sinceithasarraystyle
indexing,butalsocanexpanddynamically.Thevectorclasstemplateissofundamentallyusefulthatit
wasintroducedinaprimitivewayearlyinthisbookandwasusedregularlyinpreviousexamples.This
sectionwillgiveamoreindepthlookatvector.
Toachievemaximallyefficientindexinganditeration,vectormaintainsitsstorageasasinglecontiguous
arrayofobjects.Thisisacriticalpointtoobserveinunderstandingthebehaviorofvector.Itmeansthat
indexinganditerationarelightningfast,beingbasicallythesameasindexinganditeratingoveranarray
ofobjects.Butitalsomeansthatinsertinganobjectanywherebutattheend(thatis,appending)isnot
reallyanacceptableoperationforavector.Inaddition,whenavectorrunsoutofpreallocatedstorage,
tomaintainitscontiguousarrayitmustallocateawholenew(larger)chunkofstorageelsewhereandcopy
theobjectstothenewstorage.Thisapproachproducesanumberofunpleasantsideeffects.
Costofoverflowingallocatedstorage
Avectorstartsbygrabbingablockofstorage,asifitstakingaguessathowmanyobjectsyouplanto
putinit.Aslongasyoudonttrytoputinmoreobjectsthancanbeheldintheinitialblockofstorage,
everythingproceedsrapidly.(Ifyoudoknowhowmanyobjectstoexpect,youcanpreallocatestorage
usingreserve().)Buteventuallyyouwillputinonetoomanyobjects,andthevectorrespondsby:
1.Allocatinganew,biggerpieceofstorage.
2.Copyingalltheobjectsfromtheoldstoragetothenew(usingthecopyconstructor).
3.Destroyingalltheoldobjects(thedestructoriscalledforeachone).
4.Releasingtheoldmemory.
Forcomplexobjects,thiscopyconstructionanddestructioncanendupbeingexpensiveifyouoftenoverfill
yourvector,whichiswhyvectors(andSTLcontainersingeneral)aredesignedforvaluetypes(i.e.types
thatarecheaptocopy).Thisincludespointers.
Toseewhathappenswhenyourefillingavector,hereistheNoisyclassmentionedearlier.Itprints
informationaboutitscreations,destructions,assignments,andcopyconstructions:
//:C07:Noisy.h
//Aclasstotrackvariousobjectactivities.
#ifndefNOISY_H
#defineNOISY_H
#include<iostream>
usingstd::endl
usingstd::cout
usingstd::ostream
classNoisy{
staticlongcreate,assign,copycons,destroy
longid
public:
Noisy():id(create++){
cout<<"d["<<id<<"]"<<endl
}
Noisy(constNoisy&rv):id(rv.id){
cout<<"c["<<id<<"]"<<endl
++copycons
}
Noisy&operator=(constNoisy&rv){
cout<<"("<<id<<")=["<<rv.id<<"]"<<endl
id=rv.id
++assign
return*this
}
friendbooloperator<(constNoisy&lv,constNoisy&rv){
returnlv.id<rv.id
}
friendbooloperator==(constNoisy&lv,constNoisy&rv){
returnlv.id==rv.id
}
~Noisy(){
cout<<"~["<<id<<"]"<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
257/458
5/3/2015
ThinkinginC++2ndedVolume2
++destroy
}
friendostream&operator<<(ostream&os,constNoisy&n){
returnos<<n.id
}
friendclassNoisyReport
}
structNoisyGen{
Noisyoperator()(){returnNoisy()}
}
//ASingleton.Willautomaticallyreportthe
//statisticsastheprogramterminates:
classNoisyReport{
staticNoisyReportnr
NoisyReport(){}//Privateconstructor
NoisyReport&operator=(NoisyReport&)//Disallowed
NoisyReport(constNoisyReport&)//Disallowed
public:
~NoisyReport(){
cout<<"\n\n"
<<"Noisycreations:"<<Noisy::create
<<"\nCopyConstructions:"<<Noisy::copycons
<<"\nAssignments:"<<Noisy::assign
<<"\nDestructions:"<<Noisy::destroy<<endl
}
}
#endif//NOISY_H///:~
//:C07:Noisy.cpp{O}
#include"Noisy.h"
longNoisy::create=0,Noisy::assign=0,
Noisy::copycons=0,Noisy::destroy=0
NoisyReportNoisyReport::nr
///:~
EachNoisyobjecthasitsownidentifier,andstaticvariableskeeptrackofallthecreations,assignments
(usingoperator=),copyconstructions,anddestructions.Theidisinitializedusingthecreatecounter
insidethedefaultconstructorthecopyconstructorandassignmentoperatortaketheiridvaluesfromthe
rvalue.Withoperator=thelvalueisalreadyaninitializedobject,sotheoldvalueofidisprintedbeforeit
isoverwrittenwiththeidfromthervalue.
Tosupportcertainoperationssuchassortingandsearching(whichareusedimplicitlybysomeofthe
containers),Noisymusthaveanoperator<andoperator==.Thesesimplycomparetheidvalues.The
ostreaminserterfollowstheusualformandsimplyprintstheid.
ObjectsoftypeNoisyGenarefunctionobjects(sincethereisanoperator())thatproduceNoisyobjects
duringtesting.
NoisyReportisaSingletonobject[107]becauseweonlywantonereportprintedatprogramtermination.It
hasaprivateconstructorsonoadditionalNoisyReportobjectscanbecreated,itdisallowsassignment
andcopyconstruction,andithasasinglestaticinstanceofNoisyReportcallednr.Theonlyexecutable
statementsareinthedestructor,whichiscalledastheprogramexitsandstaticdestructorsarecalled.
ThisdestructorprintsthestatisticscapturedbythestaticvariablesinNoisy.
UsingNoisy.h,thefollowingprogramshowsavectoroverflowingitsallocatedstorage:
//:C07:VectorOverflow.cpp{bor}
//Showsthecopyconstructionanddestruction
//thatoccurswhenavectormustreallocate.
//{L}Noisy
#include<cstdlib>
#include<iostream>
#include<string>
#include<vector>
#include"Noisy.h"
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
258/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(intargc,char*argv[]){
intsize=1000
if(argc>=2)size=atoi(argv[1])
vector<Noisy>vn
Noisyn
for(inti=0i<sizei++)
vn.push_back(n)
cout<<"\ncleaningup<<endl
}///:~
Youcanusethedefaultvalueof1000,oryoucanuseyourownvaluebyputtingitonthecommandline.
Whenyourunthisprogram,youllseeasingledefaultconstructorcall(forn),thenalotofcopy
constructorcalls,thensomedestructorcalls,thensomemorecopyconstructorcalls,andsoon.Whenthe
vectorrunsoutofspaceinthelineararrayofbytesithasallocated,itmust(tomaintainalltheobjectsin
alineararray,whichisanessentialpartofitsjob)getabiggerpieceofstorageandmoveeverything
over,firstcopyingandthendestroyingtheoldobjects.Youcanimaginethatifyoustorealotoflargeand
complexobjects,thisprocesscouldrapidlybecomeprohibitive.
Therearetwosolutionstothisproblem.Thenicestonerequiresthatyouknowbeforehandhowmany
objectsyouregoingtomake.Inthatcase,youcanusereserve()totellthevectorhowmuchstorageto
preallocate,thuseliminatingallthecopiesanddestructionsandmakingeverythingveryfast(especially
randomaccesstotheobjectswithoperator[]).Notethattheuseofreserve()isdifferentfromusing
thevectorconstructorwithanintegralfirstargumentthelatterinitializesaprescribednumberof
elementsusingtheelementtypesdefaultconstructor.
Generallyyouwontknowhowmanyobjectsyoullneed.Ifvectorreallocationsareslowingthingsdown,
youcanchangesequencecontainers.Youcouldusealist,butasyoullsee,thedequeallowsspeedy
insertionsateitherendofthesequenceandneverneedstocopyordestroyobjectsasitexpandsits
storage.Thedequealsoallowsrandomaccesswithoperator[],butitsnotquiteasfastasvectors
operator[].Soifyourecreatingallyourobjectsinonepartoftheprogramandrandomlyaccessing
theminanother,youmayfindyourselffillingadequeandthencreatingavectorfromthedequeand
usingthevectorforrapidindexing.Youdontwanttoprogramthiswayhabituallyjustbeawareofthese
issues(thatis,avoidprematureoptimization).
Thereisadarkersidetovectorsreallocationofmemory,however.Becausevectorkeepsitsobjectsina
nice,neatarray,theiteratorsusedbyvectorcanbesimplepointers.Thisisgoodofallthesequence
containers,thesepointersallowthefastestselectionandmanipulation.Whethertheyaresimplepointers,
orwhethertheyareiteratorobjectsthatholdaninternalpointerintotheircontainer,considerwhat
happenswhenyouaddtheoneadditionalobjectthatcausesthevectortoreallocatestorageandmoveit
elsewhere.Theiteratorspointerisnowpointingoffintonowhere:
//:C07:VectorCoreDump.cpp
//Invalidatinganiterator.
#include<iterator>
#include<iostream>
#include<vector>
usingnamespacestd
intmain(){
vector<int>vi(10,0)
ostream_iterator<int>out(cout,"")
vector<int>::iteratori=vi.begin()
*i=47
copy(vi.begin(),vi.end(),out)
cout<<endl
//Forceittomovememory(couldalsojustadd
//enoughobjects):
vi.resize(vi.capacity()+1)
//Nowipointstowrongmemory:
*i=48//Accessviolation
copy(vi.begin(),vi.end(),out)//Nochangetovi[0]
}///:~
Thisillustratestheconceptofiteratorinvalidation.Certainoperationscauseinternalchangestoa
containersunderlyingdata,soanyiteratorsineffectbeforesuchchangesmaynolongerbevalid
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
259/458
5/3/2015
ThinkinginC++2ndedVolume2
afterward.Ifyourprogramisbreakingmysteriously,lookforplaceswhereyouholdontoaniteratorwhile
addingmoreobjectstoavector.Youllneedtogetanewiteratorafteraddingelementsoruse
operator[]insteadforelementselections.Ifyoucombinethisobservationwiththeawarenessofthe
potentialexpenseofaddingnewobjectstoavector,youmayconcludethatthesafestwaytousea
vectoristofillitupallatonce(ideally,knowingfirsthowmanyobjectsyoullneed)andthenjustuseit
(withoutaddingmoreobjects)elsewhereintheprogram.Thisisthewayvectorhasbeenusedinthebook
uptothispoint.TheStandardC++librarydocumentsthecontaineroperationsthatinvalidateiterators.
Youmayobservethatusingvectorasthebasiccontainerintheearlierchaptersofthisbookmightnot
bethebestchoiceinallcases.Thisisafundamentalissueincontainersandindatastructuresingeneral
thebestchoicevariesaccordingtothewaythecontainerisused.Thereasonvectorhasbeenthebest
choiceupuntilnowisthatitlooksalotlikeanarrayandwasthusfamiliarandeasyforyoutoadopt.But
fromnowonitsalsoworththinkingaboutotherissueswhenchoosingcontainers.
Insertinganderasingelements
Thevectorismostefficientif:
1.Youreserve()thecorrectamountofstorageatthebeginningsothevectorneverhasto
reallocate.
2.Youonlyaddandremoveelementsfromthebackend.
Itispossibletoinsertanderaseelementsfromthemiddleofavectorusinganiterator,butthefollowing
programdemonstrateswhatabadideathisis:
//:C07:VectorInsertAndErase.cpp{bor}
//Erasinganelementfromavector.
//{L}Noisy
#include<algorithm>
#include<iostream>
#include<iterator>
#include<vector>
#include"Noisy.h"
usingnamespacestd
intmain(){
vector<Noisy>v
v.reserve(11)
cout<<"11spaceshavebeenreserved"<<endl
generate_n(back_inserter(v),10,NoisyGen())
ostream_iterator<Noisy>out(cout,"")
cout<<endl
copy(v.begin(),v.end(),out)
cout<<"Insertinganelement:"<<endl
vector<Noisy>::iteratorit=
v.begin()+v.size()/2//Middle
v.insert(it,Noisy())
cout<<endl
copy(v.begin(),v.end(),out)
cout<<"\nErasinganelement:"<<endl
//Cannotusethepreviousvalueofit:
it=v.begin()+v.size()/2
v.erase(it)
cout<<endl
copy(v.begin(),v.end(),out)
cout<<endl
}///:~
Whenyouruntheprogram,youllseethatthecalltoreserve()reallydoesonlyallocatestorageno
constructorsarecalled.Thegenerate_n()callisbusy:eachcalltoNoisyGen::operator()resultsina
construction,acopyconstruction(intothevector),andadestructionofthetemporary.Butwhenanobject
isinsertedintothevectorinthemiddle,itmustshifteverythingdowntomaintainthelineararray,and,
sincethereisenoughspace,itdoesthiswiththeassignmentoperator.(Iftheargumentofreserve()is
10insteadof11,itmustreallocatestorage.)Whenanobjectiserasedfromthevector,theassignment
operatorisonceagainusedtomoveeverythinguptocovertheplacethatisbeingerased.(Noticethatthis
requiresthattheassignmentoperatorproperlycleanupthelvalue.)Last,theobjectontheendofthe
arrayisdeleted.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
260/458
5/3/2015
ThinkinginC++2ndedVolume2
deque
Thedequecontainerisabasicsequenceoptimizedforaddingandremovingelementsfromeitherend.It
alsoallowsforreasonablyfastrandomaccessithasanoperator[]likevector.However,itdoesnot
havevectorsconstraintofkeepingeverythinginasinglesequentialblockofmemory.Instead,atypical
implementationofdequeusesmultipleblocksofsequentialstorage(keepingtrackofalltheblocksand
theirorderinamappingstructure).Forthisreason,theoverheadforadequetoaddorremoveelements
ateitherendislow.Inaddition,itneverneedstocopyanddestroycontainedobjectsduringanewstorage
allocation(likevectordoes),soitisfarmoreefficientthanvectorifyouareaddinganunknownquantity
ofobjectsateitherend.Thismeansthatvectoristhebestchoiceonlyifyouhaveagoodideaofhow
manyobjectsyouneed.Inaddition,manyoftheprogramsshownearlierinthisbookthatusevectorand
push_back()mighthavebeenmoreefficienthadweusedadequeinstead.Theinterfacetodeque
differsonlyslightlyfromvector(dequehasapush_front()andpop_front()whilevectordoesnot,
forexample),soconvertingcodefromusingvectortousingdequeistrivial.ConsiderStringVector.cpp,
whichcanbechangedtousedequebyreplacingthewordvectorwithdequeeverywhere.Thefollowing
programaddsparalleldequeoperationstothevectoroperationsinStringVector.cppandperforms
timingcomparisons:
//:C07:StringDeque.cpp
//ConvertedfromStringVector.cpp.
#include<cstddef>
#include<ctime>
#include<deque>
#include<fstream>
#include<iostream>
#include<iterator>
#include<sstream>
#include<string>
#include<vector>
#include"../require.h"
usingnamespacestd
intmain(intargc,char*argv[]){
char*fname="StringDeque.cpp"
if(argc>1)fname=argv[1]
ifstreamin(fname)
assure(in,fname)
vector<string>vstrings
deque<string>dstrings
stringline
//Timereadingintovector:
clock_tticks=clock()
while(getline(in,line))
vstrings.push_back(line)
ticks=clock()ticks
cout<<"Readintovector:"<<ticks<<endl
//Repeatfordeque:
ifstreamin2(fname)
assure(in2,fname)
ticks=clock()
while(getline(in2,line))
dstrings.push_back(line)
ticks=clock()ticks
cout<<"Readintodeque:"<<ticks<<endl
//Nowcompareindexing:
ticks=clock()
for(size_ti=0i<vstrings.size()i++){
ostringstreamss
ss<<i
vstrings[i]=ss.str()+":"+vstrings[i]
}
ticks=clock()ticks
cout<<"Indexingvector:"<<ticks<<endl
ticks=clock()
for(size_tj=0j<dstrings.size()j++){
ostringstreamss
ss<<j
dstrings[j]=ss.str()+":"+dstrings[j]
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
261/458
5/3/2015
ThinkinginC++2ndedVolume2
ticks=clock()ticks
cout<<"Indexingdeque:"<<ticks<<endl
//Compareiteration
ofstreamtmp1("tmp1.tmp"),tmp2("tmp2.tmp")
ticks=clock()
copy(vstrings.begin(),vstrings.end(),
ostream_iterator<string>(tmp1,"\n"))
ticks=clock()ticks
cout<<"Iteratingvector:"<<ticks<<endl
ticks=clock()
copy(dstrings.begin(),dstrings.end(),
ostream_iterator<string>(tmp2,"\n"))
ticks=clock()ticks
cout<<"Iteratingdeque:"<<ticks<<endl
}///:~
Knowingnowwhatyoudoabouttheinefficiencyofaddingthingstovectorbecauseofstoragereallocation,
youmightexpectdramaticdifferencesbetweenthetwo.However,ona1.7MBtextfile,onecompilers
programproducedthefollowing(measuredinplatform/compilerspecificclockticks,notseconds):
Readintovector:8350
Readintodeque:7690
Indexingvector:2360
Indexingdeque:2480
Iteratingvector:2470
Iteratingdeque:2410
Adifferentcompilerandplatformroughlyagreedwiththis.Itsnotsodramatic,isit?Thispointsoutsome
importantissues:
1.We(programmersandauthors)aretypicallybadatguessingwhereinefficienciesoccurinour
programs.
2.Efficiencycomesfromacombinationofeffects.Here,readingthelinesinandconvertingthemto
stringsmaydominateoverthecostofvectorvs.deque.
3.Thestringclassisprobablyfairlywelldesignedintermsofefficiency.
Thisdoesntmeanyoushouldntuseadequeratherthanavectorwhenyouknowthatanuncertain
numberofobjectswillbepushedontotheendofthecontainer.Onthecontrary,youshouldwhenyoure
tuningforperformance.Butalsobeawarethatperformanceissuesareusuallynotwhereyouthinkthey
are,andtheonlywaytoknowforsurewhereyourbottlenecksareisbytesting.Laterinthischapter,
youllseeamorepurecomparisonofperformancebetweenvector,deque,andlist.
Convertingbetweensequences
Sometimesyouneedthebehaviororefficiencyofonekindofcontainerforonepartofyourprogram,and
youneedadifferentcontainersbehaviororefficiencyinanotherpartoftheprogram.Forexample,you
mayneedtheefficiencyofadequewhenaddingobjectstothecontainerbuttheefficiencyofavector
whenindexingthem.Eachofthebasicsequencecontainers(vector,deque,andlist)hasatwoiterator
constructor(indicatingthebeginningandendingofthesequencetoreadfromwhencreatinganewobject)
andanassign()memberfunctiontoreadintoanexistingcontainer,soyoucaneasilymoveobjectsfrom
onesequencecontainertoanother.
Thefollowingexamplereadsobjectsintoadequeandthenconvertstoavector:
//:C07:DequeConversion.cpp{bor}
//ReadingintoaDeque,convertingtoavector.
//{L}Noisy
#include<algorithm>
#include<cstdlib>
#include<deque>
#include<iostream>
#include<iterator>
#include<vector>
#include"Noisy.h"
usingnamespacestd
intmain(intargc,char*argv[]){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
262/458
5/3/2015
ThinkinginC++2ndedVolume2
intsize=25
if(argc>=2)size=atoi(argv[1])
deque<Noisy>d
generate_n(back_inserter(d),size,NoisyGen())
cout<<"\nConvertingtoavector(1)"<<endl
vector<Noisy>v1(d.begin(),d.end())
cout<<"\nConvertingtoavector(2)"<<endl
vector<Noisy>v2
v2.reserve(d.size())
v2.assign(d.begin(),d.end())
cout<<"\nCleanup"<<endl
}///:~
Youcantryvarioussizes,butnotethatitmakesnodifferencetheobjectsaresimplycopyconstructed
intothenewvectors.Whatsinterestingisthatv1doesnotcausemultipleallocationswhilebuildingthe
vector,nomatterhowmanyelementsyouuse.Youmightinitiallythinkthatyoumustfollowtheprocess
usedforv2andpreallocatethestoragetopreventmessyreallocations,butthisisunnecessarybecause
theconstructorusedforv1determinesthememoryrequirementaheadoftime.
Costofoverflowingallocatedstorage
Itsilluminatingtoseewhathappenswithadequewhenitoverflowsablockofstorage,incontrastwith
VectorOverflow.cpp:
//:C07:DequeOverflow.cpp{bor}
//Adequeismuchmoreefficientthanavectorwhen
//pushingbackalotofelements,sinceitdoesn't
//requirecopyinganddestroying.
//{L}Noisy
#include<cstdlib>
#include<deque>
#include"Noisy.h"
usingnamespacestd
intmain(intargc,char*argv[]){
intsize=1000
if(argc>=2)size=atoi(argv[1])
deque<Noisy>dn
Noisyn
for(inti=0i<sizei++)
dn.push_back(n)
cout<<"\ncleaningup<<endl
}///:~
Hereyouwillhaverelativelyfew(ifany)destructorscalledbeforethewordscleaningupappearinthe
output.Sincethedequeallocatesallitsstorageinblocksinsteadofacontiguousarraylikevector,it
neverneedstomoveexistingstorageofeachofitsdatablocks.(Thus,noadditionalcopyconstructions
anddestructionsoccur.)Thedequesimplyallocatesanewblock.Forthesamereason,thedequecanjust
asefficientlyaddelementstothebeginningofthesequence,sinceifitrunsoutofstorage,it(again)just
allocatesanewblockforthebeginning.(Theindexblockthatholdsthedatablockstogethermayneedto
bereallocated,however.)Insertionsinthemiddleofadeque,however,couldbeevenmessierthanfor
vector(butnotascostly).
Becauseofdequescleverstoragemanagement,anexistingiteratorisnotinvalidatedafteryouaddnew
thingstoeitherendofadeque,asitwasdemonstratedtodowithvector(inVectorCoreDump.cpp).If
yousticktowhatdequeisbestatinsertionsandremovalsfromeitherend,reasonablyrapidtraversals
andfairlyfastrandomaccessusingoperator[]youllbeingoodshape.
Checkedrandomaccess
Bothvectoranddequeprovidetworandomaccessfunctions:theindexingoperator(operator[]),which
youveseenalready,andat(),whichcheckstheboundariesofthecontainerthatsbeingindexedand
throwsanexceptionifyougooutofbounds.Itdoescostmoretouseat():
//:C07:IndexingVsAt.cpp
//Comparing"at()"tooperator[].
#include<ctime>
#include<deque>
#include<iostream>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
263/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<vector>
#include"../require.h"
usingnamespacestd
intmain(intargc,char*argv[]){
longcount=1000
intsz=1000
if(argc>=2)count=atoi(argv[1])
if(argc>=3)sz=atoi(argv[2])
vector<int>vi(sz)
clock_tticks=clock()
for(inti1=0i1<counti1++)
for(intj=0j<szj++)
vi[j]
cout<<"vector[]"<<clock()ticks<<endl
ticks=clock()
for(inti2=0i2<counti2++)
for(intj=0j<szj++)
vi.at(j)
cout<<"vector::at()"<<clock()ticks<<endl
deque<int>di(sz)
ticks=clock()
for(inti3=0i3<counti3++)
for(intj=0j<szj++)
di[j]
cout<<"deque[]"<<clock()ticks<<endl
ticks=clock()
for(inti4=0i4<counti4++)
for(intj=0j<szj++)
di.at(j)
cout<<"deque::at()"<<clock()ticks<<endl
//Demonstrateat()whenyougooutofbounds:
try{
di.at(vi.size()+1)
}catch(...){
cerr<<"Exceptionthrown"<<endl
}
}///:~
AsyousawinChapter1,differentsystemsmayhandletheuncaughtexceptionindifferentways,butyoull
knowonewayoranotherthatsomethingwentwrongwiththeprogramwhenusingat(),whereasits
possibletoremainignorantwhenusingoperator[].
list
Alistisimplementedasadoublylinkedlistdatastructureandisthusdesignedforrapidinsertionand
removalofelementsanywhereinthesequence,whereasforvectoranddequethisisamuchmorecostly
operation.Alistissoslowwhenrandomlyaccessingelementsthatitdoesnothaveanoperator[].Its
bestusedwhenyouretraversingasequence,inorder,frombeginningtoend(orviceversa),ratherthan
choosingelementsrandomlyfromthemiddle.Eventhenthetraversalcanbeslowerthanwithavector,
butifyouarentdoingalotoftraversals,thatwontbeyourbottleneck.
Thememoryoverheadofeachlinkinalistrequiresaforwardandbackwardpointerontopofthestorage
fortheactualobject.Thus,alistisabetterchoicewhenyouhavelargerobjectsthatyoullbeinserting
andremovingfromthemiddleofthelist.
Itsbetternottousealistifyouthinkyoumightbetraversingitalot,lookingforobjects,sincethe
amountoftimeittakestogetfromthebeginningofthelistwhichistheonlyplaceyoucanstartunless
youvealreadygotaniteratortosomewhereyouknowisclosertoyourdestinationtotheobjectof
interestisproportionaltothenumberofobjectsbetweenthebeginningandthatobject.
Theobjectsinalistnevermoveaftertheyarecreated.Movingalistelementmeanschangingthelinks,
butnevercopyingorassigningtheactualobjects.Thismeansthatiteratorsarentinvalidatedwhenitems
areaddedtothelistasitwasdemonstratedearliertobethecasevector.Heresanexampleusingalist
ofNoisyobjects:
//:C07:ListStability.cpp{bor}
//Thingsdon'tmovearoundinlists.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
264/458
5/3/2015
ThinkinginC++2ndedVolume2
//{L}Noisy
#include<algorithm>
#include<iostream>
#include<iterator>
#include<list>
#include"Noisy.h"
usingnamespacestd
intmain(){
list<Noisy>l
ostream_iterator<Noisy>out(cout,"")
generate_n(back_inserter(l),25,NoisyGen())
cout<<"\nPrintingthelist:"<<endl
copy(l.begin(),l.end(),out)
cout<<"\nReversingthelist:"<<endl
l.reverse()
copy(l.begin(),l.end(),out)
cout<<"\nSortingthelist:"<<endl
l.sort()
copy(l.begin(),l.end(),out)
cout<<"\nSwappingtwoelements:"<<endl
list<Noisy>::iteratorit1,it2
it1=it2=l.begin()
++it2
swap(*it1,*it2)
cout<<endl
copy(l.begin(),l.end(),out)
cout<<"\nUsinggenericreverse():"<<endl
reverse(l.begin(),l.end())
cout<<endl
copy(l.begin(),l.end(),out)
cout<<"\nCleanup"<<endl
}///:~
Operationsasseeminglyradicalasreversingandsortingthelistrequirenocopyingofobjectsbecause,
insteadofmovingtheobjects,thelinksaresimplychanged.However,noticethatsort()andreverse()
arememberfunctionsoflist,sotheyhavespecialknowledgeoftheinternalsoflistandcanrearrangethe
elementsinsteadofcopyingthem.Ontheotherhand,theswap()functionisagenericalgorithmand
doesntknowaboutlistinparticular,soitusesthecopyingapproachforswappingtwoelements.In
general,usethememberversionofanalgorithmifthatissuppliedinsteadofitsgenericalgorithm
equivalent.Inparticular,usethegenericsort()andreverse()algorithmsonlywitharrays,vectors,
anddeques.
Ifyouhavelarge,complexobjects,youmightwanttochoosealistfirst,especiallyifconstruction,
destruction,copyconstruction,andassignmentareexpensiveandifyouaredoingthingslikesortingthe
objectsorotherwisereorderingthemalot.
Speciallistoperations
Thelisthassomespecialbuiltinoperationstomakethebestuseofthestructureofthelist.Youve
alreadyseenreverse()andsort().Herearesomeoftheothers:
//:C07:ListSpecialFunctions.cpp
//{L}Noisy
#include<algorithm>
#include<iostream>
#include<iterator>
#include<list>
#include"Noisy.h"
#include"PrintContainer.h"
usingnamespacestd
intmain(){
typedeflist<Noisy>LN
LNl1,l2,l3,l4
generate_n(back_inserter(l1),6,NoisyGen())
generate_n(back_inserter(l2),6,NoisyGen())
generate_n(back_inserter(l3),6,NoisyGen())
generate_n(back_inserter(l4),6,NoisyGen())
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
265/458
5/3/2015
ThinkinginC++2ndedVolume2
print(l1,"l1","")print(l2,"l2","")
print(l3,"l3","")print(l4,"l4","")
LN::iteratorit1=l1.begin()
++it1++it1++it1
l1.splice(it1,l2)
print(l1,"l1aftersplice(it1,l2)","")
print(l2,"l2aftersplice(it1,l2)","")
LN::iteratorit2=l3.begin()
++it2++it2++it2
l1.splice(it1,l3,it2)
print(l1,"l1aftersplice(it1,l3,it2)","")
LN::iteratorit3=l4.begin(),it4=l4.end()
++it3it4
l1.splice(it1,l4,it3,it4)
print(l1,"l1aftersplice(it1,l4,it3,it4)","")
Noisyn
LNl5(3,n)
generate_n(back_inserter(l5),4,NoisyGen())
l5.push_back(n)
print(l5,"l5beforeremove()","")
l5.remove(l5.front())
print(l5,"l5afterremove()","")
l1.sort()l5.sort()
l5.merge(l1)
print(l5,"l5afterl5.merge(l1)","")
cout<<"\nCleanup"<<endl
}///:~
AfterfillingfourlistswithNoisyobjects,onelistissplicedintoanotherinthreeways.Inthefirst,the
entirelistl2issplicedintol1attheiteratorit1.Noticethatafterthesplice,l2isemptysplicingmeans
removingtheelementsfromthesourcelist.Thesecondspliceinsertselementsfroml3startingatit2into
l1startingatit1.Thethirdsplicestartsatit1anduseselementsfroml4startingatit3andendingatit4.
Theseeminglyredundantmentionofthesourcelistisbecausetheelementsmustbeerasedfromthe
sourcelistaspartofthetransfertothedestinationlist.
Theoutputfromthecodethatdemonstratesremove()showsthatthelistdoesnothavetobesortedin
orderforalltheelementsofaparticularvaluetoberemoved.
Finally,ifyoumerge()onelistwithanother,themergeonlyworkssensiblyifthelistshavebeensorted.
Whatyouendupwithinthatcaseisasortedlistcontainingalltheelementsfrombothlists(thesourcelist
iserasedthatis,theelementsaremovedtothedestinationlist).
Aunique()memberfunctionremovesallduplicates,butonlyifyousortthelistfirst:
//:C07:UniqueList.cpp
//Testinglist'sunique()function.
#include<iostream>
#include<iterator>
#include<list>
usingnamespacestd
inta[]={1,3,1,4,1,5,1,6,1}
constintASZ=sizeofa/sizeof*a
intmain(){
//Foroutput:
ostream_iterator<int>out(cout,"")
list<int>li(a,a+ASZ)
li.unique()
//Oops!Noduplicatesremoved:
copy(li.begin(),li.end(),out)
cout<<endl
//Mustsortitfirst:
li.sort()
copy(li.begin(),li.end(),out)
cout<<endl
//Nowunique()willhaveaneffect:
li.unique()
copy(li.begin(),li.end(),out)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
266/458
5/3/2015
ThinkinginC++2ndedVolume2
cout<<endl
}///:~
Thelistconstructorusedheretakesthestartingandpasttheenditeratorfromanothercontainerand
copiesalltheelementsfromthatcontainerintoitself.Here,thecontainerisjustanarray,andthe
iteratorsarepointersintothatarray,butbecauseofthedesignoftheSTL,thelistconstructorworks
witharraysjustaseasilyaswithanyothercontainer.
Theunique()functionwillremoveonlyadjacentduplicateelements,andthussortingistypically
necessarybeforecallingunique().Theexceptioniswhentheproblemyouretryingtosolveincludes
eliminatingadjacentduplicatesaccordingtothecurrentordering.
Fouradditionallistmemberfunctionsarenotdemonstratedhere:aremove_if()thattakesapredicate,
whichdecideswhetheranobjectshouldberemovedaunique()thattakesabinarypredicatetoperform
uniquenesscomparisonsamerge()thattakesanadditionalargumentwhichperformscomparisonsand
asort()thattakesacomparator(toprovideacomparisonoroverridetheexistingone).
listvs.set
Lookingatthepreviousexample,youmightnotethatifyouwantasortedsequencewithnoduplicates,
youcouldgetthatresultwithaset.Itsinterestingtocomparetheperformanceofthetwocontainers:
//:C07:ListVsSet.cpp
//Comparinglistandsetperformance.
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<iterator>
#include<list>
#include<set>
#include"PrintContainer.h"
usingnamespacestd
classObj{
inta[20]//Totakeupextraspace
intval
public:
Obj():val(rand()%500){}
friendbool
operator<(constObj&a,constObj&b){
returna.val<b.val
}
friendbool
operator==(constObj&a,constObj&b){
returna.val==b.val
}
friendostream&
operator<<(ostream&os,constObj&a){
returnos<<a.val
}
}
structObjGen{
Objoperator()(){returnObj()}
}
intmain(){
constintSZ=5000
srand(time(0))
list<Obj>lo
clock_tticks=clock()
generate_n(back_inserter(lo),SZ,ObjGen())
lo.sort()
lo.unique()
cout<<"list:"<<clock()ticks<<endl
set<Obj>so
ticks=clock()
generate_n(inserter(so,so.begin()),
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
267/458
5/3/2015
ThinkinginC++2ndedVolume2
SZ,ObjGen())
cout<<"set:"<<clock()ticks<<endl
print(lo)
print(so)
}///:~
Whenyouruntheprogram,youshoulddiscoverthatsetismuchfasterthanlist.Thisisreassuringafter
all,itissetsprimaryjobdescriptiontoholdonlyuniqueelementsinsortedorder!
ThisexampleusestheheaderPrintContainer.h,whichcontainsafunctiontemplatethatprintsany
sequencecontainertoanoutputstream.PrintContainer.hisdefinedasfollows:
//:C07:PrintContainer.h
//Printsasequencecontainer
#ifndefPRINT_CONTAINER_H
#definePRINT_CONTAINER_H
#include"../C06/PrintSequence.h"
template<classCont>
voidprint(Cont&c,constchar*nm="",
constchar*sep="\n",
std::ostream&os=std::cout){
print(c.begin(),c.end(),nm,sep,os)
}
#endif///:~
Theprint()templatedefinedherejustcallstheprint()functiontemplatewedefinedintheprevious
chapterinPrintSequence.h.
Swappingsequences
Wementionedearlierthatallbasicsequenceshaveamemberfunctionswap()thatsdesignedtoswitch
onesequencewithanother(butonlyforsequencesofthesametype).Thememberswap()makesuseof
itsknowledgeoftheinternalstructureoftheparticularcontainerinordertobeefficient:
//:C07:Swapping.cpp{bor}
//Allbasicsequencecontainerscanbeswapped.
//{L}Noisy
#include<algorithm>
#include<deque>
#include<iostream>
#include<iterator>
#include<list>
#include<vector>
#include"Noisy.h"
#include"PrintContainer.h"
usingnamespacestd
ostream_iterator<Noisy>out(cout,"")
template<classCont>voidtestSwap(char*cname){
Contc1,c2
generate_n(back_inserter(c1),10,NoisyGen())
generate_n(back_inserter(c2),5,NoisyGen())
cout<<endl<<cname<<":"<<endl
print(c1,"c1")print(c2,"c2")
cout<<"\nSwappingthe"<<cname<<":"<<endl
c1.swap(c2)
print(c1,"c1")print(c2,"c2")
}
intmain(){
testSwap<vector<Noisy>>("vector")
testSwap<deque<Noisy>>("deque")
testSwap<list<Noisy>>("list")
}///:~
Whenyourunthis,youlldiscoverthateachtypeofsequencecontainercanswaponesequenceforanother
withoutanycopyingorassignments,evenifthesequencesareofdifferentsizes.Ineffect,youre
completelyswappingtheresourcesofoneobjectforanother.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
268/458
5/3/2015
ThinkinginC++2ndedVolume2
TheSTLalgorithmsalsocontainaswap(),andwhenthisfunctionisappliedtotwocontainersofthesame
type,itusesthememberswap()toachievefastperformance.Consequently,ifyouapplythesort()
algorithmtoacontainerofcontainers,youwillfindthattheperformanceisveryfastitturnsoutthatfast
sortingofacontainerofcontainerswasadesigngoaloftheSTL.
set
Thesetcontaineracceptsonlyonecopyofeachelement.Italsosortstheelements.(Sortingisntintrinsic
totheconceptualdefinitionofaset,buttheSTLsetstoresitselementsinabalancedtreedatastructure
toproviderapidlookups,thusproducingsortedresultswhenyoutraverseit.)Thefirsttwoexamplesin
thischapterusedsets.
Considertheproblemofcreatinganindexforabook.Youmightliketostartwithallthewordsinthebook,
butyouonlywantoneinstanceofeachword,andyouwantthemsorted.Asetisperfectforthisand
solvestheproblemeffortlessly.However,theresalsotheproblemofpunctuationandanyothernonalpha
characters,whichmustbestrippedofftogenerateproperwords.Onesolutiontothisproblemistousethe
StandardClibraryfunctionsisalpha()andisspace()toextractonlythecharactersyouwant.Youcan
replaceallunwantedcharacterswithspacessothatyoucaneasilyextractvalidwordsfromeachlineyou
read:
//:C07:WordList.cpp
//Displayalistofwordsusedinadocument.
#include<algorithm>
#include<cctype>
#include<cstring>
#include<fstream>
#include<iostream>
#include<iterator>
#include<set>
#include<sstream>
#include<string>
#include"../require.h"
usingnamespacestd
charreplaceJunk(charc){
//Onlykeepalphas,space(asadelimiter),and'
return(isalpha(c)||c=='\'')?c:''
}
intmain(intargc,char*argv[]){
char*fname="WordList.cpp"
if(argc>1)fname=argv[1]
ifstreamin(fname)
assure(in,fname)
set<string>wordlist
stringline
while(getline(in,line)){
transform(line.begin(),line.end(),line.begin(),
replaceJunk)
istringstreamis(line)
stringword
while(is>>word)
wordlist.insert(word)
}
//Outputresults:
copy(wordlist.begin(),wordlist.end(),
ostream_iterator<string>(cout,"\n"))
}///:~
Thecalltotransform()replaceseachcharactertobeignoredwithaspace.Thesetcontainernotonly
ignoresduplicatewords,butcomparesthewordsitkeepsaccordingtothefunctionobjectless<string>
(thedefaultsecondtemplateargumentforthesetcontainer),whichinturnusesstring::operator<(),so
thewordsemergeinalphabeticalorder.
Youdontneedtouseasetjusttogetasortedsequence.Youcanusethesort()function(alongwitha
multitudeofotherfunctionsintheSTL)ondifferentSTLcontainers.However,itslikelythatsetwillbe
fasterhere.Usingasetisparticularlyhandywhenyoujustwanttodolookup,sinceitsfind()member
functionhaslogarithmiccomplexityandsoismuchfasterthanthegenericfind()algorithm.Asyourecall,
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
269/458
5/3/2015
ThinkinginC++2ndedVolume2
thegenericfind()algorithmneedstotraversethewholerangeuntilitfindsthesearchelement(resulting
inaworstcasecomplexityofN,andanaveragecomplexityofN/2).However,ifyouhaveasequence
containerthatisalreadysorted,useequal_range()forlogarithmiccomplexitywhenfindingelements.
Thefollowingversionshowshowtobuildthelistofwordswithanistreambuf_iteratorthatmovesthe
charactersfromoneplace(theinputstream)toanother(astringobject),dependingonwhetherthe
StandardClibraryfunctionisalpha()returnstrue:
//:C07:WordList2.cpp
//Illustratesistreambuf_iteratorandinsertiterators.
#include<cstring>
#include<fstream>
#include<iostream>
#include<iterator>
#include<set>
#include<string>
#include"../require.h"
usingnamespacestd
intmain(intargc,char*argv[]){
char*fname="WordList2.cpp"
if(argc>1)fname=argv[1]
ifstreamin(fname)
assure(in,fname)
istreambuf_iterator<char>p(in),end
set<string>wordlist
while(p!=end){
stringword
insert_iterator<string>ii(word,word.begin())
//Findthefirstalphacharacter:
while(p!=end&&!isalpha(*p))
++p
//Copyuntilthefirstnonalphacharacter:
while(p!=end&&isalpha(*p))
*ii++=*p++
if(word.size()!=0)
wordlist.insert(word)
}
//Outputresults:
copy(wordlist.begin(),wordlist.end(),
ostream_iterator<string>(cout,"\n"))
}///:~
ThisexamplewassuggestedbyNathanMyers,whoinventedtheistreambuf_iteratoranditsrelatives.
Thisiteratorextractsinformationcharacterbycharacterfromastream.Althoughthe
istreambuf_iteratortemplateargumentmightimplythatyoucouldextract,forexample,intsinsteadof
char,thatsnotthecase.Theargumentmustbeofsomecharactertypearegularcharorawide
character.
Afterthefileisopen,anistreambuf_iteratorcalledpisattachedtotheistreamsocharacterscanbe
extractedfromit.Theset<string>calledwordlistwillholdtheresultingwords.
Thewhileloopreadswordsuntilitfindstheendoftheinputstream.Thisisdetectedusingthedefault
constructorforistreambuf_iterator,whichproducesthepasttheenditeratorobjectend.Thus,ifyou
wanttotesttomakesureyourenotattheendofthestream,yousimplysayp!=end.
Thesecondtypeofiteratorthatsusedhereistheinsert_iterator,whichyousawpreviously.Thisinserts
objectsintoacontainer.Here,thecontaineristhestringcalledword,which,forthepurposesof
insert_iterator,behaveslikeacontainer.Theconstructorforinsert_iteratorrequiresthecontainerand
aniteratorindicatingwhereitshouldstartinsertingthecharacters.Youcouldalsousea
back_insert_iterator,whichrequiresthatthecontainerhaveapush_back()(stringdoes).
Afterthewhileloopsetseverythingup,itbeginsbylookingforthefirstalphacharacter,incrementing
startuntilthatcharacterisfound.Itthencopiescharactersfromoneiteratortotheother,stoppingwhen
anonalphacharacterisfound.Eachword,assumingitisnonempty,isaddedtowordlist.
Acompletelyreusabletokenizer
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
270/458
5/3/2015
ThinkinginC++2ndedVolume2
Thewordlistexamplesusedifferentapproachestoextracttokensfromastream,neitherofwhichisvery
flexible.SincetheSTLcontainersandalgorithmsallrevolvearounditerators,themostflexiblesolutionwill
itselfuseaniterator.YoucouldthinkoftheTokenIteratorasaniteratorthatwrapsitselfaroundany
otheriteratorthatcanproducecharacters.Becauseitiscertainlyatypeofinputiterator(themost
primitivetypeofiterator),itcanprovideinputtoanySTLalgorithm.Notonlyisitausefultoolinitself,
thefollowingTokenIteratorisalsoagoodexampleofhowyoucandesignyourowniterators.[108]
TheTokenIteratorclassisdoublyflexible.First,youcanchoosethetypeofiteratorthatwillproducethe
charinput.Second,insteadofjustsayingwhatcharactersrepresentthedelimiters,TokenIteratorwill
useapredicatethatisafunctionobjectwhoseoperator()takesacharanddecideswhetheritshouldbe
inthetoken.Althoughthetwoexamplesgivenherehaveastaticconceptofwhatcharactersbelongina
token,youcouldeasilydesignyourownfunctionobjecttochangeitsstateasthecharactersareread,
producingamoresophisticatedparser.
Thefollowingheaderfilecontainstwobasicpredicates,IsalphaandDelimiters,alongwiththetemplate
forTokenIterator:
//:C07:TokenIterator.h
#ifndefTOKENITERATOR_H
#defineTOKENITERATOR_H
#include<algorithm>
#include<cctype>
#include<functional>
#include<iterator>
#include<string>
structIsalpha:std::unary_function<char,bool>{
booloperator()(charc){returnstd::isalpha(c)}
}
classDelimiters:std::unary_function<char,bool>{
std::stringexclude
public:
Delimiters(){}
Delimiters(conststd::string&excl):exclude(excl){}
booloperator()(charc){
returnexclude.find(c)==std::string::npos
}
}
template<classInputIter,classPred=Isalpha>
classTokenIterator:publicstd::iterator<
std::input_iterator_tag,std::string,std::ptrdiff_t>{
InputIterfirst
InputIterlast
std::stringword
Predpredicate
public:
TokenIterator(InputIterbegin,InputIterend,
Predpred=Pred())
:first(begin),last(end),predicate(pred){
++*this
}
TokenIterator(){}//Endsentinel
//Prefixincrement:
TokenIterator&operator++(){
word.resize(0)
first=std::find_if(first,last,predicate)
while(first!=last&&predicate(*first))
word+=*first++
return*this
}
//Postfixincrement
classCaptureState{
std::stringword
public:
CaptureState(conststd::string&w):word(w){}
std::stringoperator*(){returnword}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
271/458
5/3/2015
ThinkinginC++2ndedVolume2
}
CaptureStateoperator++(int){
CaptureStated(word)
++*this
returnd
}
//Producetheactualvalue:
std::stringoperator*()const{returnword}
conststd::string*operator>()const{return&word}
//Compareiterators:
booloperator==(constTokenIterator&){
returnword.size()==0&&first==last
}
booloperator!=(constTokenIterator&rv){
return!(*this==rv)
}
}
#endif//TOKENITERATOR_H///:~
TheTokenIteratorclassderivesfromthestd::iteratortemplate.Itmightappearthatsomekindof
functionalitycomeswithstd::iterator,butitispurelyawayoftagginganiterator,totellacontainerthat
usesitwhatitcando.Here,youcanseeinput_iterator_tagastheiterator_categorytemplate
argumentthistellsanyonewhoasksthataTokenIteratoronlyhasthecapabilitiesofaninputiterator
andcannotbeusedwithalgorithmsrequiringmoresophisticatediterators.Apartfromthetagging,
std::iteratordoesntdoanythingbeyondprovidingseveralusefultypedefinitions.Youmustimplementall
otherfunctionalityyourself.
TheTokenIteratorclassmaylookalittlestrangeatfirst,becausethefirstconstructorrequiresbotha
beginandanenditeratorasarguments,alongwiththepredicate.Remember,thisisawrapper
iteratorthathasnoideahowtotellwhenitsattheendofitsinput,sotheendingiteratorisnecessaryin
thefirstconstructor.Thereasonforthesecond(default)constructoristhattheSTLalgorithms(andany
algorithmsyouwrite)needaTokenIteratorsentineltobethepasttheendvalue.Sinceallthe
informationnecessarytoseeiftheTokenIteratorhasreachedtheendofitsinputiscollectedinthefirst
constructor,thissecondconstructorcreatesaTokenIteratorthatismerelyusedasaplaceholderin
algorithms.
Thecoreofthebehaviorhappensinoperator++.Thiserasesthecurrentvalueofwordusing
string::resize()andthenfindsthefirstcharacterthatsatisfiesthepredicate(thusdiscoveringthe
beginningofthenewtoken)usingfind_if().Theresultingiteratorisassignedtofirst,thusmovingfirst
forwardtothebeginningofthetoken.Then,aslongastheendoftheinputisnotreachedandthe
predicateissatisfied,inputcharactersarecopiedintoword.Finally,theTokenIteratorobjectisreturned
andmustbedereferencedtoaccessthenewtoken.
ThepostfixincrementrequiresanobjectoftypeCaptureStatetoholdthevaluebeforetheincrement,so
itcanbereturned.Producingtheactualvalueisastraightforwardoperator*.Theonlyotherfunctionsto
defineforanoutputiteratoraretheoperator==andoperator!=toindicatewhethertheTokenIterator
hasreachedtheendofitsinput.Youcanseethattheargumentforoperator==isignoreditonlycares
aboutwhetherithasreacheditsinternallastiterator.Noticethatoperator!=isdefinedintermsof
operator==.
AgoodtestofTokenIteratorincludesanumberofdifferentsourcesofinputcharacters,includinga
streambuf_iterator,achar*,andadeque<char>::iterator.Finally,theoriginalwordlistproblemis
solved:
//:C07:TokenIteratorTest.cpp{g++}
#include<fstream>
#include<iostream>
#include<vector>
#include<deque>
#include<set>
#include"TokenIterator.h"
#include"../require.h"
usingnamespacestd
intmain(intargc,char*argv[]){
char*fname="TokenIteratorTest.cpp"
if(argc>1)fname=argv[1]
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
272/458
5/3/2015
ThinkinginC++2ndedVolume2
ifstreamin(fname)
assure(in,fname)
ostream_iterator<string>out(cout,"\n")
typedefistreambuf_iterator<char>IsbIt
IsbItbegin(in),isbEnd
Delimitersdelimiters("\t\n~()\"<>:{}[]+=&*#.,/\\")
TokenIterator<IsbIt,Delimiters>
wordIter(begin,isbEnd,delimiters),end
vector<string>wordlist
copy(wordIter,end,back_inserter(wordlist))
//Outputresults:
copy(wordlist.begin(),wordlist.end(),out)
*out++=""
//Useachararrayasthesource:
char*cp="typedefstd::istreambuf_iterator<char>It"
TokenIterator<char*,Delimiters>
charIter(cp,cp+strlen(cp),delimiters),end2
vector<string>wordlist2
copy(charIter,end2,back_inserter(wordlist2))
copy(wordlist2.begin(),wordlist2.end(),out)
*out++=""
//Useadeque<char>asthesource:
ifstreamin2("TokenIteratorTest.cpp")
deque<char>dc
copy(IsbIt(in2),IsbIt(),back_inserter(dc))
TokenIterator<deque<char>::iterator,Delimiters>
dcIter(dc.begin(),dc.end(),delimiters),end3
vector<string>wordlist3
copy(dcIter,end3,back_inserter(wordlist3))
copy(wordlist3.begin(),wordlist3.end(),out)
*out++=""
//ReproducetheWordlist.cppexample:
ifstreamin3("TokenIteratorTest.cpp")
TokenIterator<IsbIt,Delimiters>
wordIter2(IsbIt(in3),isbEnd,delimiters)
set<string>wordlist4
while(wordIter2!=end)
wordlist4.insert(*wordIter2++)
copy(wordlist4.begin(),wordlist4.end(),out)
}///:~
Whenusinganistreambuf_iterator,youcreateonetoattachtotheistreamobjectandonewiththe
defaultconstructorasthepasttheendmarker.BothareusedtocreatetheTokenIteratorthatwill
producethetokensthedefaultconstructorproducesthefauxTokenIteratorpasttheendsentinel.(This
isjustaplaceholderandisignored.)TheTokenIteratorproducesstringsthatareinsertedintoa
containerofstringhereavector<string>isusedinallcasesexceptthelast.(Youcouldalso
concatenatetheresultsontoastring.)Otherthanthat,aTokenIteratorworkslikeanyotherinput
iterator.
Whendefiningabidirectional(andthereforealsoarandomaccess)iterator,youcangetreverseiterators
forfreebyusingthestd::reverse_iteratoradaptor.Ifyouhavealreadydefinedaniteratorfora
containerwithbidirectionalcapabilities,youcangetareverseiteratorfromyourforwardtraversing
iteratorwithlineslikethefollowinginsideyourcontainerclass:
//Assume"iterator"isyournestediteratortype
typedefstd::reverse_iterator<iterator>reverse_iterator
reverse_iteratorrbegin(){returnreverse_iterator(end())
reverse_iteratorrend(){returnreverse_iterator(begin())
Thestd::reverse_iteratoradaptordoesalltheworkforyou.Forexample,ifyouusethe*operatorto
dereferenceyourreverseiterator,itautomaticallydecrementsatemporarycopyoftheforwarditeratorit
isholdinginordertoreturnthecorrectelement,sincereverseiteratorslogicallypointonepositionpast
theelementtheyreferto.
stack
Thestackcontainer,alongwithqueueandpriority_queue,areclassifiedasadaptors,whichmeansthey
adaptoneofthebasicsequencecontainerstostoretheirdata.Thisisanunfortunatecaseofconfusing
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
273/458
5/3/2015
ThinkinginC++2ndedVolume2
whatsomethingdoeswiththedetailsofitsunderlyingimplementationthefactthatthesearecalled
adaptorsisofprimaryvalueonlytothecreatorofthelibrary.Whenyouusethem,yougenerallydont
carethattheyreadaptors,butinsteadthattheysolveyourproblem.Admittedlyitsusefulattimesto
knowthatyoucanchooseanalternateimplementationorbuildanadaptorfromanexistingcontainer
object,butthatsgenerallyonelevelremovedfromtheadaptorsbehavior.So,whileyoumayseeit
emphasizedelsewherethataparticularcontainerisanadaptor,wellonlypointoutthatfactwhenits
useful.Notethateachtypeofadaptorhasadefaultcontainerthatitsbuiltupon,andthisdefaultisthe
mostsensibleimplementation.Inmostcasesyouwontneedtoconcernyourselfwiththeunderlying
implementation.
Thefollowingexampleshowsstack<string>implementedinthethreeways:thedefault(whichuses
deque),thenwithavector,andfinallywithalist:
//:C07:Stack1.cpp
//DemonstratestheSTLstack.
#include<fstream>
#include<iostream>
#include<list>
#include<stack>
#include<string>
#include<vector>
usingnamespacestd
//Rearrangecommentsbelowtousedifferentversions.
typedefstack<string>Stack1//Default:deque<string>
//typedefstack<string,vector<string>>Stack2
//typedefstack<string,list<string>>Stack3
intmain(){
ifstreamin("Stack1.cpp")
Stack1textlines//Trythedifferentversions
//Readfileandstorelinesinthestack:
stringline
while(getline(in,line))
textlines.push(line+"\n")
//Printlinesfromthestackandpopthem:
while(!textlines.empty()){
cout<<textlines.top()
textlines.pop()
}
}///:~
Thetop()andpop()operationswillprobablyseemnonintuitiveifyouveusedotherstackclasses.
Whenyoucallpop(),itreturnsvoidratherthanthetopelementthatyoumighthaveexpected.Ifyou
wantthetopelement,yougetareferencetoitwithtop().Itturnsoutthisismoreefficient,sincea
traditionalpop()mustreturnavalueratherthanareferenceandthusinvokesthecopyconstructor.More
important,itisexceptionsafe,aswediscussedinChapter1.Ifpop()bothchangedthestateofthestack
andattemptedtoreturnthetopelement,anexceptionintheelementscopyconstructorcouldcausethe
elementtobelost.Whenyoureusingastack(orapriority_queue,describedlater),youcanefficiently
refertotop()asmanytimesasyouwantandthendiscardthetopelementexplicitlyusingpop().
(Perhapsifsometermotherthanthefamiliarpophadbeenused,thiswouldhavebeenabitclearer.)
Thestacktemplatehasasimpleinterfaceessentiallythememberfunctionsyousawearlier.Sinceitonly
makessensetoaccessastackatitstop,noiteratorsareavailablefortraversingit.Norarethere
sophisticatedformsofinitialization,butifyouneedthat,youcanusetheunderlyingcontaineruponwhich
thestackisimplemented.Forexample,supposeyouhaveafunctionthatexpectsastackinterface,butin
therestofyourprogramyouneedtheobjectsstoredinalist.Thefollowingprogramstoreseachlineofa
filealongwiththeleadingnumberofspacesinthatline.(Youmightimagineitasastartingpointfor
performingsomekindofsourcecodereformatting.)
//:C07:Stack2.cpp
//Convertingalisttoastack.
#include<iostream>
#include<fstream>
#include<stack>
#include<list>
#include<string>
#include<cstddef>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
274/458
5/3/2015
ThinkinginC++2ndedVolume2
usingnamespacestd
//Expectsastack:
template<classStk>
voidstackOut(Stk&s,ostream&os=cout){
while(!s.empty()){
os<<s.top()<<"\n"
s.pop()
}
}
classLine{
stringline//Withoutleadingspaces
size_tlspaces//Numberofleadingspaces
public:
Line(strings):line(s){
lspaces=line.find_first_not_of('')
if(lspaces==string::npos)
lspaces=0
line=line.substr(lspaces)
}
friendostream&operator<<(ostream&os,constLine&l){
for(size_ti=0i<l.lspacesi++)
os<<''
returnos<<l.line
}
//Otherfunctionshere...
}
intmain(){
ifstreamin("Stack2.cpp")
list<Line>lines
//Readfileandstorelinesinthelist:
strings
while(getline(in,s))
lines.push_front(s)
//Turnthelistintoastackforprinting:
stack<Line,list<Line>>stk(lines)
stackOut(stk)
}///:~
Thefunctionthatrequiresthestackinterfacejustsendseachtop()objecttoanostreamandthen
removesitbycallingpop().TheLineclassdeterminesthenumberofleadingspacesandthenstoresthe
contentsofthelinewithouttheleadingspaces.Theostreamoperator<<reinsertstheleadingspacesso
thelineprintsproperly,butyoucaneasilychangethenumberofspacesbychangingthevalueoflspaces.
(Thememberfunctionstodothisarenotshownhere.)Inmain(),theinputfileisreadintoa
list<Line>,andtheneachlineinthelistiscopiedintoastackthatissenttostackOut().
Youcannotiteratethroughastackthisemphasizesthatyouonlywanttoperformstackoperationswhen
youcreateastack.Youcangetequivalentstackfunctionalityusingavectoranditsback(),
push_back(),andpop_back()memberfunctions,andthenyouhavealltheadditionalfunctionalityof
thevector.TheprogramStack1.cppcanberewrittentoshowthis:
//:C07:Stack3.cpp
//UsingavectorasastackmodifiedStack1.cpp.
#include<fstream>
#include<iostream>
#include<string>
#include<vector>
usingnamespacestd
intmain(){
ifstreamin("Stack3.cpp")
vector<string>textlines
stringline
while(getline(in,line))
textlines.push_back(line+"\n")
while(!textlines.empty()){
cout<<textlines.back()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
275/458
5/3/2015
ThinkinginC++2ndedVolume2
textlines.pop_back()
}
}///:~
ThisproducesthesameoutputasStack1.cpp,butyoucannowperformvectoroperationsaswell.Alist
canalsopushthingsatthefront,butitsgenerallylessefficientthanusingpush_back()withvector.(In
addition,dequeisusuallymoreefficientthanlistforpushingthingsatthefront.)
queue
Thequeuecontainerisarestrictedformofadequeyoucanonlyenterelementsatoneendandpull
themofftheotherend.Functionally,youcoulduseadequeanywhereyouneedaqueue,andyouwould
thenalsohavetheadditionalfunctionalityofthedeque.Theonlyreasonyouneedtouseaqueuerather
thanadeque,then,iswhenyouwanttoemphasizethatyouwillonlybeperformingqueuelikebehavior.
Thequeueclassisanadaptorlikestack,inthatitisbuiltontopofanothersequencecontainer.Asyou
mightguess,theidealimplementationforaqueueisadeque,andthatisthedefaulttemplateargument
forthequeueyoullrarelyneedadifferentimplementation.
Queuesareoftenusedifyouwanttomodelasystemwheresomeelementsarewaitingtobeservedby
otherelementsinthesystem.Aclassicexampleofthisisthebanktellerproblem.Customersarriveat
randomintervals,getintoaline,andthenareservedbyasetoftellers.Sincethecustomersarrive
randomlyandeachtakesarandomamountoftimetobeserved,theresnowaytodeterministicallyknow
howlongthelinewillbeatanytime.However,itspossibletosimulatethesituationandseewhat
happens.
Inarealisticsimulationeachcustomerandtellershouldberunbyaseparatethread.Whatwedlikeisa
multithreadedenvironmentsothateachcustomerortellerwouldhavehisownthread.However,Standard
C++hasnosupportformultithreading.Ontheotherhand,withalittleadjustmenttothecode,itspossible
tosimulateenoughmultithreadingtoprovideasatisfactorysolution.[109]
Inmultithreading,multiplethreadsofcontrolrunsimultaneously,sharingthesameaddressspace.Quite
oftenyouhavefewerCPUsthanyoudothreads(andoftenonlyoneCPU).Togivetheillusionthateach
threadhasitsownCPU,atimeslicingmechanismsaysOK,currentthread,youvehadenoughtime.Im
goingtostopyouandgivetimetosomeotherthread.Thisautomaticstoppingandstartingofthreadsis
calledpreemptive,anditmeansyou(theprogrammer)dontneedtomanagethethreadingprocess.
AnalternativeapproachhaseachthreadvoluntarilyyieldtheCPUtothescheduler,whichthenfinds
anotherthreadthatneedsrunning.Instead,wellbuildthetimeslicingintotheclassesinthesystem.
Here,itwillbethetellersthatrepresentthethreads,(thecustomerswillbepassive).Eachtellerwill
haveaninfiniteloopingrun()memberfunctionthatwillexecuteforacertainnumberoftimeunitsand
thensimplyreturn.Byusingtheordinaryreturnmechanism,weeliminatetheneedforanyswapping.The
resultingprogram,althoughsmall,providesaremarkablyreasonablesimulation:
//:C07:BankTeller.cpp{RunByHand}
//Usingaqueueandsimulatedmultithreading
//tomodelabanktellersystem.
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<iterator>
#include<list>
#include<queue>
usingnamespacestd
classCustomer{
intserviceTime
public:
Customer():serviceTime(0){}
Customer(inttm):serviceTime(tm){}
intgetTime(){returnserviceTime}
voidsetTime(intnewtime){serviceTime=newtime}
friendostream&
operator<<(ostream&os,constCustomer&c){
returnos<<'['<<c.serviceTime<<']'
}
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
276/458
5/3/2015
ThinkinginC++2ndedVolume2
classTeller{
queue<Customer>&customers
Customercurrent
enum{SLICE=5}
intttime//Timeleftinslice
boolbusy//Istellerservingacustomer?
public:
Teller(queue<Customer>&cq)
:customers(cq),ttime(0),busy(false){}
Teller&operator=(constTeller&rv){
customers=rv.customers
current=rv.current
ttime=rv.ttime
busy=rv.busy
return*this
}
boolisBusy(){returnbusy}
voidrun(boolrecursion=false){
if(!recursion)
ttime=SLICE
intservtime=current.getTime()
if(servtime>ttime){
servtime=ttime
current.setTime(servtime)
busy=true//Stillworkingoncurrent
return
}
if(servtime<ttime){
ttime=servtime
if(!customers.empty()){
current=customers.front()
customers.pop()//Removeit
busy=true
run(true)//Recurse
}
return
}
if(servtime==ttime){
//Donewithcurrent,settoempty:
current=Customer(0)
busy=false
return//Nomoretimeinthisslice
}
}
}
//Inherittoaccessprotectedimplementation:
classCustomerQ:publicqueue<Customer>{
public:
friendostream&
operator<<(ostream&os,constCustomerQ&cd){
copy(cd.c.begin(),cd.c.end(),
ostream_iterator<Customer>(os,""))
returnos
}
}
intmain(){
CustomerQcustomers
list<Teller>tellers
typedeflist<Teller>::iteratorTellIt
tellers.push_back(Teller(customers))
srand(time(0))//Seedtherandomnumbergenerator
clock_tticks=clock()
//Runsimulationforatleast5seconds:
while(clock()<ticks+5*CLOCKS_PER_SEC){
//Addarandomnumberofcustomerstothe
//queue,withrandomservicetimes:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
277/458
5/3/2015
ThinkinginC++2ndedVolume2
for(inti=0i<rand()%5i++)
customers.push(Customer(rand()%15+1))
cout<<'{'<<tellers.size()<<'}'
<<customers<<endl
//Havethetellersservicethequeue:
for(TellIti=tellers.begin()
i!=tellers.end()i++)
(*i).run()
cout<<'{'<<tellers.size()<<'}'
<<customers<<endl
//Iflineistoolong,addanotherteller:
if(customers.size()/tellers.size()>2)
tellers.push_back(Teller(customers))
//Iflineisshortenough,removeateller:
if(tellers.size()>1&&
customers.size()/tellers.size()<2)
for(TellIti=tellers.begin()
i!=tellers.end()i++)
if(!(*i).isBusy()){
tellers.erase(i)
break//Outofforloop
}
}
}///:~
Eachcustomerrequiresacertainamountofservicetime,whichisthenumberoftimeunitsthatateller
mustspendonthecustomertoservethatcustomersneeds.Theamountofservicetimewillbedifferent
foreachcustomerandwillbedeterminedrandomly.Inaddition,youwontknowhowmanycustomerswill
bearrivingineachinterval,sothiswillalsobedeterminedrandomly.
TheCustomerobjectsarekeptinaqueue<Customer>,andeachTellerobjectkeepsareferenceto
thatqueue.WhenaTellerobjectisfinishedwithitscurrentCustomerobject,thatTellerwillgetanother
CustomerfromthequeueandbeginworkingonthenewCustomer,reducingtheCustomersservice
timeduringeachtimeslicethattheTellerisallotted.Allthislogicisintherun()memberfunction,which
isbasicallyathreewayifstatementbasedonwhethertheamountoftimenecessarytoservethe
customerislessthan,greaterthan,orequaltotheamountoftimeleftinthetellerscurrenttimeslice.
NoticethatiftheTellerhasmoretimeafterfinishingwithaCustomer,itgetsanewcustomerand
recursesintoitself.
Justaswithastack,whenyouuseaqueue,itsonlyaqueueanddoesnthaveanyoftheother
functionalityofthebasicsequencecontainers.Thisincludestheabilitytogetaniteratorinordertostep
throughthestack.However,theunderlyingsequencecontainer(thatthequeueisbuiltupon)isheldasa
protectedmemberinsidethequeue,andtheidentifierforthismemberisspecifiedintheC++Standard
asc,whichmeansthatyoucanderivefromqueuetoaccesstheunderlyingimplementation.The
CustomerQclassdoesexactlythat,forthesolepurposeofdefininganostreamoperator<<thatcan
iteratethroughthequeueanddisplayitsmembers.
Thedriverforthesimulationisthewhileloopinmain(),whichusesprocessorticks(definedin
<ctime>)todetermineifthesimulationhasrunforatleast5seconds.Atthebeginningofeachpass
throughtheloop,arandomnumberofcustomersisadded,withrandomservicetimes.Boththenumberof
tellersandthequeuecontentsaredisplayedsoyoucanseethestateofthesystem.Afterrunningeach
teller,thedisplayisrepeated.Atthispoint,thesystemadaptsbycomparingthenumberofcustomersand
thenumberoftellers.Ifthelineistoolong,anothertellerisadded,andifitisshortenough,atellercan
beremoved.Inthisadaptationsectionoftheprogramyoucanexperimentwithpoliciesregardingthe
optimaladditionandremovaloftellers.Ifthisistheonlysectionthatyouremodifying,youmightwantto
encapsulatepoliciesinsidedifferentobjects.
WellrevisitthisexampleinamultithreadedexerciseinChapter11.
Priorityqueues
Whenyoupush()anobjectontoapriority_queue,thatobjectissortedintothequeueaccordingtoa
comparisonfunctionorfunctionobject.(Youcanallowthedefaultlesstemplatetosupplythis,oryoucan
provideoneofyourown.)Thepriority_queueensuresthatwhenyoulookatthetop()element,itwill
betheonewiththehighestpriority.Whenyouredonewithit,youcallpop()toremoveitandbringthe
nextoneintoplace.Thus,thepriority_queuehasnearlythesameinterfaceasastack,butitbehaves
differently.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
278/458
5/3/2015
ThinkinginC++2ndedVolume2
Likestackandqueue,priority_queueisanadaptorthatisbuiltontopofoneofthebasicsequences
thedefaultsequencebeingvector.
Itstrivialtomakeapriority_queuethatworkswithints:
//:C07:PriorityQueue1.cpp
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<queue>
usingnamespacestd
intmain(){
priority_queue<int>pqi
srand(time(0))//Seedtherandomnumbergenerator
for(inti=0i<100i++)
pqi.push(rand()%25)
while(!pqi.empty()){
cout<<pqi.top()<<''
pqi.pop()
}
}///:~
Thispushesintothepriority_queue100randomvaluesfrom0to24.Whenyourunthisprogramyoull
seethatduplicatesareallowed,andthehighestvaluesappearfirst.Toshowhowyoucanchangethe
orderingbyprovidingyourownfunctionorfunctionobject,thefollowingprogramgiveslowervalued
numbersthehighestpriority:
//:C07:PriorityQueue2.cpp
//Changingthepriority.
#include<cstdlib>
#include<ctime>
#include<functional>
#include<iostream>
#include<queue>
usingnamespacestd
intmain(){
priority_queue<int,vector<int>,greater<int>>pqi
srand(time(0))
for(inti=0i<100i++)
pqi.push(rand()%25)
while(!pqi.empty()){
cout<<pqi.top()<<''
pqi.pop()
}
}///:~
Amoreinterestingproblemisatodolist,whereeachobjectcontainsastringandaprimaryand
secondarypriorityvalue:
//:C07:PriorityQueue3.cpp
//Amorecomplexuseofpriority_queue.
#include<iostream>
#include<queue>
#include<string>
usingnamespacestd
classToDoItem{
charprimary
intsecondary
stringitem
public:
ToDoItem(stringtd,charpri='A',intsec=1)
:primary(pri),secondary(sec),item(td){}
friendbooloperator<(
constToDoItem&x,constToDoItem&y){
if(x.primary>y.primary)
returntrue
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
279/458
5/3/2015
ThinkinginC++2ndedVolume2
if(x.primary==y.primary)
if(x.secondary>y.secondary)
returntrue
returnfalse
}
friendostream&
operator<<(ostream&os,constToDoItem&td){
returnos<<td.primary<<td.secondary
<<":"<<td.item
}
}
intmain(){
priority_queue<ToDoItem>toDoList
toDoList.push(ToDoItem("Emptytrash",'C',4))
toDoList.push(ToDoItem("Feeddog",'A',2))
toDoList.push(ToDoItem("Feedbird",'B',7))
toDoList.push(ToDoItem("Mowlawn",'C',3))
toDoList.push(ToDoItem("Waterlawn",'A',1))
toDoList.push(ToDoItem("Feedcat",'B',1))
while(!toDoList.empty()){
cout<<toDoList.top()<<endl
toDoList.pop()
}
}///:~
TheToDoItemsoperator<mustbeanonmemberfunctionforittoworkwithless<>.Otherthanthat,
everythinghappensautomatically.Theoutputis
A1:Waterlawn
A2:Feeddog
B1:Feedcat
B7:Feedbird
C3:Mowlawn
C4:Emptytrash
Youcannotiteratethroughapriority_queue,butitspossibletosimulatethebehaviorofa
priority_queueusingavector,thusallowingyouaccesstothatvector.Youcandothisbylookingatthe
implementationofpriority_queue,whichusesmake_heap(),push_heap(),andpop_heap().(These
arethesoulofthepriority_queueinfactyoucouldsaythattheheapisthepriorityqueueandthat
priority_queueisjustawrapperaroundit.)Thisturnsouttobereasonablystraightforward,butyou
mightthinkthatashortcutispossible.Sincethecontainerusedbypriority_queueisprotected(andhas
theidentifier,accordingtotheStandardC++specification,namedc),youcaninheritanewclassthat
providesaccesstotheunderlyingimplementation:
//:C07:PriorityQueue4.cpp
//Manipulatingtheunderlyingimplementation.
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<iterator>
#include<queue>
usingnamespacestd
classPQI:publicpriority_queue<int>{
public:
vector<int>&impl(){returnc}
}
intmain(){
PQIpqi
srand(time(0))
for(inti=0i<100i++)
pqi.push(rand()%25)
copy(pqi.impl().begin(),pqi.impl().end(),
ostream_iterator<int>(cout,""))
cout<<endl
while(!pqi.empty()){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
280/458
5/3/2015
ThinkinginC++2ndedVolume2
cout<<pqi.top()<<''
pqi.pop()
}
}///:~
However,ifyourunthisprogram,youlldiscoverthatthevectordoesntcontaintheitemsinthe
descendingorderthatyougetwhenyoucallpop(),theorderthatyouwantfromthepriorityqueue.It
wouldseemthatifyouwanttocreateavectorthatisapriorityqueue,youhavetodoitbyhand,like
this:
//:C07:PriorityQueue5.cpp
//Buildingyourownpriorityqueue.
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<iterator>
#include<queue>
usingnamespacestd
template<classT,classCompare>
classPQV:publicvector<T>{
Comparecomp
public:
PQV(Comparecmp=Compare()):comp(cmp){
make_heap(this>begin(),this>end(),comp)
}
constT&top(){returnthis>front()}
voidpush(constT&x){
this>push_back(x)
push_heap(this>begin(),this>end(),comp)
}
voidpop(){
pop_heap(this>begin(),this>end(),comp)
this>pop_back()
}
}
intmain(){
PQV<int,less<int>>pqi
srand(time(0))
for(inti=0i<100i++)
pqi.push(rand()%25)
copy(pqi.begin(),pqi.end(),
ostream_iterator<int>(cout,""))
cout<<endl
while(!pqi.empty()){
cout<<pqi.top()<<''
pqi.pop()
}
}///:~
Butthisprogrambehavesinthesamewayasthepreviousone!Whatyouareseeingintheunderlying
vectoriscalledaheap.Thisheapdatastructurerepresentsthetreeofthepriorityqueue(storedinthe
linearstructureofthevector),butwhenyouiteratethroughit,youdonotgetalinearpriorityqueue
order.Youmightthinkthatyoucansimplycallsort_heap(),butthatonlyworksonce,andthenyoudont
haveaheapanymore,butinsteadasortedlist.Thismeansthattogobacktousingitasaheap,theuser
mustremembertocallmake_heap()first.Thiscanbeencapsulatedintoyourcustompriorityqueue:
//:C07:PriorityQueue6.cpp
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<iterator>
#include<queue>
usingnamespacestd
template<classT,classCompare>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
281/458
5/3/2015
ThinkinginC++2ndedVolume2
classPQV:publicvector<T>{
Comparecomp
boolsorted
voidassureHeap(){
if(sorted){
//Turnitbackintoaheap:
make_heap(this>begin(),this>end(),comp)
sorted=false
}
}
public:
PQV(Comparecmp=Compare()):comp(cmp){
make_heap(this>begin(),this>end(),comp)
sorted=false
}
constT&top(){
assureHeap()
returnthis>front()
}
voidpush(constT&x){
assureHeap()
this>push_back(x)//Putitattheend
//Readjusttheheap:
push_heap(this>begin(),this>end(),comp)
}
voidpop(){
assureHeap()
//Movethetopelementtothelastposition:
pop_heap(this>begin(),this>end(),comp)
this>pop_back()//Removethatelement
}
voidsort(){
if(!sorted){
sort_heap(this>begin(),this>end(),comp)
reverse(this>begin(),this>end())
sorted=true
}
}
}
intmain(){
PQV<int,less<int>>pqi
srand(time(0))
for(inti=0i<100i++){
pqi.push(rand()%25)
copy(pqi.begin(),pqi.end(),
ostream_iterator<int>(cout,""))
cout<<"\n<<endl
}
pqi.sort()
copy(pqi.begin(),pqi.end(),
ostream_iterator<int>(cout,""))
cout<<"\n<<endl
while(!pqi.empty()){
cout<<pqi.top()<<''
pqi.pop()
}
}///:~
Ifsortedistrue,thevectorisnotorganizedasaheapbutinsteadasasortedsequence.The
assureHeap()functionguaranteesthatitsputbackintoheapformbeforeperforminganyheap
operationsonit.Thefirstforloopinmain()nowhastheadditionalqualitythatitdisplaystheheapasit
isbeingbuilt.
Intheprevioustwoprogramswehadtointroduceaseeminglyextraneoususageofthethis>prefix.
Althoughsomecompilersdonotrequireit,thestandarddefinitionofC++does.NotethattheclassPQV
derivesfromvector<T>,thereforebegin()andend(),inheritedfromvector<T>,aredependent
names.[110]Compilerscantlookupnamesfromdependentbaseclassesinthedefinitionofatemplate
(vector,inthiscase)becauseforagiveninstantiationanexplicitlyspecializedversionofthetemplate
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
282/458
5/3/2015
ThinkinginC++2ndedVolume2
mightbeusedthatdoesnothaveagivenmember.Thespecialnamingrequirementguaranteesthatyou
wontendupcallingabaseclassmemberinsomecasesandpossiblyafunctionfromanenclosingscope
(suchasaglobalone)inothercases.Thecompilerhasnowayofknowingthatacalltobegin()is
dependent,sowemustgiveitacluewithathis>qualification.[111]Thistellsthecompilerthatbegin()
isinthescopeofPQV,soitwaitsuntilaninstanceofPQVisfullyinstantiated.Ifthisqualifyingprefixis
leftout,thecompilerwillattemptanearlylookupforthenamesbeginandend(attemplatedefinition
time,andwillfailtofindthembecausetherearenosuchnamesdeclaredinenclosinglexicalscopesinthis
example).Inthecodeabove,however,thecompilerwaitsuntilthepointofinstantiationofpqi,andthen
findsthecorrectspecializationsofbegin()andend()invector<int>.
Theonlydrawbacktothissolutionisthattheusermustremembertocallsort()beforeviewingitasa
sortedsequence(althoughonecouldconceivablyredefineallthememberfunctionsthatproduceiterators
sothattheyguaranteesorting).Anothersolutionistocreateapriorityqueuethatisnotavector,butwill
buildyouavectorwheneveryouwantone:
//:C07:PriorityQueue7.cpp
//Apriorityqueuethatwillhandyouavector.
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<iterator>
#include<queue>
#include<vector>
usingnamespacestd
template<classT,classCompare>classPQV{
vector<T>v
Comparecomp
public:
//Don'tneedtocallmake_heap()it'sempty:
PQV(Comparecmp=Compare()):comp(cmp){}
voidpush(constT&x){
v.push_back(x)//Putitattheend
//Readjusttheheap:
push_heap(v.begin(),v.end(),comp)
}
voidpop(){
//Movethetopelementtothelastposition:
pop_heap(v.begin(),v.end(),comp)
v.pop_back()//Removethatelement
}
constT&top(){returnv.front()}
boolempty()const{returnv.empty()}
intsize()const{returnv.size()}
typedefvector<T>TVec
TVecgetVector(){
TVecr(v.begin(),v.end())
//Itsalreadyaheap
sort_heap(r.begin(),r.end(),comp)
//Putitintopriorityqueueorder:
reverse(r.begin(),r.end())
returnr
}
}
intmain(){
PQV<int,less<int>>pqi
srand(time(0))
for(inti=0i<100i++)
pqi.push(rand()%25)
constvector<int>&v=pqi.getVector()
copy(v.begin(),v.end(),
ostream_iterator<int>(cout,""))
cout<<"\n<<endl
while(!pqi.empty()){
cout<<pqi.top()<<''
pqi.pop()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
283/458
5/3/2015
ThinkinginC++2ndedVolume2
}
}///:~
ThePQVclasstemplatefollowsthesameformastheSTLspriority_queue,buthastheadditional
membergetVector(),whichcreatesanewvectorthatsacopyoftheoneinPQV(whichmeansthatits
alreadyaheap).Itthensortsthatcopy(leavingPQVsvectoruntouched),andreversestheordersothat
traversingthenewvectorproducesthesameeffectaspoppingtheelementsfromthepriorityqueue.
Youmayobservethattheapproachofderivingfrompriority_queueusedinPriorityQueue4.cppcould
beusedwiththeabovetechniquetoproducemoresuccinctcode:
//:C07:PriorityQueue8.cpp
//AmorecompactversionofPriorityQueue7.cpp.
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<iterator>
#include<queue>
usingnamespacestd
template<classT>classPQV:publicpriority_queue<T>{
public:
typedefvector<T>TVec
TVecgetVector(){
TVecr(this>c.begin(),this>c.end())
//cisalreadyaheap
sort_heap(r.begin(),r.end(),this>comp)
//Putitintopriorityqueueorder:
reverse(r.begin(),r.end())
returnr
}
}
intmain(){
PQV<int>pqi
srand(time(0))
for(inti=0i<100i++)
pqi.push(rand()%25)
constvector<int>&v=pqi.getVector()
copy(v.begin(),v.end(),
ostream_iterator<int>(cout,""))
cout<<"\n<<endl
while(!pqi.empty()){
cout<<pqi.top()<<''
pqi.pop()
}
}///:~
Thebrevityofthissolutionmakesitthesimplestandmostdesirable,plusitsguaranteedthattheuserwill
nothaveavectorintheunsortedstate.TheonlypotentialproblemisthatthegetVector()member
functionreturnsthevector<T>byvalue,whichmightcausesomeoverheadissueswithcomplexvalues
oftheparametertypeT.
Holdingbits
BecauseCisalanguagethatpurportstobeclosetothehardware,manyhavefounditdismayingthat
thereisnonativebinaryrepresentationfornumbers.Decimal,ofcourse,andhexadecimal(tolerableonly
becauseitseasiertogroupthebitsinyourmind),butoctal?Ugh.Wheneveryoureadspecsforchips
youretryingtoprogram,theydontdescribethechipregistersinoctalorevenhexadecimaltheyuse
binary.AndyetCwontletyousay0b0101101,whichistheobvioussolutionforalanguageclosetothe
hardware.
AlthoughtheresstillnonativebinaryrepresentationinC++,thingshaveimprovedwiththeadditionoftwo
classes:bitsetandvector<bool>,bothofwhicharedesignedtomanipulateagroupofonoffvalues.
[112]
Theprimarydifferencesbetweenthesetypesare:
Eachbitsetholdsafixednumberofbits.Youestablishthequantityofbitsinthebitsettemplate
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
284/458
5/3/2015
ThinkinginC++2ndedVolume2
argument.Thevector<bool>can,likearegularvector,expanddynamicallytoholdanynumber
ofboolvalues.
Thebitsettemplateisexplicitlydesignedforperformancewhenmanipulatingbits,andisnota
regularSTLcontainer.Assuch,ithasnoiterators.Thenumberofbits,beingatemplate
parameter,isknownatcompiletimeandallowstheunderlyingintegralarraytobestoredonthe
runtimestack.Thevector<bool>container,ontheotherhand,isaspecializationofavectorand
sohasalltheoperationsofanormalvectorthespecializationisjustdesignedtobespace
efficientforbool.
Thereisnotrivialconversionbetweenabitsetandavector<bool>,whichimpliesthatthetwoarefor
verydifferentpurposes.Furthermore,neitherisatraditionalSTLcontainer.Thebitsettemplateclass
hasaninterfaceforbitleveloperationsandinnowayresemblestheSTLcontainerswevediscussedupto
thispoint.Thevector<bool>specializationofvectorissimilartoanSTLlikecontainer,butitdiffersas
discussedbelow.
bitset<n>
Thetemplateforbitsetacceptsanunsignedintegraltemplateargumentthatisthenumberofbitsto
represent.Thus,bitset<10>isadifferenttypethanbitset<20>,andyoucannotperformcomparisons,
assignments,andsoonbetweenthetwo.
Abitsetprovidesthemostcommonlyusedbitwiseoperationsinanefficientform.However,eachbitset
isimplementedbylogicallypackingbitsinanarrayofintegraltypes(typicallyunsignedlongs,which
containatleast32bits).Inaddition,theonlyconversionfromabitsettoanumericalvalueistoan
unsignedlong(viathefunctionto_ulong()).
Thefollowingexampletestsalmostallthefunctionalityofthebitset(themissingoperationsareredundant
ortrivial).Youllseethedescriptionofeachofthebitsetoutputstotherightoftheoutputsothatthebits
alllineupandyoucancomparethemtothesourcevalues.Ifyoustilldontunderstandbitwiseoperations,
runningthisprogramshouldhelp.
//:C07:BitSet.cpp{bor}
//Exercisingthebitsetclass.
#include<bitset>
#include<climits>
#include<cstdlib>
#include<ctime>
#include<cstddef>
#include<iostream>
#include<string>
usingnamespacestd
constintSZ=32
typedefbitset<SZ>BS
template<intbits>bitset<bits>randBitset(){
bitset<bits>r(rand())
for(inti=0i<bits/161i++){
r<<=16
//"OR"togetherwithanewlower16bits:
r|=bitset<bits>(rand())
}
returnr
}
intmain(){
srand(time(0))
cout<<"sizeof(bitset<16>)="
<<sizeof(bitset<16>)<<endl
cout<<"sizeof(bitset<32>)="
<<sizeof(bitset<32>)<<endl
cout<<"sizeof(bitset<48>)="
<<sizeof(bitset<48>)<<endl
cout<<"sizeof(bitset<64>)="
<<sizeof(bitset<64>)<<endl
cout<<"sizeof(bitset<65>)="
<<sizeof(bitset<65>)<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
285/458
5/3/2015
ThinkinginC++2ndedVolume2
BSa(randBitset<SZ>()),b(randBitset<SZ>())
//Convertingfromabitset:
unsignedlongul=a.to_ulong()
cout<<a<<endl
//Convertingastringtoabitset:
stringcbits("111011010110111")
cout<<"asastring="<<cbits<<endl
cout<<BS(cbits)<<"[BS(cbits)]"<<endl
cout<<BS(cbits,2)<<"[BS(cbits,2)]"<<endl
cout<<BS(cbits,2,11)<<"[BS(cbits,2,11)]"<<endl
cout<<a<<"[a]"<<endl
cout<<b<<"[b]"<<endl
//BitwiseAND:
cout<<(a&b)<<"[a&b]"<<endl
cout<<(BS(a)&=b)<<"[a&=b]"<<endl
//BitwiseOR:
cout<<(a|b)<<"[a|b]"<<endl
cout<<(BS(a)|=b)<<"[a|=b]"<<endl
//ExclusiveOR:
cout<<(a^b)<<"[a^b]"<<endl
cout<<(BS(a)^=b)<<"[a^=b]"<<endl
cout<<a<<"[a]"<<endl//Forreference
//Logicalleftshift(fillwithzeros):
cout<<(BS(a)<<=SZ/2)<<"[a<<=(SZ/2)]"<<endl
cout<<(a<<SZ/2)<<endl
cout<<a<<"[a]"<<endl//Forreference
//Logicalrightshift(fillwithzeros):
cout<<(BS(a)>>=SZ/2)<<"[a>>=(SZ/2)]"<<endl
cout<<(a>>SZ/2)<<endl
cout<<a<<"[a]"<<endl//Forreference
cout<<BS(a).set()<<"[a.set()]"<<endl
for(inti=0i<SZi++)
if(!a.test(i)){
cout<<BS(a).set(i)
<<"[a.set("<<i<<")]"<<endl
break//Justdooneexampleofthis
}
cout<<BS(a).reset()<<"[a.reset()]"<<endl
for(intj=0j<SZj++)
if(a.test(j)){
cout<<BS(a).reset(j)
<<"[a.reset("<<j<<")]"<<endl
break//Justdooneexampleofthis
}
cout<<BS(a).flip()<<"[a.flip()]"<<endl
cout<<~a<<"[~a]"<<endl
cout<<a<<"[a]"<<endl//Forreference
cout<<BS(a).flip(1)<<"[a.flip(1)]"<<endl
BSc
cout<<c<<"[c]"<<endl
cout<<"c.count()="<<c.count()<<endl
cout<<"c.any()="
<<(c.any()?"true":"false")<<endl
cout<<"c.none()="
<<(c.none()?"true":"false")<<endl
c[1].flip()c[2].flip()
cout<<c<<"[c]"<<endl
cout<<"c.count()="<<c.count()<<endl
cout<<"c.any()="
<<(c.any()?"true":"false")<<endl
cout<<"c.none()="
<<(c.none()?"true":"false")<<endl
//Arrayindexingoperations:
c.reset()
for(size_tk=0k<c.size()k++)
if(k%2==0)
c[k].flip()
cout<<c<<"[c]"<<endl
c.reset()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
286/458
5/3/2015
ThinkinginC++2ndedVolume2
//Assignmenttobool:
for(size_tii=0ii<c.size()ii++)
c[ii]=(rand()%100)<25
cout<<c<<"[c]"<<endl
//booltest:
if(c[1])
cout<<"c[1]==true"
else
cout<<"c[1]==false"<<endl
}///:~
Togenerateinterestingrandombitsets,therandBitset()functioniscreated.Thisfunctiondemonstrates
operator<<=byshiftingeach16randombitstotheleftuntilthebitset(whichistemplatizedinthis
functionforsize)isfull.Thegeneratednumberandeachnew16bitsarecombinedusingtheoperator|=.
Themain()functionfirstshowstheunitsizeofabitset.Ifitislessthan32bits,sizeofproduces4(4
bytes=32bits),whichisthesizeofasinglelongonmostimplementations.Ifitsbetween32and64,it
requirestwolongs,greaterthan64requires3longs,andsoon.Thus,youmakethebestuseofspaceif
youuseabitquantitythatfitsinanintegralnumberoflongs.However,noticetheresnoextraoverhead
fortheobjectitsasifyouwerehandcodingforalong.
Althoughtherearenoothernumericalconversionsfrombitsetbesidesto_ulong(),thereisastream
inserterthatproducesastringcontainingonesandzeros,andthiscanbeaslongastheactualbitset.
Theresstillnoprimitiveformatforbinaryvalues,butbitsetsupportsthenextbestthing:astringofones
andzeroswiththeleastsignificantbit(lsb)ontheright.Thethreeconstructorsdemonstratedtakethe
entirestring,thestringstartingatcharacter2,andthestringfromcharacter2through11.Youcanwrite
toanostreamfromabitsetusingoperator<<,anditcomesoutasonesandzeros.Youcanalsoread
fromanistreamusingoperator>>(notshownhere).
Youllnoticethatbitsetonlyhasthreenonmemberoperators:and(&),or(|),andexclusiveor(^).Each
ofthesecreatesanewbitsetasitsreturnvalue.Allthememberoperatorsoptforthemoreefficient&=,
|=,andsoon,whereatemporaryisnotcreated.However,theseformschangethebitsetsvalue(which
isainmostofthetestsintheaboveexample).Topreventthis,wecreatedatemporarytobeusedasthe
lvaluebyinvokingthecopyconstructoronathisiswhyyouseetheformBS(a).Theresultofeachtestis
displayed,andoccasionallyaisreprintedsoyoucaneasilylookatitforreference.
Therestoftheexampleshouldbeselfexplanatorywhenyourunitifnotyoucanfindthedetailsinyour
compilersdocumentationorintheotherdocumentationmentionedearlierinthischapter.
vector<bool>
Thevector<bool>containerisaspecializationofthevectortemplate.Anormalboolvariablerequires
atleastonebyte,butsinceaboolonlyhastwostates,theidealimplementationofvector<bool>issuch
thateachboolvalueonlyrequiresonebit.Sincetypicallibraryimplementationspackthebitsintointegral
arrays,theiteratormustbespeciallydefinedandcannotbeapointertobool.
Thebitmanipulationfunctionsforvector<bool>aremuchmorelimitedthanthoseofbitset.Theonly
memberfunctionthatwasaddedtothosealreadyinvectorisflip(),toinvertallthebits.Thereisno
set()orreset()asinbitset.Whenyouuseoperator[],yougetbackanobjectoftype
vector<bool>::reference,whichalsohasaflip()toinvertthatindividualbit.
//:C07:VectorOfBool.cpp
//Demonstratethevector<bool>specialization.
#include<bitset>
#include<cstddef>
#include<iostream>
#include<iterator>
#include<sstream>
#include<vector>
usingnamespacestd
intmain(){
vector<bool>vb(10,true)
vector<bool>::iteratorit
for(it=vb.begin()it!=vb.end()it++)
cout<<*it
cout<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
287/458
5/3/2015
ThinkinginC++2ndedVolume2
vb.push_back(false)
ostream_iterator<bool>out(cout,"")
copy(vb.begin(),vb.end(),out)
cout<<endl
boolab[]={true,false,false,true,true,
true,true,false,false,true}
//There'sasimilarconstructor:
vb.assign(ab,ab+sizeof(ab)/sizeof(bool))
copy(vb.begin(),vb.end(),out)
cout<<endl
vb.flip()//Flipallbits
copy(vb.begin(),vb.end(),out)
cout<<endl
for(size_ti=0i<vb.size()i++)
vb[i]=0//(Equivalentto"false")
vb[4]=true
vb[5]=1
vb[7].flip()//Invertonebit
copy(vb.begin(),vb.end(),out)
cout<<endl
//Converttoabitset:
ostringstreamos
copy(vb.begin(),vb.end(),
ostream_iterator<bool>(os,""))
bitset<10>bs(os.str())
cout<<"Bitset:<<endl<<bs<<endl
}///:~
Thelastpartofthisexampletakesavector<bool>andconvertsittoabitsetbyfirstturningitintoa
stringofonesandzeros.Here,youmustknowthesizeofthebitsetatcompiletime.Youcanseethat
thisconversionisnotthekindofoperationyoullwanttodoonaregularbasis.
Thevector<bool>specializationisacrippledSTLcontainerinthesensethatcertainguaranteesthat
othercontainersprovidearemissing.Forexample,withtheothercontainersthefollowingrelationships
hold:
//LetcbeanSTLcontainerotherthanvector<bool>:
T&r=c.front()
T*p=&*c.begin()
Forallothercontainers,thefront()functionyieldsanlvalue(somethingyoucangetanonconst
referenceto),andbegin()mustyieldsomethingyoucandereferenceandthentaketheaddressof.
Neitherispossiblebecausebitsarenotaddressable.Bothvector<bool>andbitsetuseaproxyclass
(thenestedreferenceclass,mentionedearlier)toreadandsetbitsasnecessary.
Associativecontainers
Theset,map,multiset,andmultimaparecalledassociativecontainersbecausetheyassociatekeys
withvalues.Well,atleastmapsandmultimapsassociatekeyswithvalues,butyoucanlookatasetas
amapthathasnovalues,onlykeys(andtheycaninfactbeimplementedthisway),andthesameforthe
relationshipbetweenmultisetandmultimap.So,becauseofthestructuralsimilarity,setsandmultisets
arelumpedinwithassociativecontainers.
Themostimportantbasicoperationswithassociativecontainersareputtingthingsinand,inthecaseofa
set,seeingifsomethingisintheset.Inthecaseofamap,youwanttofirstseeifakeyisinthemap,
andifitexists,youwanttheassociatedvalueforthatkey.Therearemanyvariationsonthistheme,but
thatsthefundamentalconcept.Thefollowingexampleshowsthesebasics:
//:C07:AssociativeBasics.cpp{bor}
//Basicoperationswithsetsandmaps.
//{L}Noisy
#include<cstddef>
#include<iostream>
#include<iterator>
#include<map>
#include<set>
#include"Noisy.h"
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
288/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
Noisyna[7]
//Addelementsviaconstructor:
set<Noisy>ns(na,na+sizeofna/sizeof(Noisy))
Noisyn
ns.insert(n)//Ordinaryinsertion
cout<<endl
//Checkforsetmembership:
cout<<"ns.count(n)="<<ns.count(n)<<endl
if(ns.find(n)!=ns.end())
cout<<"n("<<n<<")foundinns"<<endl
//Printelements:
copy(ns.begin(),ns.end(),
ostream_iterator<Noisy>(cout,""))
cout<<endl
cout<<"\n<<endl
map<int,Noisy>nm
for(inti=0i<10i++)
nm[i]//Automaticallymakespairs
cout<<"\n<<endl
for(size_tj=0j<nm.size()j++)
cout<<"nm["<<j<<"]="<<nm[j]<<endl
cout<<"\n<<endl
nm[10]=n
cout<<"\n<<endl
nm.insert(make_pair(47,n))
cout<<"\n<<endl
cout<<"\nnm.count(10)="<<nm.count(10)<<endl
cout<<"nm.count(11)="<<nm.count(11)<<endl
map<int,Noisy>::iteratorit=nm.find(6)
if(it!=nm.end())
cout<<"value:"<<(*it).second
<<"foundinnmatlocation6"<<endl
for(it=nm.begin()it!=nm.end()it++)
cout<<(*it).first<<":"<<(*it).second<<","
cout<<"\n<<endl
}///:~
Theset<Noisy>objectnsiscreatedusingtwoiteratorsintoanarrayofNoisyobjects,butthereisalso
adefaultconstructorandacopyconstructor,andyoucanpassinanobjectthatprovidesanalternate
schemefordoingcomparisons.Bothsetsandmapshaveaninsert()memberfunctiontoputthingsin,
andyoucanchecktoseeifanobjectisalreadyinanassociativecontainerintwoways.Thecount()
memberfunction,whengivenakey,willtellyouhowmanytimesthatkeyoccurs.(Thiscanonlybezero
oroneinasetormap,butitcanbemorethanonewithamultisetormultimap.)Thefind()member
functionwillproduceaniteratorindicatingthefirstoccurrence(withsetandmap,theonlyoccurrence)of
thekeythatyougiveitorwillproducethepasttheenditeratorifitcantfindthekey.Thecount()and
find()memberfunctionsexistforalltheassociativecontainers,whichmakessense.Theassociative
containersalsohavememberfunctionslower_bound(),upper_bound(),andequal_range(),which
onlymakesenseformultisetandmultimap,asyouwillsee.(Butdonttrytofigureouthowtheywould
beusefulforsetandmap,sincetheyaredesignedfordealingwitharangeofduplicatekeys,whichthose
containersdontallow.)
Designinganoperator[]alwayspresentsabitofadilemma.Becauseitsintendedtobetreatedasan
arrayindexingoperation,peopledonttendtothinkaboutperformingatestbeforetheyuseit.Butwhat
happensifyoudecidetoindexoutoftheboundsofthearray?Oneoptionistothrowanexception,butwith
amap,indexingoutofthearraycouldmeanthatyouwanttocreateanewentryatthatlocation,and
thatsthewaytheSTLmaptreatsit.Thefirstforloopafterthecreationofthemap<int,Noisy>nm
justlooksupobjectsusingtheoperator[],butthisisactuallycreatingnewNoisyobjects!Themap
createsanewkeyvaluepair(usingthedefaultconstructorforthevalue)ifyoulookupavaluewith
operator[]anditisntthere.Thismeansthatifyoureallyjustwanttolooksomethingupandnotcreate
anewentry,youmustusethememberfunctionscount()(toseeifitsthere)orfind()(togetan
iteratortoit).
Anumberofproblemsareassociatedwiththeforloopthatprintsthevaluesofthecontainerusing
operator[].First,itrequiresintegralkeys(whichwehappentohavehere).Nextandworse,ifallthe
keysarenotsequential,youllendupcountingfromzerotothesizeofthecontainer,andifsomespots
donthavekeyvaluepairs,youllautomaticallycreatethemandmisssomeofthehighervaluesofthe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
289/458
5/3/2015
ThinkinginC++2ndedVolume2
keys.Finally,ifyoulookattheoutputfromtheforloop,youllseethatthingsareverybusy,anditsquite
puzzlingatfirstwhytherearesomanyconstructionsanddestructionsforwhatappearstobeasimple
lookup.Theansweronlybecomesclearwhenyoulookatthecodeinthemaptemplateforoperator[],
whichwillbesomethinglikethis:
mapped_type&operator[](constkey_type&k){
value_typetmp(k,T())
return(*((insert(tmp)).first)).second
}
Themap::insert()functiontakesakeyvaluepairanddoesnothingifthereisalreadyanentryinthe
mapwiththegivenkeyotherwiseitinsertsanentryforthekey.Ineithercase,itreturnsanewkey
valuepairholdinganiteratortotheinsertedpairasitsfirstelementandholdingtrueasthesecond
elementifaninsertiontookplace.Themembersfirstandsecondgivethekeyandvalue,respectively,
becausemap::value_typeisreallyjustatypedefforastd::pair:
typedefpair<constKey,T>value_type
Youveseenthestd::pairtemplatebefore.Itsasimpleholderfortwovaluesofindependenttypes,asyou
canseebyitsdefinition:
template<classT1,classT2>structpair{
typedefT1first_type
typedefT2second_type
T1first
T2second
pair()
pair(constT1&x,constT2&y):first(x),second(y){}
//Templatizedcopyconstructor:
template<classU,classV>pair(constpair<U,V>&p)
}
Thepairtemplateclassisveryuseful,especiallywhenyouwanttoreturntwoobjectsfromafunction
(sinceareturnstatementonlytakesoneobject).Theresevenashorthandforcreatingapaircalled
make_pair(),whichisusedinAssociativeBasics.cpp.
Sotoretracethesteps,map::value_typeisapairofthekeyandthevalueofthemapactually,itsa
singleentryforthemap.Butnoticethatpairpackagesitsobjectsbyvalue,whichmeansthatcopy
constructionsarenecessarytogettheobjectsintothepair.Thus,thecreationoftmpin
map::operator[]willinvolveatleastacopyconstructorcallanddestructorcallforeachobjectinthe
pair.Here,weregettingoffeasybecausethekeyisanint.Butifyouwanttoreallyseewhatkindof
activitycanresultfrommap::operator[],tryrunningthis:
//:C07:NoisyMap.cpp
//MappingNoisytoNoisy.
//{L}Noisy
#include<map>
#include"Noisy.h"
usingnamespacestd
intmain(){
map<Noisy,Noisy>mnn
Noisyn1,n2
cout<<"\n<<endl
mnn[n1]=n2
cout<<"\n<<endl
cout<<mnn[n1]<<endl
cout<<"\n<<endl
}///:~
Youllseethatboththeinsertionandlookupgeneratealotofextraobjects,andthatsbecauseofthe
creationofthetmpobject.Ifyoulookbackupatmap::operator[],youllseethatthesecondlinecalls
insert(),passingittmpthatis,operator[]doesaninsertioneverytime.Thereturnvalueofinsert()
isadifferentkindofpair,wherefirstisaniteratorpointingtothekeyvaluepairthatwasjustinserted,
andsecondisaboolindicatingwhethertheinsertiontookplace.Youcanseethatoperator[]grabsfirst
(theiterator),dereferencesittoproducethepair,andthenreturnsthesecond,whichisthevalueatthat
location.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
290/458
5/3/2015
ThinkinginC++2ndedVolume2
Soontheupside,maphasthisfancymakeanewentryifoneisnttherebehavior,butthedownsideis
thatyoualwaysgetalotofextraobjectcreationsanddestructionswhenyouusemap::operator[].
Fortunately,AssociativeBasics.cppalsodemonstrateshowtoreducetheoverheadofinsertionsand
deletions,byavoidingoperator[]ifyoudontneedit.Theinsert()memberfunctionisslightlymore
efficientthanoperator[].Withaset,youholdonlyoneobject,butwithamap,youholdkeyvalue
pairssoinsert()requiresapairasitsargument.Hereswheremake_pair()comesinhandy,asyou
cansee.
Forlookingobjectsupinamap,youcanusecount()toseewhetherakeyisinthemap,oryoucanuse
find()toproduceaniteratorpointingdirectlyatthekeyvaluepair.Again,sincethemapcontainspairs,
thatswhattheiteratorproduceswhenyoudereferenceit,soyouhavetoselectfirstandsecond.When
yourunAssociativeBasics.cpp,youllnoticethattheiteratorapproachinvolvesnoextraobjectcreations
ordestructions.Itsnotaseasytowriteorread,though.
Generatorsandfillers
forassociativecontainers
Youveseenhowusefulthefill(),fill_n(),generate(),andgenerate_n()functiontemplatesin
<algorithm>havebeenforfillingthesequentialcontainers(vector,list,anddeque)withdata.
However,theseareimplementedbyusingoperator=toassignvaluesintothesequentialcontainers,and
thewaythatyouaddobjectstoassociativecontainersiswiththeirrespectiveinsert()memberfunctions.
Thus,thedefaultassignmentbehaviorcausesaproblemwhentryingtousethefillandgenerate
functionswithassociativecontainers.
Onesolutionistoduplicatethefillandgeneratefunctions,creatingnewonesthatcanbeusedwith
associativecontainers.Itturnsoutthatonlythefill_n()andgenerate_n()functionscanbeduplicated
(fill()andgenerate()copysequences,whichdoesntmakesensewithassociativecontainers),butthe
jobisfairlyeasy,sinceyouhavethe<algorithm>headerfiletoworkfrom:
//:C07:assocGen.h
//Thefill_n()andgenerate_n()equivalents
//forassociativecontainers.
#ifndefASSOCGEN_H
#defineASSOCGEN_H
template<classAssoc,classCount,classT>
voidassocFill_n(Assoc&a,Countn,constT&val){
while(n>0)
a.insert(val)
}
template<classAssoc,classCount,classGen>
voidassocGen_n(Assoc&a,Countn,Geng){
while(n>0)
a.insert(g())
}
#endif//ASSOCGEN_H///:~
Youcanseethatinsteadofusingiterators,thecontainerclassitselfispassed(byreference,ofcourse).
Thiscodedemonstratestwovaluablelessons.Thefirstisthatifthealgorithmsdontdowhatyouwant,
copythenearestthingandmodifyit.YouhavetheexampleathandintheSTLheader,somostofthework
hasalreadybeendone.
Thesecondlessonismorepointed:ifyoulooklongenough,theresprobablyawaytodoitintheSTL
withoutinventinganythingnew.Thepresentproblemcaninsteadbesolvedbyusinganinsert_iterator
(producedbyacalltoinserter()),whichcallsinsert()toplaceitemsinthecontainerinsteadof
operator=.Thisisnotsimplyavariationoffront_insert_iteratororback_insert_iteratorbecause
thoseiteratorsusepush_front()andpush_back(),respectively.Eachoftheinsertiteratorsisdifferent
byvirtueofthememberfunctionitusesforinsertion,andinsert()istheoneweneed.Heresa
demonstrationthatshowsfillingandgeneratingbothamapandaset.(Itcanalsobeusedwithmultimap
andmultiset.)First,sometemplatizedgeneratorsarecreated.(Thismayseemlikeoverkill,butyou
neverknowwhenyoullneedthem.Forthatreasontheyreplacedinaheaderfile.)
//:C07:SimpleGenerators.h
//Genericgenerators,includingonethatcreatespairs.
#include<iostream>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
291/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<utility>
//Ageneratorthatincrementsitsvalue:
template<typenameT>classIncrGen{
Ti
public:
IncrGen(Tii):i(ii){}
Toperator()(){returni++}
}
//AgeneratorthatproducesanSTLpair<>:
template<typenameT1,typenameT2>classPairGen{
T1i
T2j
public:
PairGen(T1ii,T2jj):i(ii),j(jj){}
std::pair<T1,T2>operator()(){
returnstd::pair<T1,T2>(i++,j++)
}
}
namespacestd{
//Agenericglobaloperator<<forprintinganySTLpair<>:
template<typenameF,typenameS>ostream&
operator<<(ostream&os,constpair<F,S>&p){
returnos<<p.first<<"\t"<<p.second<<endl
}
}///:~
BothgeneratorsexpectthatTcanbeincremented,andtheysimplyuseoperator++togeneratenew
valuesfromwhateveryouusedforinitialization.PairGencreatesanSTLpairobjectasitsreturnvalue,
andthatswhatcanbeplacedintoamapormultimapusinginsert().
Thelastfunctionisageneralizationofoperator<<forostreams,sothatanypaircanbeprinted,
assumingeachelementofthepairsupportsastreamoperator<<.(Itisinnamespacestdforthe
strangenamelookupreasonsdiscussedinChapter5,andexplainedonceagainafterThesaurus.cpplater
oninthischapter.)Asyoucanseeinthefollowing,thisallowstheuseofcopy()tooutputthemap:
//:C07:AssocInserter.cpp
//Usinganinsert_iteratorsofill_n()andgenerate_n()
//canbeusedwithassociativecontainers.
#include<iterator>
#include<iostream>
#include<algorithm>
#include<set>
#include<map>
#include"SimpleGenerators.h"
usingnamespacestd
intmain(){
set<int>s
fill_n(inserter(s,s.begin()),10,47)
generate_n(inserter(s,s.begin()),10,
IncrGen<int>(12))
copy(s.begin(),s.end(),
ostream_iterator<int>(cout,"\n"))
map<int,int>m
fill_n(inserter(m,m.begin()),10,make_pair(90,120))
generate_n(inserter(m,m.begin()),10,
PairGen<int,int>(3,9))
copy(m.begin(),m.end(),
ostream_iterator<pair<int,int>>(cout,"\n"))
}///:~
Thesecondargumenttoinserterisaniterator,whichisanoptimizationhinttohelptheinsertiongofaster
(insteadofalwaysstartingthesearchattherootoftheunderlyingtree).Sinceaninsert_iteratorcanbe
usedwithmanydifferenttypesofcontainers,withnonassociativecontainersitismorethanahintitis
required.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
292/458
5/3/2015
ThinkinginC++2ndedVolume2
Notehowtheostream_iteratoriscreatedtooutputapair.Thiswontworkiftheoperator<<isnt
created.Sinceitsatemplate,itisautomaticallyinstantiatedforpair<int,int>.
Themagicofmaps
Anordinaryarrayusesanintegralvaluetoindexintoasequentialsetofelementsofsometype.Amapis
anassociativearray,whichmeansyouassociateoneobjectwithanotherinanarraylikefashion.Instead
ofselectinganarrayelementwithanumberasyoudowithanordinaryarray,youlookitupwithan
object!Theexamplethatfollowscountsthewordsinatextfile,sotheindexisthestringobject
representingtheword,andthevaluebeinglookedupistheobjectthatkeepscountofthestrings.
Inasingleitemcontainersuchasavectororalist,onlyonethingisbeingheld.Butinamap,youve
gottwothings:thekey(whatyoulookupby,asinmapname[key])andthevaluethatresultsfromthe
lookupwiththekey.Ifyousimplywanttomovethroughtheentiremapandlisteachkeyvaluepair,you
useaniterator,whichwhendereferencedproducesapairobjectcontainingboththekeyandthevalue.
Youaccessthemembersofapairbyselectingfirstorsecond.
Thissamephilosophyofpackagingtwoitemstogetherisalsousedtoinsertelementsintothemap,butthe
pairiscreatedaspartoftheinstantiatedmapandiscalledvalue_type,containingthekeyandthe
value.Sooneoptionforinsertinganewelementistocreateavalue_typeobject,loadingitwiththe
appropriateobjectsandthencallingtheinsert()memberfunctionforthemap.Instead,thefollowing
exampleusestheaforementionedspecialfeatureofmap:ifyouretryingtofindanobjectbypassingina
keytooperator[]andthatobjectdoesntexist,operator[]willautomaticallyinsertanewkeyvalue
pairforyou,usingthedefaultconstructorforthevalueobject.Withthatinmind,consideran
implementationofawordcountingprogram:
//:C07:WordCount.cpp
//Countoccurrencesofwordsusingamap.
#include<iostream>
#include<fstream>
#include<map>
#include<string>
#include"../require.h"
usingnamespacestd
intmain(intargc,char*argv[]){
typedefmap<string,int>WordMap
typedefWordMap::iteratorWMIter
constchar*fname="WordCount.cpp"
if(argc>1)fname=argv[1]
ifstreamin(fname)
assure(in,fname)
WordMapwordmap
stringword
while(in>>word)
wordmap[word]++
for(WMIterw=wordmap.begin()w!=wordmap.end()w++)
cout<<w>first<<":"<<w>second<<endl
}///:~
Thisexampleshowsthepowerofzeroinitialization.Considerthislineofcodefromtheprogram:
wordmap[word]++
Thisexpressionincrementstheintassociatedwithword.Ifthereisntsuchawordinthemap,akey
valuepairforthewordisautomaticallyinserted,withthevalueinitializedtozerobyacalltothepseudo
constructorint(),whichreturnsa0.
Printingtheentirelistrequirestraversingitwithaniterator.(Theresnocopy()shortcutforamapunless
youwanttowriteanoperator<<forthepairinthemap.)Aspreviouslymentioned,dereferencingthis
iteratorproducesapairobject,withthefirstmemberthekeyandthesecondmemberthevalue.
Ifyouwanttofindthecountforaparticularword,youcanusethearrayindexoperator,likethis:
cout<<"the:"<<wordmap["the"]<<endl
Youcanseethatoneofthegreatadvantagesofthemapistheclarityofthesyntaxanassociativearray
makesintuitivesensetothereader.(Note,however,thatiftheisntalreadyinthewordmap,anew
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
293/458
5/3/2015
ThinkinginC++2ndedVolume2
entrywillbecreated!)
Multimapsandduplicatekeys
Amultimapisamapthatcancontainduplicatekeys.Atfirstthismayseemlikeastrangeidea,butitcan
occursurprisinglyoften.Aphonebook,forexample,canhavemanyentrieswiththesamename.
Supposeyouaremonitoringwildlife,andyouwanttokeeptrackofwhereandwheneachtypeofanimalis
spotted.Thus,youmayseemanyanimalsofthesamekind,allindifferentlocationsandatdifferent
times.Soifthetypeofanimalisthekey,youllneedamultimap.Hereswhatitlookslike:
//:C07:WildLifeMonitor.cpp
#include<algorithm>
#include<cstdlib>
#include<cstddef>
#include<ctime>
#include<iostream>
#include<iterator>
#include<map>
#include<sstream>
#include<string>
#include<vector>
usingnamespacestd
classDataPoint{
intx,y//Locationcoordinates
time_ttime//TimeofSighting
public:
DataPoint():x(0),y(0),time(0){}
DataPoint(intxx,intyy,time_ttm):
x(xx),y(yy),time(tm){}
//Synthesizedoperator=,copyconstructorOK
intgetX()const{returnx}
intgetY()const{returny}
consttime_t*getTime()const{return&time}
}
stringanimal[]={
"chipmunk","beaver","marmot","weasel",
"squirrel","ptarmigan","bear","eagle",
"hawk","vole","deer","otter","hummingbird",
}
constintASZ=sizeofanimal/sizeof*animal
vector<string>animals(animal,animal+ASZ)
//Alltheinformationiscontainedina
//"Sighting,"whichcanbesenttoanostream:
typedefpair<string,DataPoint>Sighting
ostream&
operator<<(ostream&os,constSighting&s){
returnos<<s.first<<"sightedatx="
<<s.second.getX()<<",y="<<s.second.getY()
<<",time="<<ctime(s.second.getTime())
}
//AgeneratorforSightings:
classSightingGen{
vector<string>&animals
enum{D=100}
public:
SightingGen(vector<string>&an):animals(an){}
Sightingoperator()(){
Sightingresult
intselect=rand()%animals.size()
result.first=animals[select]
result.second=DataPoint(
rand()%D,rand()%D,time(0))
returnresult
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
294/458
5/3/2015
ThinkinginC++2ndedVolume2
}
}
//Displayamenuofanimals,allowtheuserto
//selectone,returntheindexvalue:
intmenu(){
cout<<"selectananimalor'q'toquit:"
for(size_ti=0i<animals.size()i++)
cout<<'['<<i<<']'<<animals[i]<<''
cout<<endl
stringreply
cin>>reply
if(reply.at(0)=='q')return0
istringstreamr(reply)
inti
r>>i//Convertstoint
i%=animals.size()
returni
}
intmain(){
typedefmultimap<string,DataPoint>DataMap
typedefDataMap::iteratorDMIter
DataMapsightings
srand(time(0))//Randomize
generate_n(inserter(sightings,sightings.begin()),
50,SightingGen(animals))
//Printeverything:
copy(sightings.begin(),sightings.end(),
ostream_iterator<Sighting>(cout,""))
//Printsightingsforselectedanimal:
for(intcount=1count<10count++){
//Usemenutogetselection:
//inti=menu()
//Generaterandomly(forautomatedtesting):
inti=rand()%animals.size()
//Iteratorsin"range"denotebegin,one
//pastendofmatchingrange:
pair<DMIter,DMIter>range=
sightings.equal_range(animals[i])
copy(range.first,range.second,
ostream_iterator<Sighting>(cout,""))
}
}///:~
AllthedataaboutasightingisencapsulatedintotheclassDataPoint,whichissimpleenoughthatitcan
relyonthesynthesizedassignmentandcopyconstructor.ItusestheStandardClibrarytimefunctionsto
recordthetimeofthesighting.
Inthearrayofstring,animal,noticethatthechar*constructorisautomaticallyusedduring
initialization,whichmakesinitializinganarrayofstringquiteconvenient.Sinceitseasiertousethe
animalnamesinavector,thelengthofthearrayiscalculated,andavector<string>isinitializedusing
thevector(iterator,iterator)constructor.
ThekeyvaluepairsthatmakeupaSightingarethestring,whichnamesthetypeofanimal,andthe
DataPoint,whichsayswhereandwhenitwassighted.Thestandardpairtemplatecombinesthesetwo
typesandistypedefedtoproducetheSightingtype.Thenanostreamoperator<<iscreatedfor
SightingthiswillallowyoutoiteratethroughamapormultimapofSightingsanddisplayit.
SightingGengeneratesrandomsightingsatrandomdatapointstousefortesting.Ithastheusual
operator()necessaryforafunctionobject,butitalsohasaconstructortocaptureandstoreareference
toavector<string>,whichiswheretheaforementionedanimalnamesarestored.
ADataMapisamultimapofstringDataPointpairs,whichmeansitstoresSightings.Itisfilledwith
50Sightingsusinggenerate_n()anddisplayed.(Noticethatbecausethereisanoperator<<thattakes
aSighting,anostream_iteratorcanbecreated.)Atthispointtheuserisaskedtoselecttheanimalfor
whichtheywanttoseeallthesightings.Ifyoupressq,theprogramwillquit,butifyouselectananimal
number,theequal_range()memberfunctionisinvoked.Thisreturnsaniterator(DMIter)tothe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
295/458
5/3/2015
ThinkinginC++2ndedVolume2
beginningofthesetofmatchingpairsandaniteratorindicatingpasttheendoftheset.Sinceonlyone
objectcanbereturnedfromafunction,equal_range()makesuseofpair.Sincetherangepairhasthe
beginningandendingiteratorsofthematchingset,thoseiteratorscanbeusedincopy()toprintallthe
sightingsforaparticulartypeofanimal.
Multisets
Youveseentheset,whichallowsonlyoneobjectofeachvaluetobeinserted.Themultisetisoddby
comparisonsinceitallowsmorethanoneobjectofeachvaluetobeinserted.Thisseemstogoagainstthe
wholeideaofsetness,whereyoucanask,Isitinthisset?Iftherecanbemorethanoneit,what
doesthatquestionmean?
Withsomethought,youcanseethatitmakeslittlesensetohavemorethanoneobjectofthesamevalue
inasetifthoseduplicateobjectsareexactlythesame(withthepossibleexceptionofcountingoccurrences
ofobjects,butasseenearlierinthischapterthatcanbehandledinanalternative,moreelegantfashion).
Thus,eachduplicateobjectwillhavesomethingthatmakesitdifferentfromtheotherduplicatesmost
likelydifferentstateinformationthatisnotusedinthecalculationofthekeyduringthecomparison.That
is,tothecomparisonoperation,theobjectslookthesame,buttheycontainsomedifferinginternalstate.
LikeanySTLcontainerthatmustorderitselements,themultisettemplateusesthelessfunctionobject
bydefaulttodetermineelementordering.Thisusesthecontainedclasssoperator<,butyoucanalways
substituteyourowncomparisonfunction.
Considerasimpleclassthatcontainsoneelementthatisusedinthecomparisonandanotherthatisnot:
//:C07:MultiSet1.cpp
//Demonstrationofmultisetbehavior.
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<iterator>
#include<set>
usingnamespacestd
classX{
charc//Usedincomparison
inti//Notusedincomparison
//Don'tneeddefaultconstructorandoperator=
X()
X&operator=(constX&)
//Usuallyneedacopyconstructor(butthe
//synthesizedversionworkshere)
public:
X(charcc,intii):c(cc),i(ii){}
//Noticenooperator==isrequired
friendbooloperator<(constX&x,constX&y){
returnx.c<y.c
}
friendostream&operator<<(ostream&os,Xx){
returnos<<x.c<<":"<<x.i
}
}
classXgen{
staticinti
//Numberofcharacterstoselectfrom:
enum{SPAN=6}
public:
Xoperator()(){
charc='A'+rand()%SPAN
returnX(c,i++)
}
}
intXgen::i=0
typedefmultiset<X>Xmset
typedefXmset::const_iteratorXmit
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
296/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
Xmsetmset
//FillitwithX's:
srand(time(0))//Randomize
generate_n(inserter(mset,mset.begin()),25,Xgen())
//Initializearegularsetfrommset:
set<X>unique(mset.begin(),mset.end())
copy(unique.begin(),unique.end(),
ostream_iterator<X>(cout,""))
cout<<"\n<<endl
//Iterateovertheuniquevalues:
for(set<X>::iteratori=unique.begin()
i!=unique.end()i++){
pair<Xmit,Xmit>p=mset.equal_range(*i)
copy(p.first,p.second,ostream_iterator<X>(cout,""))
cout<<endl
}
}///:~
InX,allthecomparisonsaremadewiththecharc.Thecomparisonisperformedwithoperator<,which
isallthatisnecessaryforthemultiset,sinceinthisexamplethedefaultlesscomparisonobjectisused.
TheclassXgenrandomlygeneratesXobjects,butthecomparisonvalueisrestrictedtothespanfromA
toE.Inmain(),amultiset<X>iscreatedandfilledwith25XobjectsusingXgen,guaranteeingthat
therewillbeduplicatekeys.Sothatweknowwhattheuniquevaluesare,aregularset<X>iscreated
fromthemultiset(usingtheiterator,iteratorconstructor).Thesevaluesaredisplayed,andtheneach
oneproducestheequal_range()inthemultiset(equal_range()hasthesamemeaninghereasitdoes
withmultimap:alltheelementswithmatchingkeys).Eachsetofmatchingkeysisthenprinted.
Asasecondexample,a(possibly)moreelegantversionofWordCount.cppcanbecreatedusing
multiset:
//:C07:MultiSetWordCount.cpp
//Countoccurrencesofwordsusingamultiset.
#include<fstream>
#include<iostream>
#include<iterator>
#include<set>
#include<string>
#include"../require.h"
usingnamespacestd
intmain(intargc,char*argv[]){
constchar*fname="MultiSetWordCount.cpp"
if(argc>1)fname=argv[1]
ifstreamin(fname)
assure(in,fname)
multiset<string>wordmset
stringword
while(in>>word)
wordmset.insert(word)
typedefmultiset<string>::iteratorMSit
MSitit=wordmset.begin()
while(it!=wordmset.end()){
pair<MSit,MSit>p=wordmset.equal_range(*it)
intcount=distance(p.first,p.second)
cout<<*it<<":"<<count<<endl
it=p.second//Movetothenextword
}
}///:~
Thesetupinmain()isidenticaltoWordCount.cpp,buttheneachwordissimplyinsertedintothe
multiset<string>.Aniteratoriscreatedandinitializedtothebeginningofthemultisetdereferencing
thisiteratorproducesthecurrentword.Theequal_range()memberfunction(notgenericalgorithm)
producesthestartingandendingiteratorsofthewordthatscurrentlyselected,andthealgorithm
distance()(definedin<iterator>)countsthenumberofelementsinthatrange.Theiteratoritisthen
movedforwardtotheendoftherange,whichputsitatthenextword.Ifyoureunfamiliarwiththe
multiset,thiscodecanseemmorecomplex.Thedensityofitandthelackofneedforsupportingclasses
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
297/458
5/3/2015
ThinkinginC++2ndedVolume2
suchasCounthasalotofappeal.
Intheend,isthisreallyaset,orshoulditbecalledsomethingelse?Analternativeisthegenericbag
thatisdefinedinsomecontainerlibraries,sinceabagholdsanything,withoutdiscriminationincluding
duplicateobjects.Thisisclose,butitdoesntquitefitsinceabaghasnospecificationabouthowelements
shouldbeordered.Amultiset(whichrequiresthatallduplicateelementsbeadjacenttoeachother)is
evenmorerestrictivethantheconceptofaset.Asetimplementationmightuseahashingfunctiontoorder
itselements,whichwouldnotputtheminsortedorder.Besides,ifyouwanttostoreabunchofobjects
withoutanyspecialcriteria,youwillprobablyjustuseavector,deque,orlist.
CombiningSTLcontainers
Whenusingathesaurus,youwanttoknowallthewordsthataresimilartoaparticularword.Whenyou
lookupaword,then,youwantalistofwordsastheresult.Here,themulticontainers(multimapor
multiset)arenotappropriate.Thesolutionistocombinecontainers,whichiseasilydoneusingtheSTL.
Here,weneedatoolthatturnsouttobeapowerfulgeneralconcept,whichisamapthatassociatesa
stringwithavector:
//:C07:Thesaurus.cpp
//Amapofvectors.
#include<map>
#include<vector>
#include<string>
#include<iostream>
#include<iterator>
#include<algorithm>
#include<ctime>
#include<cstdlib>
usingnamespacestd
typedefmap<string,vector<string>>Thesaurus
typedefpair<string,vector<string>>TEntry
typedefThesaurus::iteratorTIter
//Namelookupworkaround:
namespacestd{
ostream&operator<<(ostream&os,constTEntry&t){
os<<t.first<<":"
copy(t.second.begin(),t.second.end(),
ostream_iterator<string>(os,""))
returnos
}
}
//Ageneratorforthesaurustestentries:
classThesaurusGen{
staticconststringletters
staticintcount
public:
intmaxSize(){returnletters.size()}
TEntryoperator()(){
TEntryresult
if(count>=maxSize())count=0
result.first=letters[count++]
intentries=(rand()%5)+2
for(inti=0i<entriesi++){
intchoice=rand()%maxSize()
charcbuf[2]={0}
cbuf[0]=letters[choice]
result.second.push_back(cbuf)
}
returnresult
}
}
intThesaurusGen::count=0
conststringThesaurusGen::letters("ABCDEFGHIJKL"
"MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
298/458
5/3/2015
ThinkinginC++2ndedVolume2
//Askfora"word"tolookup:
stringmenu(Thesaurus&thesaurus){
while(true){
cout<<"Selecta\"word\",0toquit:"
for(TIterit=thesaurus.begin()
it!=thesaurus.end()it++)
cout<<(*it).first<<''
cout<<endl
stringreply
cin>>reply
if(reply.at(0)=='0')exit(0)//Quit
if(thesaurus.find(reply)==thesaurus.end())
continue//Notinlist,tryagain
returnreply
}
}
intmain(){
srand(time(0))//Seedtherandomnumbergenerator
Thesaurusthesaurus
//Fillwith10entries:
generate_n(inserter(thesaurus,thesaurus.begin()),
10,ThesaurusGen())
//Printeverything:
copy(thesaurus.begin(),thesaurus.end(),
ostream_iterator<TEntry>(cout,"\n"))
//Createalistofthekeys:
stringkeys[10]
inti=0
for(TIterit=thesaurus.begin()
it!=thesaurus.end()it++)
keys[i++]=(*it).first
for(intcount=0count<10count++){
//Enterfromtheconsole:
//stringreply=menu(thesaurus)
//Generaterandomly
stringreply=keys[rand()%10]
vector<string>&v=thesaurus[reply]
copy(v.begin(),v.end(),
ostream_iterator<string>(cout,""))
cout<<endl
}
}///:~
AThesaurusmapsastring(theword)toavector<string>(thesynonyms).ATEntryisasingleentry
inaThesaurus.Bycreatinganostreamoperator<<foraTEntry,asingleentryfromtheThesaurus
caneasilybeprinted(andthewholeThesauruscaneasilybeprintedwithcopy()).Noticethevery
strangeplacementofthestreaminserter:weputitinsidethestdnamespace![113]Thisoperator<<()
functionisusedbyostream_iteratorinthefirstcalltocopy()inmain()above.Whenthecompiler
instantiatestheneededostream_iteratorspecialization,accordingtotherulesofargumentdependent
lookup(ADL)itonlylooksinstdbecausethatiswherealltheargumentstocopy()aredeclared.Ifwe
declaredourinserterintheglobalnamespace(byremovingthenamespaceblockaroundit),thenitwould
notbefound.ByplacingitinstdweenableADLtofindit.
TheThesaurusGencreateswords(whicharejustsingleletters)andsynonymsforthosewords(which
arejustotherrandomlychosensingleletters)tobeusedasthesaurusentries.Itrandomlychoosesthe
numberofsynonymentriestomake,buttheremustbeatleasttwo.Allthelettersarechosenbyindexing
intoastaticstringthatispartofThesaurusGen.
Inmain(),aThesaurusiscreated,filledwith10entriesandprintedusingthecopy()algorithm.The
menu()functionaskstheusertochooseawordtolookupbytypingtheletterofthatword.Thefind()
memberfunctiondiscoverswhethertheentryexistsinthemap.(Remember,youdontwanttouse
operator[],whichwillautomaticallymakeanewentryifitdoesntfindamatch!)Ifso,operator[]
fetchesoutthevector<string>thatisdisplayed.Theselectionofthereplystringisgeneratedrandomly,
toallowautomatedtesting.
Becausetemplatesmaketheexpressionofpowerfulconceptseasy,youcantakethisconceptmuch
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
299/458
5/3/2015
ThinkinginC++2ndedVolume2
further,creatingamapofvectorscontainingmaps,andsoon.Forthatmatter,youcancombineanyof
theSTLcontainersthisway.
Cleaningup
containersofpointers
InStlshape.cpp,thepointersdidnotcleanthemselvesupautomatically.Itwouldbeconvenienttobeable
todothiseasily,ratherthanwritingoutthecodeeachtime.Hereisafunctiontemplatethatwillcleanup
thepointersinanysequencecontainer.Notethatitisplacedinthebooksrootdirectoryforeasyaccess:
//::purge.h
//DeletepointersinanSTLsequencecontainer.
#ifndefPURGE_H
#definePURGE_H
#include<algorithm>
template<classSeq>voidpurge(Seq&c){
typenameSeq::iteratori
for(i=c.begin()i!=c.end()++i){
delete*i
*i=0
}
}
//Iteratorversion:
template<classInpIt>voidpurge(InpItbegin,InpItend){
while(begin!=end){
delete*begin
*begin=0
++begin
}
}
#endif//PURGE_H///:~
Inthefirstversionofpurge(),notethattypenameisabsolutelynecessary.Thisisexactlythecasethat
keywordwasdesignedtosolve:Seqisatemplateargument,anditeratorissomethingthatisnested
withinthattemplate.SowhatdoesSeq::iteratorreferto?Thetypenamekeywordspecifiesthatitrefers
toatype,andnotsomethingelse.
Althoughthecontainerversionofpurge()mustworkwithanSTLstylecontainer,theiteratorversionof
purge()willworkwithanyrange,includinganarray.
HereisarewriteofStlshape.cpp,modifiedtousethepurge()function:
//:C07:Stlshape2.cpp
//Stlshape.cppwiththepurge()function.
#include<iostream>
#include<vector>
#include"../purge.h"
usingnamespacestd
classShape{
public:
virtualvoiddraw()=0
virtual~Shape(){}
}
classCircle:publicShape{
public:
voiddraw(){cout<<"Circle::draw<<endl}
~Circle(){cout<<"~Circle<<endl}
}
classTriangle:publicShape{
public:
voiddraw(){cout<<"Triangle::draw<<endl}
~Triangle(){cout<<"~Triangle<<endl}
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
300/458
5/3/2015
ThinkinginC++2ndedVolume2
classSquare:publicShape{
public:
voiddraw(){cout<<"Square::draw<<endl}
~Square(){cout<<"~Square<<endl}
}
intmain(){
typedefstd::vector<Shape*>Container
typedefContainer::iteratorIter
Containershapes
shapes.push_back(newCircle)
shapes.push_back(newSquare)
shapes.push_back(newTriangle)
for(Iteri=shapes.begin()i!=shapes.end()i++)
(*i)>draw()
purge(shapes)
}///:~
Whenusingpurge(),carefullyconsiderownershipissues.Ifanobjectpointerisheldinmorethanone
container,besurenottodeleteittwice,andyoudontwanttodestroytheobjectinthefirstcontainer
beforethesecondoneisfinishedwithit.Purgingthesamecontainertwiceisnotaproblembecause
purge()setsthepointertozeroonceitdeletesthatpointer,andcallingdeleteforazeropointerisa
safeoperation.
Creatingyourowncontainers
WiththeSTLasafoundation,youcancreateyourowncontainers.Assumingyoufollowthesamemodelof
providingiterators,yournewcontainerwillbehaveasifitwereabuiltinSTLcontainer.
Considertheringdatastructure,whichisacircularsequencecontainer.Ifyoureachtheend,itjust
wrapsaroundtothebeginning.Thiscanbeimplementedontopofalistasfollows:
//:C07:Ring.cpp
//Makinga"ring"datastructurefromtheSTL.
#include<iostream>
#include<iterator>
#include<list>
#include<string>
usingnamespacestd
template<classT>classRing{
list<T>lst
public:
//Declarationnecessarysothefollowing
//'friend'statementseesthis'iterator'
//insteadofstd::iterator:
classiterator
friendclassiterator
classiterator:publicstd::iterator<
std::bidirectional_iterator_tag,T,ptrdiff_t>{
typenamelist<T>::iteratorit
list<T>*r
public:
iterator(list<T>&lst,
consttypenamelist<T>::iterator&i)
:it(i),r(&lst){}
booloperator==(constiterator&x)const{
returnit==x.it
}
booloperator!=(constiterator&x)const{
return!(*this==x)
}
typenamelist<T>::referenceoperator*()const{
return*it
}
iterator&operator++(){
++it
if(it==r>end())
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
301/458
5/3/2015
ThinkinginC++2ndedVolume2
it=r>begin()
return*this
}
iteratoroperator++(int){
iteratortmp=*this
++*this
returntmp
}
iterator&operator(){
if(it==r>begin())
it=r>end()
it
return*this
}
iteratoroperator(int){
iteratortmp=*this
*this
returntmp
}
iteratorinsert(constT&x){
returniterator(*r,r>insert(it,x))
}
iteratorerase(){
returniterator(*r,r>erase(it))
}
}
voidpush_back(constT&x){lst.push_back(x)}
iteratorbegin(){returniterator(lst,lst.begin())}
intsize(){returnlst.size()}
}
intmain(){
Ring<string>rs
rs.push_back("one")
rs.push_back("two")
rs.push_back("three")
rs.push_back("four")
rs.push_back("five")
Ring<string>::iteratorit=rs.begin()
++it++it
it.insert("six")
it=rs.begin()
//Twicearoundthering:
for(inti=0i<rs.size()*2i++)
cout<<*it++<<endl
}///:~
Youcanseethatmostofthecodingisintheiterator.TheRingiteratormustknowhowtoloopbackto
thebeginning,soitmustkeepareferencetothelistofitsparentRingobjectinordertoknowifitsat
theendandhowtogetbacktothebeginning.
YoullnoticethattheinterfaceforRingisquitelimitedinparticular,thereisnoend(),sincearingjust
keepslooping.ThismeansthatyouwontbeabletouseaRinginanySTLalgorithmsthatrequireapast
theenditerator,whicharemany.(Itturnsoutthataddingthisfeatureisanontrivialexercise.)Although
thiscanseemlimiting,considerstack,queue,andpriority_queue,whichdontproduceanyiteratorsat
all!
STLextensions
AlthoughtheSTLcontainersmayprovideallthefunctionalityyoulleverneed,theyarenotcomplete.For
example,thestandardimplementationsofsetandmapusetrees,andalthoughthesearereasonablyfast,
theymaynotbefastenoughforyourneeds.IntheC++StandardsCommitteeitwasgenerallyagreedthat
hashedimplementationsofsetandmapshouldhavebeenincludedinStandardC++,however,therewas
notenoughtimetoaddthesecomponents,andthustheywereleftout.[114]
Fortunately,alternativesarefreelyavailable.OneofthenicethingsabouttheSTListhatitestablishesa
basicmodelforcreatingSTLlikeclasses,soanythingbuiltusingthesamemodeliseasytounderstandif
youarealreadyfamiliarwiththeSTL.
[115]
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
302/458
5/3/2015
ThinkinginC++2ndedVolume2
[115]
TheSGISTLfromSiliconGraphics
isoneofthemostrobustimplementationsoftheSTLandcanbe
usedtoreplaceyourcompilersSTLifthatisfoundwanting.Inaddition,SGIhasaddedanumberof
extensionsincludinghash_set,hash_multiset,hash_map,hash_multimap,slist(asinglylinkedlist),
andrope(avariantofstringoptimizedforverylargestringsandfastconcatenationandsubstring
operations).
LetsconsideraperformancecomparisonbetweenatreebasedmapandtheSGIhash_map.Tokeep
thingssimple,themappingswillbefrominttoint:
//:C07:MapVsHashMap.cpp
//Thehash_mapheaderisnotpartoftheStandardC++STL.
//Itisanextensionthatisonlyavailableaspartofthe
//SGISTL(Includedwiththedmcdistribution).
//Youcanaddtheheaderbyhandforallofthese:
//{bor}{msc}{g++}{mwcc}
#include<hash_map>
#include<iostream>
#include<map>
#include<ctime>
usingnamespacestd
intmain(){
hash_map<int,int>hm
map<int,int>m
clock_tticks=clock()
for(inti=0i<100i++)
for(intj=0j<1000j++)
m.insert(make_pair(j,j))
cout<<"mapinsertions:"<<clock()ticks<<endl
ticks=clock()
for(inti=0i<100i++)
for(intj=0j<1000j++)
hm.insert(make_pair(j,j))
cout<<"hash_mapinsertions:"
<<clock()ticks<<endl
ticks=clock()
for(inti=0i<100i++)
for(intj=0j<1000j++)
m[j]
cout<<"map::operator[]lookups:"
<<clock()ticks<<endl
ticks=clock()
for(inti=0i<100i++)
for(intj=0j<1000j++)
hm[j]
cout<<"hash_map::operator[]lookups:"
<<clock()ticks<<endl
ticks=clock()
for(inti=0i<100i++)
for(intj=0j<1000j++)
m.find(j)
cout<<"map::find()lookups:"
<<clock()ticks<<endl
ticks=clock()
for(inti=0i<100i++)
for(intj=0j<1000j++)
hm.find(j)
cout<<"hash_map::find()lookups:"
<<clock()ticks<<endl
}///:~
Theperformancetestweranshowedaspeedimprovementofroughly4:1forthehash_mapoverthe
mapinalloperations(andasexpected,find()isslightlyfasterthanoperator[]forlookupsforboth
typesofmap).Ifaprofilershowsabottleneckinyourmap,considerahash_map.
NonSTLcontainers
TherearetwononSTLcontainersinthestandardlibrary:bitsetandvalarray.[116]WesaynonSTL
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
303/458
5/3/2015
ThinkinginC++2ndedVolume2
becauseneitherofthesecontainersfulfillsalltherequirementsofSTLcontainers.Thebitsetcontainer,
whichwecoveredearlierinthischapter,packsbitsintointegersanddoesnotallowdirectaddressingofits
members.Thevalarraytemplateclassisavectorlikecontainerthatisoptimizedforefficientnumeric
computation.Neithercontainerprovidesiterators.Althoughyoucaninstantiateavalarraywith
nonnumerictypes,ithasmathematicalfunctionsthatareintendedtooperatewithnumericdata,suchas
sin,cos,tan,andsoon.
Heresatooltoprintelementsinavalarray:
//:C07:PrintValarray.h
#ifndefPRINTVALARRAY_H
#definePRINTVALARRAY_H
#include<valarray>
#include<iostream>
#include<cstddef>
template<classT>
voidprint(constchar*lbl,conststd::valarray<T>&a){
std::cout<<lbl<<":"
for(std::size_ti=0i<a.size()++i)
std::cout<<a[i]<<''
std::cout<<std::endl
}
#endif//PRINTVALARRAY_H///:~
Mostofvalarraysfunctionsandoperatorsoperateonavalarrayasawhole,asthefollowingexample
illustrates:
//:C07:Valarray1.cpp{bor}
//Illustratesbasicvalarrayfunctionality.
#include"PrintValarray.h"
usingnamespacestd
doublef(doublex){return2.0*x1.0}
intmain(){
doublen[]={1.0,2.0,3.0,4.0}
valarray<double>v(n,sizeofn/sizeofn[0])
print("v",v)
valarray<double>sh(v.shift(1))
print("shift1",sh)
valarray<double>acc(v+sh)
print("sum",acc)
valarray<double>trig(sin(v)+cos(acc))
print("trig",trig)
valarray<double>p(pow(v,3.0))
print("3rdpower",p)
valarray<double>app(v.apply(f))
print("f(v)",app)
valarray<bool>eq(v==app)
print("v==app?",eq)
doublex=v.min()
doubley=v.max()
doublez=v.sum()
cout<<"x="<<x<<",y="<<y
<<",z="<<z<<endl
}///:~
Thevalarrayclassprovidesaconstructorthattakesanarrayofthetargettypeandthecountofelements
inthearraytoinitializethenewvalarray.Theshift()memberfunctionshiftseachvalarrayelement
onepositiontotheleft(ortotheright,ifitsargumentisnegative)andfillsinholeswiththedefaultvalue
forthetype(zerointhiscase).Thereisalsoacshift()memberfunctionthatdoesacircularshift(or
rotate).Allmathematicaloperatorsandfunctionsareoverloadedtooperateonvalarrays,andbinary
operatorsrequirevalarrayargumentsofthesametypeandsize.Theapply()memberfunction,likethe
transform()algorithm,appliesafunctiontoeachelement,buttheresultiscollectedintoaresult
valarray.Therelationaloperatorsreturnsuitablysizedinstancesofvalarray<bool>thatindicatethe
resultofelementbyelementcomparisons,suchaswitheqabove.Mostoperationsreturnanewresult
array,butafew,suchasmin(),max(),andsum(),returnasinglescalarvalue,forobviousreasons.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
304/458
5/3/2015
ThinkinginC++2ndedVolume2
Themostinterestingthingyoucandowithvalarraysisreferencesubsetsoftheirelements,notonlyfor
extractinginformation,butalsoforupdatingit.Asubsetofavalarrayiscalledaslice,andcertain
operatorsuseslicestodotheirwork.Thefollowingsampleprogramusesslices:
//:C07:Valarray2.cpp{bor}{dmc}
//Illustratesslicesandmasks.
#include"PrintValarray.h"
usingnamespacestd
intmain(){
intdata[]={1,2,3,4,5,6,7,8,9,10,11,12}
valarray<int>v(data,12)
valarray<int>r1(v[slice(0,4,3)])
print("slice(0,4,3)",r1)
//Extractconditionally
valarray<int>r2(v[v>6])
print("elements>6",r2)
//Squarefirstcolumn
v[slice(0,4,3)]*=valarray<int>(v[slice(0,4,3)])
print("aftersquaringfirstcolumn",v)
//Restoreit
intidx[]={1,4,7,10}
valarray<int>save(idx,4)
v[slice(0,4,3)]=save
print("vrestored",v)
//Extracta2dsubset:{{1,3,5},{7,9,11}}
valarray<size_t>siz(2)
siz[0]=2
siz[1]=3
valarray<size_t>gap(2)
gap[0]=6
gap[1]=2
valarray<int>r3(v[gslice(0,siz,gap)])
print("2dslice",r3)
//Extractasubsetviaabooleanmask(boolelements)
valarray<bool>mask(false,5)
mask[1]=mask[2]=mask[4]=true
valarray<int>r4(v[mask])
print("v[mask]",r4)
//Extractasubsetviaanindexmask(size_telements)
size_tidx2[]={2,2,3,6}
valarray<size_t>mask2(idx2,4)
valarray<int>r5(v[mask2])
print("v[mask2]",r5)
//Useanindexmaskinassignment
valarray<char>text("nowisthetime",15)
valarray<char>caps("NITT",4)
valarray<size_t>idx3(4)
idx3[0]=0
idx3[1]=4
idx3[2]=7
idx3[3]=11
text[idx3]=caps
print("capitalized",text)
}///:~
Asliceobjecttakesthreearguments:thestartingindex,thenumberofelementstoextract,andthe
stride,whichisthegapbetweenelementsofinterest.Slicescanbeusedasindexesintoanexisting
valarray,andanewvalarraycontainingtheextractedelementsisreturned.Avalarrayofbool,suchas
isreturnedbytheexpressionv>6,canbeusedasanindexintoanothervalarraytheelements
correspondingtothetrueslotsareextracted.Asyoucansee,youcanalsouseslicesandmasksas
indexesontheleftsideofanassignment.Agsliceobject(forgeneralizedslice)islikeaslice,except
thatthecountsandstridesarethemselvesarrays,whichmeansyoucaninterpretavalarrayasa
multidimensionalarray.Theexampleaboveextractsa2by3arrayfromv,wherethenumbersstartat
zeroandthenumbersforthefirstdimensionarefoundsixslotsapartinv,andtheotherstwoapart,which
effectivelyextractsthematrix
135
7911
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
305/458
5/3/2015
ThinkinginC++2ndedVolume2
Hereisthecompleteoutputforthisprogram:
slice(0,4,3):14710
elements>6:78910
aftersquaringv:123165649891001112
vrestored:123456789101112
2dslice:1357911
v[mask]:235
v[mask2]:3347
capitalized:NowIsTheTime
Apracticalexampleofslicesisfoundinmatrixmultiplication.Considerhowyouwouldwriteafunctionto
multiplytwomatricesofintegerswitharrays.
voidmatmult(constinta[][MAXCOLS],size_tm,size_tn,
constintb[][MAXCOLS],size_tp,size_tq,
intresult[][MAXCOLS)
Thisfunctionmultipliesthembynmatrixabythepbyqmatrixb,wherenandpareexpectedtobe
equal.Asyoucansee,withoutsomethinglikevalarray,youneedtofixthemaximumvalueforthe
seconddimensionofeachmatrix,sincelocationsinarraysarestaticallydetermined.Itisalsodifficultto
returnaresultarraybyvalue,sothecallerusuallypassestheresultarrayasanargument.
Usingvalarray,youcannotonlypassanysizematrix,butyoucanalsoeasilyprocessmatricesofany
type,andreturntheresultbyvalue.Hereshow:
//:C07:MatrixMultiply.cpp
//Usesvalarraytomultiplymatrices
#include<cassert>
#include<cstddef>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<valarray>
usingnamespacestd
//Printsavalarrayasasquarematrix
template<classT>
voidprintMatrix(constvalarray<T>&a,size_tn){
size_tsiz=n*n
assert(siz<=a.size())
for(size_ti=0i<siz++i){
cout<<setw(5)<<a[i]
cout<<((i+1)%n?'':'\n')
}
cout<<endl
}
//Multipliescompatiblematricesinvalarrays
template<classT>
valarray<T>
matmult(constvalarray<T>&a,size_tarows,size_tacols,
constvalarray<T>&b,size_tbrows,size_tbcols){
assert(acols==brows)
valarray<T>result(arows*bcols)
for(size_ti=0i<arows++i)
for(size_tj=0j<bcols++j){
//Takedotproductofrowa[i]andcolb[j]
valarray<T>row=a[slice(acols*i,acols,1)]
valarray<T>col=b[slice(j,brows,bcols)]
result[i*bcols+j]=(row*col).sum()
}
returnresult
}
intmain(){
constintn=3
intadata[n*n]={1,0,1,2,2,3,3,4,0}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
306/458
5/3/2015
ThinkinginC++2ndedVolume2
intbdata[n*n]={3,4,1,1,3,0,1,1,2}
valarray<int>a(adata,n*n)
valarray<int>b(bdata,n*n)
valarray<int>c(matmult(a,n,n,b,n,n))
printMatrix(c,n)
}///:~
Eachentryintheresultmatrixcisthedotproductofarowinawithacolumninb.Bytakingslices,you
canextracttheserowsandcolumnsasvalarraysandusetheglobal*operatorandsum()function
providedbyvalarraytodotheworksuccinctly.Theresultvalarrayiscomputedatruntimetheresno
needtoworryaboutthestaticlimitationsofarraydimensions.Youdohavetocomputelinearoffsetsofthe
position[i][j]yourself(seetheformulai*bcols+jabove),butthesizeandtypefreedomisworthit.
Summary
ThegoalofthischapterwasnotjusttointroducetheSTLcontainersinsomeconsiderabledepth.Although
everydetailcouldnotbecoveredhere,younowknowenoughthatyoucanlookupfurtherinformationin
theotherresources.OurhopeisthatthischapterhashelpedyougraspthepoweravailableintheSTLand
shownyouhowmuchfasterandmoreefficientyourprogrammingactivitiescanbebyunderstandingand
usingtheSTL.
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.Createaset<char>,openafile(whosenameisprovidedonthecommandline),andreadthat
fileinacharatatime,placingeachcharintheset.Printtheresults,andobservethe
organization.Arethereanylettersinthealphabetthatarenotusedinthatparticularfile?
2.CreatethreesequencesofNoisyobjects,avector,deque,andlist.Sortthem.Nowwritea
functiontemplatetoreceivethevectoranddequesequencesasaparametertosortthemand
recordthesortingtime.Writeaspecializedtemplatefunctiontodothesameforlist(ensureto
callitsmembersort()insteadofthegenericalgorithm).Comparetheperformanceofthe
differentsequencetypes.
3.Writeaprogramtocomparethespeedofsortingalistusinglist::sort()vs.usingstd::sort()
(theSTLalgorithmversionofsort()).
4.Createageneratorthatproducesrandomintvaluesbetween0and20inclusive,anduseittofill
amultiset<int>.Counttheoccurrencesofeachvalue,followingtheexamplegivenin
MultiSetWordCount.cpp.
5.ChangeStlShape.cppsothatitusesadequeinsteadofavector.
6.ModifyReversible.cppsoitworkswithdequeandlistinsteadofvector.
7.Useastack<int>andpopulateitwithaFibonaccisequence.Theprogramscommandline
shouldtakethenumberofFibonaccielementsdesired,andyoushouldhavealoopthatlooksat
thelasttwoelementsonthestackandpushesanewoneforeverypassthroughtheloop.
8.Usingonlythreestacks(source,sorted,andlosers),sortarandomsequenceofnumbersbyfirst
placingthenumbersonthesourcestack.Assumethenumberonthetopofthesourceisthe
largest,andpushitonthesortedstack.Continuetopopthesourcestackcomparingitwiththe
topofthesortedstack.Whichevernumberisthesmallest,popitfromitsstackandpushitonto
theonthelosersstack.Oncethesourcestackisempty,repeattheprocessusingthelosers
stackasthesourcestack,andusethesourcestackasthelosersstack.Thealgorithmcompletes
whenallthenumbershavebeenplacedintothewinnersstack.
9.Openatextfilewhosenameisprovidedonthecommandline.Readthefileawordatatime,
anduseamultiset<string>tocreateawordcountforeachword.
10.ModifyWordCount.cppsothatitusesinsert()insteadofoperator[]toinsertelementsinthe
map.
11.Createaclassthathasanoperator<andanostream&operator<<.Theclassshouldcontain
aprioritynumber.Createageneratorforyourclassthatmakesarandomprioritynumber.Filla
priority_queueusingyourgenerator,andthenpulltheelementsouttoshowtheyareinthe
properorder.
12.RewriteRing.cppsoitusesadequeinsteadofalistforitsunderlyingimplementation.
13.ModifyRing.cppsothattheunderlyingimplementationcanbechosenusingatemplate
argument.(Letthattemplateargumentdefaulttolist.)
14.CreateaniteratorclasscalledBitBucketthatjustabsorbswhateveryousendtoitwithout
writingitanywhere.
15.Createakindofhangmangame.Createaclassthatcontainsacharandabooltoindicate
whetherthatcharhasbeenguessedyet.Randomlyselectawordfromafile,andreaditintoa
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
307/458
5/3/2015
ThinkinginC++2ndedVolume2
vectorofyournewtype.Repeatedlyasktheuserforacharacterguess,andaftereachguess,
displaythecharactersinthewordthathavebeenguessed,anddisplayunderscoresforthe
charactersthathavent.Allowawayfortheusertoguessthewholeword.Decrementavalue
foreachguess,andiftheusercangetthewholewordbeforethevaluegoestozero,theywin.
16.Openafileandreaditintoasinglestring.Turnthestringintoastringstream.Readtokens
fromthestringstreamintoalist<string>usingaTokenIterator.
17.Comparetheperformanceofstackbasedonwhetheritisimplementedwithvector,deque,or
list.
18.CreateatemplatethatimplementsasinglylinkedlistcalledSList.Provideadefaultconstructor
andbegin()andend()functions(viaanappropriatenestediterator),insert(),erase()and
adestructor.
19.Generateasequenceofrandomintegers,storingthemintoanarrayofint.Initializea
valarray<int>withitscontents.Computethesum,minimumvalue,maximumvalue,average,
andmedianoftheintegersusingvalarrayoperations.
20.Createavalarray<int>with12randomvalues.Createanothervalarray<int>with20random
values.Youwillinterpretthefirstvalarrayasa3x4matrixofintsandthesecondasa4x5
matrixofints,andmultiplythembytherulesofmatrixmultiplication.Storetheresultina
valarray<int>ofsize15,representingthe3x5resultmatrix.Useslicestomultiplytherows
ofthefirstmatrixtimethecolumnsofthesecond.Printtheresultinrectangularmatrixform.
Part3:SpecialTopics
Themarkofaprofessionalappearsinhisorherattentiontothefinerpointsofthe
craft.InthissectionofthebookwediscussadvancedfeaturesofC++alongwith
developmenttechniquesusedbypolishedC++professionals.
Sometimesyoumayneedtodepartfromtheconventionalwisdomofsoundobjectorienteddesignby
inspectingtheruntimetypeofanobject.Mostofthetimeyoushouldletvirtualfunctionsdothatjobfor
you,butwhenwritingspecialpurposesoftwaretools,suchasdebuggers,databaseviewers,orclass
browsers,youllneedtodeterminetypeinformationatruntime.Thisiswheretheruntimetype
identification(RTTI)mechanismbecomesuseful.RTTIisthetopicofChapter8.
Multipleinheritancehastakenabuseovertheyears,andsomelanguagesdontevensupportit.
Nonetheless,whenusedproperly,itcanbeapowerfultoolforcraftingelegant,efficientcode.Anumberof
standardpracticesinvolvingmultipleinheritancehaveevolvedovertheyears,whichwepresentinChapter
9.
Perhapsthemostnotableinnovationinsoftwaredevelopmentsinceobjectorientedtechniquesistheuseof
designpatterns.Adesignpatterndescribessolutionsformanyofthecommonproblemsinvolvedin
designingsoftware,andcanbeappliedinmanysituationsandimplementedinanylanguage.Inchapter10
wedescribeaselectednumberofdesignpatternsandimplementtheminC++.
Chapter11explainsthebenefitsandchallengesofmultithreadedprogramming.Thecurrentversionof
StandardC++doesnotspecifysupportforthreads,eventhoughmostoperatingsystemsprovidethem.We
useaportable,freelyavailablethreadinglibrarytoillustratehowC++programmerscantakeadvantageof
threadstobuildmoreusableandresponsiveapplications.
8:RuntimeTypeIdentification
Runtimetypeidentification(RTTI)letsyoufindthedynamictypeofanobject
whenyouhaveonlyapointerorareferencetothebasetype.
ThiscanbethoughtofasasecondaryfeatureinC++,pragmatismtohelpoutwhenyougetintorare
difficultsituations.Normally,youllwanttointentionallyignoretheexacttypeofanobjectandletthe
virtualfunctionmechanismimplementthecorrectbehaviorforthattype.Onoccasion,however,itsuseful
toknowtheexactruntime(thatis,mostderived)typeofanobjectforwhichyouonlyhaveabasepointer.
Withthisinformation,youmayperformaspecialcaseoperationmoreefficientlyorpreventabaseclass
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
308/458
5/3/2015
ThinkinginC++2ndedVolume2
interfacefrombecomingungainly.Ithappensenoughthatmostclasslibrariescontainvirtualfunctionsto
produceruntimetypeinformation.WhenexceptionhandlingwasaddedtoC++,thatfeaturerequired
informationabouttheruntimetypeofobjects,soitbecameaneasynextsteptobuildinaccesstothat
information.ThischapterexplainswhatRTTIisforandhowtouseit.
Runtimecasts
Onewaytodeterminetheruntimetypeofanobjectthroughapointerorreferenceistoemployaruntime
cast,whichverifiesthattheattemptedconversionisvalid.Thisisusefulwhenyouneedtocastabase
classpointertoaderivedtype.Sinceinheritancehierarchiesaretypicallydepictedwithbaseclassesabove
derivedclasses,suchacastiscalledadowncast.
Considerthefollowingclasshierarchy:
Inthecodethatfollows,theInvestmentclasshasanextraoperationthattheotherclassesdonot,soit
isimportanttobeabletoknowatruntimewhetheraSecuritypointerreferstoaInvestmentobjector
not.Toimplementcheckedruntimecasts,eachclasskeepsanintegralidentifiertodistinguishitfrom
otherclassesinthehierarchy.
//:C08:CheckedCast.cpp
//Checkscastsatruntime.
#include<iostream>
#include<vector>
#include"../purge.h"
usingnamespacestd
classSecurity{
protected:
enum{BASEID=0}
public:
virtual~Security(){}
virtualboolisA(intid){return(id==BASEID)}
}
classStock:publicSecurity{
typedefSecuritySuper
protected:
enum{OFFSET=1,TYPEID=BASEID+OFFSET}
public:
boolisA(intid){
returnid==TYPEID||Super::isA(id)
}
staticStock*dynacast(Security*s){
return(s>isA(TYPEID))?static_cast<Stock*>(s):0
}
}
classBond:publicSecurity{
typedefSecuritySuper
protected:
enum{OFFSET=2,TYPEID=BASEID+OFFSET}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
309/458
5/3/2015
ThinkinginC++2ndedVolume2
public:
boolisA(intid){
returnid==TYPEID||Super::isA(id)
}
staticBond*dynacast(Security*s){
return(s>isA(TYPEID))?static_cast<Bond*>(s):0
}
}
classInvestment:publicSecurity{
typedefSecuritySuper
protected:
enum{OFFSET=3,TYPEID=BASEID+OFFSET}
public:
boolisA(intid){
returnid==TYPEID||Super::isA(id)
}
staticInvestment*dynacast(Security*s){
return(s>isA(TYPEID))?
static_cast<Investment*>(s):0
}
voidspecial(){
cout<<"specialInvestmentfunction"<<endl
}
}
classMetal:publicInvestment{
typedefInvestmentSuper
protected:
enum{OFFSET=4,TYPEID=BASEID+OFFSET}
public:
boolisA(intid){
returnid==TYPEID||Super::isA(id)
}
staticMetal*dynacast(Security*s){
return(s>isA(TYPEID))?static_cast<Metal*>(s):0
}
}
intmain(){
vector<Security*>portfolio
portfolio.push_back(newMetal)
portfolio.push_back(newInvestment)
portfolio.push_back(newBond)
portfolio.push_back(newStock)
for(vector<Security*>::iteratorit=portfolio.begin()
it!=portfolio.end()++it){
Investment*cm=Investment::dynacast(*it)
if(cm)
cm>special()
else
cout<<"notanInvestment"<<endl
}
cout<<"castfromintermediatepointer:"<<endl
Security*sp=newMetal
Investment*cp=Investment::dynacast(sp)
if(cp)cout<<"it'sanInvestment"<<endl
Metal*mp=Metal::dynacast(sp)
if(mp)cout<<"it'saMetaltoo!"<<endl
purge(portfolio)
}///:~
ThepolymorphicisA()functioncheckstoseeifitsargumentiscompatiblewithitstypeargument(id),
whichmeansthateitheridmatchestheobjectstypeIDexactlyoritmatchesoneoftheobjectsancestors
(hencethecalltoSuper::isA()inthatcase).Thedynacast()function,whichisstaticineachclass,calls
isA()foritspointerargumenttocheckifthecastisvalid.IfisA()returnstrue,thecastisvalid,anda
suitablycastpointerisreturned.Otherwise,thenullpointerisreturned,whichtellsthecallerthatthecast
isnotvalid,meaningthattheoriginalpointerisnotpointingtoanobjectcompatiblewith(convertibleto)
thedesiredtype.Allthismachineryisnecessarytobeabletocheckintermediatecasts,suchasfroma
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
310/458
5/3/2015
ThinkinginC++2ndedVolume2
SecuritypointerthatreferstoaMetalobjecttoaInvestmentpointerinthepreviousexampleprogram.
[117]
Formostprogramsdowncastingisunnecessary,andisactuallydiscouraged,sinceeverydaypolymorphism
solvesmostproblemsinobjectorientedapplicationprograms.However,theabilitytocheckacasttoa
morederivedtypeisimportantforutilityprogramssuchasdebuggers,classbrowsers,anddatabases.
C++providessuchacheckedcastwiththedynamic_castoperator.Thefollowingprogramisarewriteof
thepreviousexampleusingdynamic_cast:
//:C08:Security.h
#ifndefSECURITY_H
#defineSECURITY_H
#include<iostream>
classSecurity{
public:
virtual~Security(){}
}
classStock:publicSecurity{}
classBond:publicSecurity{}
classInvestment:publicSecurity{
public:
voidspecial(){
std::cout<<"specialInvestmentfunction<<std::endl
}
}
classMetal:publicInvestment{}
#endif//SECURITY_H///:~
//:C08:CheckedCast2.cpp
//UsesRTTIsdynamic_cast.
#include<vector>
#include"../purge.h"
#include"Security.h"
usingnamespacestd
intmain(){
vector<Security*>portfolio
portfolio.push_back(newMetal)
portfolio.push_back(newInvestment)
portfolio.push_back(newBond)
portfolio.push_back(newStock)
for(vector<Security*>::iteratorit=
portfolio.begin()
it!=portfolio.end()++it){
Investment*cm=dynamic_cast<Investment*>(*it)
if(cm)
cm>special()
else
cout<<"notaInvestment"<<endl
}
cout<<"castfromintermediatepointer:<<endl
Security*sp=newMetal
Investment*cp=dynamic_cast<Investment*>(sp)
if(cp)cout<<"it'sanInvestment<<endl
Metal*mp=dynamic_cast<Metal*>(sp)
if(mp)cout<<"it'saMetaltoo!<<endl
purge(portfolio)
}///:~
Thisexampleismuchshorter,sincemostofthecodeintheoriginalexamplewasjusttheoverheadfor
checkingthecasts.Thetargettypeofadynamic_castisplacedinanglebrackets,liketheothernew
styleC++casts(static_cast,andsoon),andtheobjecttocastappearsastheoperand.
dynamic_castrequiresthatthetypesyouuseitwithbepolymorphicifyouwantsafedowncasts.[118]This
inturnrequiresthattheclassmusthaveatleastonevirtualfunction.Fortunately,theSecuritybaseclass
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
311/458
5/3/2015
ThinkinginC++2ndedVolume2
hasavirtualdestructor,sowedidnthavetoinventanextrafunctiontogetthejobdone.Because
dynamic_castdoesitsworkatruntime,usingthevirtualtable,ittendstobemoreexpensivethanthe
othernewstylecasts.
Youcanalsousedynamic_castwithreferencesinsteadofpointers,butsincethereisnosuchthingasa
nullreference,youneedanotherwaytoknowifthecastfails.Thatotherwayistocatcha
bad_castexception,asfollows:
//:C08:CatchBadCast.cpp
#include<typeinfo>
#include"Security.h"
usingnamespacestd
intmain(){
Metalm
Security&s=m
try{
Investment&c=dynamic_cast<Investment&>(s)
cout<<"It'sanInvestment"<<endl
}catch(bad_cast&){
cout<<"sisnotanInvestmenttype"<<endl
}
try{
Bond&b=dynamic_cast<Bond&>(s)
cout<<"It'saBond"<<endl
}catch(bad_cast&){
cout<<"It'snotaBondtype"<<endl
}
}///:~
Thebad_castclassisdefinedinthe<typeinfo>header,and,likemostofthestandardlibrary,is
declaredinthestdnamespace.
Thetypeidoperator
Theotherwaytogetruntimeinformationforanobjectisthroughthetypeidoperator.Thisoperator
returnsanobjectofclasstype_info,whichyieldsinformationaboutthetypeofobjecttowhichitwas
applied.Ifthetypeispolymorphic,itgivesinformationaboutthemostderivedtypethatapplies(the
dynamictype)otherwiseityieldsstatictypeinformation.Oneuseofthetypeidoperatoristogetthe
nameofthedynamictypeofanobjectasaconstchar*,asyoucanseeinthefollowingexample:
//:C08:TypeInfo.cpp
//Illustratesthetypeidoperator.
#include<iostream>
#include<typeinfo>
usingnamespacestd
structPolyBase{virtual~PolyBase(){}}
structPolyDer:PolyBase{PolyDer(){}}
structNonPolyBase{}
structNonPolyDer:NonPolyBase{NonPolyDer(int){}}
intmain(){
//TestpolymorphicTypes
constPolyDerpd
constPolyBase*ppb=&pd
cout<<typeid(ppb).name()<<endl
cout<<typeid(*ppb).name()<<endl
cout<<boolalpha<<(typeid(*ppb)==typeid(pd))
<<endl
cout<<(typeid(PolyDer)==typeid(constPolyDer))
<<endl
//TestnonpolymorphicTypes
constNonPolyDernpd(1)
constNonPolyBase*nppb=&npd
cout<<typeid(nppb).name()<<endl
cout<<typeid(*nppb).name()<<endl
cout<<(typeid(*nppb)==typeid(npd))<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
312/458
5/3/2015
ThinkinginC++2ndedVolume2
//Testabuiltintype
inti
cout<<typeid(i).name()<<endl
}///:~
Theoutputfromthisprogramusingoneparticularcompileris
structPolyBaseconst*
structPolyDer
true
true
structNonPolyBaseconst*
structNonPolyBase
false
int
Thefirstoutputlinejustechoesthestatictypeofppbbecauseitisapointer.TogetRTTItokickin,you
needtolookatthepointerorreferencedestinationobject,whichisillustratedinthesecondline.Notice
thatRTTIignorestoplevelconstandvolatilequalifiers.Withnonpolymorphictypes,youjustgetthe
statictype(thetypeofthepointeritself).Asyoucansee,builtintypesarealsosupported.
Itturnsoutthatyoucantstoretheresultofatypeidoperationinatype_infoobject,becausethereare
noaccessibleconstructorsandassignmentisdisallowed.Youmustuseitaswehaveshown.Inaddition,
theactualstringreturnedbytype_info::name()iscompilerdependent.ForaclassnamedC,for
example,somecompilersreturnclassCinsteadofjustC.Applyingtypeidtoanexpressionthat
dereferencesanullpointerwillcauseabad_typeidexception(alsodefinedin<typeinfo>)tobethrown.
Thefollowingexampleshowsthattheclassnamethattype_info::name()returnsisfullyqualified:
//:C08:RTTIandNesting.cpp
#include<iostream>
#include<typeinfo>
usingnamespacestd
classOne{
classNested{}
Nested*n
public:
One():n(newNested){}
~One(){deleten}
Nested*nested(){returnn}
}
intmain(){
Oneo
cout<<typeid(*o.nested()).name()<<endl
}///:~
SinceNestedisamembertypeoftheOneclass,theresultisOne::Nested.
Youcanalsoaskatype_infoobjectifitprecedesanothertype_infoobjectintheimplementationdefined
collationsequence(thenativeorderingrulesfortext),usingbefore(type_info&),whichreturnstrueor
false.Whenyousay,
if(typeid(me).before(typeid(you)))//...
youreaskingifmeoccursbeforeyouinthecurrentcollationsequence.Thisisusefulifyouuse
type_infoobjectsaskeys.
Castingtointermediatelevels
AsyousawintheearlierprogramthatusedthehierarchyofSecurityclasses,dynamic_castcandetect
bothexacttypesand,inaninheritancehierarchywithmultiplelevels,intermediatetypes.Hereisanother
example.
//:C08:IntermediateCast.cpp
#include<cassert>
#include<typeinfo>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
313/458
5/3/2015
ThinkinginC++2ndedVolume2
usingnamespacestd
classB1{
public:
virtual~B1(){}
}
classB2{
public:
virtual~B2(){}
}
classMI:publicB1,publicB2{}
classMi2:publicMI{}
intmain(){
B2*b2=newMi2
Mi2*mi2=dynamic_cast<Mi2*>(b2)
MI*mi=dynamic_cast<MI*>(b2)
B1*b1=dynamic_cast<B1*>(b2)
assert(typeid(b2)!=typeid(Mi2*))
assert(typeid(b2)==typeid(B2*))
deleteb2
}///:~
Thisexamplehastheextracomplicationofmultipleinheritance(youlllearnmoreaboutmultiple
inheritancelaterinthischapter,andinChapter9).IfyoucreateanMi2andupcastittotheroot(inthis
case,oneofthetwopossiblerootsischosen),thedynamic_castbacktoeitherofthederivedlevelsMI
orMi2issuccessful.
Youcanevencastfromoneroottotheother:
B1*b1=dynamic_cast<B1*>(b2)
ThisissuccessfulbecauseB2isactuallypointingtoaMi2object,whichcontainsasubobjectoftypeB1.
Castingtointermediatelevelsbringsupaninterestingdifferencebetweendynamic_castandtypeid.The
typeidoperatoralwaysproducesareferencetoastatictype_infoobjectthatdescribesthedynamictype
oftheobject.Thus,itdoesntgiveyouintermediatelevelinformation.Inthefollowingexpression(whichis
true),typeiddoesntseeb2asapointertothederivedtype,likedynamic_castdoes:
typeid(b2)!=typeid(Mi2*)
Thetypeofb2issimplytheexacttypeofthepointer:
typeid(b2)==typeid(B2*)
voidpointers
RTTIonlyworksforcompletetypes,meaningthatallclassinformationmustbeavailablewhentypeidis
used.Inparticular,itdoesntworkwithvoidpointers:
//:C08:VoidRTTI.cpp
//RTTI&voidpointers.
//!#include<iostream>
#include<typeinfo>
usingnamespacestd
classStimpy{
public:
virtualvoidhappy(){}
virtualvoidjoy(){}
virtual~Stimpy(){}
}
intmain(){
void*v=newStimpy
//Error:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
314/458
5/3/2015
ThinkinginC++2ndedVolume2
//!Stimpy*s=dynamic_cast<Stimpy*>(v)
//Error:
//!cout<<typeid(*v).name()<<endl
}///:~
Avoid*trulymeansnotypeinformation.[119]
UsingRTTIwithtemplates
ClasstemplatesworkwellwithRTTI,sincealltheydoisgenerateclasses.Asusual,RTTIprovidesa
convenientwaytoobtainthenameoftheclassyourein.Thefollowingexampleprintstheorderof
constructoranddestructorcalls:
//:C08:ConstructorOrder.cpp
//Orderofconstructorcalls.
#include<iostream>
#include<typeinfo>
usingnamespacestd
template<intid>classAnnounce{
public:
Announce(){
cout<<typeid(*this).name()<<"constructor"<<endl
}
~Announce(){
cout<<typeid(*this).name()<<"destructor"<<endl
}
}
classX:publicAnnounce<0>{
Announce<1>m1
Announce<2>m2
public:
X(){cout<<"X::X()"<<endl}
~X(){cout<<"X::~X()"<<endl}
}
intmain(){Xx}///:~
Thistemplateusesaconstantinttodifferentiateoneclassfromanother,buttypeargumentswouldwork
aswell.Insideboththeconstructoranddestructor,RTTIinformationproducesthenameoftheclassto
print.TheclassXusesbothinheritanceandcompositiontocreateaclassthathasaninterestingorderof
constructoranddestructorcalls.Theoutputis
Announce<0>constructor
Announce<1>constructor
Announce<2>constructor
X::X()
X::~X()
Announce<2>destructor
Announce<1>destructor
Announce<0>destructor
Ofcourse,youmaygetdifferentoutputdependingonhowyourcompilerrepresentsitsname()
information.
Multipleinheritance
TheRTTImechanismsmustworkproperlywithallthecomplexitiesofmultipleinheritance,including
virtualbaseclasses(discussedindepthinthenextchapteryoumaywanttocomebackhereafter
readingChapter9):
//:C08:RTTIandMultipleInheritance.cpp
#include<iostream>
#include<typeinfo>
usingnamespacestd
classBB{
public:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
315/458
5/3/2015
ThinkinginC++2ndedVolume2
virtualvoidf(){}
virtual~BB(){}
}
classB1:virtualpublicBB{}
classB2:virtualpublicBB{}
classMI:publicB1,publicB2{}
intmain(){
BB*bbp=newMI//Upcast
//Propernamedetection:
cout<<typeid(*bbp).name()<<endl
//Dynamic_castworksproperly:
MI*mip=dynamic_cast<MI*>(bbp)
//Can'tforceoldstylecast:
//!MI*mip2=(MI*)bbp//Compileerror
}///:~
Thetypeid()operatorproperlydetectsthenameoftheactualobject,eventhroughthevirtualbaseclass
pointer.Thedynamic_castalsoworkscorrectly.Butthecompilerwontevenallowyoutotrytoforcea
casttheoldway:
MI*mip=(MI*)bbp//Compiletimeerror
Thecompilerknowsthisisnevertherightthingtodo,soitrequiresthatyouuseadynamic_cast.
SensibleusesforRTTI
Becauseyoucandiscovertypeinformationfromananonymouspolymorphicpointer,RTTIisripefor
misusebythenovice,becauseRTTImaymakesensebeforevirtualfunctionsdo.Formanypeoplecoming
fromaproceduralbackground,itsdifficultnottoorganizeprogramsintosetsofswitchstatements.They
couldaccomplishthiswithRTTIandthuslosetheimportantvalueofpolymorphismincodedevelopment
andmaintenance.TheintentofC++isthatyouusevirtualfunctionsthroughoutyourcodeandthatyou
onlyuseRTTIwhenyoumust.
However,usingvirtualfunctionsastheyareintendedrequiresthatyouhavecontrolofthebaseclass
definition,becauseatsomepointintheextensionofyourprogramyoumaydiscoverthebaseclassdoesnt
includethevirtualfunctionyouneed.Ifthebaseclasscomesfromalibraryorisotherwisecontrolledby
someoneelse,onesolutiontotheproblemisRTTIyoucanderiveanewtypeandaddyourextramember
function.Elsewhereinthecodeyoucandetectyourparticulartypeandcallthatmemberfunction.This
doesntdestroythepolymorphismandextensibilityoftheprogram,becauseaddinganewtypewillnot
requireyoutohuntforswitchstatements.However,whenyouaddnewcodeinthemainbodythat
requiresyournewfeature,youllhavetodetectyourparticulartype.
Puttingafeatureinabaseclassmightmeanthat,forthebenefitofoneparticularclass,alltheother
classesderivedfromthatbaserequiresomemeaninglessstubforapurevirtualfunction.Thismakesthe
interfacelessclearandannoysthosewhomustoverridepurevirtualfunctionswhentheyderivefromthat
baseclass.
Finally,RTTIwillsometimessolveefficiencyproblems.Ifyourcodeusespolymorphisminaniceway,but
itturnsoutthatoneofyourobjectsreactstothisgeneralpurposecodeinahorriblyinefficientway,you
canpickthattypeoutusingRTTIandwritecasespecificcodetoimprovetheefficiency.
Atrashrecycler
TofurtherillustrateapracticaluseofRTTI,thefollowingprogramsimulatesatrashrecycler.Different
kindsoftrashareinsertedintoasinglecontainerandthenlatersortedaccordingtotheirdynamictypes.
//:C08:Trash.h
//Describingtrash.
#ifndefTRASH_H
#defineTRASH_H
#include<iostream>
classTrash{
float_weight
public:
Trash(floatwt):_weight(wt){}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
316/458
5/3/2015
ThinkinginC++2ndedVolume2
virtualfloatvalue()const=0
floatweight()const{return_weight}
virtual~Trash(){
std::cout<<"~Trash()"<<std::endl
}
}
classAluminum:publicTrash{
staticfloatval
public:
Aluminum(floatwt):Trash(wt){}
floatvalue()const{returnval}
staticvoidvalue(floatnewval){
val=newval
}
}
classPaper:publicTrash{
staticfloatval
public:
Paper(floatwt):Trash(wt){}
floatvalue()const{returnval}
staticvoidvalue(floatnewval){
val=newval
}
}
classGlass:publicTrash{
staticfloatval
public:
Glass(floatwt):Trash(wt){}
floatvalue()const{returnval}
staticvoidvalue(floatnewval){
val=newval
}
}
#endif//TRASH_H///:~
Thestaticvaluesrepresentingthepriceperunitofthetrashtypesaredefinedintheimplementationfile:
//:C08:Trash.cpp{O}
//ATrashRecycler.
#include"Trash.h"
floatAluminum::val=1.67
floatPaper::val=0.10
floatGlass::val=0.23
///:~
ThesumValue()templateiteratesthroughacontainer,displayingandcalculatingresults:
//:C08:Recycle.cpp
//{L}Trash
//ATrashRecycler.
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<typeinfo>
#include<vector>
#include"Trash.h"
#include"../purge.h"
usingnamespacestd
//SumsupthevalueoftheTrashinabin:
template<classContainer>
voidsumValue(Container&bin,ostream&os){
typenameContainer::iteratortally=bin.begin()
floatval=0
while(tally!=bin.end()){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
317/458
5/3/2015
ThinkinginC++2ndedVolume2
val+=(*tally)>weight()*(*tally)>value()
os<<"weightof"<<typeid(**tally).name()
<<"="<<(*tally)>weight()<<endl
++tally
}
os<<"Totalvalue="<<val<<endl
}
intmain(){
srand(time(0))//Seedtherandomnumbergenerator
vector<Trash*>bin
//FilluptheTrashbin:
for(inti=0i<30i++)
switch(rand()%3){
case0:
bin.push_back(newAluminum((rand()%1000)/10.0))
break
case1:
bin.push_back(newPaper((rand()%1000)/10.0))
break
case2:
bin.push_back(newGlass((rand()%1000)/10.0))
break
}
//Note:binsholdexacttypeofobject,notbasetype:
vector<Glass*>glassBin
vector<Paper*>paperBin
vector<Aluminum*>alumBin
vector<Trash*>::iteratorsorter=bin.begin()
//SorttheTrash:
while(sorter!=bin.end()){
Aluminum*ap=dynamic_cast<Aluminum*>(*sorter)
Paper*pp=dynamic_cast<Paper*>(*sorter)
Glass*gp=dynamic_cast<Glass*>(*sorter)
if(ap)alumBin.push_back(ap)
elseif(pp)paperBin.push_back(pp)
elseif(gp)glassBin.push_back(gp)
++sorter
}
sumValue(alumBin,cout)
sumValue(paperBin,cout)
sumValue(glassBin,cout)
sumValue(bin,cout)
purge(bin)
}///:~
Thetrashisthrownunclassifiedintoasinglebin,sothespecifictypeinformationislost.Butlaterthe
specifictypeinformationmustberecoveredtoproperlysortthetrash,andsoRTTIisused.
Wecanimprovethissolutionbyusingamapthatassociatespointerstotype_infoobjectswithavector
ofTrashpointers.Sinceamaprequiresanorderingpredicate,weprovideonenamedTInfoLessthat
callstype_info::before().AsweinsertTrashpointersintothemap,theyareautomaticallyassociated
withtheirtype_infokey.NoticethatsumValue()mustbedefineddifferentlyhere:
//:C08:Recycle2.cpp
//{L}Trash
//Recylingwithamap.
#include<cstdlib>
#include<ctime>
#include<iostream>
#include<map>
#include<typeinfo>
#include<utility>
#include<vector>
#include"Trash.h"
#include"../purge.h"
usingnamespacestd
//Comparatorfortype_infopointers
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
318/458
5/3/2015
ThinkinginC++2ndedVolume2
structTInfoLess{
booloperator()(consttype_info*t1,consttype_info*t2)
const{returnt1>before(*t2)}
}
typedefmap<consttype_info*,vector<Trash*>,TInfoLess>
TrashMap
//SumsupthevalueoftheTrashinabin:
voidsumValue(constTrashMap::value_type&p,ostream&os){
vector<Trash*>::const_iteratortally=p.second.begin()
floatval=0
while(tally!=p.second.end()){
val+=(*tally)>weight()*(*tally)>value()
os<<"weightof"
<<p.first>name()//type_info::name()
<<"="<<(*tally)>weight()<<endl
++tally
}
os<<"Totalvalue="<<val<<endl
}
intmain(){
srand(time(0))//Seedtherandomnumbergenerator
TrashMapbin
//FilluptheTrashbin:
for(inti=0i<30i++){
Trash*tp
switch(rand()%3){
case0:
tp=newAluminum((rand()%1000)/10.0)
break
case1:
tp=newPaper((rand()%1000)/10.0)
break
case2:
tp=newGlass((rand()%1000)/10.0)
break
}
bin[&typeid(*tp)].push_back(tp)
}
//Printsortedresults
for(TrashMap::iteratorp=bin.begin()
p!=bin.end()++p){
sumValue(*p,cout)
purge(p>second)
}
}///:~
WevemodifiedsumValue()tocalltype_info::name()directly,sincethetype_infoobjectisnow
availableasthefirstmemberoftheTrashMap::value_typepair.Thisavoidstheextracalltotypeidto
getthenameofthetypeofTrashbeingprocessedthatwasnecessaryinthepreviousversionofthis
program.
MechanismandoverheadofRTTI
Typically,RTTIisimplementedbyplacinganadditionalpointerinaclasssvirtualfunctiontable.This
pointerpointstothetype_infostructureforthatparticulartype.Theeffectofatypeid()expressionis
quitesimple:thevirtualfunctiontablepointerfetchesthetype_infopointer,andareferencetothe
resultingtype_infostructureisproduced.Sincethisisjustatwopointerdereferenceoperation,itisa
constanttimeoperation.
Foradynamic_cast<destination*>(source_pointer),mostcasesarequitestraightforward:
source_pointersRTTIinformationisretrieved,andRTTIinformationforthetypedestination*is
fetched.Alibraryroutinethendetermineswhethersource_pointerstypeisoftypedestination*ora
baseclassofdestination*.Thepointeritreturnsmaybeadjustedbecauseofmultipleinheritanceifthe
basetypeisntthefirstbaseofthederivedclass.Thesituationismorecomplicatedwithmultiple
inheritancebecauseabasetypemayappearmorethanonceinaninheritancehierarchyandvirtualbase
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
319/458
5/3/2015
ThinkinginC++2ndedVolume2
classesareused.
Becausethelibraryroutineusedfordynamic_castmustcheckthroughalistofbaseclasses,the
overheadfordynamic_castmaybehigherthantypeid()(butyougetdifferentinformation,whichmay
beessentialtoyoursolution),anditmaytakemoretimetodiscoverabaseclassthanaderivedclass.In
addition,dynamic_castcomparesanytypetoanyothertypeyouarentrestrictedtocomparingtypes
withinthesamehierarchy.Thisaddsextraoverheadtothelibraryroutineusedbydynamic_cast.
Summary
Althoughnormallyyouupcastapointertoabaseclassandthenusethegenericinterfaceofthatbaseclass
(viavirtualfunctions),occasionallyyougetintoacornerwherethingscanbemoreeffectiveifyouknow
thedynamictypeoftheobjectpointedtobyabasepointer,andthatswhatRTTIprovides.Themost
commonmisusemaycomefromtheprogrammerwhodoesntunderstandvirtualfunctionsandusesRTTI
todotypecheckcodinginstead.ThephilosophyofC++seemstobetoprovideyouwithpowerfultools
andguardfortypeviolationsandintegrity,butifyouwanttodeliberatelymisuseorgetaroundalanguage
feature,theresnothingtostopyou.Sometimesaslightburnisthefastestwaytogainexperience.
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.CreateaBaseclasswithavirtualdestructorandaDerivedclassthatinheritsfromBase.
CreateavectorofBasepointersthatpointtoBaseandDerivedobjectsrandomly.Usingthe
contentsyourvector,fillasecondvectorwithalltheDerivedpointers.Compareexecution
timesbetweentypeid()anddynamic_casttoseewhichisfaster.
2.ModifyC16:AutoCounter.hinVolume1ofthisbooksothatitbecomesausefuldebuggingtool.
Itwillbeusedasanestedmemberofeachclassthatyouareinterestedintracing.Turn
AutoCounterintoatemplatethattakestheclassnameofthesurroundingclassasthetemplate
argument,andinalltheerrormessagesuseRTTItoprintthenameoftheclass.
3.UseRTTItoassistinprogramdebuggingbyprintingouttheexactnameofatemplateusing
typeid().Instantiatethetemplateforvarioustypesandseewhattheresultsare.
4.ModifytheInstrumenthierarchyfromChapter14ofVolume1byfirstcopyingWind5.cpptoa
newlocation.NowaddavirtualclearSpitValve()functiontotheWindclass,andredefineit
foralltheclassesinheritedfromWind.InstantiateavectortoholdInstrumentpointers,and
fillitwithvarioustypesofInstrumentobjectscreatedusingthenewoperator.NowuseRTTIto
movethroughthecontainerlookingforobjectsinclassWind,orderivedfromWind.Callthe
clearSpitValve()functionfortheseobjects.Noticethatitwouldunpleasantlyconfusethe
InstrumentbaseclassifitcontainedaclearSpitValve()function.
5.ModifythepreviousexercisetoplaceaprepareInstrument()functioninthebaseclass,which
callsappropriatefunctions(suchasclearSpitValve(),whenitfits).Notethat
prepareInstrument()isasensiblefunctiontoplaceinthebaseclass,anditeliminatesthe
needforRTTIinthepreviousexercise.
6.Createavectorofpointersto10randomShapeobjects(atleastSquaresandCircles,for
example).Thedraw()memberfunctionshouldbeoverriddenineachconcreteclasstoprintthe
dimensionsoftheobjectbeingdrawn(thelengthortheradius,whicheverapplies).Writea
main()programthatdrawsalltheSquaresinyourcontainerfirst,sortedbylength,andthen
drawsallCircles,sortedbyradius.
7.CreatealargevectorofpointerstorandomShapeobjects.Writeanonvirtualdraw()function
inShapethatusesRTTItodeterminethedynamictypeofeachobjectandexecutesthe
appropriatecodetodrawtheobjectwithaswitchstatement.ThenrewriteyourShape
hierarchytherightway,usingvirtualfunctions.Comparethecodesizesandexecutiontimesof
thetwoapproaches.
8.CreateahierarchyofPetclasses,includingDog,Cat,andHorse.Alsocreateahierarchyof
Foodclasses:Beef,Fish,andOats.TheDogclasshasamemberfunction,eat(),thattakesa
Beefparameter,likewise,Cat::eat()takesaFishobject,andOatsobjectsarepassedto
Horse::eat().CreateavectorofpointerstorandomPetobjects,andvisiteachPet,passing
thecorrecttypeofFoodobjecttoitseat()function.
9.CreateaglobalfunctionnameddrawQuad()thattakesareferencetoaShapeobject.Itcalls
thedraw()functionofitsShapeparameterifithasfoursides(thatis,ifitsaSquareor
Rectangle).Otherwise,itprintsthemessageNotaquadrilateral.Traverseavectorof
pointerstorandomShapes,callingdrawQuad()foreachone.PlaceSquares,Rectangles,
CirclesandTrianglesinyourvector.
10.SortavectorofrandomShapeobjectsbyclassname.Usetype_info::before()asthe
comparisonfunctionforsorting.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
320/458
5/3/2015
ThinkinginC++2ndedVolume2
9:MultipleInheritance
Thebasicconceptofmultipleinheritance(MI)soundssimpleenough:youcreatea
newtypebyinheritingfrommorethanonebaseclass.Thesyntaxisexactlywhat
youdexpect,andaslongastheinheritancediagramsaresimple,MIcanbe
simpleaswell.
However,MIcanintroduceanumberofambiguitiesandstrangesituations,whicharecoveredinthis
chapter.Butfirst,itishelpfultogetsomeperspectiveonthesubject.
Perspective
BeforeC++,themostsuccessfulobjectorientedlanguagewasSmalltalk.Smalltalkwascreatedfromthe
groundupasanobjectorientedlanguage.Itisoftenreferredtoaspure,whereasC++iscalledahybrid
languagebecauseitsupportsmultipleprogrammingparadigms,notjusttheobjectorientedparadigm.One
ofthedesigndecisionsmadewithSmalltalkwasthatallclasseswouldbederivedinasinglehierarchy,
rootedinasinglebaseclass(calledObjectthisisthemodelfortheobjectbasedhierarchy).[120]You
cannotcreateanewclassinSmalltalkwithoutderivingitfromanexistingclass,whichiswhyittakesa
certainamountoftimetobecomeproductiveinSmalltalk:youmustlearntheclasslibrarybeforeyoucan
startmakingnewclasses.TheSmalltalkclasshierarchyisthereforeasinglemonolithictree.
ClassesinSmalltalkusuallyhaveanumberofthingsincommon,andtheyalwayshavesomethingsin
common(thecharacteristicsandbehaviorsofObject),soyoudontoftenrunintoasituationwhereyou
needtoinheritfrommorethanonebaseclass.However,withC++youcancreateasmanydistinct
inheritancetreesasyouwant.Soforlogicalcompletenessthelanguagemustbeabletocombinemore
thanoneclassatatimethustheneedformultipleinheritance.
Itwasnotobvious,however,thatprogrammersrequiredmultipleinheritance,andtherewas(andstillis)a
lotofdisagreementaboutwhetheritisessentialinC++.MIwasaddedinAT&Tcfrontrelease2.0in1989
andwasthefirstsignificantchangetothelanguageoverversion1.0.[121]Sincethen,anumberofother
featureshavebeenaddedtoStandardC++(notablytemplates)thatchangethewaywethinkabout
programmingandplaceMIinamuchlessimportantrole.YoucanthinkofMIasaminorlanguage
featurethatisseldominvolvedinyourdailydesigndecisions.
OneofthemostpressingargumentsforMIinvolvedcontainers.Supposeyouwanttocreateacontainer
thateveryonecaneasilyuse.Oneapproachistousevoid*asthetypeinsidethecontainer.TheSmalltalk
approach,however,istomakeacontainerthatholdsObjects,sinceObjectisthebasetypeofthe
Smalltalkhierarchy.BecauseeverythinginSmalltalkisultimatelyderivedfromObject,acontainerthat
holdsObjectscanholdanything.
NowconsiderthesituationinC++.SupposevendorAcreatesanobjectbasedhierarchythatincludesa
usefulsetofcontainersincludingoneyouwanttousecalledHolder.NextyoucomeacrossvendorBs
classhierarchythatcontainssomeotherclassthatisimportanttoyou,aBitImageclass,forexample,
thatholdsgraphicimages.TheonlywaytomakeaHolderofBitImagesistoderiveanewclassfrom
bothObject,soitcanbeheldintheHolder,andBitImage:
ThiswasseenasanimportantreasonforMI,andanumberofclasslibrarieswerebuiltonthismodel.
However,asyousawinChapter5,theadditionoftemplateshaschangedthewaycontainersarecreated,
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
321/458
5/3/2015
ThinkinginC++2ndedVolume2
sothissituationisnolongeradrivingissueforMI.
TheotherreasonyoumayneedMIisrelatedtodesign.YoucanintentionallyuseMItomakeadesign
moreflexibleoruseful(oratleastseeminglyso).Anexampleofthisisintheoriginaliostreamlibrary
design(whichstillpersistsintodaystemplatedesign,asyousawinChapter4):
Bothistreamandostreamareusefulclassesbythemselves,buttheycanalsobederivedfrom
simultaneouslybyaclassthatcombinesboththeircharacteristicsandbehaviors.Theclassiosprovides
whatiscommontoallstreamclasses,andsointhiscaseMIisacodefactoringmechanism.
RegardlessofwhatmotivatesyoutouseMI,itshardertousethanitmightappear.
Interfaceinheritance
Oneuseofmultipleinheritancethatisnotcontroversialpertainstointerfaceinheritance.InC++,all
inheritanceisimplementationinheritance,becauseeverythinginabaseclass,interfaceand
implementation,becomespartofaderivedclass.Itisnotpossibletoinheritonlypartofaclass(the
interfacealone,say).AsChapter14ofVolume1explains,privateandprotectedinheritancemakeit
possibletorestrictaccesstomembersinheritedfrombaseclasseswhenusedbyclientsofaderivedclass
object,butthisdoesntaffectthederivedclassitstillcontainsallbaseclassdataandcanaccessallnon
privatebaseclassmembers.
Interfaceinheritance,ontheotherhand,onlyaddsmemberfunctiondeclarationstoaderivedclass
interfaceandisnotdirectlysupportedinC++.Theusualtechniquetosimulateinterfaceinheritancein
C++istoderivefromaninterfaceclass,whichisaclassthatcontainsonlydeclarations(nodataor
functionbodies).Thesedeclarationswillbepurevirtualfunctions,exceptforthedestructor.Hereisan
example:
//:C09:Interfaces.cpp
//Multipleinterfaceinheritance.
#include<iostream>
#include<sstream>
#include<string>
usingnamespacestd
classPrintable{
public:
virtual~Printable(){}
virtualvoidprint(ostream&)const=0
}
classIntable{
public:
virtual~Intable(){}
virtualinttoInt()const=0
}
classStringable{
public:
virtual~Stringable(){}
virtualstringtoString()const=0
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
322/458
5/3/2015
ThinkinginC++2ndedVolume2
classAble:publicPrintable,publicIntable,
publicStringable{
intmyData
public:
Able(intx){myData=x}
voidprint(ostream&os)const{os<<myData}
inttoInt()const{returnmyData}
stringtoString()const{
ostringstreamos
os<<myData
returnos.str()
}
}
voidtestPrintable(constPrintable&p){
p.print(cout)
cout<<endl
}
voidtestIntable(constIntable&n){
cout<<n.toInt()+1<<endl
}
voidtestStringable(constStringable&s){
cout<<s.toString()+"th"<<endl
}
intmain(){
Ablea(7)
testPrintable(a)
testIntable(a)
testStringable(a)
}///:~
TheclassAbleimplementstheinterfacesPrintable,Intable,andStringablebecauseitprovides
implementationsforthefunctionstheydeclare.BecauseAblederivesfromallthreeclasses,Ableobjects
havemultipleisarelationships.Forexample,theobjectacanactasaPrintableobjectbecauseits
class,Able,derivespubliclyfromPrintableandprovidesanimplementationforprint().Thetest
functionshavenoneedtoknowthemostderivedtypeoftheirparametertheyjustneedanobjectthatis
substitutablefortheirparameterstype.
Asusual,atemplatesolutionismorecompact:
//:C09:Interfaces2.cpp
//Implicitinterfaceinheritanceviatemplates.
#include<iostream>
#include<sstream>
#include<string>
usingnamespacestd
classAble{
intmyData
public:
Able(intx){myData=x}
voidprint(ostream&os)const{os<<myData}
inttoInt()const{returnmyData}
stringtoString()const{
ostringstreamos
os<<myData
returnos.str()
}
}
template<classPrintable>
voidtestPrintable(constPrintable&p){
p.print(cout)
cout<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
323/458
5/3/2015
ThinkinginC++2ndedVolume2
template<classIntable>
voidtestIntable(constIntable&n){
cout<<n.toInt()+1<<endl
}
template<classStringable>
voidtestStringable(constStringable&s){
cout<<s.toString()+"th"<<endl
}
intmain(){
Ablea(7)
testPrintable(a)
testIntable(a)
testStringable(a)
}///:~
ThenamesPrintable,Intable,andStringablearenowjusttemplateparametersthatassumethe
existenceoftheoperationsindicatedintheirrespectivecontexts.Inotherwords,thetestfunctionscan
acceptargumentsofanytypethatprovidesamemberfunctiondefinitionwiththecorrectsignatureand
returntypederivingfromacommonbaseclassinnotnecessary.Somepeoplearemorecomfortablewith
thefirstversionbecausethetypenamesguaranteebyinheritancethattheexpectedinterfacesare
implemented.Othersarecontentwiththefactthatiftheoperationsrequiredbythetestfunctionsarenot
satisfiedbytheirtemplatetypearguments,theerrorisstillcaughtatcompiletime.Thelatterapproachis
technicallyaweakerformoftypecheckingthantheformer(inheritance)approach,buttheeffectonthe
programmer(andtheprogram)isthesame.Thisisoneformofweaktypingthatisacceptabletomanyof
todaysC++programmers.
Implementationinheritance
Aswestatedearlier,C++providesonlyimplementationinheritance,meaningthatyoualwaysinherit
everythingfromyourbaseclasses.Thiscanbegoodbecauseitfreesyoufromhavingtoimplement
everythinginthederivedclass,aswehadtodowiththeinterfaceinheritanceexamplesearlier.Acommon
useofmultipleinheritanceinvolvesusingmixinclasses,whichareclassesthatexisttoaddcapabilitiesto
otherclassesthroughinheritance.Mixinclassesarenotintendedtobeinstantiatedbythemselves.
Asanexample,supposeweareclientsofaclassthatsupportsaccesstoadatabase.Inthisscenario,you
onlyhaveaheaderfileavailablepartofthepointhereisthatyoudonthaveaccesstothesourcecode
fortheimplementation.Forillustration,assumethefollowingimplementationofaDatabaseclass:
//:C09:Database.h
//Aprototypicalresourceclass.
#ifndefDATABASE_H
#defineDATABASE_H
#include<iostream>
#include<stdexcept>
#include<string>
structDatabaseError:std::runtime_error{
DatabaseError(conststd::string&msg)
:std::runtime_error(msg){}
}
classDatabase{
std::stringdbid
public:
Database(conststd::string&dbStr):dbid(dbStr){}
virtual~Database(){}
voidopen()throw(DatabaseError){
std::cout<<"Connectedto"<<dbid<<std::endl
}
voidclose(){
std::cout<<dbid<<"closed"<<std::endl
}
//Otherdatabasefunctions...
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
324/458
5/3/2015
ThinkinginC++2ndedVolume2
#endif//DATABASE_H///:~
Wereleavingoutactualdatabasefunctionality(storing,retrieving,andsoon),butthatsnotimportant
here.UsingthisclassrequiresadatabaseconnectionstringandthatyoucallDatabase::open()to
connectandDatabase::close()todisconnect:
//:C09:UseDatabase.cpp
#include"Database.h"
intmain(){
Databasedb("MyDatabase")
db.open()
//Useotherdbfunctions...
db.close()
}
/*Output:
connectedtoMyDatabase
MyDatabaseclosed
*////:~
Inatypicalclientserversituation,aclientwillhavemultipleobjectssharingaconnectiontoadatabase.It
isimportantthatthedatabaseeventuallybeclosed,butonlyafteraccesstoitisnolongerrequired.Itis
commontoencapsulatethisbehaviorthroughaclassthattracksthenumberofcliententitiesusingthe
databaseconnectionandtoautomaticallyterminatetheconnectionwhenthatcountgoestozero.Toadd
referencecountingtotheDatabaseclass,weusemultipleinheritancetomixaclassnamedCountable
intotheDatabaseclasstocreateanewclass,DBConnection.HerestheCountablemixinclass:
//:C09:Countable.h
//A"mixin"class.
#ifndefCOUNTABLE_H
#defineCOUNTABLE_H
#include<cassert>
classCountable{
longcount
protected:
Countable(){count=0}
virtual~Countable(){assert(count==0)}
public:
longattach(){return++count}
longdetach(){
return(count>0)?count:(deletethis,0)
}
longrefCount()const{returncount}
}
#endif//COUNTABLE_H///:~
Itisevidentthatthisisnotastandaloneclassbecauseitsconstructorisprotecteditrequiresafriendor
aderivedclasstouseit.Itisimportantthatthedestructorisvirtual,becauseitiscalledonlyfromthe
deletethisstatementindetach(),andwewantderivedobjectstobeproperlydestroyed.[122]
TheDBConnectionclassinheritsbothDatabaseandCountableandprovidesastaticcreate()function
thatinitializesitsCountablesubobject.ThisisanexampleoftheFactoryMethoddesignpattern,discussed
inthenextchapter:
//:C09:DBConnection.h
//Usesa"mixin"class.
#ifndefDBCONNECTION_H
#defineDBCONNECTION_H
#include<cassert>
#include<string>
#include"Countable.h"
#include"Database.h"
usingstd::string
classDBConnection:publicDatabase,publicCountable{
DBConnection(constDBConnection&)//Disallowcopy
DBConnection&operator=(constDBConnection&)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
325/458
5/3/2015
ThinkinginC++2ndedVolume2
protected:
DBConnection(conststring&dbStr)throw(DatabaseError)
:Database(dbStr){open()}
~DBConnection(){close()}
public:
staticDBConnection*
create(conststring&dbStr)throw(DatabaseError){
DBConnection*con=newDBConnection(dbStr)
con>attach()
assert(con>refCount()==1)
returncon
}
//Otheraddedfunctionalityasdesired...
}
#endif//DBCONNECTION_H///:~
WenowhaveareferencecounteddatabaseconnectionwithoutmodifyingtheDatabaseclass,andwecan
safelyassumethatitwillnotbesurreptitiouslyterminated.Theopeningandclosingisdoneusingthe
ResourceAcquisitionIsInitialization(RAII)idiommentionedinChapter1viatheDBConnection
constructoranddestructor.ThismakestheDBConnectioneasytouse:
//:C09:UseDatabase2.cpp
//TeststheCountable"mixin"class.
#include<cassert>
#include"DBConnection.h"
classDBClient{
DBConnection*db
public:
DBClient(DBConnection*dbCon){
db=dbCon
db>attach()
}
~DBClient(){db>detach()}
//Otherdatabaserequestsusingdb
}
intmain(){
DBConnection*db=DBConnection::create("MyDatabase")
assert(db>refCount()==1)
DBClientc1(db)
assert(db>refCount()==2)
DBClientc2(db)
assert(db>refCount()==3)
//Usedatabase,thenreleaseattachfromoriginalcreate
db>detach()
assert(db>refCount()==2)
}///:~
ThecalltoDBConnection::create()callsattach(),sowhenwerefinished,wemustexplicitlycall
detach()toreleasetheoriginalholdontheconnection.NotethattheDBClientclassalsousesRAIIto
manageitsuseoftheconnection.Whentheprogramterminates,thedestructorsforthetwoDBClient
objectswilldecrementthereferencecount(bycallingdetach(),whichDBConnectioninheritedfrom
Countable),andthedatabaseconnectionwillbeclosed(becauseofCountablesvirtualdestructor)when
thecountreacheszeroaftertheobjectc1isdestroyed.
Atemplateapproachiscommonlyusedformixininheritance,allowingtheusertospecifyatcompiletime
whichflavorofmixinisdesired.Thiswayyoucanusedifferentreferencecountingapproacheswithout
explicitlydefiningDBConnectiontwice.Hereshowitsdone:
//:C09:DBConnection2.h
//Aparameterizedmixin.
#ifndefDBCONNECTION2_H
#defineDBCONNECTION2_H
#include<cassert>
#include<string>
#include"Database.h"
usingstd::string
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
326/458
5/3/2015
ThinkinginC++2ndedVolume2
template<classCounter>
classDBConnection:publicDatabase,publicCounter{
DBConnection(constDBConnection&)//Disallowcopy
DBConnection&operator=(constDBConnection&)
protected:
DBConnection(conststring&dbStr)throw(DatabaseError)
:Database(dbStr){open()}
~DBConnection(){close()}
public:
staticDBConnection*create(conststring&dbStr)
throw(DatabaseError){
DBConnection*con=newDBConnection(dbStr)
con>attach()
assert(con>refCount()==1)
returncon
}
//Otheraddedfunctionalityasdesired...
}
#endif//DBCONNECTION2_H///:~
Theonlychangehereisthetemplateprefixtotheclassdefinition(andrenamingCountabletoCounter
forclarity).Wecouldalsomakethedatabaseclassatemplateparameter(hadwemultipledatabase
accessclassestochoosefrom),butitisnotamixinsinceitisastandaloneclass.Thefollowingexample
usestheoriginalCountableastheCountermixintype,butwecoulduseanytypethatimplementsthe
appropriateinterface(attach(),detach(),andsoon):
//:C09:UseDatabase3.cpp
//Testsaparameterized"mixin"class.
#include<cassert>
#include"Countable.h"
#include"DBConnection2.h"
classDBClient{
DBConnection<Countable>*db
public:
DBClient(DBConnection<Countable>*dbCon){
db=dbCon
db>attach()
}
~DBClient(){db>detach()}
}
intmain(){
DBConnection<Countable>*db=
DBConnection<Countable>::create("MyDatabase")
assert(db>refCount()==1)
DBClientc1(db)
assert(db>refCount()==2)
DBClientc2(db)
assert(db>refCount()==3)
db>detach()
assert(db>refCount()==2)
}///:~
Thegeneralpatternformultipleparameterizedmixinsissimply
template<classMixin1,classMixin2,,classMixinK>
classSubject:publicMixin1,
publicMixin2,
publicMixinK{}
Duplicatesubobjects
Whenyouinheritfromabaseclass,yougetacopyofallthedatamembersofthatbaseclassinyour
derivedclass.Thefollowingprogramshowshowmultiplebasesubobjectsmightbelaidoutinmemory:[123]
//:C09:Offset.cpp
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
327/458
5/3/2015
ThinkinginC++2ndedVolume2
//IllustrateslayoutofsubobjectswithMI.
#include<iostream>
usingnamespacestd
classA{intx}
classB{inty}
classC:publicA,publicB{intz}
intmain(){
cout<<"sizeof(A)=="<<sizeof(A)<<endl
cout<<"sizeof(B)=="<<sizeof(B)<<endl
cout<<"sizeof(C)=="<<sizeof(C)<<endl
Cc
cout<<"&c=="<<&c<<endl
A*ap=&c
B*bp=&c
cout<<"ap=="<<static_cast<void*>(ap)<<endl
cout<<"bp=="<<static_cast<void*>(bp)<<endl
C*cp=static_cast<C*>(bp)
cout<<"cp=="<<static_cast<void*>(cp)<<endl
cout<<"bp==cp?"<<boolalpha<<(bp==cp)<<endl
cp=0
bp=cp
cout<<bp<<endl
}
/*Output:
sizeof(A)==4
sizeof(B)==4
sizeof(C)==12
&c==1245052
ap==1245052
bp==1245056
cp==1245052
bp==cp?true
0
*////:~
Asyoucansee,theBportionoftheobjectcisoffset4bytesfromthebeginningoftheentireobject,
suggestingthefollowinglayout:
TheobjectcbeginswithitsAsubobject,thentheBportion,andfinallythedatafromthecompletetypeC
itself.SinceaCisanAandisaB,itispossibletoupcasttoeitherbasetype.WhenupcastingtoanA,the
resultingpointerpointstotheAportion,whichhappenstobeatthebeginningoftheCobject,sothe
addressapisthesameastheexpression&c.WhenupcastingtoaB,however,theresultingpointermust
pointtowheretheBsubobjectactuallyresidesbecauseclassBknowsnothingaboutclassC(orclassA,
forthatmatter).Inotherwords,theobjectpointedtobybpmustbeabletobehaveasastandaloneB
object(exceptforanyrequiredpolymorphicbehavior).
WhencastingbpbacktoaC*,sincetheoriginalobjectwasaCinthefirstplace,thelocationwheretheB
subobjectresidesisknown,sothepointerisadjustedbacktotheoriginaladdressofthecompleteobject.
IfbphadbeenpointingtoastandaloneBobjectinsteadofaCobjectinthefirstplace,thecastwouldbe
illegal.[124]Furthermore,inthecomparisonbp==cp,cpisimplicitlyconvertedtoaB*,sincethatisthe
onlywaytomakethecomparisonmeaningful(thatis,upcastingisalwaysallowed),hencethetrueresult.
Sowhenconvertingbackandforthbetweensubobjectsandcompletetypes,theappropriateoffsetis
applied.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
328/458
5/3/2015
ThinkinginC++2ndedVolume2
Thenullpointerrequiresspecialhandling,obviously,sinceblindlysubtractinganoffsetwhenconvertingto
orfromaBsubobjectwillresultinaninvalidaddressifthepointerwaszerotostartwith.Forthisreason,
whencastingtoorfromaB*,thecompilergenerateslogictocheckfirsttoseeifthepointeriszero.Ifit
isnt,itappliestheoffsetotherwise,itleavesitaszero.
Withthesyntaxweveseensofar,ifyouhavemultiplebaseclasses,andifthosebaseclassesinturnhave
acommonbaseclass,youwillhavetwocopiesofthetoplevelbase,asyoucanseeinthefollowing
example:
//:C09:Duplicate.cpp
//Showsduplicatesubobjects.
#include<iostream>
usingnamespacestd
classTop{
intx
public:
Top(intn){x=n}
}
classLeft:publicTop{
inty
public:
Left(intm,intn):Top(m){y=n}
}
classRight:publicTop{
intz
public:
Right(intm,intn):Top(m){z=n}
}
classBottom:publicLeft,publicRight{
intw
public:
Bottom(inti,intj,intk,intm)
:Left(i,k),Right(j,k){w=m}
}
intmain(){
Bottomb(1,2,3,4)
cout<<sizeofb<<endl//20
}///:~
Sincethesizeofbis20bytes,[125]therearefiveintegersaltogetherinacompleteBottomobject.A
typicalclassdiagramforthisscenariousuallyappearsas:
Thisisthesocalleddiamondinheritance,butinthiscaseitwouldbebetterrenderedas:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
329/458
5/3/2015
ThinkinginC++2ndedVolume2
TheawkwardnessofthisdesignsurfacesintheconstructorfortheBottomclassinthepreviouscode.The
userthinksthatonlyfourintegersarerequired,butwhichargumentsshouldbepassedtothetwo
parametersthatLeftandRightrequire?Althoughthisdesignisnotinherentlywrong,itisusuallynot
whatanapplicationneeds.ItalsopresentsaproblemwhentryingtoconvertapointertoaBottomobject
toapointertoTop.Asweshowedearlier,theaddressmayneedtobeadjusted,dependingonwherethe
subobjectresideswithinthecompleteobject,butheretherearetwoTopsubobjectstochoosefrom.The
compilerdoesntknowwhichtochoose,sosuchanupcastisambiguousandisnotallowed.Thesame
reasoningexplainswhyaBottomobjectwouldnotbeabletocallafunctionthatisonlydefinedinTop.If
suchafunctionTop::f()existed,callingb.f()abovewouldneedtorefertoaTopsubobjectasan
executioncontext,andtherearetwotochoosefrom.
Virtualbaseclasses
Whatweusuallywantinsuchcasesistruediamondinheritance,whereasingleTopobjectissharedby
bothLeftandRightsubobjectswithinacompleteBottomobject,whichiswhatthefirstclassdiagram
depicts.ThisisachievedbymakingTopavirtualbaseclassofLeftandRight:
//:C09:VirtualBase.cpp
//Showsasharedsubobjectviaavirtualbase.
#include<iostream>
usingnamespacestd
classTop{
protected:
intx
public:
Top(intn){x=n}
virtual~Top(){}
friendostream&
operator<<(ostream&os,constTop&t){
returnos<<t.x
}
}
classLeft:virtualpublicTop{
protected:
inty
public:
Left(intm,intn):Top(m){y=n}
}
classRight:virtualpublicTop{
protected:
intz
public:
Right(intm,intn):Top(m){z=n}
}
classBottom:publicLeft,publicRight{
intw
public:
Bottom(inti,intj,intk,intm)
:Top(i),Left(0,j),Right(0,k){w=m}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
330/458
5/3/2015
ThinkinginC++2ndedVolume2
friendostream&
operator<<(ostream&os,constBottom&b){
returnos<<b.x<<','<<b.y<<','<<b.z
<<','<<b.w
}
}
intmain(){
Bottomb(1,2,3,4)
cout<<sizeofb<<endl
cout<<b<<endl
cout<<static_cast<void*>(&b)<<endl
Top*p=static_cast<Top*>(&b)
cout<<*p<<endl
cout<<static_cast<void*>(p)<<endl
cout<<dynamic_cast<void*>(p)<<endl
}///:~
Eachvirtualbaseofagiventypereferstothesameobject,nomatterwhereitappearsinthehierarchy.
[126]
ThismeansthatwhenaBottomobjectisinstantiated,theobjectlayoutmaylooksomethinglikethis:
TheLeftandRightsubobjectseachhaveapointer(orsomeconceptualequivalent)tothesharedTop
subobject,andallreferencestothatsubobjectinLeftandRightmemberfunctionswillgothroughthose
thesepointers.[127]Here,thereisnoambiguitywhenupcastingfromaBottomtoaTopobject,sincethere
isonlyoneTopobjecttoconvertto.
Theoutputofthepreviousprogramisasfollows:
36
1,2,3,4
1245032
1
1245060
1245032
TheaddressesprintedsuggestthatthisparticularimplementationdoesindeedstoretheTopsubobjectat
theendofthecompleteobject(althoughitsnotreallyimportantwhereitgoes).Theresultofa
dynamic_casttovoid*alwaysresolvestotheaddressofthecompleteobject.
Althoughitistechnicallyillegaltodoso[128],ifyouremovethevirtualdestructor(andthedynamic_cast
statement,sotheprogramwillcompile),thesizeofBottomdecreasesto24bytes.Thatseemstobea
decreaseequivalenttothesizeofthreepointers.Why?
Itsimportantnottotakethesenumberstooliterally.Othercompilersweusemanageonlytoincreasethe
sizebyfourbyteswhenthevirtualconstructorisadded.Notbeingcompilerwriters,wecanttellyoutheir
secrets.Wecantellyou,however,thatwithmultipleinheritance,aderivedobjectmustbehaveasifithas
multipleVPTRs,oneforeachofitsdirectbaseclassesthatalsohavevirtualfunctions.Itsassimpleas
that.Compilersmakewhateveroptimizationstheirauthorsinvent,butthebehaviormustbethesame.
ThestrangestthinginthepreviouscodeistheinitializerforTopintheBottomconstructor.Normallyone
doesntworryaboutinitializingsubobjectsbeyonddirectbaseclasses,sinceallclassestakecareof
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
331/458
5/3/2015
ThinkinginC++2ndedVolume2
initializingtheirownbases.Thereare,however,multiplepathsfromBottomtoTop,sorelyingonthe
intermediateclassesLeftandRighttopassalongthenecessaryinitializationdataresultsinanambiguity
whoisresponsibleforperformingtheinitialization?Forthisreason,themostderivedclassmustinitialize
avirtualbase.ButwhatabouttheexpressionsintheLeftandRightconstructorsthatalsoinitializeTop?
TheyarecertainlynecessarywhencreatingstandaloneLeftorRightobjects,butmustbeignoredwhena
Bottomobjectiscreated(hencethezerosintheirinitializersintheBottomconstructoranyvaluesin
thoseslotsareignoredwhentheLeftandRightconstructorsexecuteinthecontextofaBottomobject).
Thecompilertakescareofallthisforyou,butitsimportanttounderstandwheretheresponsibilitylies.
Alwaysmakesurethatallconcrete(nonabstract)classesinamultipleinheritancehierarchyareawareof
anyvirtualbasesandinitializethemappropriately.
Theserulesofresponsibilityapplynotonlytoinitialization,buttoalloperationsthatspantheclass
hierarchy.Considerthestreaminserterinthepreviouscode.Wemadethedataprotectedsowecould
cheatandaccessinheriteddatainoperator<<(ostream&,constBottom&).Itusuallymakesmore
sensetoassigntheworkofprintingeachsubobjecttoitscorrespondingclassandhavethederivedclass
callitsbaseclassfunctionsasneeded.Whatwouldhappenifwetriedthatwithoperator<<(),asthe
followingcodeillustrates?
//:C09:VirtualBase2.cpp
//HowNOTtoimplementoperator<<.
#include<iostream>
usingnamespacestd
classTop{
intx
public:
Top(intn){x=n}
virtual~Top(){}
friendostream&operator<<(ostream&os,constTop&t){
returnos<<t.x
}
}
classLeft:virtualpublicTop{
inty
public:
Left(intm,intn):Top(m){y=n}
friendostream&operator<<(ostream&os,constLeft&l){
returnos<<static_cast<constTop&>(l)<<','<<l.y
}
}
classRight:virtualpublicTop{
intz
public:
Right(intm,intn):Top(m){z=n}
friendostream&operator<<(ostream&os,constRight&r){
returnos<<static_cast<constTop&>(r)<<','<<r.z
}
}
classBottom:publicLeft,publicRight{
intw
public:
Bottom(inti,intj,intk,intm)
:Top(i),Left(0,j),Right(0,k){w=m}
friendostream&operator<<(ostream&os,constBottom&b){
returnos<<static_cast<constLeft&>(b)
<<','<<static_cast<constRight&>(b)
<<','<<b.w
}
}
intmain(){
Bottomb(1,2,3,4)
cout<<b<<endl//1,2,1,3,4
}///:~
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
332/458
5/3/2015
ThinkinginC++2ndedVolume2
Youcantjustblindlysharetheresponsibilityupwardintheusualfashion,becausetheLeftandRight
streaminserterseachcalltheTopinserter,andagaintherewillbeduplicationofdata.Insteadyouneedto
mimicwhatthecompilerdoeswithinitialization.Onesolutionistoprovidespecialfunctionsintheclasses
thatknowaboutthevirtualbaseclass,whichignorethevirtualbasewhenprinting(leavingthejobtothe
mostderivedclass):
//:C09:VirtualBase3.cpp
//Acorrectstreaminserter.
#include<iostream>
usingnamespacestd
classTop{
intx
public:
Top(intn){x=n}
virtual~Top(){}
friendostream&operator<<(ostream&os,constTop&t){
returnos<<t.x
}
}
classLeft:virtualpublicTop{
inty
protected:
voidspecialPrint(ostream&os)const{
//OnlyprintLeft'spart
os<<','<<y
}
public:
Left(intm,intn):Top(m){y=n}
friendostream&operator<<(ostream&os,constLeft&l){
returnos<<static_cast<constTop&>(l)<<','<<l.y
}
}
classRight:virtualpublicTop{
intz
protected:
voidspecialPrint(ostream&os)const{
//OnlyprintRight'spart
os<<','<<z
}
public:
Right(intm,intn):Top(m){z=n}
friendostream&operator<<(ostream&os,constRight&r){
returnos<<static_cast<constTop&>(r)<<','<<r.z
}
}
classBottom:publicLeft,publicRight{
intw
public:
Bottom(inti,intj,intk,intm)
:Top(i),Left(0,j),Right(0,k){w=m}
friendostream&operator<<(ostream&os,constBottom&b){
os<<static_cast<constTop&>(b)
b.Left::specialPrint(os)
b.Right::specialPrint(os)
returnos<<','<<b.w
}
}
intmain(){
Bottomb(1,2,3,4)
cout<<b<<endl//1,2,3,4
}///:~
ThespecialPrint()functionsareprotectedsincetheywillbecalledonlybyBottom.Theyprintonly
theirowndataandignoretheirTopsubobjectbecausetheBottominserterisincontrolwhenthese
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
333/458
5/3/2015
ThinkinginC++2ndedVolume2
functionsarecalled.TheBottominsertermustknowaboutthevirtualbase,justasaBottomconstructor
needsto.Thissamereasoningappliestoassignmentoperatorsinahierarchywithavirtualbase,aswell
astoanyfunction,memberornot,thatwantstosharetheworkthroughoutallclassesinthehierarchy.
Havingdiscussedvirtualbaseclasses,wecannowillustratethefullstoryofobjectinitialization.Since
virtualbasesgiverisetosharedsubobjects,itmakessensethattheyshouldbeavailablebeforethe
sharingtakesplace.Sotheorderofinitializationofsubobjectsfollowstheserules,recursively:
1.Allvirtualbaseclasssubobjectsareinitialized,intopdown,lefttorightorderaccordingto
wheretheyappearinclassdefinitions.
2.Nonvirtualbaseclassesaretheninitializedintheusualorder.
3.Allmemberobjectsareinitializedindeclarationorder.
4.Thecompleteobjectsconstructorexecutes.
Thefollowingprogramillustratesthisbehavior:
//:C09:VirtInit.cpp
//Illustratesinitializationorderwithvirtualbases.
#include<iostream>
#include<string>
usingnamespacestd
classM{
public:
M(conststring&s){cout<<"M"<<s<<endl}
}
classA{
Mm
public:
A(conststring&s):m("inA"){
cout<<"A"<<s<<endl
}
virtual~A(){}
}
classB{
Mm
public:
B(conststring&s):m("inB"){
cout<<"B"<<s<<endl
}
virtual~B(){}
}
classC{
Mm
public:
C(conststring&s):m("inC"){
cout<<"C"<<s<<endl
}
virtual~C(){}
}
classD{
Mm
public:
D(conststring&s):m("inD"){
cout<<"D"<<s<<endl
}
virtual~D(){}
}
classE:publicA,virtualpublicB,virtualpublicC{
Mm
public:
E(conststring&s):A("fromE"),B("fromE"),
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
334/458
5/3/2015
ThinkinginC++2ndedVolume2
C("fromE"),m("inE"){
cout<<"E"<<s<<endl
}
}
classF:virtualpublicB,virtualpublicC,publicD{
Mm
public:
F(conststring&s):B("fromF"),C("fromF"),
D("fromF"),m("inF"){
cout<<"F"<<s<<endl
}
}
classG:publicE,publicF{
Mm
public:
G(conststring&s):B("fromG"),C("fromG"),
E("fromG"),F("fromG"),m("inG"){
cout<<"G"<<s<<endl
}
}
intmain(){
Gg("frommain")
}///:~
Theclassesinthiscodecanberepresentedbythefollowingdiagram:
Eachclasshasanembedded
memberoftypeM.Notethatonlyfourderivationsarevirtual:EfromBandC,andFfromBandC.The
outputofthisprogramis:
MinB
BfromG
MinC
CfromG
MinA
AfromE
MinE
EfromG
MinD
DfromF
MinF
FfromG
MinG
Gfrommain
TheinitializationofgrequiresitsEandFparttofirstbeinitialized,buttheBandCsubobjectsare
initializedfirstbecausetheyarevirtualbasesandareinitializedfromGsinitializer,Gbeingthemost
derivedclass.TheclassBhasnobaseclasses,soaccordingtorule3,itsmemberobjectmisinitialized,
thenitsconstructorprintsBfromG,andsimilarlyfortheCsubjectofE.TheEsubobjectrequiresA,B,
andCsubobjects.SinceBandChavealreadybeeninitialized,theAsubobjectoftheEsubobjectis
initializednext,andthentheEsubobjectitself.ThesamescenariorepeatsforgsFsubobject,butwithout
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
335/458
5/3/2015
ThinkinginC++2ndedVolume2
duplicatingtheinitializationofthevirtualbases.
Namelookupissues
Theambiguitieswehaveillustratedwithsubobjectsapplytoanynames,includingfunctionnames.Ifa
classhasmultipledirectbaseclassesthatsharememberfunctionsofthesamename,andyoucalloneof
thosememberfunctions,thecompilerdoesntknowwhichonetochoose.Thefollowingsampleprogram
wouldreportsuchanerror:
//:C09:AmbiguousName.cpp{xo}
classTop{
public:
virtual~Top(){}
}
classLeft:virtualpublicTop{
public:
voidf(){}
}
classRight:virtualpublicTop{
public:
voidf(){}
}
classBottom:publicLeft,publicRight{}
intmain(){
Bottomb
b.f()//Errorhere
}///:~
TheclassBottomhasinheritedtwofunctionsofthesamename(thesignatureisirrelevant,sincename
lookupoccursbeforeoverloadresolution),andthereisnowaytochoosebetweenthem.Theusual
techniquetodisambiguatethecallistoqualifythefunctioncallwiththebaseclassname:
//:C09:BreakTie.cpp
classTop{
public:
virtual~Top(){}
}
classLeft:virtualpublicTop{
public:
voidf(){}
}
classRight:virtualpublicTop{
public:
voidf(){}
}
classBottom:publicLeft,publicRight{
public:
usingLeft::f
}
intmain(){
Bottomb
b.f()//CallsLeft::f()
}///:~
ThenameLeft::fisnowfoundinthescopeofBottom,sothenameRight::fisnotevenconsidered.To
introduceextrafunctionalitybeyondwhatLeft::f()provides,youimplementaBottom::f()functionthat
callsLeft::f().
Functionswiththesamenameoccurringindifferentbranchesofahierarchyoftenconflict.Thefollowing
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
336/458
5/3/2015
ThinkinginC++2ndedVolume2
hierarchyhasnosuchproblem:
//:C09:Dominance.cpp
classTop{
public:
virtual~Top(){}
virtualvoidf(){}
}
classLeft:virtualpublicTop{
public:
voidf(){}
}
classRight:virtualpublicTop{}
classBottom:publicLeft,publicRight{}
intmain(){
Bottomb
b.f()//CallsLeft::f()
}///:~
Here,thereisnoexplicitRight::f().SinceLeft::f()isthemostderived,itischosen.Why?Well,pretend
thatRightdidnotexist,givingthesingleinheritancehierarchyTop<=Left<=Bottom.Youwould
certainlyexpectLeft::f()tobethefunctioncalledbytheexpressionb.f()becauseofnormalscoperules:
aderivedclassisconsideredanestedscopeofabaseclass.Ingeneral,anameA::fdominatesthename
B::fifAderivesfromB,directlyorindirectly,orinotherwords,ifAismorederivedinthehierarchy
thanB.[129]Therefore,inchoosingbetweentwofunctionswiththesamename,thecompilerchoosesthe
onethatdominates.Ifthereisnodominantname,thereisanambiguity.
Thefollowingprogramfurtherillustratesthedominanceprinciple:
//:C09:Dominance2.cpp
#include<iostream>
usingnamespacestd
classA{
public:
virtual~A(){}
virtualvoidf(){cout<<"A::f\n"}
}
classB:virtualpublicA{
public:
voidf(){cout<<"B::f\n"}
}
classC:publicB{}
classD:publicC,virtualpublicA{}
intmain(){
B*p=newD
p>f()//CallsB::f()
deletep
}///:~
Theclassdiagramforthishierarchyis
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
337/458
5/3/2015
ThinkinginC++2ndedVolume2
TheclassAisa(direct,inthiscase)baseclassforB,andsothenameB::fdominatesA::f.
AvoidingMI
Whenthequestionofwhethertousemultipleinheritancecomesup,askatleasttwoquestions:
1.Doyouneedtoshowthepublicinterfacesofboththeseclassesthroughyournewtype?(See
insteadifoneclasscanbecontainedwithintheother,withonlysomeofitsinterfaceexposedin
thenewclass.)
2.Doyouneedtoupcasttobothofthebaseclasses?(Thisalsoapplieswhenyouhavemorethan
twobaseclasses.)
Ifyoucananswernotoeitherquestion,youcanavoidusingMIandshouldprobablydoso.
Watchforthesituationwhereoneclassneedstobeupcastonlyasafunctionargument.Inthatcase,the
classcanbeembeddedandanautomatictypeconversionfunctionprovidedinyournewclasstoproducea
referencetotheembeddedobject.Anytimeyouuseanobjectofyournewclassasanargumenttoa
functionthatexpectstheembeddedobject,thetypeconversionfunctionisused.[130]However,type
conversioncantbeusedfornormalpolymorphicmemberfunctionselectionthatrequiresinheritance.
Preferringcompositionoverinheritanceisagoodoveralldesignguideline.
Extendinganinterface
Oneofthebestusesformultipleinheritanceinvolvescodethatsoutofyourcontrol.Supposeyouve
acquiredalibrarythatconsistsofaheaderfileandcompiledmemberfunctions,butnosourcecodefor
memberfunctions.Thislibraryisaclasshierarchywithvirtualfunctions,anditcontainssomeglobal
functionsthattakepointerstothebaseclassofthelibrarythatis,itusesthelibraryobjects
polymorphically.Nowsupposeyoubuildanapplicationaroundthislibraryandwriteyourowncodethat
usesthebaseclasspolymorphically.
Laterinthedevelopmentoftheprojectorsometimeduringitsmaintenance,youdiscoverthatthebase
classinterfaceprovidedbythevendordoesntprovidewhatyouneed:afunctionmaybenonvirtualand
youneedittobevirtual,oravirtualfunctioniscompletelymissingintheinterface,butessentialtothe
solutionofyourproblem.Multipleinheritancecanbethesolution.
Forexample,herestheheaderfileforalibraryyouacquire:
//:C09:Vendor.h
//Vendorsuppliedclassheader
//Youonlygetthis&thecompiledVendor.obj.
#ifndefVENDOR_H
#defineVENDOR_H
classVendor{
public:
virtualvoidv()const
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
338/458
5/3/2015
ThinkinginC++2ndedVolume2
voidf()const//Mightwantthistobevirtual...
~Vendor()//Oops!Notvirtual!
}
classVendor1:publicVendor{
public:
voidv()const
voidf()const
~Vendor1()
}
voidA(constVendor&)
voidB(constVendor&)
//Etc.
#endif//VENDOR_H///:~
Assumethelibraryismuchbigger,withmorederivedclassesandalargerinterface.Noticethatitalso
includesthefunctionsA()andB(),whichtakeabasereferenceandtreatitpolymorphically.Heresthe
implementationfileforthelibrary:
//:C09:Vendor.cpp{O}
//Assumethisiscompiledandunavailabletoyou.
#include"Vendor.h"
#include<iostream>
usingnamespacestd
voidVendor::v()const{cout<<"Vendor::v()"<<endl}
voidVendor::f()const{cout<<"Vendor::f()"<<endl}
Vendor::~Vendor(){cout<<"~Vendor()"<<endl}
voidVendor1::v()const{cout<<"Vendor1::v()"<<endl}
voidVendor1::f()const{cout<<"Vendor1::f()"<<endl}
Vendor1::~Vendor1(){cout<<"~Vendor1()"<<endl}
voidA(constVendor&v){
//...
v.v()
v.f()
//...
}
voidB(constVendor&v){
//...
v.v()
v.f()
//...
}///:~
Inyourproject,thissourcecodeisunavailabletoyou.Instead,yougetacompiledfileasVendor.objor
Vendor.lib(orwiththeequivalentfilesuffixesforyoursystem).
Theproblemoccursintheuseofthislibrary.First,thedestructorisntvirtual.[131]Inaddition,f()wasnot
madevirtualweassumethelibrarycreatordecideditwouldntneedtobe.Youalsodiscoverthatthe
interfacetothebaseclassismissingafunctionessentialtothesolutionofyourproblem.Alsosuppose
youvealreadywrittenafairamountofcodeusingtheexistinginterface(nottomentionthefunctionsA()
andB(),whichareoutofyourcontrol),andyoudontwanttochangeit.
Torepairtheproblem,createyourownclassinterfaceandmultiplyinheritanewsetofderivedclasses
fromyourinterfaceandfromtheexistingclasses:
//:C09:Paste.cpp
//{L}Vendor
//FixingamesswithMI.
#include<iostream>
#include"Vendor.h"
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
339/458
5/3/2015
ThinkinginC++2ndedVolume2
usingnamespacestd
classMyBase{//RepairVendorinterface
public:
virtualvoidv()const=0
virtualvoidf()const=0
//Newinterfacefunction:
virtualvoidg()const=0
virtual~MyBase(){cout<<"~MyBase()"<<endl}
}
classPaste1:publicMyBase,publicVendor1{
public:
voidv()const{
cout<<"Paste1::v()"<<endl
Vendor1::v()
}
voidf()const{
cout<<"Paste1::f()"<<endl
Vendor1::f()
}
voidg()const{cout<<"Paste1::g()<<endl}
~Paste1(){cout<<"~Paste1()<<endl}
}
intmain(){
Paste1&p1p=*newPaste1
MyBase&mp=p1p//Upcast
cout<<"callingf()<<endl
mp.f()//Rightbehavior
cout<<"callingg()<<endl
mp.g()//Newbehavior
cout<<"callingA(p1p)<<endl
A(p1p)//Sameoldbehavior
cout<<"callingB(p1p)<<endl
B(p1p)//Sameoldbehavior
cout<<"deletemp<<endl
//Deletingareferencetoaheapobject:
delete&mp//Rightbehavior
}///:~
InMyBase(whichdoesnotuseMI),bothf()andthedestructorarenowvirtual,andanewvirtual
functiong()isaddedtotheinterface.Noweachofthederivedclassesintheoriginallibrarymustbere
created,mixinginthenewinterfacewithMI.ThefunctionsPaste1::v()andPaste1::f()needtocall
onlytheoriginalbaseclassversionsoftheirfunctions.Butnow,ifyouupcasttoMyBaseasinmain():
MyBase*mp=p1p//Upcast
anyfunctioncallsmadethroughmpwillbepolymorphic,includingdelete.Also,thenewinterfacefunction
g()canbecalledthroughmp.Herestheoutputoftheprogram:
callingf()
Paste1::f()
Vendor1::f()
callingg()
Paste1::g()
callingA(p1p)
Paste1::v()
Vendor1::v()
Vendor::f()
callingB(p1p)
Paste1::v()
Vendor1::v()
Vendor::f()
deletemp
~Paste1()
~Vendor1()
~Vendor()
~MyBase()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
340/458
5/3/2015
ThinkinginC++2ndedVolume2
TheoriginallibraryfunctionsA()andB()stillworkthesame(assumingthenewv()callsitsbaseclass
version).Thedestructorisnowvirtualandexhibitsthecorrectbehavior.
Althoughthisisamessyexample,itdoesoccurinpractice,anditsagooddemonstrationofwhere
multipleinheritanceisclearlynecessary:Youmustbeabletoupcasttobothbaseclasses.
Summary
OnereasonMIexistsinC++isthatitisahybridlanguageandcouldntenforceasinglemonolithicclass
hierarchythewaySmalltalkandJavado.Instead,C++allowsmanyinheritancetreestobeformed,so
sometimesyoumayneedtocombinetheinterfacesfromtwoormoretreesintoanewclass.
Ifnodiamondsappearinyourclasshierarchy,MIisfairlysimple(althoughidenticalfunctionsignatures
inbaseclassesmuststillberesolved).Ifadiamondappears,youmaywanttoeliminateduplicate
subobjectsbyintroducingvirtualbaseclasses.Thisnotonlyaddsconfusion,buttheunderlying
representationbecomesmorecomplexandlessefficient.
Multipleinheritancehasbeencalledthegotoofthe90s.[132]Thisseemsappropriatebecause,likeagoto,
MIisbestavoidedinnormalprogramming,butcanoccasionallybeveryuseful.Itsaminorbutmore
advancedfeatureofC++,designedtosolveproblemsthatariseinspecialsituations.Ifyoufindyourself
usingitoften,youmightwanttotakealookatyourreasoning.Askyourself,MustIupcasttoallthebase
classes?Ifnot,yourlifewillbeeasierifyouembedinstancesofalltheclassesyoudontneedtoupcast
to.
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.CreateabaseclassXwithasingleconstructorthattakesanintargumentandamember
functionf(),whichtakesnoargumentsandreturnsvoid.NowderiveYandZfromX,creating
constructorsforeachofthemthattakeasingleintargument.Next,deriveAfromYandZ.
CreateanobjectofclassA,andcallf()forthatobject.Fixtheproblemwithexplicit
disambiguation.
2.StartingwiththeresultsofExercise1,createapointertoanXcalledpxandassigntoitthe
addressoftheobjectoftypeAyoucreatedbefore.Fixtheproblemusingavirtualbaseclass.
NowfixXsoyounolongerhavetocalltheconstructorforXinsideA.
3.StartingwiththeresultsofExercise2,removetheexplicitdisambiguationforf()andseeifyou
cancallf()throughpx.Traceittoseewhichfunctiongetscalled.Fixtheproblemsothecorrect
functionwillbecalledinaclasshierarchy.
4.MakeanAnimalinterfaceclasswithamakeNoise()functiondeclaration.MakeaSuperHero
interfaceclasswithasavePersonFromFire()functiondeclaration.Placeamove()function
declarationinbothinterfaceclasses.(Remembertomakeyourinterfacemethodspurevirtual.)
Nowdefinethreeseparateclasses:SuperlativeMan,Amoeba(asuperheroofuncertain
gender),andTarantulaWomanSuperlativeManimplementstheSuperHerointerfacewhile
AmoebaandTarantulaWomanimplementbothAnimalandSuperHero.Definetwoglobal
functionsanimalSound(Animal*)andsaveFromFire(SuperHero*).Invokeallthemethods
thatarecallablefromeachinterfaceinbothofthesefunctions.
5.Repeatthepreviousexercise,butusetemplatesinsteadofinheritancetoimplementthe
interfaces,aswedidinInterfaces2.cpp.
6.Definesomeconcretemixinclassesthatrepresentsuperherocapabilities(suchasStopTrain,
BendSteel,ClimbBuilding,etc.).Redoexercise4sothatyourderivedSuperHeroclasses
derivefromthesemixinsandcalltheirmemberfunctions.
7.Repeatthepreviousexerciseusingtemplatesbymakingyoursuperheropowersmixintemplate
parameters.Usethesepowerstodosomegoodinthecommunity.
8.DroppingtheAnimalinterfacefromexercise4,redefineAmoebatoonlyimplement
SuperHero.NowdefineaSuperlativeAmoebaclassthatinheritsfrombothSuperlativeMan
andAmoeba.TrytopassaSuperlativeAmoebaobjecttosaveFromFire().Whatdoyou
havetodotomakethislegal?Howdoesusingvirtualinheritancechangethesizeofyour
objects?
9.Continuingwiththepreviousexercise,addanintegerstrengthFactordatamemberto
SuperHerofromexercise4,alongwithaconstructortoinitializeit.Addconstructorsinthe
threederivedclassestoinitializestrengthFactoraswell.Whatmustyoudodifferentlyin
SuperlativeAmoeba?
10.Continuingwiththepreviousexercise,addaneatFood()memberfunctiontoboth
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
341/458
5/3/2015
ThinkinginC++2ndedVolume2
SuperlativeManandAmoeba(butnotSuperlativeAmoeba),suchthatthetwoversionsof
eatFood()takedifferenttypesoffoodobjects(sothesignaturesofthetwofunctionsdiffer).
WhatmustyoudoinSuperlativeAmoebatocalleithereatFood()function?Why?
11.DefineawellbehavedoutputstreaminserterandassignmentoperatorforSuperlativeAmoeba.
12.RemoveSuperlativeAmoebafromyourhierarchyandmodifyAmoebatoderivefromboth
SuperlativeMan(whichstillderivesfromSuperHero)andSuperHero.Implementavirtual
workout()functioninbothSuperHeroandSuperlativeMan(withidenticalsignatures),and
callitwithaAmoebaobject.Whichfunctiongetscalled?
13.RedefineSuperlativeAmoebatousecompositioninsteadofinheritancetoactasa
SuperlativeManorAmoeba.Useconversionoperatorstoprovideimplicitupcasting.Compare
thisapproachtotheinheritanceapproach.
14.SupposeyouaregivenaprecompiledPersonclass(youonlyhavetheheaderandcompiled
objectfile).SupposealsothatPersonhasanonvirtualwork()function.HaveSuperHerobe
abletoactasamildmanneredordinaryPersonbyderivingfromPersonandusingthe
implementationofPerson::work(),butmakeSuperHero::work()virtual.
15.Defineareferencecountederrorloggingmixinclass,ErrorLog,thatholdsastaticfilestreamto
whichyoucansendmessages.Theclassopensthestreamwhenitsreferencecountexceeds0
andclosesthestreamwhenthecountreturnsto0(andalwaysappendstothefile).Haveobjects
ofmultipleclassessendmessagestothestaticlogstream.Watchthestreamopenandclosevia
tracestatementsinErrorLog.
16.ModifyBreakTie.cppbyaddingaclassnamedVeryBottomthatderives(nonvirtually)from
Bottom.VeryBottomshouldlookjustlikeBottomexceptchangeLefttoRightintheusing
declarationforf.Changemain()toinstantiateaVeryBottominsteadofaBottomobject.
Whichf()getscalled?
10:DesignPatterns
describeaproblemwhichoccursoverandoveragaininourenvironment,and
thendescribethecoreofthesolutiontothatproblem,insuchawaythatyoucan
usethissolutionamilliontimesover,withouteverdoingitthesamewaytwice
ChristopherAlexander
Thischapterintroducestheimportantandyetnontraditionalpatternsapproach
toprogramdesign.
Themostimportantrecentstepforwardinobjectorienteddesignisprobablythedesignpatterns
movement,initiallychronicledinDesignPatterns,byGamma,Helm,Johnson&Vlissides(AddisonWesley,
1995),[133]whichiscommonlycalledtheGangofFourbook(GoF).GoFshows23solutionstoparticular
classesofproblems.Inthischapter,wediscussthebasicconceptsofdesignpatternsandprovidecode
examplesthatillustrateselectedpatterns.Thisshouldwhetyourappetiteforreadingmoreaboutdesign
patterns,asourceofwhathasnowbecomeanessential,almostmandatoryvocabularyforobjectoriented
programming.[134]
Thepatternconcept
Initially,youcanthinkofapatternasanespeciallycleverandinsightfulwaytosolveaparticularclassof
problem.Itappearsthatateamofpeoplehaveworkedoutalltheanglesofaproblemandhavecomeup
withthemostgeneral,flexiblesolutionforthattypeofproblem.Thisproblemcouldbeonethatyouhave
seenandsolvedbefore,butyoursolutionprobablydidnthavethekindofcompletenessyoullsee
embodiedinapattern.Furthermore,thepatternexistsindependentlyofanyparticularimplementationand
itcanbeimplementedinanumberofways.
Althoughtheyrecalleddesignpatterns,theyreallyarenttiedtotherealmofdesign.Apatternseemsto
standapartfromthetraditionalwayofthinkingaboutanalysis,design,andimplementation.Instead,a
patternembodiesacompleteideawithinaprogram,andthusitmightalsospantheanalysisphaseand
highleveldesignphase.However,becauseapatternoftenhasadirectimplementationincode,itmight
notshowupuntillowleveldesignorimplementation(andyoumightnotrealizethatyouneedaparticular
patternuntilyougettothosephases).
Thebasicconceptofapatterncanalsobeseenasthebasicconceptofprogramdesigningeneral:adding
layersofabstraction.Wheneveryouabstractsomething,youreisolatingparticulardetails,andoneofthe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
342/458
5/3/2015
ThinkinginC++2ndedVolume2
mostcompellingmotivationsforthisistoseparatethingsthatchangefromthingsthatstaythesame.
Anotherwaytoputthisisthatonceyoufindsomepartofyourprogramthatslikelytochange,youllwant
tokeepthosechangesfrompropagatingsideeffectsthroughoutyourcode.Ifyouachievethis,yourcode
willnotonlybeeasiertoreadandunderstand,butalsoeasiertomaintainwhichinvariablyresultsin
loweredcostsovertime.
Themostdifficultpartofdevelopinganelegantandmaintainabledesignisoftendiscoveringwhatwecall
thevectorofchange.(Here,vectorreferstothemaximumgradientasunderstoodinthesciences,and
notacontainerclass.)Thismeansfindingthemostimportantthingthatchangesinyoursystemor,put
anotherway,discoveringwhereyourgreatestcostis.Onceyoudiscoverthevectorofchange,youhave
thefocalpointaroundwhichtostructureyourdesign.
Sothegoalofdesignpatternsistoencapsulatechange.Ifyoulookatitthisway,youvebeenseeing
somedesignpatternsalreadyinthisbook.Forexample,inheritancecouldbethoughtofasadesign
pattern(albeitoneimplementedbythecompiler).Itexpressesdifferencesinbehavior(thatsthething
thatchanges)inobjectsthatallhavethesameinterface(thatswhatstaysthesame).Compositioncould
alsobeconsideredapattern,sinceyoucanchangedynamicallyorstaticallytheobjectsthatimplement
yourclass,andthusthewaythatclassworks.Normally,however,featuresthataredirectlysupportedby
aprogramminglanguagehavenotbeenclassifiedasdesignpatterns.
YouvealsoalreadyseenanotherpatternthatappearsinGoF:theiterator.Thisisthefundamentaltool
usedinthedesignoftheSTL,describedearlierinthisbook.Theiteratorhidestheparticular
implementationofthecontainerasyouresteppingthroughandselectingtheelementsonebyone.
Iteratorsallowyoutowritegenericcodethatperformsanoperationonalltheelementsinarangewithout
regardtothecontainerthatholdstherange.Thus,yourgenericcodecanbeusedwithanycontainerthat
canproduceiterators.
Prefercompositiontoinheritance
ThemostimportantcontributionofGoFmaynotbeapattern,butratheramaximthattheyintroducein
Chapter1:Favorobjectcompositionoverclassinheritance.Understandinginheritanceandpolymorphism
issuchachallengethatyoumaybegintoassignundueimportancetothesetechniques.Weseemany
overcomplicateddesigns(ourownincluded)thatresultfrominheritanceindulgenceforexample,many
multipleinheritancedesignsevolvebyinsistingthatinheritancebeusedeverywhere.
OneoftheguidelinesinExtremeProgrammingisDothesimplestthingthatcouldpossiblywork.Adesign
thatseemstowantinheritancecanoftenbedramaticallysimplifiedbyusingcompositioninstead,andyou
willalsodiscoverthattheresultismoreflexible,asyouwillunderstandbystudyingsomeofthedesign
patternsinthischapter.Sowhenponderingadesign,askyourself:Couldthisbesimplerusing
composition?DoIreallyneedinheritancehere,andwhatisitbuyingme?
Classifyingpatterns
GoFdiscusses23patterns,classifiedunderthreepurposes(allofwhichrevolvearoundtheparticular
aspectthatcanvary):
1.Creational:Howanobjectcanbecreated.Thisofteninvolvesisolatingthedetailsofobjectcreation
soyourcodeisntdependentonwhattypesofobjectsthereareandthusdoesnthavetobechanged
whenyouaddanewtypeofobject.ThischapterintroducesSingleton,Factories,andBuilder.
2.Structural:Theseaffectthewayobjectsareconnectedwithotherobjectstoensurethatchangesin
thesystemdontrequirechangestothoseconnections.Structuralpatternsareoftendictatedby
projectconstraints.InthischapteryoullseeProxyandAdapter.
3.Behavioral:Objectsthathandleparticulartypesofactionswithinaprogram.Theseencapsulate
processesthatyouwanttoperform,suchasinterpretingalanguage,fulfillingarequest,moving
throughasequence(asinaniterator),orimplementinganalgorithm.Thischaptercontains
examplesofCommand,TemplateMethod,State,Strategy,ChainofResponsibility,Observer,
MultipleDispatching,andVisitor.
GoFincludesasectiononeachofits23patternsalongwithoneormoreexamplesofeach,typicallyin
C++butsometimesinSmalltalk.ThisbookwillnotrepeatthedetailsofthepatternsshowninGoFsince
thatbookstandsonitsownandshouldbestudiedseparately.Thedescriptionandexamplesprovidedhere
areintendedtogiveyouagraspofthepatterns,soyoucangetafeelforwhatpatternsareaboutandwhy
theyareimportant.
Features,idioms,patterns
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
343/458
5/3/2015
ThinkinginC++2ndedVolume2
WorkcontinuesbeyondwhatisintheGoFbook.Sinceitspublication,therearemorepatternsandamore
refinedprocessfordefiningdesignpatterns.[135]Thisisimportantbecauseitisnoteasytoidentifynew
patternsortoproperlydescribethem.Thereissomeconfusioninthepopularliteratureonwhatadesign
patternis,forexample.Patternsarenottrivial,noraretheytypicallyrepresentedbyfeaturesthatare
builtintoaprogramminglanguage.Constructorsanddestructors,forexample,couldbecalledthe
guaranteedinitializationandcleanupdesignpattern.Theseareimportantandessentialconstructs,but
theyreroutinelanguagefeaturesandarenotrichenoughtobeconsidereddesignpatterns.
Anothernonexamplecomesfromvariousformsofaggregation.Aggregationisacompletelyfundamental
principleinobjectorientedprogramming:youmakeobjectsoutofotherobjects.Yetsometimesthisidea
iserroneouslyclassifiedasapattern.Thisisunfortunatebecauseitpollutestheideaofthedesignpattern
andsuggeststhatanythingthatsurprisesyouthefirsttimeyouseeitshouldbemadeintoadesign
pattern.
TheJavalanguageprovidesanothermisguidedexample:ThedesignersoftheJavaBeansspecification
decidedtorefertothesimpleget/setnamingconventionasadesignpattern(forexample,getInfo()
returnsanInfopropertyandsetInfo()changesit).Thisisjustacommonplacenamingconventionandin
nowayconstitutesadesignpattern.
SimplifyingIdioms
Beforegettingintomorecomplextechniques,itshelpfultolookatsomebasicwaystokeepcodesimple
andstraightforward.
Messenger
Themosttrivialoftheseisthemessenger,[136]whichpackagesinformationintoanobjectwhichispassed
around,insteadofpassingallthepiecesaroundseparately.Notethatwithoutthemessenger,thecodefor
translate()wouldbemuchmoreconfusingtoread:
//:C10:MessengerDemo.cpp
#include<iostream>
#include<string>
usingnamespacestd
classPoint{//Amessenger
public:
intx,y,z//Sinceit'sjustacarrier
Point(intxi,intyi,intzi):x(xi),y(yi),z(zi){}
Point(constPoint&p):x(p.x),y(p.y),z(p.z){}
Point&operator=(constPoint&rhs){
x=rhs.x
y=rhs.y
z=rhs.z
return*this
}
friendostream&
operator<<(ostream&os,constPoint&p){
returnos<<"x="<<p.x<<"y="<<p.y
<<"z="<<p.z
}
}
classVector{//Mathematicalvector
public:
intmagnitude,direction
Vector(intm,intd):magnitude(m),direction(d){}
}
classSpace{
public:
staticPointtranslate(Pointp,Vectorv){
//Copyconstructorpreventsmodifyingtheoriginal.
//Adummycalculation:
p.x+=v.magnitude+v.direction
p.y+=v.magnitude+v.direction
p.z+=v.magnitude+v.direction
returnp
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
344/458
5/3/2015
ThinkinginC++2ndedVolume2
}
}
intmain(){
Pointp1(1,2,3)
Pointp2=Space::translate(p1,Vector(11,47))
cout<<"p1:"<<p1<<"p2:"<<p2<<endl
}///:~
Thecodehereistrivializedtopreventdistractions.
Sincethegoalofamessengerisonlytocarrydata,thatdataismadepublicforeasyaccess.However,
youmayalsohavereasonstomakethefieldsprivate.
CollectingParameter
Messengersbigbrotheristhecollectingparameter,whosejobistocaptureinformationfromthefunction
towhichitispassed.Generally,thisisusedwhenthecollectingparameterispassedtomultiplefunctions,
soitslikeabeecollectingpollen.
Acontainermakesanespeciallyusefulcollectingparameter,sinceitisalreadysetuptodynamicallyadd
objects:
//:C10:CollectingParameterDemo.cpp
#include<iostream>
#include<string>
#include<vector>
usingnamespacestd
classCollectingParameter:publicvector<string>{}
classFiller{
public:
voidf(CollectingParameter&cp){
cp.push_back("accumulating")
}
voidg(CollectingParameter&cp){
cp.push_back("items")
}
voidh(CollectingParameter&cp){
cp.push_back("aswego")
}
}
intmain(){
Fillerfiller
CollectingParametercp
filler.f(cp)
filler.g(cp)
filler.h(cp)
vector<string>::iteratorit=cp.begin()
while(it!=cp.end())
cout<<*it++<<""
cout<<endl
}///:~
Thecollectingparametermusthavesomewaytosetorinsertvalues.Notethatbythisdefinition,a
messengercouldbeusedasacollectingparameter.Thekeyisthatacollectingparameterispassedabout
andmodifiedbythefunctionsthatreceiveit.
Singleton
PossiblythesimplestGoFdesignpatternistheSingleton,whichisawaytoallowoneandonlyone
instanceofaclass.ThefollowingprogramshowshowtoimplementaSingletoninC++:
//:C10:SingletonPattern.cpp
#include<iostream>
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
345/458
5/3/2015
ThinkinginC++2ndedVolume2
classSingleton{
staticSingletons
inti
Singleton(intx):i(x){}
Singleton&operator=(Singleton&)//Disallowed
Singleton(constSingleton&)//Disallowed
public:
staticSingleton&instance(){returns}
intgetValue(){returni}
voidsetValue(intx){i=x}
}
SingletonSingleton::s(47)
intmain(){
Singleton&s=Singleton::instance()
cout<<s.getValue()<<endl
Singleton&s2=Singleton::instance()
s2.setValue(9)
cout<<s.getValue()<<endl
}///:~
ThekeytocreatingaSingletonistopreventtheclientprogrammerfromhavinganycontroloverthe
lifetimeoftheobject.Todothis,declareallconstructorsprivate,andpreventthecompilerfromimplicitly
generatinganyconstructors.Notethatthecopyconstructorandassignmentoperator(whichintentionally
havenoimplementations,sincetheywillneverbecalled)aredeclaredprivatetopreventanysortof
copiesbeingmade.
Youmustalsodecidehowyouregoingtocreatetheobject.Here,itscreatedstatically,butyoucanalso
waituntiltheclientprogrammerasksforoneandcreateitondemand.Thisiscalledlazyinitialization,and
itonlymakessenseifitisexpensivetocreateyourobject,andifyoudontalwaysneedit.
Ifyoureturnapointerinsteadofareference,theusercouldinadvertentlydeletethepointer,sothe
implementationaboveisconsideredsafest(thedestructorcanalsobedeclaredprivateorprotectedto
alleviatethatproblem).Inanycase,theobjectshouldbestoredprivately.
Youprovideaccessthroughpublicmemberfunctions.Here,instance()producesareferencetothe
Singletonobject.Therestoftheinterface(getValue()andsetValue())istheregularclassinterface.
Notethatyouarentrestrictedtocreatingonlyoneobject.Thistechniquealsosupportsthecreationofa
limitedpoolofobjects.Inthatcase,however,youcanbeconfrontedwiththeproblemofsharingobjectsin
thepool.Ifthisisanissue,youcancreateasolutioninvolvingacheckoutandcheckinoftheshared
objects.
VariationsonSingleton
AnystaticmemberobjectinsideaclassisanexpressionofSingleton:oneandonlyonewillbemade.So
inasense,thelanguagehasdirectsupportfortheideawecertainlyuseitonaregularbasis.However,
theresaproblemwithstaticobjects(memberornot):theorderofinitialization,asdescribedinVolume1
ofthisbook.Ifonestaticobjectdependsonanother,itsimportantthattheobjectsareinitializedinthe
correctorder.
InVolume1,youwereshownhowtocontrolinitializationorderbydefiningastaticobjectinsidea
function.Thisdelaystheinitializationoftheobjectuntilthefirsttimethefunctioniscalled.Ifthefunction
returnsareferencetothestaticobject,itgivesyoutheeffectofaSingletonwhileremovingmuchofthe
worryofstaticinitialization.Forexample,supposeyouwanttocreatealogfileuponthefirstcalltoa
functionthatreturnsareferencetothatlogfile.Thisheaderfilewilldothetrick:
//:C10:LogFile.h
#ifndefLOGFILE_H
#defineLOGFILE_H
#include<fstream>
std::ofstream&logfile()
#endif//LOGFILE_H///:~
Theimplementationmustnotbeinlinedbecausethatwouldmeanthatthewholefunction,includingthe
staticobjectdefinitionwithin,couldbeduplicatedinanytranslationunitwhereitsincluded,whichviolates
C++sonedefinitionrule.[137]Thiswouldmostcertainlyfoiltheattemptstocontroltheorderof
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
346/458
5/3/2015
ThinkinginC++2ndedVolume2
initialization(butpotentiallyinasubtleandhardtodetectfashion).Sotheimplementationmustbe
separate:
//:C10:LogFile.cpp{O}
#include"LogFile.h"
std::ofstream&logfile(){
staticstd::ofstreamlog("Logfile.log")
returnlog
}///:~
Nowthelogobjectwillnotbeinitializeduntilthefirsttimelogfile()iscalled.Soifyoucreateafunction:
//:C10:UseLog1.h
#ifndefUSELOG1_H
#defineUSELOG1_H
voidf()
#endif//USELOG1_H///:~
thatuseslogfile()initsimplementation:
//:C10:UseLog1.cpp{O}
#include"UseLog1.h"
#include"LogFile.h"
voidf(){
logfile()<<__FILE__<<std::endl
}///:~
Andyouuselogfile()againinanotherfile:
//:C10:UseLog2.cpp
//{L}LogFileUseLog1
#include"UseLog1.h"
#include"LogFile.h"
usingnamespacestd
voidg(){
logfile()<<__FILE__<<endl
}
intmain(){
f()
g()
}///:~
thelogobjectdoesntgetcreateduntilthefirstcalltof().
YoucaneasilycombinethecreationofthestaticobjectinsideamemberfunctionwiththeSingletonclass.
SingletonPattern.cppcanbemodifiedtousethisapproach:[138]
//:C10:SingletonPattern2.cpp
//MeyersSingleton.
#include<iostream>
usingnamespacestd
classSingleton{
inti
Singleton(intx):i(x){}
voidoperator=(Singleton&)
Singleton(constSingleton&)
public:
staticSingleton&instance(){
staticSingletons(47)
returns
}
intgetValue(){returni}
voidsetValue(intx){i=x}
}
intmain(){
Singleton&s=Singleton::instance()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
347/458
5/3/2015
ThinkinginC++2ndedVolume2
cout<<s.getValue()<<endl
Singleton&s2=Singleton::instance()
s2.setValue(9)
cout<<s.getValue()<<endl
}///:~
AnespeciallyinterestingcaseoccursiftwoSingletonsdependoneachother,likethis:
//:C10:FunctionStaticSingleton.cpp
classSingleton1{
Singleton1(){}
public:
staticSingleton1&ref(){
staticSingleton1single
returnsingle
}
}
classSingleton2{
Singleton1&s1
Singleton2(Singleton1&s):s1(s){}
public:
staticSingleton2&ref(){
staticSingleton2single(Singleton1::ref())
returnsingle
}
Singleton1&f(){returns1}
}
intmain(){
Singleton1&s1=Singleton2::ref().f()
}///:~
WhenSingleton2::ref()iscalled,itcausesitssoleSingleton2objecttobecreated.Intheprocessof
thiscreation,Singleton1::ref()iscalled,andthatcausesthesoleSingleton1objecttobecreated.
Becausethistechniquedoesntrelyontheorderoflinkingorloading,theprogrammerhasmuchbetter
controloverinitialization,leadingtofewerproblems.
YetanothervariationonSingletonseparatestheSingletonnessofanobjectfromitsimplementation.
ThisisachievedusingtheCuriouslyRecurringTemplatePatternmentionedinChapter5:
//:C10:CuriousSingleton.cpp
//SeparatesaclassfromitsSingletonness(almost).
#include<iostream>
usingnamespacestd
template<classT>classSingleton{
Singleton(constSingleton&)
Singleton&operator=(constSingleton&)
protected:
Singleton(){}
virtual~Singleton(){}
public:
staticT&instance(){
staticTtheInstance
returntheInstance
}
}
//AsampleclasstobemadeintoaSingleton
classMyClass:publicSingleton<MyClass>{
intx
protected:
friendclassSingleton<MyClass>
MyClass(){x=0}
public:
voidsetValue(intn){x=n}
intgetValue()const{returnx}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
348/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
MyClass&m=MyClass::instance()
cout<<m.getValue()<<endl
m.setValue(1)
cout<<m.getValue()<<endl
}///:~
MyClassismadeaSingletonby:
1.Makingitsconstructorprivateorprotected.
2.MakingSingleton<MyClass>afriend.
3.DerivingMyClassfromSingleton<MyClass>.
Theselfreferencinginstep3maysoundimplausible,butasweexplainedinChapter5,itworksbecause
thereisonlyastaticdependencyonthetemplateargumentintheSingletontemplate.Inotherwords,the
codefortheclassSingleton<MyClass>canbeinstantiatedbythecompilerbecauseitisnotdependent
onthesizeofMyClass.Itsonlylater,whenSingleton<MyClass>::instance()isfirstcalled,thatthe
sizeofMyClassisneeded,andbythenMyClasshasbeencompiledanditssizeisknown.[139]
ItsinterestinghowintricatesuchasimplepatternasSingletoncanbe,andwehaventevenaddressed
issuesofthreadsafety.Finally,Singletonshouldbeusedsparingly.TrueSingletonobjectsariserarely,and
thelastthingaSingletonshouldbeusedforistoreplaceaglobalvariable.[140]
Command:choosingtheoperation
TheCommandpatternisstructurallyverysimple,butcanhaveanimportantimpactondecouplingand
thuscleaningupyourcode.
InAdvancedC++:ProgrammingStylesAndIdioms(AddisonWesley,1992),JimCopliencoinstheterm
functorwhichisanobjectwhosesolepurposeistoencapsulateafunction(sincefunctorhasameaning
inmathematics,weshallusethemoreexplicittermfunctionobject).Thepointistodecouplethechoiceof
functiontobecalledfromthesitewherethatfunctioniscalled.
ThistermismentionedbutnotusedinGoF.However,thethemeofthefunctionobjectisrepeatedina
numberofpatternsinthatbook.
ACommandisafunctionobjectinitspurestsense:afunctionthatsanobject.Bywrappingafunctionin
anobject,youcanpassittootherfunctionsorobjectsasaparameter,totellthemtoperformthis
particularoperationintheprocessoffulfillingyourrequest.YoucouldsaythataCommandisaMessenger
thatcarriesbehavior.
//:C10:CommandPattern.cpp
#include<iostream>
#include<vector>
usingnamespacestd
classCommand{
public:
virtualvoidexecute()=0
}
classHello:publicCommand{
public:
voidexecute(){cout<<"Hello"}
}
classWorld:publicCommand{
public:
voidexecute(){cout<<"World!"}
}
classIAm:publicCommand{
public:
voidexecute(){cout<<"I'mthecommandpattern!"}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
349/458
5/3/2015
ThinkinginC++2ndedVolume2
//Anobjectthatholdscommands:
classMacro{
vector<Command*>commands
public:
voidadd(Command*c){commands.push_back(c)}
voidrun(){
vector<Command*>::iteratorit=commands.begin()
while(it!=commands.end())
(*it++)>execute()
}
}
intmain(){
Macromacro
macro.add(newHello)
macro.add(newWorld)
macro.add(newIAm)
macro.run()
}///:~
TheprimarypointofCommandistoallowyoutohandadesiredactiontoafunctionorobject.Inthe
aboveexample,thisprovidesawaytoqueueasetofactionstobeperformedcollectively.Here,youcan
dynamicallycreatenewbehavior,somethingyoucannormallyonlydobywritingnewcodebutinthe
aboveexamplecouldbedonebyinterpretingascript(seetheInterpreterpatternifwhatyouneedtodo
getsverycomplex).
GoFsaysthatCommandsareanobjectorientedreplacementforcallbacks.[141]However,wethinkthat
thewordbackisanessentialpartoftheconceptofcallbacksacallbackreachesbacktothecreatorof
thecallback.Ontheotherhand,withaCommandobjectyoutypicallyjustcreateitandhandittosome
functionorobject,andyouarenototherwiseconnectedovertimetotheCommandobject.
AcommonexampleofCommandistheimplementationofundofunctionalityinanapplication.Eachtime
theuserperformsanoperation,thecorrespondingundoCommandobjectisplacedintoaqueue.Each
Commandobjectthatisexecutedbacksupthestateoftheprogrambyonestep.
DecouplingeventhandlingwithCommand
Asyoushallseeinthenextchapter,oneofthereasonsforemployingconcurrencytechniquesistomore
easilymanageeventdrivenprogramming,wheretheeventscanappearunpredictablyinyourprogram.
Forexample,auserpressingaquitbuttonwhileyoureperforminganoperationexpectstheprogramto
respondquickly.
Anargumentforusingconcurrencyisthatitpreventscouplingacrossthepiecesofyourcode.Thatis,if
yourerunningaseparatethreadtowatchthequitbutton,yourprogramsnormaloperationsdontneed
toknowaboutthequitbuttonoranyoftheotheroperationsthatneedtobewatched.
However,onceyouunderstandthatcouplingistheissue,youcanavoiditusingtheCommandpattern.
Eachnormaloperationmustperiodicallycallafunctiontocheckthestateoftheevents,butwiththe
Commandpatternthesenormaloperationsdontneedtoknowanythingaboutwhattheyarechecking,and
thusaredecoupledfromtheeventhandlingcode:
//:C10:MulticastCommand.cpp{RunByHand}
//DecouplingeventmanagementwiththeCommandpattern.
#include<iostream>
#include<vector>
#include<string>
#include<ctime>
#include<cstdlib>
usingnamespacestd
//Frameworkforrunningtasks:
classTask{
public:
virtualvoidoperation()=0
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
350/458
5/3/2015
ThinkinginC++2ndedVolume2
classTaskRunner{
staticvector<Task*>tasks
TaskRunner(){}//MakeitaSingleton
TaskRunner&operator=(TaskRunner&)//Disallowed
TaskRunner(constTaskRunner&)//Disallowed
staticTaskRunnertr
public:
staticvoidadd(Task&t){tasks.push_back(&t)}
staticvoidrun(){
vector<Task*>::iteratorit=tasks.begin()
while(it!=tasks.end())
(*it++)>operation()
}
}
TaskRunnerTaskRunner::tr
vector<Task*>TaskRunner::tasks
classEventSimulator{
clock_tcreation
clock_tdelay
public:
EventSimulator():creation(clock()){
delay=CLOCKS_PER_SEC/4*(rand()%20+1)
cout<<"delay="<<delay<<endl
}
boolfired(){
returnclock()>creation+delay
}
}
//Somethingthatcanproduceasynchronousevents:
classButton{
boolpressed
stringid
EventSimulatore//Fordemonstration
public:
Button(stringname):pressed(false),id(name){}
voidpress(){pressed=true}
boolisPressed(){
if(e.fired())press()//Simulatetheevent
returnpressed
}
friendostream&
operator<<(ostream&os,constButton&b){
returnos<<b.id
}
}
//TheCommandobject
classCheckButton:publicTask{
Button&button
boolhandled
public:
CheckButton(Button&b):button(b),handled(false){}
voidoperation(){
if(button.isPressed()&&!handled){
cout<<button<<"pressed"<<endl
handled=true
}
}
}
//Theproceduresthatperformthemainprocessing.These
//needtobeoccasionally"interrupted"inorderto
//checkthestateofthebuttonsorotherevents:
voidprocedure1(){
//Performprocedure1operationshere.
//...
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
351/458
5/3/2015
ThinkinginC++2ndedVolume2
TaskRunner::run()//Checkallevents
}
voidprocedure2(){
//Performprocedure2operationshere.
//...
TaskRunner::run()//Checkallevents
}
voidprocedure3(){
//Performprocedure3operationshere.
//...
TaskRunner::run()//Checkallevents
}
intmain(){
srand(time(0))//Randomize
Buttonb1("Button1"),b2("Button2"),b3("Button3")
CheckButtoncb1(b1),cb2(b2),cb3(b3)
TaskRunner::add(cb1)
TaskRunner::add(cb2)
TaskRunner::add(cb3)
cout<<"ControlCtoexit"<<endl
while(true){
procedure1()
procedure2()
procedure3()
}
}///:~
Here,theCommandobjectisrepresentedbyTasksexecutedbytheSingletonTaskRunner.
EventSimulatorcreatesarandomdelaytime,soifyouperiodicallycallfired()theresultwillchange
fromfalsetotrueatsomerandomtime.EventSimulatorobjectsareusedinsideButtonstosimulate
theactofausereventoccurringatsomeunpredictabletime.CheckButtonistheimplementationofthe
Taskthatisperiodicallycheckedbyallthenormalcodeintheprogramyoucanseethishappeningat
theendofprocedure1(),procedure2()andprocedure3().
Althoughthisrequiresalittlebitofextrathoughttosetup,youllseeinChapter11thatthreadingrequires
alotofthoughtandcaretopreventthevariousdifficultiesinherenttoconcurrentprogramming,sothe
simplersolutionmaybepreferable.Youcanalsocreateaverysimplethreadingschemebymovingthe
TaskRunner::run()callsintoamultithreadedtimerobject.Bydoingthis,youeliminateallcoupling
betweenthenormaloperations(procedures,intheaboveexample)andtheeventcode.
Objectdecoupling
BothProxyandStateprovideasurrogateclass.Yourcodetalkstothissurrogateclass,andtherealclass
thatdoestheworkishiddenbehindthissurrogateclass.Whenyoucallafunctioninthesurrogate,it
simplyturnsaroundandcallsthefunctionintheimplementingclass.Thesetwopatternsaresosimilar
that,structurally,ProxyissimplyaspecialcaseofState.Oneistemptedtojustlumpthetwotogetherinto
apatterncalledSurrogate,buttheintentofthetwopatternsisdifferent.Itcanbeeasytofallintothetrap
ofthinkingthatifthestructureisthesame,thepatternsarethesame.Youmustalwayslooktotheintent
ofthepatterninordertobeclearaboutwhatitdoes.
Thebasicideaissimple:fromabaseclass,thesurrogateisderivedalongwiththeclassorclassesthat
providetheactualimplementation:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
352/458
5/3/2015
ThinkinginC++2ndedVolume2
Whenasurrogateobjectiscreated,itisgivenanimplementationtowhichitsendsthefunctioncalls.
Structurally,thedifferencebetweenProxyandStateissimple:aProxyhasonlyoneimplementation,while
Statehasmorethanone.Theapplicationofthepatternsisconsidered(inGoF)tobedistinct:Proxy
controlsaccesstoitsimplementation,whileStatechangestheimplementationdynamically.However,if
youexpandyournotionofcontrollingaccesstoimplementationthenthetwoseemtobepartofa
continuum.
Proxy:frontingforanotherobject
IfweimplementProxyusingtheabovediagram,itlookslikethis:
//:C10:ProxyDemo.cpp
//SimpledemonstrationoftheProxypattern.
#include<iostream>
usingnamespacestd
classProxyBase{
public:
virtualvoidf()=0
virtualvoidg()=0
virtualvoidh()=0
virtual~ProxyBase(){}
}
classImplementation:publicProxyBase{
public:
voidf(){cout<<"Implementation.f()"<<endl}
voidg(){cout<<"Implementation.g()"<<endl}
voidh(){cout<<"Implementation.h()"<<endl}
}
classProxy:publicProxyBase{
ProxyBase*implementation
public:
Proxy(){implementation=newImplementation()}
~Proxy(){deleteimplementation}
//Forwardcallstotheimplementation:
voidf(){implementation>f()}
voidg(){implementation>g()}
voidh(){implementation>h()}
}
intmain(){
Proxyp
p.f()
p.g()
p.h()
}///:~
Insomecases,ImplementationdoesntneedthesameinterfaceasProxyaslongasProxyis
somehowspeakingfortheImplementationclassandreferringfunctioncallstoit,thenthebasicideais
satisfied(notethatthisstatementisatoddswiththedefinitionforProxyinGoF).However,withacommon
interfaceyouareabletodoadropinreplacementoftheproxyintotheclientcodetheclientcodeis
writtentotalktotheoriginalobject,anditdoesntneedtobechangedinordertoaccepttheproxy(Thisis
probablythekeyissuewithProxy).Inaddition,Implementationisforced,throughthecommoninterface,
tofulfillallthefunctionsthatProxyneedstocall.
ThedifferencebetweenProxyandStateisintheproblemsthataresolved.ThecommonusesforProxyas
describedinGoFare:
1.Remoteproxy.Thisproxiesforanobjectinadifferentaddressspace.Thisisimplementedby
someremoteobjecttechnologies.
2.Virtualproxy.Thisprovideslazyinitializationtocreateexpensiveobjectsondemand.
3.Protectionproxy.Usedwhenyoudontwanttheclientprogrammertohavefullaccesstothe
proxiedobject.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
353/458
5/3/2015
ThinkinginC++2ndedVolume2
4.Smartreference.Toaddadditionalactionswhentheproxiedobjectisaccessed.Reference
countingisanexample:thiskeepstrackofthenumberofreferencesthatareheldforaparticular
object,inordertoimplementthecopyonwriteidiomandpreventobjectaliasing.[142]Asimpler
exampleiscountingthecallstoaparticularfunction.
State:changingobjectbehavior
TheStatepatternproducesanobjectthatappearstochangeitsclass,andisusefulwhenyoudiscoverthat
youhaveconditionalcodeinmostorallfunctions.LikeProxy,Stateiscreatedbyhavingafrontendobject
thatusesabackendimplementationobjecttofulfillitsduties.However,theStatepatternswitchesfrom
oneimplementationtoanotherduringthelifetimeofthefrontendobject,inordertoproducedifferent
behaviorforthesamefunctioncall(s).Itsawaytoimprovetheimplementationofyourcodewhenyou
seemtobedoingalotoftestinginsideeachofyourfunctionsbeforedecidingwhattodoforthatfunction.
Forexample,thefairytaleofthefrogprincecontainsanobject(thecreature)thatbehavesdifferently
dependingonwhatstateitsin.Youcouldimplementthisbytestingabool:
//:C10:KissingPrincess.cpp
#include<iostream>
usingnamespacestd
classCreature{
boolisFrog
public:
Creature():isFrog(true){}
voidgreet(){
if(isFrog)
cout<<"Ribbet!"<<endl
else
cout<<"Darling!"<<endl
}
voidkiss(){isFrog=false}
}
intmain(){
Creaturecreature
creature.greet()
creature.kiss()
creature.greet()
}///:~
However,thegreet()function,andanyotherfunctionsthatmusttestisFrogbeforetheyperformtheir
operations,endupwithawkwardcode,especiallyifyoufindyourselfaddingadditionalstatestothe
system.BydelegatingtheoperationstoaStateobjectthatcanbechanged,thiscodeissimplified.
//:C10:KissingPrincess2.cpp
//TheStatepattern.
#include<iostream>
#include<string>
usingnamespacestd
classCreature{
classState{
public:
virtualstringresponse()=0
}
classFrog:publicState{
public:
stringresponse(){return"Ribbet!"}
}
classPrince:publicState{
public:
stringresponse(){return"Darling!"}
}
State*state
public:
Creature():state(newFrog()){}
voidgreet(){
cout<<state>response()<<endl
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
354/458
5/3/2015
ThinkinginC++2ndedVolume2
voidkiss(){
deletestate
state=newPrince()
}
}
intmain(){
Creaturecreature
creature.greet()
creature.kiss()
creature.greet()
}///:~
Itisnotnecessarytomaketheimplementingclassesnestedorprivate,butifyoucanitcreatescleaner
code.
NotethatchangestotheStateclassesareautomaticallypropagatedthroughoutyourcode,ratherthan
requiringaneditacrosstheclassesinordertoeffectchanges.
Adapter
Adaptertakesonetypeandproducesaninterfacetosomeothertype.Thisisusefulwhenyouregivena
libraryorpieceofcodethathasaparticularinterface,andyouvegotasecondlibraryorpieceofcode
thatusesthesamebasicideasasthefirstpiece,butexpressesitselfdifferently.Ifyouadapttheformsof
expressiontoeachother,youcanrapidlyproduceasolution.
SupposeyouhaveageneratorclassthatproducesFibonaccinumbers:
//:C10:FibonacciGenerator.h
#ifndefFIBONACCIGENERATOR_H
#defineFIBONACCIGENERATOR_H
classFibonacciGenerator{
intn
intval[2]
public:
FibonacciGenerator():n(0){val[0]=val[1]=0}
intoperator()(){
intresult=n>2?val[0]+val[1]:n>0?1:0
++n
val[0]=val[1]
val[1]=result
returnresult
}
intcount(){returnn}
}
#endif//FIBONACCIGENERATOR_H///:~
Sinceitsagenerator,youuseitbycallingtheoperator(),likethis:
//:C10:FibonacciGeneratorTest.cpp
#include<iostream>
#include"FibonacciGenerator.h"
usingnamespacestd
intmain(){
FibonacciGeneratorf
for(inti=0i<20i++)
cout<<f.count()<<":"<<f()<<endl
}///:~
PerhapsyouwouldliketotakethisgeneratorandperformSTLnumericalgorithmoperationswithit.
Unfortunately,theSTLalgorithmsonlyworkwithiterators,soyouhaveaninterfacemismatch.The
solutionistocreateanadapterthatwilltaketheFibonacciGeneratorandproduceaniteratorfortheSTL
algorithmstouse.Sincethenumericalgorithmsonlyrequireaninputiterator,theAdapterisfairly
straightforward(forsomethingthatproducesanSTLiterator,thatis):
//:C10:FibonacciAdapter.cpp
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
355/458
5/3/2015
ThinkinginC++2ndedVolume2
//Adaptinganinterfacetosomethingyoualreadyhave.
#include<iostream>
#include<numeric>
#include"FibonacciGenerator.h"
#include"../C06/PrintSequence.h"
usingnamespacestd
classFibonacciAdapter{//Produceaniterator
FibonacciGeneratorf
intlength
public:
FibonacciAdapter(intsize):length(size){}
classiterator
friendclassiterator
classiterator:publicstd::iterator<
std::input_iterator_tag,FibonacciAdapter,ptrdiff_t>{
FibonacciAdapter&ap
public:
typedefintvalue_type
iterator(FibonacciAdapter&a):ap(a){}
booloperator==(constiterator&)const{
returnap.f.count()==ap.length
}
booloperator!=(constiterator&x)const{
return!(*this==x)
}
intoperator*()const{returnap.f()}
iterator&operator++(){return*this}
iteratoroperator++(int){return*this}
}
iteratorbegin(){returniterator(*this)}
iteratorend(){returniterator(*this)}
}
intmain(){
constintSZ=20
FibonacciAdaptera1(SZ)
cout<<"accumulate:"
<<accumulate(a1.begin(),a1.end(),0)<<endl
FibonacciAdaptera2(SZ),a3(SZ)
cout<<"innerproduct:"
<<inner_product(a2.begin(),a2.end(),a3.begin(),0)
<<endl
FibonacciAdaptera4(SZ)
intr1[SZ]={0}
int*end=partial_sum(a4.begin(),a4.end(),r1)
print(r1,end,"partial_sum","")
FibonacciAdaptera5(SZ)
intr2[SZ]={0}
end=adjacent_difference(a5.begin(),a5.end(),r2)
print(r2,end,"adjacent_difference","")
}///:~
YouinitializeaFibonacciAdapterbytellingithowlongtheFibonaccisequencecanbe.Whenaniterator
iscreated,itsimplycapturesareferencetothecontainingFibonacciAdaptersothatitcanaccessthe
FibonacciGeneratorandlength.Notethattheequivalencecomparisonignorestherighthandvalue
becausetheonlyimportantissueiswhetherthegeneratorhasreacheditslength.Inaddition,the
operator++()doesntmodifytheiteratortheonlyoperationthatchangesthestateofthe
FibonacciAdapteriscallingthegeneratorfunctionoperator()ontheFibonacciGenerator.Wecanget
awaywiththisextremelysimpleversionoftheiteratorbecausetheconstraintsonanInputIteratorareso
stronginparticular,youcanonlyreadeachvalueinthesequenceonce.
Inmain(),youcanseethatallfourdifferenttypesofnumericalgorithmsaresuccessfullytestedwiththe
FibonacciAdapter.
TemplateMethod
Anapplicationframeworkallowsyoutoinheritfromaclassorsetofclassesandcreateanewapplication,
reusingmostofthecodeintheexistingclassesandoverridingoneormorefunctionsinordertocustomize
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
356/458
5/3/2015
ThinkinginC++2ndedVolume2
theapplicationtoyourneeds.AfundamentalconceptintheapplicationframeworkistheTemplateMethod,
whichistypicallyhiddenbeneaththecoversanddrivestheapplicationbycallingthevariousfunctionsin
thebaseclass(someofwhichyouhaveoverriddeninordertocreatetheapplication).
AnimportantcharacteristicoftheTemplateMethodisthatitisdefinedinthebaseclass(sometimesasa
privatememberfunction)andcannotbechangedtheTemplateMethodisthethingthatstaysthesame.
Itcallsotherbaseclassfunctions(theonesyouoverride)inordertodoitsjob,buttheclientprogrammer
isntnecessarilyabletocallitdirectly,asyoucanseehere:
//:C10:TemplateMethod.cpp
//SimpledemonstrationofTemplateMethod.
#include<iostream>
usingnamespacestd
classApplicationFramework{
protected:
virtualvoidcustomize1()=0
virtualvoidcustomize2()=0
public:
voidtemplateMethod(){
for(inti=0i<5i++){
customize1()
customize2()
}
}
}
//Createanew"application":
classMyApp:publicApplicationFramework{
protected:
voidcustomize1(){cout<<"Hello"}
voidcustomize2(){cout<<"World!"<<endl}
}
intmain(){
MyAppapp
app.templateMethod()
}///:~
TheenginethatrunstheapplicationistheTemplateMethod.InaGUIapplication,thisenginewouldbe
themaineventloop.Theclientprogrammersimplyprovidesdefinitionsforcustomize1()and
customize2()andtheapplicationisreadytorun.
Strategy:choosingthealgorithmatruntime
NotethattheTemplateMethodisthecodethatstaysthesame,andthefunctionsthatyouoverrideare
thecodethatchanges.However,thischangeisfixedatcompiletimeviainheritance.Followingthe
maximofprefercompositiontoinheritance,wecanusecompositiontoapproachtheproblemof
separatingcodethatchangesfromcodethatstaysthesame,andproducetheStrategypattern.This
approachhasadistinctbenefit:atruntime,youcanpluginthecodethatchanges.Strategyalsoaddsa
Contextwhichcanbeasurrogateclassthatcontrolstheselectionanduseoftheparticularstrategy
objectjustlikeState!
Strategymeansjustthat:youcansolveaprobleminanumberofways.Considerthesituationwhere
youveforgottensomeonesname.Herearethedifferentwaysyoucancope:
//:C10:Strategy.cpp
//TheStrategydesignpattern.
#include<iostream>
usingnamespacestd
classNameStrategy{
public:
virtualvoidgreet()=0
}
classSayHi:publicNameStrategy{
public:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
357/458
5/3/2015
ThinkinginC++2ndedVolume2
voidgreet(){
cout<<"Hi!How'sitgoing?"<<endl
}
}
classIgnore:publicNameStrategy{
public:
voidgreet(){
cout<<"(PretendIdon'tseeyou)"<<endl
}
}
classAdmission:publicNameStrategy{
public:
voidgreet(){
cout<<"I'msorry.Iforgotyourname."<<endl
}
}
//The"Context"controlsthestrategy:
classContext{
NameStrategy&strategy
public:
Context(NameStrategy&strat):strategy(strat){}
voidgreet(){strategy.greet()}
}
intmain(){
SayHisayhi
Ignoreignore
Admissionadmission
Contextc1(sayhi),c2(ignore),c3(admission)
c1.greet()
c2.greet()
c3.greet()
}///:~
Context::greet()wouldnormallybemorecomplexitstheanalogoftheTemplateMethodbecauseit
containsthecodethatdoesntchange.Butyoucanseeinmain()thatthechoiceofstrategycanbemade
atruntime.IfyougoonestepfurtheryoucancombinethiswiththeStatepatternandchangetheStrategy
duringthelifetimeoftheContextobject.
ChainofResponsibility:tryingasequenceofstrategies
ChainofResponsibilitymightbethoughtofasadynamicgeneralizationofrecursionusingStrategy
objects.Youmakeacall,andeachStrategyinalinkedsequencetriestosatisfythecall.Theprocessends
whenoneofthestrategiesissuccessfulorthechainends.Inrecursion,onefunctioncallsitselfoverand
overuntilaterminationconditionisreachedwithChainofResponsibility,afunctioncallsitself,which(by
movingdownthechainofStrategies)callsadifferentimplementationofthefunction,etc.,untila
terminationconditionisreached.Theterminationconditioniseitherthatthebottomofthechainisreached
(thisreturnsadefaultobjectyoumayormaynotbeabletoprovideadefaultresultsoyoumustbeable
todeterminethesuccessorfailureofthechain)oroneoftheStrategiesissuccessful.
Insteadofcallingasinglefunctiontosatisfyarequest,multiplefunctionsinthechainhaveachanceto
satisfytherequest,soithastheflavorofanexpertsystem.Sincethechainiseffectivelyalist,itcanbe
dynamicallycreated,soyoucouldalsothinkofitasamoregeneral,dynamicallybuiltswitchstatement.
InGoF,theresafairamountofdiscussionofhowtocreatethechainofresponsibilityasalinkedlist.
However,whenyoulookatthepatternitreallyshouldntmatterhowthechainiscreatedthatsan
implementationdetail.SinceGoFwaswrittenbeforetheSTLcontainerswereavailableinmostC++
compilers,thereasonforthisismostlikely(1)therewasnobuiltinlistandthustheyhadtocreateone
and(2)datastructuresareoftentaughtasafundamentalskillinacademia,andtheideathatdata
structuresshouldbestandardtoolsavailablewiththeprogramminglanguagemaynothaveoccurredtothe
GoFauthors.WemaintainthatthedetailsofthecontainerusedtoimplementChainofResponsibilityasa
chain(inGoF,alinkedlist)addsnothingtothesolutionandcanjustaseasilybeimplementedusingan
STLcontainer,asshownbelow.
HereyoucanseeChainofResponsibilityautomaticallyfindingasolutionusingamechanismto
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
358/458
5/3/2015
ThinkinginC++2ndedVolume2
automaticallyrecursethrougheachStrategyinthechain:
//:C10:ChainOfReponsibility.cpp
//Theapproachofthefiveyearold.
#include<iostream>
#include<vector>
#include"../purge.h"
usingnamespacestd
enumAnswer{NO,YES}
classGimmeStrategy{
public:
virtualAnswercanIHave()=0
virtual~GimmeStrategy(){}
}
classAskMom:publicGimmeStrategy{
public:
AnswercanIHave(){
cout<<"Mooom?CanIhavethis?"<<endl
returnNO
}
}
classAskDad:publicGimmeStrategy{
public:
AnswercanIHave(){
cout<<"Dad,Ireallyneedthis!"<<endl
returnNO
}
}
classAskGrandpa:publicGimmeStrategy{
public:
AnswercanIHave(){
cout<<"Grandpa,isitmybirthdayyet?"<<endl
returnNO
}
}
classAskGrandma:publicGimmeStrategy{
public:
AnswercanIHave(){
cout<<"Grandma,Ireallyloveyou!"<<endl
returnYES
}
}
classGimme:publicGimmeStrategy{
vector<GimmeStrategy*>chain
public:
Gimme(){
chain.push_back(newAskMom())
chain.push_back(newAskDad())
chain.push_back(newAskGrandpa())
chain.push_back(newAskGrandma())
}
AnswercanIHave(){
vector<GimmeStrategy*>::iteratorit=chain.begin()
while(it!=chain.end())
if((*it++)>canIHave()==YES)
returnYES
//Reachedendwithoutsuccess...
cout<<"Whiiiiinnne!"<<endl
returnNO
}
~Gimme(){purge(chain)}
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
359/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
Gimmechain
chain.canIHave()
}///:~
NoticethattheContextclassGimmeandalltheStrategyclassesareallderivedfromthesamebase
class,GimmeStrategy.
IfyoustudythesectiononChainofResponsibilityinGoF,youllfindthatthestructuredifferssignificantly
fromtheoneabovebecausetheyfocusoncreatingtheirownlinkedlist.However,ifyoukeepinmindthat
theessenceofChainofResponsibilityistotryanumberofsolutionsuntilyoufindonethatworks,youll
realizethattheimplementationofthesequencingmechanismisnotanessentialpartofthepattern.
Factories:encapsulatingobjectcreation
Whenyoudiscoverthatyouneedtoaddnewtypestoasystem,themostsensiblefirststepistouse
polymorphismtocreateacommoninterfacetothosenewtypes.Thisseparatestherestofthecodeinyour
systemfromtheknowledgeofthespecifictypesthatyouareadding.Newtypescanbeaddedwithout
disturbingexistingcodeorsoitseems.Atfirstitwouldappearthatyouneedtochangethecodeonlyin
theplacewhereyouinheritanewtype,butthisisnotquitetrue.Youmuststillcreateanobjectofyour
newtype,andatthepointofcreationyoumustspecifytheexactconstructortouse.Thus,ifthecodethat
createsobjectsisdistributedthroughoutyourapplication,youhavethesameproblemwhenaddingnew
typesyoumuststillchasedownallthepointsofyourcodewheretypematters.Itisthecreationofthe
typethatmattershere,ratherthantheuseofthetype(whichistakencareofbypolymorphism),butthe
effectisthesame:addinganewtypecancauseproblems.
Thesolutionistoforcethecreationofobjectstooccurthroughacommonfactoryratherthantoallowthe
creationalcodetobespreadthroughoutyoursystem.Ifallthecodeinyourprogrammustgotothis
factorywheneveritneedstocreateoneofyourobjects,allyoumustdowhenyouaddanewobjectis
modifythefactory.ThisdesignisavariationofthepatterncommonlyknownasFactoryMethod.Since
everyobjectorientedprogramcreatesobjects,andsinceitslikelyyouwillextendyourprogrambyadding
newtypes,factoriesmaybethemostusefulofalldesignpatterns.
Asanexample,considerthecommonlyusedShapeexample.Oneapproachtoimplementingafactoryis
todefineastaticmemberfunctioninthebaseclass:
//:C10:ShapeFactory1.cpp
#include<iostream>
#include<stdexcept>
#include<cstddef>
#include<string>
#include<vector>
#include"../purge.h"
usingnamespacestd
classShape{
public:
virtualvoiddraw()=0
virtualvoiderase()=0
virtual~Shape(){}
classBadShapeCreation:publiclogic_error{
public:
BadShapeCreation(stringtype)
:logic_error("Cannotcreatetype"+type){}
}
staticShape*factory(conststring&type)
throw(BadShapeCreation)
}
classCircle:publicShape{
Circle(){}//Privateconstructor
friendclassShape
public:
voiddraw(){cout<<"Circle::draw<<endl}
voiderase(){cout<<"Circle::erase<<endl}
~Circle(){cout<<"Circle::~Circle<<endl}
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
360/458
5/3/2015
ThinkinginC++2ndedVolume2
classSquare:publicShape{
Square(){}
friendclassShape
public:
voiddraw(){cout<<"Square::draw<<endl}
voiderase(){cout<<"Square::erase<<endl}
~Square(){cout<<"Square::~Square<<endl}
}
Shape*Shape::factory(conststring&type)
throw(Shape::BadShapeCreation){
if(type=="Circle")returnnewCircle
if(type=="Square")returnnewSquare
throwBadShapeCreation(type)
}
char*sl[]={"Circle","Square","Square",
"Circle","Circle","Circle","Square"}
intmain(){
vector<Shape*>shapes
try{
for(size_ti=0i<sizeofsl/sizeofsl[0]i++)
shapes.push_back(Shape::factory(sl[i]))
}catch(Shape::BadShapeCreatione){
cout<<e.what()<<endl
purge(shapes)
returnEXIT_FAILURE
}
for(size_ti=0i<shapes.size()i++){
shapes[i]>draw()
shapes[i]>erase()
}
purge(shapes)
}///:~
Thefactory()functiontakesanargumentthatallowsittodeterminewhattypeofShapetocreate.Here,
theargumentisastring,butitcouldbeanysetofdata.Thefactory()isnowtheonlyothercodeinthe
systemthatneedstobechangedwhenanewtypeofShapeisadded.(Theinitializationdataforthe
objectswillpresumablycomefromsomewhereoutsidethesystemandwillnotbeahardcodedarrayasin
thisexample.)
Toensurethatthecreationcanonlyhappeninthefactory(),theconstructorsforthespecifictypesof
Shapearemadeprivate,andShapeisdeclaredafriendsothatfactory()hasaccesstothe
constructors.(YoucouldalsodeclareonlyShape::factory()tobeafriend,butitseemsreasonably
harmlesstodeclaretheentirebaseclassasafriend.)Thereisanotherimportantimplicationofthis
designthebaseclass,Shape,mustnowknowthedetailsabouteveryderivedclassapropertythat
objectorienteddesignstrytoavoid.Forframeworksoranyclasslibrarythatshouldsupportextension,
thiscanquicklybecomeunwieldy,asthebaseclassmustbeupdatedassoonasanewtypeisaddedtothe
hierarchy.Polymorphicfactories,describedinthenextsubsection,canbeusedtoavoidthisunfortunate
circulardependency.
Polymorphicfactories
Thestaticfactory()memberfunctioninthepreviousexampleforcesallthecreationoperationstobe
focusedinonespot,sothatstheonlyplaceyouneedtochangethecode.Thisiscertainlyareasonable
solution,asitnicelyencapsulatestheprocessofcreatingobjects.However,GoFemphasizesthatthe
reasonfortheFactoryMethodpatternissothatdifferenttypesoffactoriescanbederivedfromthebasic
factory.FactoryMethodisinfactaspecialtypeofpolymorphicfactory.HereisShapeFactory1.cpp
modifiedsotheFactoryMethodsareinaseparateclassasvirtualfunctions:
//:C10:ShapeFactory2.cpp
//PolymorphicFactoryMethods.
#include<iostream>
#include<map>
#include<string>
#include<vector>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
361/458
5/3/2015
ThinkinginC++2ndedVolume2
#include<stdexcept>
#include<cstddef>
#include"../purge.h"
usingnamespacestd
classShape{
public:
virtualvoiddraw()=0
virtualvoiderase()=0
virtual~Shape(){}
}
classShapeFactory{
virtualShape*create()=0
staticmap<string,ShapeFactory*>factories
public:
virtual~ShapeFactory(){}
friendclassShapeFactoryInitializer
classBadShapeCreation:publiclogic_error{
public:
BadShapeCreation(stringtype)
:logic_error("Cannotcreatetype"+type){}
}
staticShape*
createShape(conststring&id)throw(BadShapeCreation){
if(factories.find(id)!=factories.end())
returnfactories[id]>create()
else
throwBadShapeCreation(id)
}
}
//Definethestaticobject:
map<string,ShapeFactory*>ShapeFactory::factories
classCircle:publicShape{
Circle(){}//Privateconstructor
friendclassShapeFactoryInitializer
classFactory
friendclassFactory
classFactory:publicShapeFactory{
public:
Shape*create(){returnnewCircle}
friendclassShapeFactoryInitializer
}
public:
voiddraw(){cout<<"Circle::draw<<endl}
voiderase(){cout<<"Circle::erase<<endl}
~Circle(){cout<<"Circle::~Circle<<endl}
}
classSquare:publicShape{
Square(){}
friendclassShapeFactoryInitializer
classFactory
friendclassFactory
classFactory:publicShapeFactory{
public:
Shape*create(){returnnewSquare}
friendclassShapeFactoryInitializer
}
public:
voiddraw(){cout<<"Square::draw<<endl}
voiderase(){cout<<"Square::erase<<endl}
~Square(){cout<<"Square::~Square<<endl}
}
//SingletontoinitializetheShapeFactory:
classShapeFactoryInitializer{
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
362/458
5/3/2015
ThinkinginC++2ndedVolume2
staticShapeFactoryInitializersi
ShapeFactoryInitializer(){
ShapeFactory::factories["Circle"]=newCircle::Factory
ShapeFactory::factories["Square"]=newSquare::Factory
}
~ShapeFactoryInitializer(){
map<string,ShapeFactory*>::iteratorit=
ShapeFactory::factories.begin()
while(it!=ShapeFactory::factories.end())
deleteit++>second
}
}
//Staticmemberdefinition:
ShapeFactoryInitializerShapeFactoryInitializer::si
char*sl[]={"Circle","Square","Square",
"Circle","Circle","Circle","Square"}
intmain(){
vector<Shape*>shapes
try{
for(size_ti=0i<sizeofsl/sizeofsl[0]i++)
shapes.push_back(ShapeFactory::createShape(sl[i]))
}catch(ShapeFactory::BadShapeCreatione){
cout<<e.what()<<endl
returnEXIT_FAILURE
}
for(size_ti=0i<shapes.size()i++){
shapes[i]>draw()
shapes[i]>erase()
}
purge(shapes)
}///:~
NowtheFactoryMethodappearsinitsownclass,ShapeFactory,asvirtualcreate().Thisisaprivate
memberfunction,whichmeansitcannotbecalleddirectlybutcanbeoverridden.ThesubclassesofShape
musteachcreatetheirownsubclassesofShapeFactoryandoverridethecreate()memberfunctionto
createanobjectoftheirowntype.Thesefactoriesareprivate,sothattheyareonlyaccessiblefromthe
mainFactoryMethod.Thisway,allclientcodemustgothroughtheFactoryMethodinordertocreate
objects.
TheactualcreationofshapesisperformedbycallingShapeFactory::createShape(),whichisastatic
memberfunctionthatusesthemapinShapeFactorytofindtheappropriatefactoryobjectbasedonan
identifierthatyoupassit.Thefactorycreatestheshapeobjectdirectly,butyoucouldimagineamore
complexproblemwheretheappropriatefactoryobjectisreturnedandthenusedbythecallertocreatean
objectinamoresophisticatedway.However,itseemsthatmuchofthetimeyoudontneedtheintricacies
ofthepolymorphicFactoryMethod,andasinglestaticmemberfunctioninthebaseclass(asshownin
ShapeFactory1.cpp)willworkfine.
NoticethattheShapeFactorymustbeinitializedbyloadingitsmapwithfactoryobjects,whichtakes
placeintheSingletonShapeFactoryInitializer.Sotoaddanewtypetothisdesignyoumustdefinethe
type,createafactory,andmodifyShapeFactoryInitializersothataninstanceofyourfactoryisinserted
inthemap.ThisextracomplexityagainsuggeststheuseofastaticFactoryMethodifyoudontneedto
createindividualfactoryobjects.
Abstractfactories
TheAbstractFactorypatternlookslikethefactoriesweveseenpreviously,butwithseveralFactory
Methods.EachoftheFactoryMethodscreatesadifferentkindofobject.Whenyoucreatethefactory
object,youdecidehowalltheobjectscreatedbythatfactorywillbeused.TheexampleinGoFimplements
portabilityacrossvariousgraphicaluserinterfaces(GUIs):youcreateafactoryobjectappropriatetothe
GUIthatyoureworkingwith,andfromthenonwhenyouaskitforamenu,abutton,aslider,andsoon,
itwillautomaticallycreatetheappropriateversionofthatitemfortheGUI.Thus,youreabletoisolate,in
oneplace,theeffectofchangingfromoneGUItoanother.
Asanotherexample,supposeyouarecreatingageneralpurposegamingenvironmentandyouwanttobe
abletosupportdifferenttypesofgames.HereshowitmightlookusinganAbstractFactory:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
363/458
5/3/2015
ThinkinginC++2ndedVolume2
//:C10:AbstractFactory.cpp
//Agamingenvironment.
#include<iostream>
usingnamespacestd
classObstacle{
public:
virtualvoidaction()=0
}
classPlayer{
public:
virtualvoidinteractWith(Obstacle*)=0
}
classKitty:publicPlayer{
virtualvoidinteractWith(Obstacle*ob){
cout<<"Kittyhasencountereda"
ob>action()
}
}
classKungFuGuy:publicPlayer{
virtualvoidinteractWith(Obstacle*ob){
cout<<"KungFuGuynowbattlesagainsta"
ob>action()
}
}
classPuzzle:publicObstacle{
public:
voidaction(){cout<<"Puzzle"<<endl}
}
classNastyWeapon:publicObstacle{
public:
voidaction(){cout<<"NastyWeapon"<<endl}
}
//Theabstractfactory:
classGameElementFactory{
public:
virtualPlayer*makePlayer()=0
virtualObstacle*makeObstacle()=0
}
//Concretefactories:
classKittiesAndPuzzles:publicGameElementFactory{
public:
virtualPlayer*makePlayer(){returnnewKitty}
virtualObstacle*makeObstacle(){returnnewPuzzle}
}
classKillAndDismember:publicGameElementFactory{
public:
virtualPlayer*makePlayer(){returnnewKungFuGuy}
virtualObstacle*makeObstacle(){
returnnewNastyWeapon
}
}
classGameEnvironment{
GameElementFactory*gef
Player*p
Obstacle*ob
public:
GameEnvironment(GameElementFactory*factory)
:gef(factory),p(factory>makePlayer()),
ob(factory>makeObstacle()){}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
364/458
5/3/2015
ThinkinginC++2ndedVolume2
voidplay(){p>interactWith(ob)}
~GameEnvironment(){
deletep
deleteob
deletegef
}
}
intmain(){
GameEnvironment
g1(newKittiesAndPuzzles),
g2(newKillAndDismember)
g1.play()
g2.play()
}
/*Output:
KittyhasencounteredaPuzzle
KungFuGuynowbattlesagainstaNastyWeapon*////:~
Inthisenvironment,PlayerobjectsinteractwithObstacleobjects,butthetypesofplayersandobstacles
dependonthegame.YoudeterminethekindofgamebychoosingaparticularGameElementFactory,
andthentheGameEnvironmentcontrolsthesetupandplayofthegame.Inthisexample,thesetupand
playaresimple,butthoseactivities(theinitialconditionsandthestatechange)candeterminemuchofthe
gamesoutcome.Here,GameEnvironmentisnotdesignedtobeinherited,althoughitcouldpossibly
makesensetodothat.
Thisexamplealsoillustratesdoubledispatching,whichwillbeexplainedlater.
Virtualconstructors
Oneoftheprimarygoalsofusingafactoryistoorganizeyourcodesoyoudontneedtoselecttheexact
constructortypewhencreatinganobject.Thatis,youcantellafactory:Idontknowpreciselywhatkind
ofobjectIneed,butherestheinformation.Createtheappropriatetype.
Inaddition,duringaconstructorcallthevirtualmechanismdoesnotoperate(earlybindingoccurs).
Sometimesthisisawkward.Forexample,intheShapeprogramitseemslogicalthatinsidethe
constructorforaShapeobject,youwouldwanttoseteverythingupandthendraw()theShape.The
draw()functionshouldbeavirtualfunction,amessagetotheShapethatitshoulddrawitself
appropriately,dependingonwhetheritisaCircle,aSquare,aLine,andsoon.However,thisdoesnt
workinsidetheconstructorbecausevirtualfunctionsresolvetothelocalfunctionbodieswhencalledin
constructors.
Ifyouwanttobeabletocallavirtualfunctioninsidetheconstructorandhaveitdotherightthing,you
mustuseatechniquetosimulateavirtualconstructor.Thisisaconundrum.Remember,theideaofa
virtualfunctionisthatyousendamessagetoanobjectandlettheobjectfigureouttherightthingtodo.
Butaconstructorbuildsanobject.Soavirtualconstructorwouldbelikesaying,Idontknowexactlywhat
kindofobjectyouare,butbuildtherighttypeanyway.Inanordinaryconstructor,thecompilermust
knowwhichVTABLEaddresstobindtotheVPTR,andevenifitexisted,avirtualconstructorcouldntdo
thisbecauseitdoesntknowallthetypeinformationatcompiletime.Itmakessensethataconstructor
cantbevirtualbecauseitistheonefunctionthatabsolutelymustknoweverythingaboutthetypeofthe
object.
Andyettherearetimeswhenyouwantsomethingapproximatingthebehaviorofavirtualconstructor.
IntheShapeexample,itwouldbenicetohandtheShapeconstructorsomespecificinformationinthe
argumentlistandlettheconstructorcreateaspecifictypeofShape(aCircleoraSquare)withno
furtherintervention.Ordinarily,youdhavetomakeanexplicitcalltotheCircleorSquareconstructor
yourself.
Coplien[143]callshissolutiontothisproblemenvelopeandletterclasses.Theenvelopeclassisthebase
class,ashellthatcontainsapointertoanobject,alsoofthebaseclasstype.Theconstructorforthe
envelopedetermines(atruntime,whentheconstructoriscalled,notatcompiletime,whenthetype
checkingisnormallydone)whatspecifictypetomake,createsanobjectofthatspecifictype(onthe
heap),andthenassignstheobjecttoitspointer.Allthefunctioncallsarethenhandledbythebaseclass
throughitspointer.ItsreallyjustaslightvariationoftheStatepattern,wherethebaseclassisactingas
asurrogateforthederivedclass,andthederivedclassprovidesthevariationinbehavior:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
365/458
5/3/2015
ThinkinginC++2ndedVolume2
//:C10:VirtualConstructor.cpp
#include<iostream>
#include<string>
#include<stdexcept>
#include<stdexcept>
#include<cstddef>
#include<vector>
#include"../purge.h"
usingnamespacestd
classShape{
Shape*s
//Preventcopyconstruction&operator=
Shape(Shape&)
Shapeoperator=(Shape&)
protected:
Shape(){s=0}
public:
virtualvoiddraw(){s>draw()}
virtualvoiderase(){s>erase()}
virtualvoidtest(){s>test()}
virtual~Shape(){
cout<<"~Shape"<<endl
if(s){
cout<<"Makingvirtualcall:"
s>erase()//Virtualcall
}
cout<<"deletes:"
deletes//Thepolymorphicdeletion
//(delete0islegalitproducesanoop)
}
classBadShapeCreation:publiclogic_error{
public:
BadShapeCreation(stringtype)
:logic_error("Cannotcreatetype"+type){}
}
Shape(stringtype)throw(BadShapeCreation)
}
classCircle:publicShape{
Circle(Circle&)
Circleoperator=(Circle&)
Circle(){}//Privateconstructor
friendclassShape
public:
voiddraw(){cout<<"Circle::draw"<<endl}
voiderase(){cout<<"Circle::erase"<<endl}
voidtest(){draw()}
~Circle(){cout<<"Circle::~Circle"<<endl}
}
classSquare:publicShape{
Square(Square&)
Squareoperator=(Square&)
Square(){}
friendclassShape
public:
voiddraw(){cout<<"Square::draw"<<endl}
voiderase(){cout<<"Square::erase"<<endl}
voidtest(){draw()}
~Square(){cout<<"Square::~Square"<<endl}
}
Shape::Shape(stringtype)throw(Shape::BadShapeCreation){
if(type=="Circle")
s=newCircle
elseif(type=="Square")
s=newSquare
elsethrowBadShapeCreation(type)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
366/458
5/3/2015
ThinkinginC++2ndedVolume2
draw()//Virtualcallintheconstructor
}
char*sl[]={"Circle","Square","Square",
"Circle","Circle","Circle","Square"}
intmain(){
vector<Shape*>shapes
cout<<"virtualconstructorcalls:"<<endl
try{
for(size_ti=0i<sizeofsl/sizeofsl[0]i++)
shapes.push_back(newShape(sl[i]))
}catch(Shape::BadShapeCreatione){
cout<<e.what()<<endl
purge(shapes)
returnEXIT_FAILURE
}
for(size_ti=0i<shapes.size()i++){
shapes[i]>draw()
cout<<"test"<<endl
shapes[i]>test()
cout<<"endtest"<<endl
shapes[i]>erase()
}
Shapec("Circle")//Createonthestack
cout<<"destructorcalls:"<<endl
purge(shapes)
}///:~
ThebaseclassShapecontainsapointertoanobjectoftypeShapeasitsonlydatamember.(Whenyou
createavirtualconstructorscheme,exercisespecialcaretoensurethispointerisalwaysinitializedtoa
liveobject.)Thisbaseclassiseffectivelyaproxybecauseitistheonlythingtheclientcodeseesand
interactswith.
EachtimeyouderiveanewsubtypefromShape,youmustgobackandaddthecreationforthattypein
oneplace,insidethevirtualconstructorintheShapebaseclass.Thisisnottooonerousatask,butthe
disadvantageisyounowhaveadependencybetweentheShapeclassandallclassesderivedfromit.
Inthisexample,theinformationyoumusthandthevirtualconstructoraboutwhattypetocreateis
explicit:itsastringthatnamesthetype.However,yourschemecanuseotherinformationforexample,
inaparsertheoutputofthescannercanbehandedtothevirtualconstructor,whichthenusesthat
informationtodeterminewhichtokentocreate.
ThevirtualconstructorShape(type)cannotbedefineduntilafterallthederivedclasseshavebeen
declared.However,thedefaultconstructorcanbedefinedinsideclassShape,butitshouldbemade
protectedsotemporaryShapeobjectscannotbecreated.Thisdefaultconstructorisonlycalledbythe
constructorsofderivedclassobjects.Youareforcedtoexplicitlycreateadefaultconstructorbecausethe
compilerwillcreateoneforyouautomaticallyonlyiftherearenoconstructorsdefined.Becauseyoumust
defineShape(type),youmustalsodefineShape().
Thedefaultconstructorinthisschemehasatleastoneimportantchoreitmustsetthevalueofthes
pointertozero.Thismaysoundstrangeatfirst,butrememberthatthedefaultconstructorwillbecalledas
partoftheconstructionoftheactualobjectinCopliensterms,theletter,nottheenvelope.However,
theletterisderivedfromtheenvelope,soitalsoinheritsthedatamembers.Intheenvelope,sis
importantbecauseitpointstotheactualobject,butintheletter,sissimplyexcessbaggage.Even
excessbaggageshouldbeinitialized,however,andifsisnotsettozerobythedefaultconstructorcalled
fortheletter,badthingshappen(asyoullseelater).
Thevirtualconstructortakesasitsargumentinformationthatcompletelydeterminesthetypeofthe
object.Notice,though,thatthistypeinformationisntreadandacteduponuntilruntime,whereasnormally
thecompilermustknowtheexacttypeatcompiletime(oneotherreasonthissystemeffectivelyimitates
virtualconstructors).
Thevirtualconstructorusesitsargumenttoselecttheactual(letter)objecttoconstruct,whichisthen
assignedtothepointerinsidetheenvelope.Atthatpoint,theconstructionoftheletterhasbeen
completed,soanyvirtualcallswillbeproperlyredirected.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
367/458
5/3/2015
ThinkinginC++2ndedVolume2
Asanexample,considerthecalltodraw()insidethevirtualconstructor.Ifyoutracethiscall(eitherby
handorwithadebugger),youcanseethatitstartsinthedraw()functioninthebaseclass,Shape.This
functioncallsdraw()fortheenvelopespointertoitsletter.AlltypesderivedfromShapesharethe
sameinterface,sothisvirtualcallisproperlyexecuted,eventhoughitseemstobeintheconstructor.
(Actually,theconstructorfortheletterhasalreadycompleted.)Aslongasallvirtualcallsinthebase
classsimplymakecallstoidenticalvirtualfunctionsthroughthepointertotheletter,thesystem
operatesproperly.
Tounderstandhowitworks,considerthecodeinmain().Tofillthevectorshapes,virtualconstructor
callsaremadetoShape.Ordinarilyinasituationlikethis,youwouldcalltheconstructorfortheactual
type,andtheVPTRforthattypewouldbeinstalledintheobject.Here,however,theVPTRusedineach
caseistheoneforShape,nottheoneforthespecificCircle,Square,orTriangle.
Intheforloopwherethedraw()anderase()functionsarecalledforeachShape,thevirtualfunction
callresolves,throughtheVPTR,tothecorrespondingtype.However,thisisShapeineachcase.Infact,
youmightwonderwhydraw()anderase()weremadevirtual.Thereasonshowsupinthenextstep:
thebaseclassversionofdraw()makesacall,throughtheletterpointers,tothevirtualfunction
draw()fortheletter.Thistimethecallresolvestotheactualtypeoftheobject,notjustthebaseclass
Shape.Thus,theruntimecostofusingvirtualconstructorsisoneextravirtualindirectioneverytimeyou
makeavirtualfunctioncall.
Tocreateanyfunctionthatisoverridden,suchasdraw(),erase(),ortest(),youmustforwardall
callstothespointerinthebaseclassimplementation,asshownearlier.Thisisbecause,whenthecallis
made,thecalltotheenvelopesmemberfunctionwillresolveasbeingtoShape,andnottoaderivedtype
ofShape.Onlywhenyouforwardthecalltoswillthevirtualbehaviortakeplace.Inmain(),youcan
seethateverythingworkscorrectly,evenwhencallsaremadeinsideconstructorsanddestructors.
Destructoroperation
Theactivitiesofdestructioninthisschemearealsotricky.Tounderstand,letsverballywalkthroughwhat
happenswhenyoucalldeleteforapointertoaShapeobjectspecifically,aSquarecreatedonthe
heap.(Thisismorecomplicatedthananobjectcreatedonthestack.)Thiswillbeadeletethroughthe
polymorphicinterface,andwillhappenviathecalltopurge().
ThetypeofanypointerinshapesisofthebaseclassShape,sothecompilermakesthecallthrough
Shape.Normally,youmightsaythatitsavirtualcall,soSquaresdestructorwillbecalled.Butwiththe
virtualconstructorscheme,thecompileriscreatingactualShapeobjects,eventhoughtheconstructor
initializestheletterpointertoaspecifictypeofShape.Thevirtualmechanismisused,buttheVPTRinside
theShapeobjectisShapesVPTR,notSquares.ThisresolvestoShapesdestructor,whichcallsdelete
fortheletterpointers,whichactuallypointstoaSquareobject.Thisisagainavirtualcall,butthistimeit
resolvestoSquaresdestructor.
C++guarantees,viathecompiler,thatalldestructorsinthehierarchyarecalled.Squaresdestructoris
calledfirst,followedbyanyintermediatedestructors,inorder,untilfinallythebaseclassdestructoris
called.Thisbaseclassdestructorcontainscodethatsaysdeletes.Whenthisdestructorwascalled
originally,itwasfortheenvelopes,butnowitsfortheletters,whichistherebecausetheletter
wasinheritedfromtheenvelope,andnotbecauseitcontainsanything.Sothiscalltodeleteshoulddo
nothing.
Thesolutiontotheproblemistomaketheletterspointerzero.Thenwhentheletterbaseclass
destructoriscalled,yougetdelete0,whichbydefinitiondoesnothing.Becausethedefaultconstructoris
protected,itwillbecalledonlyduringtheconstructionofaletter,sothatstheonlysituationwheresis
settozero.
Althoughitsinteresting,youcanseethisisacomplexapproach,andthemostcommontoolforhiding
constructionwillgenerallybeordinaryFactoryMethodsratherthansomethinglikethisvirtualconstructor
scheme.
Builder:creatingcomplexobjects
ThegoalofBuilder(whichisaCreationalpattern,liketheFactorieswevejustlookedat)istoseparatethe
constructionofanobjectfromitsrepresentation.Thismeansthattheconstructionprocessstaysthe
same,buttheresultingobjecthasdifferentpossiblerepresentations.GoFpointsoutthatthemain
differencebetweenBuilderandAbstractFactoryisthataBuildercreatestheobjectstepbystep,sothe
factthatthecreationprocessisspreadoutintimeseemstobeimportant.Inaddition,thedirectorgetsa
streamofpiecesthatitpassestotheBuilder,andeachpieceisusedtoperformoneofthestepsinthe
buildprocess.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
368/458
5/3/2015
ThinkinginC++2ndedVolume2
Thefollowingexamplemodelsabicyclethatcanhaveachoiceofparts,accordingtoitstype(mountain
bike,touringbike,orracingbike).ABuilderclassisassociatedwitheachtypeofbicycle,andeachBuilder
implementstheinterfacespecifiedintheabstractclassBicycleBuilder.Aseparateclass,
BicycleTechnician,representsthedirectorobjectdescribedinGoF,andusesaconcrete
BicycleBuilderobjecttoconstructaBicycleobject.
//:C10:Bicycle.h
//Definesclassestobuildbicycles
//IllustratestheBuilderdesignpattern.
#ifndefBICYCLE_H
#defineBICYCLE_H
#include<iostream>
#include<string>
#include<vector>
#include<cstddef>
#include"../purge.h"
usingstd::size_t
classBicyclePart{
public:
enumBPart{FRAME,WHEEL,SEAT,DERAILLEUR,
HANDLEBAR,SPROCKET,RACK,SHOCK,NPARTS}
private:
BPartid
staticstd::stringnames[NPARTS]
public:
BicyclePart(BPartbp){id=bp}
friendstd::ostream&
operator<<(std::ostream&os,constBicyclePart&bp){
returnos<<bp.names[bp.id]
}
}
classBicycle{
std::vector<BicyclePart*>parts
public:
~Bicycle(){purge(parts)}
voidaddPart(BicyclePart*bp){parts.push_back(bp)}
friendstd::ostream&
operator<<(std::ostream&os,constBicycle&b){
os<<"{"
for(size_ti=0i<b.parts.size()++i)
os<<*b.parts[i]<<''
returnos<<'}'
}
}
classBicycleBuilder{
protected:
Bicycle*product
public:
BicycleBuilder(){product=0}
voidcreateProduct(){product=newBicycle}
virtualvoidbuildFrame()=0
virtualvoidbuildWheel()=0
virtualvoidbuildSeat()=0
virtualvoidbuildDerailleur()=0
virtualvoidbuildHandlebar()=0
virtualvoidbuildSprocket()=0
virtualvoidbuildRack()=0
virtualvoidbuildShock()=0
virtualstd::stringgetBikeName()const=0
Bicycle*getProduct(){
Bicycle*temp=product
product=0//Relinquishproduct
returntemp
}
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
369/458
5/3/2015
ThinkinginC++2ndedVolume2
classMountainBikeBuilder:publicBicycleBuilder{
public:
voidbuildFrame()
voidbuildWheel()
voidbuildSeat()
voidbuildDerailleur()
voidbuildHandlebar()
voidbuildSprocket()
voidbuildRack()
voidbuildShock()
std::stringgetBikeName()const{return"MountainBike"}
}
classTouringBikeBuilder:publicBicycleBuilder{
public:
voidbuildFrame()
voidbuildWheel()
voidbuildSeat()
voidbuildDerailleur()
voidbuildHandlebar()
voidbuildSprocket()
voidbuildRack()
voidbuildShock()
std::stringgetBikeName()const{return"TouringBike"}
}
classRacingBikeBuilder:publicBicycleBuilder{
public:
voidbuildFrame()
voidbuildWheel()
voidbuildSeat()
voidbuildDerailleur()
voidbuildHandlebar()
voidbuildSprocket()
voidbuildRack()
voidbuildShock()
std::stringgetBikeName()const{return"RacingBike"}
}
classBicycleTechnician{
BicycleBuilder*builder
public:
BicycleTechnician(){builder=0}
voidsetBuilder(BicycleBuilder*b){builder=b}
voidconstruct()
}
#endif//BICYCLE_H///:~
ABicycleholdsavectorofpointerstoBicyclePart,representingthepartsusedtoconstructthebicycle.
Toinitiatetheconstructionofabicycle,aBicycleTechnician(thedirectorinthisexample)calls
BicycleBuilder::createproduct()onaderivedBicycleBuilderobject.The
BicycleTechnician::construct()functioncallsallthefunctionsintheBicycleBuilderinterface(sinceit
doesntknowwhattypeofconcretebuilderithas).Theconcretebuilderclassesomit(viaemptyfunction
bodies)thoseactionsthatdonotapplytothetypeofbicycletheybuild,asyoucanseeinthefollowing
implementationfile:
//:C10:Bicycle.cpp{O}{mwcc}
#include"Bicycle.h"
#include<cassert>
#include<cstddef>
usingnamespacestd
std::stringBicyclePart::names[NPARTS]={
"Frame","Wheel","Seat","Derailleur",
"Handlebar","Sprocket","Rack","Shock"}
//MountainBikeBuilderimplementation
voidMountainBikeBuilder::buildFrame(){
product>addPart(newBicyclePart(BicyclePart::FRAME))
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
370/458
5/3/2015
ThinkinginC++2ndedVolume2
}
voidMountainBikeBuilder::buildWheel(){
product>addPart(newBicyclePart(BicyclePart::WHEEL))
}
voidMountainBikeBuilder::buildSeat(){
product>addPart(newBicyclePart(BicyclePart::SEAT))
}
voidMountainBikeBuilder::buildDerailleur(){
product>addPart(
newBicyclePart(BicyclePart::DERAILLEUR))
}
voidMountainBikeBuilder::buildHandlebar(){
product>addPart(
newBicyclePart(BicyclePart::HANDLEBAR))
}
voidMountainBikeBuilder::buildSprocket(){
product>addPart(newBicyclePart(BicyclePart::SPROCKET))
}
voidMountainBikeBuilder::buildRack(){}
voidMountainBikeBuilder::buildShock(){
product>addPart(newBicyclePart(BicyclePart::SHOCK))
}
//TouringBikeBuilderimplementation
voidTouringBikeBuilder::buildFrame(){
product>addPart(newBicyclePart(BicyclePart::FRAME))
}
voidTouringBikeBuilder::buildWheel(){
product>addPart(newBicyclePart(BicyclePart::WHEEL))
}
voidTouringBikeBuilder::buildSeat(){
product>addPart(newBicyclePart(BicyclePart::SEAT))
}
voidTouringBikeBuilder::buildDerailleur(){
product>addPart(
newBicyclePart(BicyclePart::DERAILLEUR))
}
voidTouringBikeBuilder::buildHandlebar(){
product>addPart(
newBicyclePart(BicyclePart::HANDLEBAR))
}
voidTouringBikeBuilder::buildSprocket(){
product>addPart(newBicyclePart(BicyclePart::SPROCKET))
}
voidTouringBikeBuilder::buildRack(){
product>addPart(newBicyclePart(BicyclePart::RACK))
}
voidTouringBikeBuilder::buildShock(){}
//RacingBikeBuilderimplementation
voidRacingBikeBuilder::buildFrame(){
product>addPart(newBicyclePart(BicyclePart::FRAME))
}
voidRacingBikeBuilder::buildWheel(){
product>addPart(newBicyclePart(BicyclePart::WHEEL))
}
voidRacingBikeBuilder::buildSeat(){
product>addPart(newBicyclePart(BicyclePart::SEAT))
}
voidRacingBikeBuilder::buildDerailleur(){}
voidRacingBikeBuilder::buildHandlebar(){
product>addPart(
newBicyclePart(BicyclePart::HANDLEBAR))
}
voidRacingBikeBuilder::buildSprocket(){
product>addPart(newBicyclePart(BicyclePart::SPROCKET))
}
voidRacingBikeBuilder::buildRack(){}
voidRacingBikeBuilder::buildShock(){}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
371/458
5/3/2015
ThinkinginC++2ndedVolume2
//BicycleTechnicianimplementation
voidBicycleTechnician::construct(){
assert(builder)
builder>createProduct()
builder>buildFrame()
builder>buildWheel()
builder>buildSeat()
builder>buildDerailleur()
builder>buildHandlebar()
builder>buildSprocket()
builder>buildRack()
builder>buildShock()
}///:~
TheBicyclestreaminsertercallsthecorrespondinginserterforeachBicyclePart,andthatprintsitstype
namesothatyoucanseewhataBicyclecontains.Hereisasampleprogram:
//:C10:BuildBicycles.cpp
//{L}Bicycle
//TheBuilderdesignpattern.
#include<cstddef>
#include<iostream>
#include<map>
#include<vector>
#include"Bicycle.h"
#include"../purge.h"
usingnamespacestd
//Constructsabikeviaaconcretebuilder
Bicycle*buildMeABike(
BicycleTechnician&t,BicycleBuilder*builder){
t.setBuilder(builder)
t.construct()
Bicycle*b=builder>getProduct()
cout<<"Builta"<<builder>getBikeName()<<endl
returnb
}
intmain(){
//Createanorderforsomebicycles
map<string,size_t>order
order["mountain"]=2
order["touring"]=1
order["racing"]=3
//Buildbikes
vector<Bicycle*>bikes
BicycleBuilder*m=newMountainBikeBuilder
BicycleBuilder*t=newTouringBikeBuilder
BicycleBuilder*r=newRacingBikeBuilder
BicycleTechniciantech
map<string,size_t>::iteratorit=order.begin()
while(it!=order.end()){
BicycleBuilder*builder
if(it>first=="mountain")
builder=m
elseif(it>first=="touring")
builder=t
elseif(it>first=="racing")
builder=r
for(size_ti=0i<it>second++i)
bikes.push_back(buildMeABike(tech,builder))
++it
}
deletem
deletet
deleter
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
372/458
5/3/2015
ThinkinginC++2ndedVolume2
//Displayinventory
for(size_ti=0i<bikes.size()++i)
cout<<"Bicycle:"<<*bikes[i]<<endl
purge(bikes)
}
/*Output:
BuiltaMountainBike
BuiltaMountainBike
BuiltaRacingBike
BuiltaRacingBike
BuiltaRacingBike
BuiltaTouringBike
Bicycle:{
FrameWheelSeatDerailleurHandlebarSprocketShock}
Bicycle:{
FrameWheelSeatDerailleurHandlebarSprocketShock}
Bicycle:{FrameWheelSeatHandlebarSprocket}
Bicycle:{FrameWheelSeatHandlebarSprocket}
Bicycle:{FrameWheelSeatHandlebarSprocket}
Bicycle:{
FrameWheelSeatDerailleurHandlebarSprocketRack}
*////:~
Thepowerofthispatternisthatitseparatesthealgorithmforassemblingpartsintoacompleteproduct
fromthepartsthemselvesandallowsdifferentalgorithmsfordifferentproductsviadifferent
implementationsofacommoninterface.
Observer
TheObserverpatternsolvesafairlycommonproblem:whatifagroupofobjectsneedstoupdate
themselveswhensomeotherobjectchangesstate?Thiscanbeseeninthemodelviewaspectof
SmalltalksMVC(modelviewcontroller)orthealmostequivalentDocumentViewArchitecture.Suppose
thatyouhavesomedata(thedocument)andtwoviews:aplotviewandatextualview.Whenyou
changethedata,theviewsmustbetoldtoupdatethemselves,andthatswhattheobserverfacilitates.
Twotypesofobjectsareusedtoimplementtheobserverpatterninthefollowingcode.TheObservable
classkeepstrackoftheobjectsthatwanttobeinformedwhenachangehappens.TheObservableclass
callsthenotifyObservers()memberfunctionforeachobserveronthelist.ThenotifyObservers()
memberfunctionispartofthebaseclassObservable.
Therearetwothingsthatchangeintheobserverpattern:thequantityofobservingobjectsandtheway
anupdateoccurs.Thatis,theobserverpatternallowsyoutomodifybothofthesewithoutaffectingthe
surroundingcode.
Youcanimplementtheobserverpatterninanumberofways,butthecodeshownherewillcreatea
frameworkfromwhichyoucanbuildyourownobservercode,byfollowingtheexample.First,this
interfacedescribeswhatanobserverlookslike:
//:C10:Observer.h
//TheObserverinterface.
#ifndefOBSERVER_H
#defineOBSERVER_H
classObservable
classArgument{}
classObserver{
public:
//Calledbytheobservedobject,whenever
//theobservedobjectischanged:
virtualvoidupdate(Observable*o,Argument*arg)=0
virtual~Observer(){}
}
#endif//OBSERVER_H///:~
SinceObserverinteractswithObservableinthisapproach,Observablemustbedeclaredfirst.In
addition,theArgumentclassisemptyandonlyactsasabaseclassforanytypeofargumentyouwantto
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
373/458
5/3/2015
ThinkinginC++2ndedVolume2
passduringanupdate.Ifyouwant,youcansimplypasstheextraargumentasavoid*.Youllhaveto
downcastineithercase.
TheObservertypeisaninterfaceclassthatonlyhasonememberfunction,update().Thisfunctionis
calledbytheobjectthatsbeingobserved,whenthatobjectdecidesitstimetoupdateallitsobservers.
Theargumentsareoptionalyoucouldhaveanupdate()withnoarguments,andthatwouldstillfitthe
observerpattern.Howeverthisismoregeneralitallowstheobservedobjecttopasstheobjectthat
causedtheupdate(sinceanObservermayberegisteredwithmorethanoneobservedobject)andany
extrainformationifthatshelpful,ratherthanforcingtheObserverobjecttohuntaroundtoseewhois
updatingandtofetchanyotherinformationitneeds.
TheobservedobjectwillbeoftypeObservable:
//:C10:Observable.h
//TheObservableclass.
#ifndefOBSERVABLE_H
#defineOBSERVABLE_H
#include<set>
#include"Observer.h"
classObservable{
boolchanged
std::set<Observer*>observers
protected:
virtualvoidsetChanged(){changed=true}
virtualvoidclearChanged(){changed=false}
public:
virtualvoidaddObserver(Observer&o){
observers.insert(&o)
}
virtualvoiddeleteObserver(Observer&o){
observers.erase(&o)
}
virtualvoiddeleteObservers(){
observers.clear()
}
virtualintcountObservers(){
returnobservers.size()
}
virtualboolhasChanged(){returnchanged}
//Ifthisobjecthaschanged,notifyall
//ofitsobservers:
virtualvoidnotifyObservers(Argument*arg=0){
if(!hasChanged())return
clearChanged()//Not"changed"anymore
std::set<Observer*>::iteratorit
for(it=observers.begin()it!=observers.end()it++)
(*it)>update(this,arg)
}
virtual~Observable(){}
}
#endif//OBSERVABLE_H///:~
Again,thedesignhereismoreelaboratethanisnecessary.Aslongastheresawaytoregisteran
ObserverwithanObservableandawayfortheObservabletoupdateitsObservers,thesetof
memberfunctionsdoesntmatter.However,thisdesignisintendedtobereusable.(Itwasliftedfromthe
designusedintheJavastandardlibrary.)[144]
TheObservableobjecthasaflagtoindicatewhetheritsbeenchanged.Inasimplerdesign,therewould
benoflagifsomethinghappened,everyonewouldbenotified.Notice,however,thatthecontrolofthe
flagsstateisprotectedsothatonlyaninheritorcandecidewhatconstitutesachange,andnottheend
useroftheresultingderivedObserverclass.
ThecollectionofObserverobjectsiskeptinaset<Observer*>topreventduplicatesthesetinsert(),
erase(),clear(),andsize()functionsareexposedtoallowObserverstobeaddedandremovedat
anytime,thusprovidingruntimeflexibility.
MostoftheworkisdoneinnotifyObservers().Ifthechangedflaghasnotbeenset,thisdoesnothing.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
374/458
5/3/2015
ThinkinginC++2ndedVolume2
Otherwise,itfirstclearsthechangedflagsothatrepeatedcallstonotifyObservers()wontwastetime.
Thisisdonebeforenotifyingtheobserversincasethecallstoupdate()doanythingthatcausesachange
backtothisObservableobject.Itthenmovesthroughthesetandcallsbacktotheupdate()member
functionofeachObserver.
AtfirstitmayappearthatyoucanuseanordinaryObservableobjecttomanagetheupdates.Butthis
doesntworktogetanyeffect,youmustderivefromObservableandsomewhereinyourderivedclass
codecallsetChanged().Thisisthememberfunctionthatsetsthechangedflag,whichmeansthatwhen
youcallnotifyObservers()alltheobserverswill,infact,getnotified.WhereyoucallsetChanged()
dependsonthelogicofyourprogram.
Nowweencounteradilemma.Objectsthatarebeingobservedmayhavemorethanonesuchitemof
interest.Forexample,ifyouredealingwithaGUIitemabutton,saytheitemsofinterestmightbethe
mouseclickedthebutton,themousemovedoverthebutton,and(forsomereason)thebuttonchangedits
color.Sowedliketobeabletoreportalltheseeventstodifferentobservers,eachofwhichisinterested
inadifferenttypeofevent.
Theproblemisthatwewouldnormallyreachformultipleinheritanceinsuchasituation:Illinheritfrom
Observabletodealwithmouseclicks,andIllerinheritfromObservabletodealwithmouseovers,
and,well,hmm,thatdoesntwork.
Theinnerclassidiom
Heresasituationwherewemust(ineffect)upcasttomorethanonetype,butinthiscaseweneedto
provideseveraldifferentimplementationsofthesamebasetype.Thesolutionissomethingwevelifted
fromJava,whichtakesC++snestedclassonestepfurther.Javahasabuiltinfeaturecalledaninner
class,whichislikeanestedclassinC++,butithasaccesstothenonstaticdataofitscontainingclassby
implicitlyusingthethispointeroftheclassobjectitwascreatedwithin.[145]
ToimplementtheinnerclassidiominC++,wemustobtainanduseapointertothecontainingobject
explicitly.Heresanexample:
//:C10:InnerClassIdiom.cpp
//Exampleofthe"innerclass"idiom.
#include<iostream>
#include<string>
usingnamespacestd
classPoingable{
public:
virtualvoidpoing()=0
}
voidcallPoing(Poingable&p){
p.poing()
}
classBingable{
public:
virtualvoidbing()=0
}
voidcallBing(Bingable&b){
b.bing()
}
classOuter{
stringname
//Defineoneinnerclass:
classInner1
friendclassOuter::Inner1
classInner1:publicPoingable{
Outer*parent
public:
Inner1(Outer*p):parent(p){}
voidpoing(){
cout<<"poingcalledfor"
<<parent>name<<endl
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
375/458
5/3/2015
ThinkinginC++2ndedVolume2
//Accessesdataintheouterclassobject
}
}inner1
//Defineasecondinnerclass:
classInner2
friendclassOuter::Inner2
classInner2:publicBingable{
Outer*parent
public:
Inner2(Outer*p):parent(p){}
voidbing(){
cout<<"bingcalledfor"
<<parent>name<<endl
}
}inner2
public:
Outer(conststring&nm)
:name(nm),inner1(this),inner2(this){}
//Returnreferencetointerfaces
//implementedbytheinnerclasses:
operatorPoingable&(){returninner1}
operatorBingable&(){returninner2}
}
intmain(){
Outerx("PingPong")
//Likeupcastingtomultiplebasetypes!:
callPoing(x)
callBing(x)
}///:~
Theexample(intendedtoshowthesimplestsyntaxfortheidiomyoullseearealuseshortly)beginswith
thePoingableandBingableinterfaces,eachcontainingasinglememberfunction.Theservicesprovided
bycallPoing()andcallBing()requirethattheobjecttheyreceiveimplementsthePoingableand
Bingableinterfaces,respectively,buttheyputnootherrequirementsonthatobjectsoastomaximizethe
flexibilityofusingcallPoing()andcallBing().Notethelackofvirtualdestructorsineitherinterface
theintentisthatyouneverperformobjectdestructionviatheinterface.
TheOuterconstructorcontainssomeprivatedata(name),anditwantstoprovidebothaPoingable
interfaceandaBingableinterfacesoitcanbeusedwithcallPoing()andcallBing().(Inthissituation
wecouldsimplyusemultipleinheritance,butitiskeptsimpleforclarity.)ToprovideaPoingableobject
withoutderivingOuterfromPoingable,theinnerclassidiomisused.First,thedeclarationclassInner
saysthat,somewhere,thereisanestedclassofthisname.Thisallowsthefrienddeclarationforthe
class,whichfollows.Finally,nowthatthenestedclasshasbeengrantedaccesstoalltheprivateelements
ofOuter,theclasscanbedefined.NoticethatitkeepsapointertotheOuterwhichcreatedit,andthis
pointermustbeinitializedintheconstructor.Finally,thepoing()functionfromPoingableis
implemented.ThesameprocessoccursforthesecondinnerclasswhichimplementsBingable.Eachinner
classhasasingleprivateinstancecreated,whichisinitializedintheOuterconstructor.Bycreatingthe
memberobjectsandreturningreferencestothem,issuesofobjectlifetimeareeliminated.
Noticethatbothinnerclassdefinitionsareprivate,andinfacttheclientcodedoesnthaveanyaccessto
detailsoftheimplementation,sincethetwoaccessfunctionsoperatorPoingable&()andoperator
Bingable&()onlyreturnareferencetotheupcastinterface,nottotheobjectthatimplementsit.Infact,
sincethetwoinnerclassesareprivate,theclientcodecannotevendowncasttotheimplementation
classes,thusprovidingcompleteisolationbetweeninterfaceandimplementation.
Wevetakentheextralibertyhereofdefiningtheautomatictypeconversionfunctionsoperator
Poingable&()andoperatorBingable&().Inmain(),youcanseethattheseallowasyntaxthatlooks
asifOutermultiplyinheritsfromPoingableandBingable.Thedifferenceisthatthecastsinthiscase
areoneway.YoucangettheeffectofanupcasttoPoingableorBingable,butyoucannotdowncastback
toanOuter.Inthefollowingexampleofobserver,youllseethemoretypicalapproach:youprovide
accesstotheinnerclassobjectsusingordinarymemberfunctions,notautomatictypeconversion
functions.
Theobserverexample
ArmedwiththeObserverandObservableheaderfilesandtheinnerclassidiom,wecanlookatan
exampleoftheObserverpattern:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
376/458
5/3/2015
ThinkinginC++2ndedVolume2
//:C10:ObservedFlower.cpp
//Demonstrationof"observer"pattern.
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include"Observable.h"
usingnamespacestd
classFlower{
boolisOpen
public:
Flower():isOpen(false),
openNotifier(this),closeNotifier(this){}
voidopen(){//Opensitspetals
isOpen=true
openNotifier.notifyObservers()
closeNotifier.open()
}
voidclose(){//Closesitspetals
isOpen=false
closeNotifier.notifyObservers()
openNotifier.close()
}
//Usingthe"innerclass"idiom:
classOpenNotifier
friendclassFlower::OpenNotifier
classOpenNotifier:publicObservable{
Flower*parent
boolalreadyOpen
public:
OpenNotifier(Flower*f):parent(f),
alreadyOpen(false){}
voidnotifyObservers(Argument*arg=0){
if(parent>isOpen&&!alreadyOpen){
setChanged()
Observable::notifyObservers()
alreadyOpen=true
}
}
voidclose(){alreadyOpen=false}
}openNotifier
classCloseNotifier
friendclassFlower::CloseNotifier
classCloseNotifier:publicObservable{
Flower*parent
boolalreadyClosed
public:
CloseNotifier(Flower*f):parent(f),
alreadyClosed(false){}
voidnotifyObservers(Argument*arg=0){
if(!parent>isOpen&&!alreadyClosed){
setChanged()
Observable::notifyObservers()
alreadyClosed=true
}
}
voidopen(){alreadyClosed=false}
}closeNotifier
}
classBee{
stringname
//An"innerclass"forobservingopenings:
classOpenObserver
friendclassBee::OpenObserver
classOpenObserver:publicObserver{
Bee*parent
public:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
377/458
5/3/2015
ThinkinginC++2ndedVolume2
OpenObserver(Bee*b):parent(b){}
voidupdate(Observable*,Argument*){
cout<<"Bee"<<parent>name
<<"'sbreakfasttime!<<endl
}
}openObsrv
//Another"innerclass"forclosings:
classCloseObserver
friendclassBee::CloseObserver
classCloseObserver:publicObserver{
Bee*parent
public:
CloseObserver(Bee*b):parent(b){}
voidupdate(Observable*,Argument*){
cout<<"Bee"<<parent>name
<<"'sbedtime!<<endl
}
}closeObsrv
public:
Bee(stringnm):name(nm),
openObsrv(this),closeObsrv(this){}
Observer&openObserver(){returnopenObsrv}
Observer&closeObserver(){returncloseObsrv}
}
classHummingbird{
stringname
classOpenObserver
friendclassHummingbird::OpenObserver
classOpenObserver:publicObserver{
Hummingbird*parent
public:
OpenObserver(Hummingbird*h):parent(h){}
voidupdate(Observable*,Argument*){
cout<<"Hummingbird"<<parent>name
<<"'sbreakfasttime!<<endl
}
}openObsrv
classCloseObserver
friendclassHummingbird::CloseObserver
classCloseObserver:publicObserver{
Hummingbird*parent
public:
CloseObserver(Hummingbird*h):parent(h){}
voidupdate(Observable*,Argument*){
cout<<"Hummingbird"<<parent>name
<<"'sbedtime!<<endl
}
}closeObsrv
public:
Hummingbird(stringnm):name(nm),
openObsrv(this),closeObsrv(this){}
Observer&openObserver(){returnopenObsrv}
Observer&closeObserver(){returncloseObsrv}
}
intmain(){
Flowerf
Beeba("A"),bb("B")
Hummingbirdha("A"),hb("B")
f.openNotifier.addObserver(ha.openObserver())
f.openNotifier.addObserver(hb.openObserver())
f.openNotifier.addObserver(ba.openObserver())
f.openNotifier.addObserver(bb.openObserver())
f.closeNotifier.addObserver(ha.closeObserver())
f.closeNotifier.addObserver(hb.closeObserver())
f.closeNotifier.addObserver(ba.closeObserver())
f.closeNotifier.addObserver(bb.closeObserver())
//HummingbirdBdecidestosleepin:
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
378/458
5/3/2015
ThinkinginC++2ndedVolume2
f.openNotifier.deleteObserver(hb.openObserver())
//Somethingchangesthatinterestsobservers:
f.open()
f.open()//It'salreadyopen,nochange.
//BeeAdoesn'twanttogotobed:
f.closeNotifier.deleteObserver(
ba.closeObserver())
f.close()
f.close()//It'salreadyclosednochange
f.openNotifier.deleteObservers()
f.open()
f.close()
}///:~
TheeventsofinterestarethataFlowercanopenorclose.Becauseoftheuseoftheinnerclassidiom,
boththeseeventscanbeseparatelyobservablephenomena.TheOpenNotifierandCloseNotifierclasses
bothderivefromObservable,sotheyhaveaccesstosetChanged()andcanbehandedtoanythingthat
needsanObservable.Youllnoticethat,contrarytoInnerClassIdiom.cpp,theObservabledescendants
arepublic.Thisisbecausesomeoftheirmemberfunctionsmustbeavailabletotheclientprogrammer.
TheresnothingthatsaysthataninnerclassmustbeprivateinInnerClassIdiom.cppweweresimply
followingthedesignguidelinemakethingsasprivateaspossible.Youcouldmaketheclassesprivate
andexposetheappropriatememberfunctionsbyproxyinFlower,butitwouldntgainmuch.
TheinnerclassidiomalsocomesinhandytodefinemorethanonekindofObserverinBeeand
Hummingbird,sinceboththoseclassesmaywanttoindependentlyobserveFloweropeningsand
closings.Noticehowtheinnerclassidiomprovidessomethingthathasmostofthebenefitsofinheritance
(theabilitytoaccesstheprivatedataintheouterclass,forexample).
Inmain(),youcanseeoneoftheprimarybenefitsoftheObserverpattern:theabilitytochange
behavioratruntimebydynamicallyregisteringandunregisteringObserverswithObservables.This
flexibilityisachievedatthecostofsignificantadditionalcodeyouwilloftenseethiskindoftradeoffin
designpatterns:morecomplexityinoneplaceinexchangeforincreasedflexibilityand/orlowered
complexityinanotherplace.
Ifyoustudythepreviousexample,youllseethatOpenNotifierandCloseNotifierusethebasic
Observableinterface.ThismeansthatyoucouldderivefromothercompletelydifferentObserver
classestheonlyconnectiontheObservershavewithFlowersistheObserverinterface.
Anotherwaytoaccomplishthisfinegranularityofobservablephenomenaistousesomeformoftagsfor
thephenomena,forexampleemptyclasses,strings,orenumerationsthatdenotedifferenttypesof
observablebehavior.Thisapproachcanbeimplementedusingaggregationratherthaninheritance,andthe
differencesaremainlytradeoffsbetweentimeandspaceefficiency.Fortheclient,thedifferencesare
negligible.
Multipledispatching
Whendealingwithmultipleinteractingtypes,aprogramcangetparticularlymessy.Forexample,consider
asystemthatparsesandexecutesmathematicalexpressions.YouwanttobeabletosayNumber+
Number,Number*Number,andsoon,whereNumberisthebaseclassforafamilyofnumerical
objects.Butwhenyousaya+b,andyoudontknowtheexacttypeofeitheraorb,howcanyouget
themtointeractproperly?
Theanswerstartswithsomethingyouprobablydontthinkabout:C++performsonlysingledispatching.
Thatis,ifyouareperforminganoperationonmorethanoneobjectwhosetypeisunknown,C++can
invokethedynamicbindingmechanismononlyoneofthosetypes.Thisdoesntsolvetheproblem
describedhere,soyouendupdetectingsometypesmanuallyandeffectivelyproducingyourowndynamic
bindingbehavior.
Thesolutioniscalledmultipledispatching(describedinGoFinthecontextoftheVisitorpattern,shownin
thenextsection).Here,therewillbeonlytwodispatches,whichisreferredtoasdoubledispatching.
Rememberthatpolymorphismcanoccuronlyviavirtualfunctioncalls,soifyouwantmultipledispatching
tooccur,theremustbeavirtualfunctioncalltodetermineeachunknowntype.Thus,ifyouareworking
withtwodifferenttypehierarchiesthatareinteracting,youllneedavirtualcallineachhierarchy.
Generally,youllsetupaconfigurationsuchthatasinglememberfunctioncallgeneratesmorethanone
virtualmemberfunctioncallandthusdeterminesmorethanonetypeintheprocess:youllneedavirtual
functioncallforeachdispatch.Thevirtualfunctionsinthefollowingexamplearecalledcompete()and
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
[146]
379/458
5/3/2015
ThinkinginC++2ndedVolume2
eval()andarebothmembersofthesametype(thisisnotarequirementformultipledispatching):[146]
//:C10:PaperScissorsRock.cpp
//Demonstrationofmultipledispatching.
#include<algorithm>
#include<iostream>
#include<iterator>
#include<vector>
#include<ctime>
#include<cstdlib>
#include"../purge.h"
usingnamespacestd
classPaper
classScissors
classRock
enumOutcome{WIN,LOSE,DRAW}
ostream&operator<<(ostream&os,constOutcomeout){
switch(out){
default:
caseWIN:returnos<<"win"
caseLOSE:returnos<<"lose"
caseDRAW:returnos<<"draw"
}
}
classItem{
public:
virtualOutcomecompete(constItem*)=0
virtualOutcomeeval(constPaper*)const=0
virtualOutcomeeval(constScissors*)const=0
virtualOutcomeeval(constRock*)const=0
virtualostream&print(ostream&os)const=0
virtual~Item(){}
friendostream&operator<<(ostream&os,constItem*it){
returnit>print(os)
}
}
classPaper:publicItem{
public:
Outcomecompete(constItem*it){returnit>eval(this)}
Outcomeeval(constPaper*)const{returnDRAW}
Outcomeeval(constScissors*)const{returnWIN}
Outcomeeval(constRock*)const{returnLOSE}
ostream&print(ostream&os)const{
returnos<<"Paper"
}
}
classScissors:publicItem{
public:
Outcomecompete(constItem*it){returnit>eval(this)}
Outcomeeval(constPaper*)const{returnLOSE}
Outcomeeval(constScissors*)const{returnDRAW}
Outcomeeval(constRock*)const{returnWIN}
ostream&print(ostream&os)const{
returnos<<"Scissors"
}
}
classRock:publicItem{
public:
Outcomecompete(constItem*it){returnit>eval(this)}
Outcomeeval(constPaper*)const{returnWIN}
Outcomeeval(constScissors*)const{returnLOSE}
Outcomeeval(constRock*)const{returnDRAW}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
380/458
5/3/2015
ThinkinginC++2ndedVolume2
ostream&print(ostream&os)const{
returnos<<"Rock"
}
}
structItemGen{
Item*operator()(){
switch(rand()%3){
default:
case0:returnnewScissors
case1:returnnewPaper
case2:returnnewRock
}
}
}
structCompete{
Outcomeoperator()(Item*a,Item*b){
cout<<a<<"\t"<<b<<"\t"
returna>compete(b)
}
}
intmain(){
srand(time(0))//Seedtherandomnumbergenerator
constintsz=20
vector<Item*>v(sz*2)
generate(v.begin(),v.end(),ItemGen())
transform(v.begin(),v.begin()+sz,
v.begin()+sz,
ostream_iterator<Outcome>(cout,"\n"),
Compete())
purge(v)
}///:~
Outcomecategorizesthedifferentpossibleresultsofacompete(),andtheoperator<<simplifiesthe
processofdisplayingaparticularOutcome.
Itemisthebaseclassforthetypesthatwillbemultiplydispatched.Compete::operator()takestwo
Item*(theexacttypeofbothareunknown)andbeginsthedoubledispatchingprocessbycallingthe
virtualItem::compete()function.Thevirtualmechanismdeterminesthetypea,soitwakesupinside
thecompete()functionofasconcretetype.Thecompete()functionperformstheseconddispatchby
callingeval()ontheremainingtype.Passingitself(this)asanargumenttoeval()producesacallto
theoverloadedeval()function,thuspreservingthetypeinformationofthefirstdispatch.Whenthe
seconddispatchiscompleted,youknowtheexacttypesofbothItemobjects.
Inmain(),theSTLalgorithmgenerate()populatesthevectorv,thentransform()applies
Compete::operator()tothetworanges.Thisversionoftransform()takesthestartandendpointof
thefirstrange(containingthelefthandItemsusedinthedoubledispatch)thestartingpointofthe
secondrange,whichholdstherighthandItemsthedestinationiterator,whichinthiscaseisstandard
outputandthefunctionobject(atemporaryoftypeCompete)tocallforeachobject.
Itrequiresalotofceremonytosetupmultipledispatching,butkeepinmindthatthebenefitisthe
syntacticeleganceachievedwhenmakingthecallinsteadofwritingawkwardcodetodeterminethetype
ofoneormoreobjectsduringacall,yousimplysay:Youtwo!Idontcarewhattypesyouare,interact
properlywitheachother!Makesurethiskindofeleganceisimportanttoyoubeforeembarkingon
multipledispatching,however.
Notethatmultipledispatchingis,ineffect,performingatablelookup.Here,thelookupisperformedusing
virtualfunctions,butyoucouldinsteadperformaliteraltablelookup.Withmorethanafewdispatches
(andifyouarepronetomakingadditionsandchanges),atablelookupmaybeabettersolutiontothe
problem.
MultipledispatchingwithVisitor
ThegoalofVisitor(thefinal,andarguablymostcomplex,patterninGoF)istoseparatetheoperationson
aclasshierarchyfromthehierarchyitself.Thisisquiteanoddmotivationbecausemostofwhatwedoin
objectorientedprogrammingistocombinedataandoperationsintoobjects,andtousepolymorphismto
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
381/458
5/3/2015
ThinkinginC++2ndedVolume2
automaticallyselectthecorrectvariationofanoperation,dependingontheexacttypeofanobject.
WithVisitoryouextracttheoperationsfrominsideyourclasshierarchyintoaseparate,externalhierarchy.
Themainhierarchythencontainsavisit()functionthatacceptsanyobjectfromyourhierarchyof
operations.Asaresult,yougettwoclasshierarchiesinsteadofone.Inaddition,youllseethatyour
mainhierarchybecomesverybrittleifyouaddanewclass,youwillforcechangesthroughoutthe
secondhierarchy.GoFsaysthatthemainhierarchyshouldthusrarelychange.Thisconstraintisvery
limiting,anditfurtherreducestheapplicabilityofthispattern.
Forthesakeofargument,then,assumethatyouhaveaprimaryclasshierarchythatisfixedperhapsits
fromanothervendorandyoucantmakechangestothathierarchy.Ifyouhadthesourcecodeforthe
library,youcouldaddnewvirtualfunctionsinthebaseclass,butthisis,forsomereason,notfeasible.A
morelikelyscenarioisthataddingnewvirtualfunctionsissomehowawkward,uglyorotherwisedifficultto
maintain.GoFarguesthatdistributingalltheseoperationsacrossthevariousnodeclassesleadstoa
systemthatshardtounderstand,maintain,andchange.(Asyoullsee,Visitorcanbemuchharderto
understand,maintainandchange.)AnotherGoFargumentisthatyouwanttoavoidpollutingtheinterface
ofthemainhierarchywithtoomanyoperations(butifyourinterfaceistoofat,youmightaskwhether
theobjectistryingtodotoomanythings).
Thelibrarycreatormusthaveforeseen,however,thatyouwillwanttoaddnewoperationstothat
hierarchy,sothattheycanknowtoincludethevisit()function.
So(assumingyoureallyneedtodothis)thedilemmaisthatyouneedtoaddmemberfunctionstothe
baseclass,butforsomereasonyoucanttouchthebaseclass.Howdoyougetaroundthis?
Visitorbuildsonthedoubledispatchingschemeshownintheprevioussection.TheVisitorpatternallows
youtoeffectivelyextendtheinterfaceoftheprimarytypebycreatingaseparateclasshierarchyoftype
Visitortovirtualizetheoperationsperformedontheprimarytype.Theobjectsoftheprimarytype
simplyacceptthevisitorandthencallthevisitorsdynamicallyboundmemberfunction.Thus,youcreate
avisitor,passitintotheprimaryhierarchy,andyougettheeffectofavirtualfunction.Heresasimple
example:
//:C10:BeeAndFlowers.cpp
//Demonstrationof"visitor"pattern.
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<ctime>
#include<cstdlib>
#include"../purge.h"
usingnamespacestd
classGladiolus
classRenuculus
classChrysanthemum
classVisitor{
public:
virtualvoidvisit(Gladiolus*f)=0
virtualvoidvisit(Renuculus*f)=0
virtualvoidvisit(Chrysanthemum*f)=0
virtual~Visitor(){}
}
classFlower{
public:
virtualvoidaccept(Visitor&)=0
virtual~Flower(){}
}
classGladiolus:publicFlower{
public:
virtualvoidaccept(Visitor&v){
v.visit(this)
}
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
382/458
5/3/2015
ThinkinginC++2ndedVolume2
classRenuculus:publicFlower{
public:
virtualvoidaccept(Visitor&v){
v.visit(this)
}
}
classChrysanthemum:publicFlower{
public:
virtualvoidaccept(Visitor&v){
v.visit(this)
}
}
//Addtheabilitytoproduceastring:
classStringVal:publicVisitor{
strings
public:
operatorconststring&(){returns}
virtualvoidvisit(Gladiolus*){
s="Gladiolus"
}
virtualvoidvisit(Renuculus*){
s="Renuculus"
}
virtualvoidvisit(Chrysanthemum*){
s="Chrysanthemum"
}
}
//Addtheabilitytodo"Bee"activities:
classBee:publicVisitor{
public:
virtualvoidvisit(Gladiolus*){
cout<<"BeeandGladiolus<<endl
}
virtualvoidvisit(Renuculus*){
cout<<"BeeandRenuculus<<endl
}
virtualvoidvisit(Chrysanthemum*){
cout<<"BeeandChrysanthemum<<endl
}
}
structFlowerGen{
Flower*operator()(){
switch(rand()%3){
default:
case0:returnnewGladiolus
case1:returnnewRenuculus
case2:returnnewChrysanthemum
}
}
}
intmain(){
srand(time(0))//Seedtherandomnumbergenerator
vector<Flower*>v(10)
generate(v.begin(),v.end(),FlowerGen())
vector<Flower*>::iteratorit
//It'salmostasifIaddedavirtualfunction
//toproduceaFlowerstringrepresentation:
StringValsval
for(it=v.begin()it!=v.end()it++){
(*it)>accept(sval)
cout<<string(sval)<<endl
}
//Perform"Bee"operationonallFlowers:
Beebee
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
383/458
5/3/2015
ThinkinginC++2ndedVolume2
for(it=v.begin()it!=v.end()it++)
(*it)>accept(bee)
purge(v)
}///:~
Floweristheprimaryhierarchy,andeachsubtypeofFlowercanaccept()aVisitor.TheFlower
hierarchyhasnooperationsotherthanaccept(),soallthefunctionalityoftheFlowerhierarchyis
containedintheVisitorhierarchy.NotethattheVisitorclassesmustknowaboutallthespecifictypesof
Flower,andifyouaddanewtypeofFlowertheentireVisitorhierarchymustbereworked.
Theaccept()functionineachFlowerbeginsadoubledispatchasdescribedintheprevioussection.The
firstdispatchdeterminestheexacttypeofFlowerandtheseconddeterminestheexacttypeofVisitor.
Onceyouknowtheexacttypesyoucanperformanoperationappropriatetoboth.
ItsveryunlikelythatyoulluseVisitorbecauseitsmotivationisunusualanditsconstraintsarestultifying.
TheGoFexamplesarenotconvincingthefirstisacompiler(notmanypeoplewritecompilers,andit
seemsquiterarethatVisitorisusedwithinthesecompilers),andtheyapologizefortheotherexamples,
sayingyouwouldntactuallyuseVisitorforanythinglikethis.Youwouldneedastrongercompulsionthan
thatpresentedinGoFtoabandonanordinaryOOstructureforVisitorwhatbenefitdoesitreallybuyyou
inexchangeformuchgreatercomplexityandconstraint?Whycantyousimplyaddmorevirtualfunctions
inthebaseclasswhenyoudiscoveryouneedthem?Or,ifyoureallyneedtopastenewfunctionsintoan
existinghierarchyandyouareunabletomodifythathierarchy,whynottrymultipleinheritancefirst?
(Eventhen,thelikelihoodofsavingtheexistinghierarchythiswayisslim).Consideralsothat,touse
Visitor,theexistinghierarchymustincorporateavisit()functionfromthebeginning,becausetoaddit
laterwouldmeanthatyouhadpermissiontomodifythehierarchy,soyoucouldjustaddordinaryvirtual
functionsasyouneedthem.No,Visitormustbepartofthearchitecturefromthebeginning,andtouseit
requiresamotivationgreaterthanthatinGoF.[147]
WepresentVisitorherebecausewehaveseenitusedwhenitshouldntbe,justasmultipleinheritanceand
anynumberofotherapproacheshavebeenusedinappropriately.IfyoufindyourselfusingVisitor,ask
why.Areyoureallyunabletoaddnewvirtualfunctionsinthebaseclass?Doyoureallywanttobe
restrictedfromaddingnewtypesinyourprimaryhierarchy?
Summary
Thepointofdesignpatterns,likethepointofanyabstraction,istomakeyourlifeeasier.Usually
somethinginyoursystemischangingthiscouldbecodeduringthelifetimeoftheproject,orobjects
duringthelifetimeofoneprogramexecution.Discoverwhatischanging,andadesignpatternmayhelp
youencapsulatethatchange,andthusbringitundercontrol.
Itseasytogetinfatuatedwithaparticulardesign,andtocreatetroubleforyourselfbyapplyingitjust
becauseyouknowhow.Whatshard,ironically,istofollowtheXPmaximofdothesimplestthingthat
couldpossiblywork.Butbydoingthesimplestthing,younotonlygetadesignthatsfastertoimplement,
butalsoeasiertomaintain.Andifthesimplestthingdoesntdothejob,youllfindoutalotsoonerthanif
youspendthetimeimplementingsomethingcomplex,andthenfindoutthatdoesntwork.
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.CreateavariationofSingletonPattern.cppwhereallfunctionsarestatic.Istheinstance()
functionstillnecessaryinthiscase?
2.StartingwithSingletonPattern.cpp,createaclassthatprovidesaconnectiontoaservicethat
storesandretrievesdatafromaconfigurationfile.
3.UsingSingletonPattern.cppasastartingpoint,createaclassthatmanagesafixednumberofits
ownobjects.Assumetheobjectsaredatabaseconnectionsandyouonlyhavealicensetousea
fixedquantityoftheseatanyonetime.
4.ModifyKissingPrincess2.cppbyaddinganotherstatetothesystem,sothateachkisscyclesthe
creaturetothenextstate.
5.FindC16:TStack.hfromThinkinginC++,Volume1,2ndEdition(downloadablefromwww.
BruceEckel.com).CreateanAdapterforthisclasssuchthatyoucanapplytheSTLalgorithm
for_each()totheelementsoftheTStack,usingyouradapter.CreateaTStackofstring,fillit
withstringsandusefor_each()tocountallthelettersinallthestringsintheTStack.
6.Createaframework(thatis,usetheTemplateMethodpattern)thattakesalistoffilenamesonthe
commandline.Itopenseachfileexceptthelastforreading,andthelastfileitopensforwriting.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
384/458
5/3/2015
ThinkinginC++2ndedVolume2
Theframeworkwillprocesseachinputfileusinganundeterminedpolicyandwritetheoutputtothe
lastfile.Inherittocustomizethisframeworktocreatetwoseparateapplications:
1)Convertsallthelettersineachfiletouppercase.
2)Searchesthefilesforwordsgiveninthefirstfile.
7.ModifyExercise6touseStrategyinsteadofTemplateMethod.
8.ModifyStrategy.cpptoincludeStatebehavior,sothattheStrategycanbechangedduringthe
lifetimeoftheContextobject.
9.ModifyStrategy.cpptouseaChainofResponsibilityapproach,whereyoukeeptryingdifferent
waystogetsomeonetosaytheirnamewithoutadmittingyouveforgottenit.
10.AddaclassTriangletoShapeFactory1.cpp.
11.AddaclassTriangletoShapeFactory2.cpp.
12.AddanewtypeofGameEnvironmentcalledGnomesAndFairiestoAbstractFactory.cpp.
13.ModifyShapeFactory2.cppsothatitusesanAbstractFactorytocreatedifferentsetsofshapes(for
example,oneparticulartypeoffactoryobjectcreatesthickshapes,anothercreatesthinshapes,
buteachfactoryobjectcancreatealltheshapes:circles,squares,triangles,andsoon).
14.ModifyVirtualConstructor.cpptouseamapinsteadofifelsestatementsinside
Shape::Shape(stringtype).
15.Breakatextfileupintoaninputstreamofwords(keepitsimple:justbreaktheinputstreamon
whitespace).CreateoneBuilderthatputsthewordsintoaset,andanotherthatproducesamap
containingwordsandoccurrencesofthosewords(thatis,itdoesawordcount).
16.CreateaminimalObserverObservabledesignintwoclasses,withoutbaseclassesandwithoutthe
extraargumentsinObserver.handthememberfunctionsinObservable.h.Justcreatethebare
minimuminthetwoclasses,andthendemonstrateyourdesignbycreatingoneObservableand
manyObserversandcausetheObservabletoupdatetheObservers.
17.ChangeInnerClassIdiom.cppsothatOuterusesmultipleinheritanceinsteadoftheinnerclass
idiom.
18.ModifyPaperScissorsRock.javatoreplacethedoubledispatchwithatablelookup.Theeasiestway
todothisistocreateamapofmaps,withthekeyofeachmapthetypeid(obj).name()
informationofeachobject.Thenyoucandothelookupbysaying:map[typeid(obj1).name()]
[typeid(obj2).name()].
Noticehowmucheasieritistoreconfigurethesystem.Whenisitmoreappropriatetousethis
approachvs.hardcodingthedynamicdispatches?Canyoucreateasystemthathasthesyntactic
simplicityofuseofthedynamicdispatchbutusesatablelookup?
19.CreateabusinessmodelingenvironmentwiththreetypesofInhabitant:Dwarf(forengineers),Elf
(formarketers),andTroll(formanagers).NowcreateaclasscalledProjectthatinstantiatesthe
differentinhabitantsandcausesthemtointeract()witheachotherusingmultipledispatching.
20.Modifythepreviousexercisetomaketheinteractionsmoredetailed.EachInhabitantcanrandomly
produceaWeaponusinggetWeapon():aDwarfusesJargonorPlay,anElfuses
InventFeatureorSellImaginaryProduct,andaTrollusesEdictandSchedule.Youdecide
whichweaponswinandloseineachinteraction(asinPaperScissorsRock.cpp).Adda
battle()memberfunctiontoProjectthattakestwoInhabitantsandmatchesthemagainsteach
other.Nowcreateameeting()memberfunctionforProjectthatcreatesgroupsofDwarf,Elf,
andManagerandbattlesthegroupsagainsteachotheruntilonlymembersofonegroupareleft
standing.Thesearethewinners.
21.AddaHummingbirdVisitortoBeeAndFlowers.cpp.
22.AddaSunflowertypetoBeeAndFlowers.cppandnoticewhatyouneedtochangetoaccommodate
thisnewtype.
23.ModifyBeeAndFlowers.cppsothatitdoesnotuseVisitor,butrevertstoaregularclasshierarchy
instead.TurnBeeintoacollectingparameter.
11:Concurrency
Objectsprovideawaytodivideaprogramintoindependentsections.Often,you
alsoneedtopartitionaprogramintoseparate,independentlyrunningsubtasks.
Usingmultithreading,eachoftheseindependentsubtasksisdrivenbyathreadofexecution,andyou
programasifeachthreadhastheCPUtoitself.AnunderlyingmechanismisdividinguptheCPUtimefor
you,butingeneral,youdontneedtothinkaboutit,whichhelpstosimplifyprogrammingwithmultiple
threads.
Aprocessisaselfcontainedprogramrunningwithinitsownaddressspace.Amultitaskingoperating
systemcanrunmorethanoneprocess(program)atatime,whilemakingitlookasifeachoneischugging
alongonitsown,byperiodicallyswitchingtheCPUfromonetasktoanother.Athreadisasingle
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
385/458
5/3/2015
ThinkinginC++2ndedVolume2
sequentialflowofcontrolwithinaprocess.Asingleprocesscanthushavemultipleconcurrentlyexecuting
threads.Sincethethreadsrunwithinasingleprocess,theysharememoryandotherresources.The
fundamentaldifficultyinwritingmultithreadedprogramsiscoordinatingtheuseofthoseresources
betweendifferentthreads.
Therearemanypossibleusesformultithreading,butyoullmostoftenwanttouseitwhenyouhavesome
partofyourprogramtiedtoaparticulareventorresource.Tokeepfromholdinguptherestofyour
program,youcreateathreadassociatedwiththateventorresourceandletitrunindependentlyofthe
mainprogram.
Concurrentprogrammingislikesteppingintoanentirelynewworldandlearninganewprogramming
language,oratleastanewsetoflanguageconcepts.Withtheappearanceofthreadsupportinmost
microcomputeroperatingsystems,extensionsforthreadshavealsobeenappearinginprogramming
languagesorlibraries.Inallcases,threadprogramming:
1.Seemsmysteriousandrequiresashiftinthewayyouthinkaboutprogramming.
2.Lookssimilartothreadsupportinotherlanguages.Whenyouunderstandthreads,youunderstanda
commontongue.
Understandingconcurrentprogrammingisonthesameorderofdifficultyasunderstandingpolymorphism.
Ifyouapplysomeeffort,youcanfathomthebasicmechanism,butitgenerallytakesdeepstudyand
understandingtodevelopatruegraspofthesubject.Thegoalofthischapteristogiveyouasolid
foundationinthebasicsofconcurrencysothatyoucanunderstandtheconceptsandwritereasonable
multithreadedprograms.Beawarethatyoucaneasilybecomeoverconfident.Ifyouarewritinganything
complex,youwillneedtostudydedicatedbooksonthetopic.
Motivation
Oneofthemostcompellingreasonsforusingconcurrencyistoproducearesponsiveuserinterface.
ConsideraprogramthatperformssomeCPUintensiveoperationandthusendsupignoringuserinputand
beingunresponsive.Theprogramneedstocontinueperformingitsoperations,andatthesametimeit
needstoreturncontroltotheuserinterfacesothattheprogramcanrespondtotheuser.Ifyouhavea
quitbutton,youdontwanttobeforcedtopollitineverypieceofcodeyouwriteinyourprogram.(This
wouldcoupleyourquitbuttonacrosstheprogramandbeamaintenanceheadache.)Yetyouwantthequit
buttontoberesponsive,asifyouwerecheckingitregularly.
Aconventionalfunctioncannotcontinueperformingitsoperationsandatthesametimereturncontrolto
therestoftheprogram.Infact,thissoundslikeanimpossibility,asiftheCPUmustbeintwoplacesat
once,butthisispreciselytheillusionthatconcurrencyprovides(inthecaseofmultiprocessorsystems,
thismaybemorethananillusion).
Youcanalsouseconcurrencytooptimizethroughput.Forexample,youmightbeabletodoimportantwork
whileyourestuckwaitingforinputtoarriveonanI/Oport.Withoutthreading,theonlyreasonable
solutionistopolltheI/Oport,whichisawkwardandcanbedifficult.
Ifyouhaveamultiprocessormachine,multiplethreadscanbedistributedacrossmultipleprocessors,
whichcandramaticallyimprovethroughput.Thisisoftenthecasewithpowerfulmultiprocessorweb
servers,whichcandistributelargenumbersofuserrequestsacrossCPUsinaprogramthatallocatesone
threadperrequest.
AprogramthatusesthreadsonasingleCPUmachineisstilljustdoingonethingatatime,soitmustbe
theoreticallypossibletowritethesameprogramwithoutusinganythreads.However,multithreading
providesanimportantorganizationalbenefit:Thedesignofyourprogramcanbegreatlysimplified.Some
typesofproblems,suchassimulationavideogame,forexamplearedifficulttosolvewithoutsupport
forconcurrency.
Thethreadingmodelisaprogrammingconveniencetosimplifyjugglingseveraloperationsatthesame
timewithinasingleprogram:TheCPUwillpoparoundandgiveeachthreadsomeofitstime.[148]Each
threadhastheconsciousnessofconstantlyhavingtheCPUtoitself,buttheCPUstimeisactuallysliced
amongallthethreads.TheexceptionisaprogramthatisrunningonmultipleCPU.Butoneofthegreat
thingsaboutthreadingisthatyouareabstractedawayfromthislayer,soyourcodedoesnotneedtoknow
whetheritisrunningonasingleCPUormany.[149]Thus,usingthreadsisawaytocreatetransparently
scalableprogramsifaprogramisrunningtooslowly,youcaneasilyspeeditupbyaddingCPUstoyour
computer.Multitaskingandmultithreadingtendtobethemostreasonablewaystoutilizemultiprocessor
systems.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
386/458
5/3/2015
ThinkinginC++2ndedVolume2
Threadingcanreducecomputingefficiencysomewhat,butthenetimprovementinprogramdesign,
resourcebalancing,anduserconvenienceisoftenquitevaluable.Ingeneral,threadsenableyoutocreate
amorelooselycoupleddesignotherwise,partsofyourcodewouldbeforcedtopayexplicitattentionto
tasksthatwouldnormallybehandledbythreads.
ConcurrencyinC++
WhentheC++StandardsCommitteewascreatingtheinitialC++Standard,aconcurrencymechanismwas
explicitlyexcludedbecauseCdidnthaveoneandalsobecausetherewereanumberofcompeting
approachestoimplementingconcurrency.Itseemedtoomuchofaconstrainttoforceprogrammerstouse
onlyoneofthese.
Thealternativeturnedouttobeworse,however.Touseconcurrency,youhadtofindandlearnalibrary
anddealwithitsidiosyncrasiesandtheuncertaintiesofworkingwithaparticularvendor.Inaddition,there
wasnoguaranteethatsuchalibrarywouldworkondifferentcompilersoracrossdifferentplatforms.Also,
sinceconcurrencywasnotpartofthestandardlanguage,itwasmoredifficulttofindC++programmers
whoalsounderstoodconcurrentprogramming.
AnotherinfluencemayhavebeentheJavalanguage,whichincludedconcurrencyinthecorelanguage.
Althoughmultithreadingisstillcomplicated,Javaprogrammerstendtostartlearningandusingitfromthe
beginning.
TheC++StandardsCommitteeisconsideringtheadditionofconcurrencysupporttothenextiterationof
C++,butatthetimeofthiswritingitisunclearwhatthelibrarywilllooklike.Wedecidedtousethe
ZThreadlibraryasthebasisforthischapter.Wepreferredthedesign,anditisopensourceandfreely
availableathttp://zthread.sourceforge.net.EricCrahenofIBM,theauthoroftheZThreadlibrary,was
instrumentalincreatingthischapter.[150]
ThischapterusesonlyasubsetoftheZThreadlibrary,inordertoconveythefundamentalideasof
threading.TheZThreadlibrarycontainssignificantlymoresophisticatedthreadsupportthanisshownhere,
andyoushouldstudythatlibraryfurtherinordertofullyunderstanditscapabilities.
InstallingZThreads
PleasenotethattheZThreadlibraryisanindependentprojectandisnotsupportedbytheauthorsofthis
bookwearesimplyusingthelibraryinthischapterandcannotprovidetechnicalsupportforinstallation
issues.SeetheZThreadwebsiteforinstallationsupportanderrorreports.
TheZThreadlibraryisdistributedassourcecode.Afterdownloadingit(version2.3orgreater)fromthe
ZThreadwebsite,youmustfirstcompilethelibrary,andthenconfigureyourprojecttousethelibrary.
ThepreferredmethodforcompilingZThreadsformostflavorsofUNIX(Linux,SunOS,Cygwin,etc.)isto
usetheconfigurescript.Afterunpackingthefiles(usingtar),simplyexecute:
./configure&&makeinstall
fromthemaindirectoryoftheZThreadsarchivetocompileandinstallacopyofthelibraryinthe
/usr/localdirectory.Youcancustomizeanumberofoptionswhenusingthisscript,includingthelocations
offiles.Fordetails,usethiscommand:
./configurehelp
TheZThreadscodeisstructuredtosimplifycompilationforotherplatformsandcompilers(suchas
Borland,Microsoft,andMetrowerks).Todothis,createanewprojectandaddallthe.cxxfilesinthesrc
directoryoftheZThreadsarchivetothelistoffilestobecompiled.Also,besuretoincludetheinclude
directoryofthearchiveintheheadersearchpathforyourproject.Theexactdetailswillvaryfrom
compilertocompilersoyoullneedtobesomewhatfamiliarwithyourtoolsettobeabletousethisoption.
Oncethecompilationhassucceeded,thenextstepistocreateaprojectthatusesthenewlycompiled
library.First,letthecompilerknowwheretheheadersarelocatedsothatyour#includestatementswill
workproperly.Typically,youwilladdanoptionsuchasthefollowingtoyourproject:
I/path/to/installation/include
Ifyouusedtheconfigurescript,theinstallationpathwillbewhateveryouselectedfortheprefix(which
defaultsto/usr/local).Ifyouusedoneoftheprojectfilesinthebuilddirectory,theinstallationpathwould
simplybethepathtothemaindirectoryoftheZThreadsarchive.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
387/458
5/3/2015
ThinkinginC++2ndedVolume2
Next,youllneedtoaddanoptiontoyourprojectthatwillletthelinkerknowwheretofindthelibrary.If
youusedtheconfigurescript,thiswilllooklike:
L/path/to/installation/liblZThread
Ifyouusedoneoftheprojectfilesprovided,thiswilllooklike:
L/path/to/installation/DebugZThread.lib
Again,ifyouusedtheconfigurescript,theinstallationpathwillbewhateveryouselectedfortheprefix.If
youusedaprovidedprojectfile,thepathwillbethepathtothemaindirectoryoftheZThreadsarchive.
NotethatifyoureusingLinux,orifyouareusingCygwin(www.cygwin.com)underWindows,youmaynot
needtomodifyyourincludeorlibrarypaththeinstallationprocessanddefaultswilloftentakecareof
everythingforyou.
UnderLinux,youwillprobablyneedtoaddthefollowingtoyour.bashrcsothattheruntimesystemcan
findthesharedlibraryfileLibZThreadx.x.so.Owhenitexecutestheprogramsinthischapter:
exportLD_LIBRARY_PATH=/usr/local/lib:${LD_LIBRARY_PATH}
(Assumingyouusedthedefaultinstallationprocessandthesharedlibraryendedupin/user/local/lib
otherwise,changethepathtoyourlocation).
DefiningTasks
Athreadcarriesoutatask,soyouneedawaytodescribethattask.TheRunnableclassprovidesa
commoninterfacetoexecuteanyarbitrarytask.HereisthecoreoftheZThreadRunnableclass,which
youwillfindinRunnable.hintheincludedirectory,afterinstallingtheZThreadlibrary:
classRunnable{
public:
virtualvoidrun()=0
virtual~Runnable(){}
}
Bymakingthisanabstractbaseclass,Runnableiseasilycombinablewithabaseclassandotherclasses.
Todefineatask,simplyinheritfromtheRunnableclassandoverriderun()tomakethetaskdoyour
bidding.
Forexample,thefollowingLiftOfftaskdisplaysthecountdownbeforeliftoff:
//:C11:LiftOff.h
//DemonstrationoftheRunnableinterface.
#ifndefLIFTOFF_H
#defineLIFTOFF_H
#include<iostream>
#include"zthread/Runnable.h"
classLiftOff:publicZThread::Runnable{
intcountDown
intid
public:
LiftOff(intcount,intident=0):
countDown(count),id(ident){}
~LiftOff(){
std::cout<<id<<"completed"<<std::endl
}
voidrun(){
while(countDown)
std::cout<<id<<":"<<countDown<<std::endl
std::cout<<"Liftoff!"<<std::endl
}
}
#endif//LIFTOFF_H///:~
Theidentifieriddistinguishesbetweenmultipleinstancesofthetask.Ifyouonlymakeasingleinstance,
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
388/458
5/3/2015
ThinkinginC++2ndedVolume2
youcanusethedefaultvalueforident.Thedestructorwillallowyoutoseethatataskisproperly
destroyed.
Inthefollowingexample,thetasksrun()isnotdrivenbyaseparatethreaditissimplycalleddirectlyin
main():
//:C11:NoThread.cpp
#include"LiftOff.h"
intmain(){
LiftOfflaunch(10)
launch.run()
}///:~
WhenaclassisderivedfromRunnable,itmusthavearun()function,butthatsnothingspecialit
doesntproduceanyinnatethreadingabilities.
Toachievethreadingbehavior,youmustusetheThreadclass.
UsingThreads
TodriveaRunnableobjectwithathread,youcreateaseparateThreadobjectandhandaRunnable
pointertotheThreadsconstructor.ThisperformsthethreadinitializationandthencallstheRunnables
run()asaninterruptiblethread.BydrivingLiftOffwithaThread,theexamplebelowshowshowany
taskcanberuninthecontextofanotherthread:
//:C11:BasicThreads.cpp
//ThemostbasicuseoftheThreadclass.
//{L}ZThread
#include<iostream>
#include"LiftOff.h"
#include"zthread/Thread.h"
usingnamespaceZThread
usingnamespacestd
intmain(){
try{
Threadt(newLiftOff(10))
cout<<"WaitingforLiftOff"<<endl
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
Synchronization_ExceptionispartoftheZThreadlibraryandisthebaseclassforallZThread
exceptions.Itwillbethrownifthereisanerrorstartingorusingathread.
AThreadconstructoronlyneedsapointertoaRunnableobject.CreatingaThreadobjectwillperform
thenecessaryinitializationforthethreadandthencallthatRunnablesrun()memberfunctiontostart
thetask.EventhoughtheThreadconstructoris,ineffect,makingacalltoalongrunningfunction,that
constructorquicklyreturns.Ineffect,youhavemadeamemberfunctioncalltoLiftOff::run(),andthat
functionhasnotyetfinished,butbecauseLiftOff::run()isbeingexecutedbyadifferentthread,youcan
stillperformotheroperationsinthemain()thread.(Thisabilityisnotrestrictedtothemain()thread
anythreadcanstartanotherthread.)Youcanseethisbyrunningtheprogram.Eventhough
LiftOff::run()hasbeencalled,theWaitingforLiftOffmessagewillappearbeforethecountdownhas
completed.Thus,theprogramisrunningtwofunctionsatonceLiftOff::run()andmain().
Youcaneasilyaddmorethreadstodrivemoretasks.Here,youcanseehowallthethreadsruninconcert
withoneanother:
//:C11:MoreBasicThreads.cpp
//Addingmorethreads.
//{L}ZThread
#include<iostream>
#include"LiftOff.h"
#include"zthread/Thread.h"
usingnamespaceZThread
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
389/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
constintSZ=5
try{
for(inti=0i<SZi++)
Threadt(newLiftOff(10,i))
cout<<"WaitingforLiftOff"<<endl
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
ThesecondargumentfortheLiftOffconstructoridentifieseachtask.Whenyouruntheprogram,youllsee
thattheexecutionofthedifferenttasksismixedtogetherasthethreadsareswappedinandout.This
swappingisautomaticallycontrolledbythethreadscheduler.Ifyouhavemultipleprocessorsonyour
machine,thethreadschedulerwillquietlydistributethethreadsamongtheprocessors.
Theforloopcanseemalittlestrangeatfirstbecausetisbeingcreatedlocallyinsidetheforloopand
thenimmediatelygoesoutofscopeandisdestroyed.Thismakesitappearthatthethreaditselfmightbe
immediatelylost,butyoucanseefromtheoutputthatthethreadsareindeedrunningtoconclusion.When
youcreateaThreadobject,theassociatedthreadisregisteredwiththethreadingsystem,whichkeepsit
alive.EventhoughthestackbasedThreadobjectislost,thethreaditselflivesonuntilitsassociatedtask
completes.AlthoughthismaybecounterintuitivefromaC++standpoint,theconceptofthreadsisa
departurefromthenorm:athreadcreatesaseparatethreadofexecutionthatpersistsafterthefunction
callends.Thisdepartureisreflectedinthepersistenceoftheunderlyingthreadaftertheobjectvanishes.
Creatingresponsiveuserinterfaces
Asstatedearlier,oneofthemotivationsforusingthreadingistocreatearesponsiveuserinterface.
Althoughwedontcovergraphicaluserinterfacesinthisbook,youcanstillseeasimpleexampleofa
consolebaseduserinterface.
Thefollowingexamplereadslinesfromafileandprintsthemtotheconsole,sleeping(suspendingthe
currentthread)forasecondaftereachlineisdisplayed.(Youlllearnmoreaboutsleepinglaterinthe
chapter.)Duringthisprocess,theprogramdoesntlookforuserinput,sotheUIisunresponsive:
//:C11:UnresponsiveUI.cpp{RunByHand}
//LackofthreadingproducesanunresponsiveUI.
//{L}ZThread
#include<iostream>
#include<fstream>
#include<string>
#include"zthread/Thread.h"
usingnamespacestd
usingnamespaceZThread
intmain(){
cout<<"Press<Enter>toquit:"<<endl
ifstreamfile("UnresponsiveUI.cpp")
stringline
while(getline(file,line)){
cout<<line<<endl
Thread::sleep(1000)//Timeinmilliseconds
}
//Readinputfromtheconsole
cin.get()
cout<<"Shuttingdown..."<<endl
}///:~
Tomaketheprogramresponsive,youcanexecuteataskthatdisplaysthefileinaseparatethread.The
mainthreadcanthenwatchforuserinputsotheprogrambecomesresponsive:
//:C11:ResponsiveUI.cpp{RunByHand}
//Threadingforaresponsiveuserinterface.
//{L}ZThread
#include<iostream>
#include<fstream>
#include<string>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
390/458
5/3/2015
ThinkinginC++2ndedVolume2
#include"zthread/Thread.h"
usingnamespaceZThread
usingnamespacestd
classDisplayTask:publicRunnable{
ifstreamin
stringline
boolquitFlag
public:
DisplayTask(conststring&file):quitFlag(false){
in.open(file.c_str())
}
~DisplayTask(){in.close()}
voidrun(){
while(getline(in,line)&&!quitFlag){
cout<<line<<endl
Thread::sleep(1000)
}
}
voidquit(){quitFlag=true}
}
intmain(){
try{
cout<<"Press<Enter>toquit:"<<endl
DisplayTask*dt=newDisplayTask("ResponsiveUI.cpp")
Threadt(dt)
cin.get()
dt>quit()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
cout<<"Shuttingdown..."<<endl
}///:~
Nowthemain()threadcanrespondimmediatelywhenyoupress<Return>andcallquit()onthe
DisplayTask.
Thisexamplealsoshowstheneedforcommunicationbetweentasksthetaskinthemain()threadneeds
totelltheDisplayTasktoshutdown.SincewehaveapointertotheDisplayTask,youmightthinkofjust
callingdeleteonthatpointertokillthetask,butthisproducesunreliableprograms.Theproblemisthat
thetaskcouldbeinthemiddleofsomethingimportantwhenyoudestroyit,andsoyouarelikelytoput
theprograminanunstablestate.Here,thetaskitselfdecideswhenitssafetoshutdown.Theeasiestway
todothisisbysimplynotifyingthetaskthatyoudlikeittostopbysettingaBooleanflag.Whenthetask
getstoastablepointitcancheckthatflaganddowhateverisnecessarytocleanupbeforereturningfrom
run().Whenthetaskreturnsfromrun(),theThreadknowsthatthetaskhascompleted.
Althoughthisprogramissimpleenoughthatitshouldnothaveanyproblems,therearesomesmallflaws
regardingintertaskcommunication.Thisisanimportanttopicthatwillbecoveredlaterinthischapter.
SimplifyingwithExecutors
YoucansimplifyyourcodingoverheadbyusingZThreadExecutors.Executorsprovidealayerofindirection
betweenaclientandtheexecutionofataskinsteadofaclientexecutingataskdirectly,anintermediate
objectexecutesthetask.
WecanshowthisbyusinganExecutorinsteadofexplicitlycreatingThreadobjectsin
MoreBasicThreads.cpp.ALiftOffobjectknowshowtorunaspecifictaskliketheCommandPattern,it
exposesasinglefunctiontobeexecuted.AnExecutorobjectknowshowbuildtheappropriatecontextto
executeRunnableobjects.Inthefollowingexample,theThreadedExecutorcreatesonethreadpertask:
//:c11:ThreadedExecutor.cpp
//{L}ZThread
#include<iostream>
#include"zthread/ThreadedExecutor.h"
#include"LiftOff.h"
usingnamespaceZThread
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
391/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
try{
ThreadedExecutorexecutor
for(inti=0i<5i++)
executor.execute(newLiftOff(10,i))
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
NotethatinsomecasesasingleExecutorcanbeusedtocreateandmanageallthethreadsinyour
system.YoumuststillplacethethreadingcodeinsideatryblockbecauseanExecutorsexecute()
functionmaythrowaSynchronization_Exceptionifsomethinggoeswrong.Thisistrueforanyfunction
thatinvolveschangingthestateofasynchronizationobject(startingthreads,acquiringmutexes,waiting
onconditions,etc.),asyouwilllearnlaterinthischapter.
TheprogramwillexitassoonasallthetasksintheExecutorcomplete.
Inthepreviousexample,theThreadedExecutorcreatesathreadforeachtaskthatyouwanttorun,but
youcaneasilychangethewaythesetasksareexecutedbyreplacingtheThreadedExecutorwitha
differenttypeofExecutor.Inthischapter,usingaThreadedExecutorisfine,butinproductioncodeit
mightresultinexcessivecostsfromthecreationoftoomanythreads.Inthatcase,youcanreplaceitwith
aPoolExecutor,whichwillusealimitedsetofthreadstoexecutethesubmittedtasksinparallel:
//:C11:PoolExecutor.cpp
//{L}ZThread
#include<iostream>
#include"zthread/PoolExecutor.h"
#include"LiftOff.h"
usingnamespaceZThread
usingnamespacestd
intmain(){
try{
//Constructorargumentisminimumnumberofthreads:
PoolExecutorexecutor(5)
for(inti=0i<5i++)
executor.execute(newLiftOff(10,i))
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
WiththePoolExecutor,youdoexpensivethreadallocationonce,upfront,andthethreadsarereused
whenpossible.Thissavestimebecauseyouarentconstantlypayingforthreadcreationoverheadfor
everysingletask.Also,inaneventdrivensystem,eventsthatrequirethreadstohandlethemcanbe
generatedasquicklyasyouwantbysimplyfetchingthemfromthepool.Youdontoverruntheavailable
resourcesbecausethePoolExecutorusesaboundednumberofThreadobjects.Thus,althoughthisbook
willuseThreadedExecutors,considerusingPoolExecutorsinproductioncode.
AConcurrentExecutorislikeaPoolExecutorwithafixedsizeofonethread.Thisisusefulforanything
youwanttoruninanotherthreadcontinually(alonglivedtask),suchasataskthatlistenstoincoming
socketconnections.Itisalsohandyforshorttasksthatyouwanttoruninathread,forexample,small
tasksthatupdatealocalorremotelog,orforaneventdispatchingthread.
IfmorethanonetaskissubmittedtoaConcurrentExecutor,eachtaskwillruntocompletionbeforethe
nexttaskisbegun,allusingthesamethread.Inthefollowingexample,youllseeeachtaskcompleted,in
theorderthatitwassubmitted,beforethenextoneisbegun.Thus,aConcurrentExecutorserializesthe
tasksthataresubmittedtoit.
//:C11:ConcurrentExecutor.cpp
//{L}ZThread
#include<iostream>
#include"zthread/ConcurrentExecutor.h"
#include"LiftOff.h"
usingnamespaceZThread
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
392/458
5/3/2015
ThinkinginC++2ndedVolume2
intmain(){
try{
ConcurrentExecutorexecutor
for(inti=0i<5i++)
executor.execute(newLiftOff(10,i))
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
LikeaConcurrentExecutor,aSynchronousExecutorisusedwhenyouwantonlyonetaskatatimeto
run,seriallyinsteadofconcurrently.UnlikeConcurrentExecutor,aSynchronousExecutordoesnt
createormanagethreadsonitown.Itusesthethreadthatsubmitsthetaskandthusonlyactsasafocal
pointforsynchronization.IfyouhaventhreadssubmittingtaskstoaSynchronousExecutor,notwo
tasksareeverrunatonce.Instead,eachoneisruntocompletion,thenthenextoneinthequeueis
begun.
Forexample,supposeyouhaveanumberofthreadsrunningtasksthatusethefilesystem,butyouare
writingportablecodesoyoudontwanttouseflock()oranotherOSspecificcalltolockafile.Youcan
runthesetaskswithaSynchronousExecutortoensurethatonlyonetaskatatimeisrunningfromany
thread.Thisway,youdontneedtodealwithsynchronizingonthesharedresource(andyouwontclobber
thefilesysteminthemeantime).Abettersolutionistosynchronizeontheresource(whichyoulllearn
aboutlaterinthischapter),butaSynchronousExecutorletsyouskipthetroubleofgettingcoordinated
properlyjusttoprototypesomething.
//:C11:SynchronousExecutor.cpp
//{L}ZThread
#include<iostream>
#include"zthread/SynchronousExecutor.h"
#include"LiftOff.h"
usingnamespaceZThread
usingnamespacestd
intmain(){
try{
SynchronousExecutorexecutor
for(inti=0i<5i++)
executor.execute(newLiftOff(10,i))
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
Whenyouruntheprogram,youllseethatthetasksareexecutedintheordertheyaresubmitted,and
eachtaskrunstocompletionbeforethenextonestarts.Whatyoudontseeisthatnonewthreadsare
createdthemain()threadisusedforeachtask,sinceinthisexample,thatsthethreadthatsubmitsall
thetasks.BecauseSynchronousExecutorisprimarilyforprototyping,youmaynotuseitmuchin
productioncode.
Yielding
Ifyouknowthatyouveaccomplishedwhatyouneedtoduringonepassthroughaloopinyourrun()
function(mostrun()functionsinvolvealongrunningloop),youcangiveahinttothethreadscheduling
mechanismthatyouvedoneenoughandthatsomeotherthreadmightaswellhavetheCPU.Thishint(and
itisahinttheresnoguaranteeyourimplementationwilllistentoit)takestheformoftheyield()
function.
WecanmakeamodifiedversionoftheLiftOffexamplesbyyieldingaftereachloop:
//:C11:YieldingTask.cpp
//Suggestingwhentoswitchthreadswithyield().
//{L}ZThread
#include<iostream>
#include"zthread/Thread.h"
#include"zthread/ThreadedExecutor.h"
usingnamespaceZThread
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
393/458
5/3/2015
ThinkinginC++2ndedVolume2
classYieldingTask:publicRunnable{
intcountDown
intid
public:
YieldingTask(intident=0):countDown(5),id(ident){}
~YieldingTask(){
cout<<id<<"completed"<<endl
}
friendostream&
operator<<(ostream&os,constYieldingTask&yt){
returnos<<"#"<<yt.id<<":"<<yt.countDown
}
voidrun(){
while(true){
cout<<*this<<endl
if(countDown==0)return
Thread::yield()
}
}
}
intmain(){
try{
ThreadedExecutorexecutor
for(inti=0i<5i++)
executor.execute(newYieldingTask(i))
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
Youcanseethatthetasksrun()memberfunctionconsistsentirelyofaninfiniteloop.Byusingyield(),
theoutputisevenedupquiteabitoverthatwithoutyielding.Trycommentingoutthecallto
Thread::yield()toseethedifference.Ingeneral,however,yield()isusefulonlyinraresituations,and
youcantrelyonittodoanyserioustuningofyourapplication.
Sleeping
Anotherwayyoucancontrolthebehaviorofyourthreadsisbycallingsleep()toceaseexecutionofa
threadforagivennumberofmilliseconds.Intheprecedingexample,ifyoureplacethecalltoyield()
withacalltosleep(),yougetthefollowing:
//:C11:SleepingTask.cpp
//Callingsleep()topauseforawhile.
//{L}ZThread
#include<iostream>
#include"zthread/Thread.h"
#include"zthread/ThreadedExecutor.h"
usingnamespaceZThread
usingnamespacestd
classSleepingTask:publicRunnable{
intcountDown
intid
public:
SleepingTask(intident=0):countDown(5),id(ident){}
~SleepingTask(){
cout<<id<<"completed"<<endl
}
friendostream&
operator<<(ostream&os,constSleepingTask&st){
returnos<<"#"<<st.id<<":"<<st.countDown
}
voidrun(){
while(true){
try{
cout<<*this<<endl
if(countDown==0)return
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
394/458
5/3/2015
ThinkinginC++2ndedVolume2
Thread::sleep(100)
}catch(Interrupted_Exception&e){
cerr<<e.what()<<endl
}
}
}
}
intmain(){
try{
ThreadedExecutorexecutor
for(inti=0i<5i++)
executor.execute(newSleepingTask(i))
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
Thread::sleep()canthrowanInterrupted_Exception(youlllearnaboutinterruptslater),andyoucan
seethatthisiscaughtinrun().Butthetaskiscreatedandexecutedinsideatryblockinmain()that
catchesSynchronization_Exception(thebaseclassforallZThreadexceptions),sowouldntitbe
possibletojustignoretheexceptioninrun()andassumethatitwillpropagatetothehandlerinmain()?
Thiswontworkbecauseexceptionswontpropagateacrossthreadsbacktomain().Thus,youmust
handleanyexceptionslocallythatmayarisewithinatask.
Youllnoticethatthethreadstendtoruninanyorder,whichmeansthatsleep()isalsonotawayforyou
tocontroltheorderofthreadexecution.Itjuststopstheexecutionofthethreadforawhile.Theonly
guaranteethatyouhaveisthatthethreadwillsleepatleast100milliseconds(inthisexample),butitmay
takelongerbeforethethreadresumesexecutionbecausethethreadschedulerstillhastogetbacktoit
afterthesleepintervalexpires.
Ifyoumustcontroltheorderofexecutionofthreads,yourbestbetistousesynchronizationcontrols
(describedlater)or,insomecases,nottousethreadsatall,butinsteadtowriteyourowncooperative
routinesthathandcontroltoeachotherinaspecifiedorder.
Priority
Thepriorityofathreadconveystheimportanceofathreadtothescheduler.Althoughtheorderthatthe
CPUrunsasetofthreadsisindeterminate,theschedulerwillleantowardrunningthewaitingthreadwith
thehighestpriorityfirst.However,thisdoesntmeanthatthreadswithlowerpriorityarentrun(thatis,
youcantgetdeadlockedbecauseofpriorities).Lowerprioritythreadsjusttendtorunlessoften.
HeresMoreBasicThreads.cppmodifiedsothattheprioritylevelsaredemonstrated.Theprioritiesare
adjustingbyusingThreadssetPriority()function.
//:C11:SimplePriorities.cpp
//Showstheuseofthreadpriorities.
//{L}ZThread
#include<iostream>
#include"zthread/Thread.h"
usingnamespaceZThread
usingnamespacestd
constdoublepi=3.14159265358979323846
constdoublee=2.7182818284590452354
classSimplePriorities:publicRunnable{
intcountDown
volatiledoubled//Nooptimization
intid
public:
SimplePriorities(intident=0):countDown(5),id(ident){}
~SimplePriorities(){
cout<<id<<"completed"<<endl
}
friendostream&
operator<<(ostream&os,constSimplePriorities&sp){
returnos<<"#"<<sp.id<<"priority:"
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
395/458
5/3/2015
ThinkinginC++2ndedVolume2
<<Thread().getPriority()
<<"count:"<<sp.countDown
}
voidrun(){
while(true){
//Anexpensive,interruptableoperation:
for(inti=1i<100000i++)
d=d+(pi+e)/double(i)
cout<<*this<<endl
if(countDown==0)return
}
}
}
intmain(){
try{
Threadhigh(newSimplePriorities)
high.setPriority(High)
for(inti=0i<5i++){
Threadlow(newSimplePriorities(i))
low.setPriority(Low)
}
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
Here,operator<<()isoverriddentodisplaytheidentifier,priority,andcountDownvalueofthetask.
Youcanseethattheprioritylevelofthreadhighisatthehighestlevel,andalltherestofthethreadsare
atthelowestlevel.WearenotusinganExecutorinthisexamplebecauseweneeddirectaccesstothe
threadsinordertosettheirpriorities.
InsideSimplePriorities::run(),100,000repetitionsofaratherexpensivefloatingpointcalculationare
performed,involvingdoubleadditionanddivision.Thevariabledisvolatiletotrytoensurethatno
compilersoptimizationsareperformed.Withoutthiscalculation,youdontseetheeffectofsettingthe
prioritylevels.(Tryit:commentouttheforloopcontainingthedoublecalculations.)Withthecalculation,
youseethatthreadhighisgivenahigherpreferencebythethreadscheduler.(Atleast,thiswasthe
behavioronaWindowsmachine.)Thecalculationtakeslongenoughthatthethreadschedulingmechanism
jumpsin,changesthreads,andpaysattentiontotheprioritiessothatthreadhighgetspreference.
YoucanalsoreadthepriorityofanexistingthreadwithgetPriority()andchangeitatanytime(notjust
beforethethreadisrun,asinSimplePriorities.cpp)withsetPriority().
Mappingprioritiestooperatingsystemsisproblematic.Forexample,Windows2000hassevenpriority
levels,whileSunsSolarishas231levels.Theonlyportableapproachistosticktoverylargepriority
granulations,suchastheLow,Medium,andHighusedintheZThreadlibrary.
Sharinglimitedresources
Youcanthinkofasinglethreadedprogramasonelonelyentitymovingaroundthroughyourproblem
spaceanddoingonethingatatime.Becausetheresonlyoneentity,youneverhavetothinkaboutthe
problemoftwoentitiestryingtousethesameresourceatthesametime:problemssuchastwopeople
tryingtoparkinthesamespace,walkthroughadooratthesametime,oreventalkatthesametime.
Withmultithreadingthingsarentlonelyanymore,butyounowhavethepossibilityoftwoormorethreads
tryingtousethesameresourceatonce.Thiscancausetwodifferentkindsofproblems.Thefirstisthat
thenecessaryresourcesmaynotexist.InC++,theprogrammerhascompletecontroloverthelifetimeof
objects,anditseasytocreatethreadsthattrytouseobjectsthatgetdestroyedbeforethosethreads
complete.
Thesecondproblemisthattwoormorethreadsmaycollidewhentheytrytoaccessthesameresourceat
thesametime.Ifyoudontpreventsuchacollision,youllhavetwothreadstryingtoaccessthesame
bankaccountatthesametime,printtothesameprinter,adjustthesamevalve,andsoon.
Thissectionintroducestheproblemofobjectsthatvanishwhiletasksarestillusingthemandtheproblem
oftaskscollidingoversharedresources.Youlllearnaboutthetoolsthatareusedtosolvetheseproblems.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
396/458
5/3/2015
ThinkinginC++2ndedVolume2
Ensuringtheexistenceofobjects
MemoryandresourcemanagementaremajorconcernsinC++.WhenyoucreateanyC++program,you
havetheoptionofcreatingobjectsonthestackorontheheap(usingnew).Inasinglethreadedprogram,
itsusuallyeasytokeeptrackofobjectlifetimessothatyoudonttrytouseobjectsthatarealready
destroyed.
TheexamplesshowninthischaptercreateRunnableobjectsontheheapusingnew,butyoullnoticethat
theseobjectsareneverexplicitlydeleted.However,youcanseefromtheoutputwhenyourunthe
programsthatthethreadlibrarykeepstrackofeachtaskandeventuallydeletesit,becausethe
destructorsforthetasksarecalled.ThishappenswhentheRunnable::run()memberfunctioncompletes
returningfromrun()indicatesthatthetaskisfinished.
Burdeningthethreadwithdeletingataskisaproblem.Thatthreaddoesntnecessarilyknowifanother
threadstillneedstomakeareferencetothatRunnable,andsotheRunnablemaybeprematurely
destroyed.Todealwiththisproblem,tasksinZThreadsareautomaticallyreferencecountedbythe
ZThreadlibrarymechanism.Ataskismaintaineduntilthereferencecountforthattaskgoestozero,at
whichpointthetaskisdeleted.Thismeansthattasksmustalwaysbedeleteddynamically,andsothey
cannotbecreatedonthestack.Instead,tasksmustalwaysbecreatedusingnew,asyouseeinallthe
examplesinthischapter.
Oftenyoumustalsoensurethatnontaskobjectsstayaliveaslongastasksneedthem.Otherwise,its
easyforobjectsthatareusedbytaskstogooutofscopebeforethosetasksarecompleted.Ifthis
happens,thetaskswilltrytoaccessillegalstorageandwillcauseprogramfaults.Heresasimple
example:
//:C11:Incrementer.cpp{RunByHand}
//Destroyingobjectswhilethreadsarestill
//runningwillcauseseriousproblems.
//{L}ZThread
#include<iostream>
#include"zthread/Thread.h"
#include"zthread/ThreadedExecutor.h"
usingnamespaceZThread
usingnamespacestd
classCount{
enum{SZ=100}
intn[SZ]
public:
voidincrement(){
for(inti=0i<SZi++)
n[i]++
}
}
classIncrementer:publicRunnable{
Count*count
public:
Incrementer(Count*c):count(c){}
voidrun(){
for(intn=100n>0n){
Thread::sleep(250)
count>increment()
}
}
}
intmain(){
cout<<"Thiswillcauseasegmentationfault!"<<endl
Countcount
try{
Threadt0(newIncrementer(&count))
Threadt1(newIncrementer(&count))
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
397/458
5/3/2015
ThinkinginC++2ndedVolume2
TheCountclassmayseemlikeoverkillatfirst,butifnisonlyasingleint(ratherthananarray),the
compilercanputitintoaregisterandthatstoragewillstillbeavailable(albeittechnicallyillegal)afterthe
Countobjectgoesoutofscope.Itsdifficulttodetectthememoryviolationinthatcase.Yourresultsmay
varydependingonyourcompilerandoperatingsystem,buttrymakingitnasingleintandseewhat
happens.Inanyevent,ifCountcontainsanarrayofintsasabove,thecompilerisforcedtoputitonthe
stackandnotinaregister.
IncrementerisasimpletaskthatusesaCountobject.Inmain(),youcanseethattheIncrementer
tasksarerunningforlongenoughthattheCountobjectwillgooutofscope,andsothetaskstrytoaccess
anobjectthatnolongerexists.Thisproducesaprogramfault.
Tofixtheproblem,wemustguaranteethatanyobjectssharedbetweentaskswillbearoundaslongas
thosetasksneedthem.(Iftheobjectswerenotshared,theycouldbecomposeddirectlyintothetasks
classandthustietheirlifetimetothattask.)Sincewedontwantthestaticprogramscopetocontrolthe
lifetimeoftheobject,weputtheobjectontheheap.Andtomakesurethattheobjectisnotdestroyed
untiltherearenootherobjects(tasks,inthiscase)usingit,weusereferencecounting.
Referencecountingwasexplainedthoroughlyinvolumeoneofthisbookandfurtherrevisitedinthis
volume.TheZThreadlibraryincludesatemplatecalledCountedPtrthatautomaticallyperformsreference
countinganddeletesanobjectwhenthereferencecountgoestozero.Herestheaboveprogrammodified
touseCountedPtrtopreventthefault:
//:C11:ReferenceCounting.cpp
//ACountedPtrpreventstooearlydestruction.
//{L}ZThread
#include<iostream>
#include"zthread/Thread.h"
#include"zthread/CountedPtr.h"
usingnamespaceZThread
usingnamespacestd
classCount{
enum{SZ=100}
intn[SZ]
public:
voidincrement(){
for(inti=0i<SZi++)
n[i]++
}
}
classIncrementer:publicRunnable{
CountedPtr<Count>count
public:
Incrementer(constCountedPtr<Count>&c):count(c){}
voidrun(){
for(intn=100n>0n){
Thread::sleep(250)
count>increment()
}
}
}
intmain(){
CountedPtr<Count>count(newCount)
try{
Threadt0(newIncrementer(count))
Threadt1(newIncrementer(count))
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
IncrementernowcontainsaCountedPtrobject,whichmanagesaCount.Inmain(),theCountedPtr
objectsarepassedintothetwoIncrementerobjectsbyvalue,sothecopyconstructoriscalled,
increasingthereferencecount.Aslongasthetasksarestillrunning,thereferencecountwillbenonzero,
andsotheCountobjectmanagedbytheCountedPtrwillnotbedestroyed.Onlywhenallthetasksusing
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
398/458
5/3/2015
ThinkinginC++2ndedVolume2
theCountarecompletedwilldeletebecalled(automatically)ontheCountobjectbytheCountedPtr.
Wheneveryouhaveobjectsthatareusedbymorethanonetask,youllalmostalwaysneedtomanage
thoseobjectsusingtheCountedPtrtemplateinordertopreventproblemsarisingfromobjectlifetime
issues.
Improperlyaccessingresources
Considerthefollowingexamplewhereonetaskgeneratesevennumbersandothertasksconsumethose
numbers.Here,theonlyjoboftheconsumerthreadsistocheckthevalidityoftheevennumbers.
WellfirstdefineEvenChecker,theconsumerthread,sinceitwillbereusedinallthesubsequent
examples.TodecoupleEvenCheckerfromthevarioustypesofgeneratorsthatwewillexperimentwith,
wellcreateaninterfacecalledGenerator,whichcontainstheminimumnecessaryfunctionsthat
EvenCheckermustknowabout:thatithasanextValue()functionandthatitcanbecanceled.
//:C11:EvenChecker.h
#ifndefEVENCHECKER_H
#defineEVENCHECKER_H
#include<iostream>
#include"zthread/CountedPtr.h"
#include"zthread/Thread.h"
#include"zthread/Cancelable.h"
#include"zthread/ThreadedExecutor.h"
classGenerator:publicZThread::Cancelable{
boolcanceled
public:
Generator():canceled(false){}
virtualintnextValue()=0
voidcancel(){canceled=true}
boolisCanceled(){returncanceled}
}
classEvenChecker:publicZThread::Runnable{
ZThread::CountedPtr<Generator>generator
intid
public:
EvenChecker(ZThread::CountedPtr<Generator>&g,intident)
:generator(g),id(ident){}
~EvenChecker(){
std::cout<<"~EvenChecker"<<id<<std::endl
}
voidrun(){
while(!generator>isCanceled()){
intval=generator>nextValue()
if(val%2!=0){
std::cout<<val<<"noteven!"<<std::endl
generator>cancel()//CancelsallEvenCheckers
}
}
}
//Testanytypeofgenerator:
template<typenameGenType>staticvoidtest(intn=10){
std::cout<<"PressControlCtoexit"<<std::endl
try{
ZThread::ThreadedExecutorexecutor
ZThread::CountedPtr<Generator>gp(newGenType)
for(inti=0i<ni++)
executor.execute(newEvenChecker(gp,i))
}catch(ZThread::Synchronization_Exception&e){
std::cerr<<e.what()<<std::endl
}
}
}
#endif//EVENCHECKER_H///:~
TheGeneratorclassintroducestheabstractCancelableclass,whichispartoftheZThreadlibrary.The
goalofCancelableistoprovideaconsistentinterfacetochangethestateofanobjectviathecancel()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
399/458
5/3/2015
ThinkinginC++2ndedVolume2
functionandtoseewhethertheobjecthasbeencanceledwiththeisCanceled()function.Here,weuse
thesimpleapproachofaboolcanceledflag,similartothequitFlagpreviouslyseenin
ResponsiveUI.cpp.NotethatinthisexampletheclassthatisCancelableisnotRunnable.Instead,all
theEvenCheckertasksthatdependontheCancelableobject(theGenerator)testittoseeifitsbeen
canceled,asyoucanseeinrun().Thisway,thetasksthatsharethecommonresource(theCancelable
Generator)watchthatresourceforthesignaltoterminate.Thiseliminatesthesocalledracecondition,
wheretwoormoretasksracetorespondtoaconditionandthuscollideorotherwiseproduceinconsistent
results.Youmustbecarefultothinkaboutandprotectagainstallthepossiblewaysaconcurrentsystem
canfail.Forexample,ataskcannotdependonanothertaskbecausetaskshutdownorderisnot
guaranteed.Here,bymakingtasksdependonnontaskobjects(whicharereferencecountedusing
CountedPtr)weeliminatethepotentialracecondition.
Inlatersections,youllseethattheZThreadlibrarycontainsmoregeneralmechanismsforterminationof
threads.
SincemultipleEvenCheckerobjectsmayendupsharingaGenerator,theCountedPtrtemplateisused
toreferencecounttheGeneratorobjects.
ThelastmemberfunctioninEvenCheckerisastaticmembertemplatethatsetsupandperformsatest
ofanytypeofGeneratorbycreatingoneinsideaCountedPtrandthenstartinganumberof
EvenCheckersthatusethatGenerator.IftheGeneratorcausesafailure,test()willreportitand
returnotherwise,youmustpressControlCtoterminateit.
EvenCheckertasksconstantlyreadandtestthevaluesfromtheirassociatedGenerator.Notethatif
generator>isCanceled()istrue,run()returns,whichtellstheExecutorinEvenChecker::test()
thatthetaskiscomplete.AnyEvenCheckertaskcancallcancel()onitsassociatedGenerator,which
willcauseallotherEvenCheckersusingthatGeneratortogracefullyshutdown.
TheEvenGeneratorissimplenextValue()producesthenextevenvalue:
//:C11:EvenGenerator.cpp
//Whenthreadscollide.
//{L}ZThread
#include<iostream>
#include"EvenChecker.h"
#include"zthread/ThreadedExecutor.h"
usingnamespaceZThread
usingnamespacestd
classEvenGenerator:publicGenerator{
unsignedintcurrentEvenValue//Unsignedcantoverflow
public:
EvenGenerator(){currentEvenValue=0}
~EvenGenerator(){cout<<"~EvenGenerator"<<endl}
intnextValue(){
++currentEvenValue//Dangerpointhere!
++currentEvenValue
returncurrentEvenValue
}
}
intmain(){
EvenChecker::test<EvenGenerator>()
}///:~
ItspossibleforonethreadtocallnextValue()afterthefirstincrementofcurrentEvenValueandbefore
thesecond(attheplaceinthecodecommentedDangerpointhere!),whichputsthevalueintoan
incorrectstate.Toprovethatthiscanhappen,EvenChecker::test()createsagroupofEvenChecker
objectstocontinuallyreadtheoutputofanEvenGeneratorandtesttoseeifeachoneiseven.Ifnot,the
errorisreportedandtheprogramisshutdown.
ThisprogrammaynotdetecttheproblemuntiltheEvenGeneratorhascompletedmanycycles,depending
ontheparticularsofyouroperatingsystemandotherimplementationdetails.Ifyouwanttoseeitfail
muchfaster,tryputtingacalltoyield()betweenthefirstandsecondincrements.Inanyevent,itwill
eventuallyfailbecausetheEvenCheckerthreadsareabletoaccesstheinformationinEvenGenerator
whileitsinanincorrectstate.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
400/458
5/3/2015
ThinkinginC++2ndedVolume2
Controllingaccess
Thepreviousexampleshowsafundamentalproblemwhenusingthreads:Youneverknowwhenathread
mightberun.Imaginesittingatatablewithafork,abouttospearthelastpieceoffoodonaplatter,and
asyourforkreachesforit,thefoodsuddenlyvanishes(becauseyourthreadwassuspendedandanother
dinercameinandatethefood).Thatstheproblemyouredealingwithwhenwritingconcurrentprograms.
Occasionallyyoudontcareifaresourceisbeingaccessedatthesametimeyouretryingtouseit.Butin
mostcasesyoudocare,andformultithreadingtowork,youneedsomewaytopreventtwothreadsfrom
accessingthesameresource,atleastduringcriticalperiods.
Preventingthiskindofcollisionissimplyamatterofputtingalockonaresourcewhenonethreadisusing
it.Thefirstthreadthataccessesaresourcelocksit,andthentheotherthreadscannotaccessthat
resourceuntilitisunlocked,atwhichtimeanotherthreadlocksandusesit,andsoon.Ifthefrontseatof
thecaristhelimitedresource,thechildwhoshoutsDibs!acquiresthelock.
Thus,weneedtobeabletopreventanyothertasksfromaccessingthestoragewhenthatstorageisnotin
aproperstate.Thatis,weneedtohaveamechanismthatexcludesasecondtaskfromaccessingthe
storagewhenafirsttaskisalreadyusingit.Thisideaisfundamentaltoallmultithreadingsystemsandis
calledmutualexclusionthemechanismusedabbreviatesthistomutex.TheZThreadlibrarycontainsa
mutexmechanismdeclaredintheheaderMutex.h.
Tosolvetheproblemintheaboveprogram,weidentifythecriticalsectionswheremutualexclusionmust
applythenweacquirethemutexbeforeenteringthecriticalsectionandreleaseitattheendofthecritical
section.Onlyonethreadcanacquirethemutexatanytime,somutualexclusionisachieved:
//:C11:MutexEvenGenerator.cpp{RunByHand}
//Preventingthreadcollisionswithmutexes.
//{L}ZThread
#include<iostream>
#include"EvenChecker.h"
#include"zthread/ThreadedExecutor.h"
#include"zthread/Mutex.h"
usingnamespaceZThread
usingnamespacestd
classMutexEvenGenerator:publicGenerator{
unsignedintcurrentEvenValue
Mutexlock
public:
MutexEvenGenerator(){currentEvenValue=0}
~MutexEvenGenerator(){
cout<<"~MutexEvenGenerator"<<endl
}
intnextValue(){
lock.acquire()
++currentEvenValue
Thread::yield()//Causefailurefaster
++currentEvenValue
intrval=currentEvenValue
lock.release()
returnrval
}
}
intmain(){
EvenChecker::test<MutexEvenGenerator>()
}///:~
MutexEvenGeneratoraddsaMutexcalledlockandusesacquire()andrelease()tocreateacritical
sectionwithinnextValue().Inaddition,acalltoThread::yield()isinsertedbetweenthetwo
increments,toraisethelikelihoodofacontextswitchwhilecurrentEvenValueisinanoddstate.Because
themutexpreventsmorethanonethreadatatimeinthecriticalsection,thiswillnotproduceafailure,
butcallingyield()isahelpfulwaytopromoteafailureifitsgoingtohappen.
NotethatnextValue()mustcapturethereturnvalueinsidethecriticalsectionbecauseifyoureturnfrom
insidethecriticalsection,youwontreleasethelockandwillthuspreventitfrombeingacquiredagain.
(Thisusuallyleadstodeadlock,whichyoulllearnaboutattheendofthischapter.)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
401/458
5/3/2015
ThinkinginC++2ndedVolume2
ThefirstthreadthatentersnextValue()acquiresthelock,andanyfurtherthreadsthattrytoacquirethe
lockareblockedfromdoingsountilthefirstthreadreleasesthelock.Atthatpoint,thescheduling
mechanismselectsanotherthreadthatiswaitingonthelock.Thisway,onlyonethreadatatimecanpass
throughthecodethatisguardedbythemutex.
SimplifiedcodingwithGuards
Theuseofmutexesrapidlybecomescomplicatedwhenexceptionsareintroduced.Tomakesurethatthe
mutexisalwaysreleased,youmustensurethateachpossibleexceptionpathincludesacalltorelease().
Inaddition,anyfunctionthathasmultiplereturnpathsmustcarefullyensurethatitcallsrelease()atthe
appropriatepoints.
Theseproblemscanbeeasilysolvedbyusingthefactthatastackbased(auto)objecthasadestructor
thatisalwayscalledregardlessofhowyouexitfromafunctionscope.IntheZThreadlibrary,thisis
implementedastheGuardtemplate.TheGuardtemplatecreatesobjectsthatacquire()aLockable
objectwhenconstructedandrelease()thatlockwhendestroyed.Guardobjectscreatedonthelocal
stackwillautomaticallybedestroyedregardlessofhowthefunctionexitsandwillalwaysunlockthe
Lockableobject.HerestheaboveexamplereimplementedusingGuards:
//:C11:GuardedEvenGenerator.cpp{RunByHand}
//SimplifyingmutexeswiththeGuardtemplate.
//{L}ZThread
#include<iostream>
#include"EvenChecker.h"
#include"zthread/ThreadedExecutor.h"
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
usingnamespaceZThread
usingnamespacestd
classGuardedEvenGenerator:publicGenerator{
unsignedintcurrentEvenValue
Mutexlock
public:
GuardedEvenGenerator(){currentEvenValue=0}
~GuardedEvenGenerator(){
cout<<"~GuardedEvenGenerator"<<endl
}
intnextValue(){
Guard<Mutex>g(lock)
++currentEvenValue
Thread::yield()
++currentEvenValue
returncurrentEvenValue
}
}
intmain(){
EvenChecker::test<GuardedEvenGenerator>()
}///:~
NotethatthetemporaryreturnvalueisnolongernecessaryinnextValue().Ingeneral,thereisless
codetowrite,andtheopportunityforusererrorisgreatlyreduced.
AninterestingfeatureoftheGuardtemplateisthatitcanbeusedtomanipulateotherguardssafely.For
example,asecondGuardcanbeusedtotemporarilyunlockaguard:
//:C11:TemporaryUnlocking.cpp
//Temporarilyunlockinganotherguard.
//{L}ZThread
#include"zthread/Thread.h"
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
usingnamespaceZThread
classTemporaryUnlocking{
Mutexlock
public:
voidf(){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
402/458
5/3/2015
ThinkinginC++2ndedVolume2
Guard<Mutex>g(lock)
//lockisacquired
//...
{
Guard<Mutex,UnlockedScope>h(g)
//lockisreleased
//...
//lockisacquired
}
//...
//lockisreleased
}
}
intmain(){
TemporaryUnlockingt
t.f()
}///:~
AGuardcanalsobeusedtotrytoacquirealockforacertainamountoftimeandthengiveup:
//:C11:TimedLocking.cpp
//Limitedtimelocking.
//{L}ZThread
#include"zthread/Thread.h"
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
usingnamespaceZThread
classTimedLocking{
Mutexlock
public:
voidf(){
Guard<Mutex,TimedLockedScope<500>>g(lock)
//...
}
}
intmain(){
TimedLockingt
t.f()
}///:~
Inthisexample,aTimeout_Exceptionwillbethrownifthelockcannotbeacquiredwithin500
milliseconds.
Synchronizingentireclasses
TheZThreadlibraryalsoprovidesaGuardedClasstemplatetoautomaticallycreateasynchronized
wrapperforanentireclass.Thismeansthateverymemberfunctionintheclasswillautomaticallybe
guarded:
//:C11:SynchronizedClass.cpp{dmc}
//{L}ZThread
#include"zthread/GuardedClass.h"
usingnamespaceZThread
classMyClass{
public:
voidfunc1(){}
voidfunc2(){}
}
intmain(){
MyClassa
a.func1()//Notsynchronized
a.func2()//Notsynchronized
GuardedClass<MyClass>b(newMyClass)
//Synchronizedcalls,onlyonethreadatatimeallowed:
b>func1()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
403/458
5/3/2015
ThinkinginC++2ndedVolume2
b>func2()
}///:~
Objectaisanotsynchronized,sofunc1()andfunc2()canbecalledatanytimebyanynumberof
threads.ObjectbisprotectedbytheGuardedClasswrapper,soeachmemberfunctionisautomatically
synchronizedandonlyonefunctionperobjectcanbecalledanytime.
Thewrapperlocksataclasslevelofgranularity,whichmayaffectperformance.[151]Ifaclasscontains
someunrelatedfunctions,itmaybebettertosynchronizethosefunctionsinternallywithtwodifferent
locks.However,ifyoufindyourselfdoingthis,itmeansthatoneclasscontainsgroupsofdatathatmay
notbestronglyassociated.Considerbreakingtheclassintotwoclasses.
Guardingallmemberfunctionsofaclasswithamutexdoesnotautomaticallymakethatclassthreadsafe.
Youmustcarefullyconsiderallthreadingissuesinordertoguaranteethreadsafety.
Threadlocalstorage
Asecondwaytoeliminatetheproblemoftaskscollidingoversharedresourcesistoeliminatethesharing
ofvariables,whichcanbedonebycreatingdifferentstorageforthesamevariable,foreachdifferent
threadthatusesanobject.Thus,ifyouhavefivethreadsusinganobjectwithavariablex,threadlocal
storageautomaticallygeneratesfivedifferentpiecesofstorageforx.Fortunately,thecreationand
managementofthreadlocalstorageistakencareofautomaticallybyZThreadsThreadLocaltemplate,as
seenhere:
//:C11:ThreadLocalVariables.cpp{RunByHand}
//Automaticallygivingeachthreaditsownstorage.
//{L}ZThread
#include<iostream>
#include"zthread/Thread.h"
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
#include"zthread/ThreadedExecutor.h"
#include"zthread/Cancelable.h"
#include"zthread/ThreadLocal.h"
#include"zthread/CountedPtr.h"
usingnamespaceZThread
usingnamespacestd
classThreadLocalVariables:publicCancelable{
ThreadLocal<int>value
boolcanceled
Mutexlock
public:
ThreadLocalVariables():canceled(false){
value.set(0)
}
voidincrement(){value.set(value.get()+1)}
intget(){returnvalue.get()}
voidcancel(){
Guard<Mutex>g(lock)
canceled=true
}
boolisCanceled(){
Guard<Mutex>g(lock)
returncanceled
}
}
classAccessor:publicRunnable{
intid
CountedPtr<ThreadLocalVariables>tlv
public:
Accessor(CountedPtr<ThreadLocalVariables>&tl,intidn)
:id(idn),tlv(tl){}
voidrun(){
while(!tlv>isCanceled()){
tlv>increment()
cout<<*this<<endl
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
404/458
5/3/2015
ThinkinginC++2ndedVolume2
}
friendostream&
operator<<(ostream&os,Accessor&a){
returnos<<"#"<<a.id<<":"<<a.tlv>get()
}
}
intmain(){
cout<<"Press<Enter>toquit"<<endl
try{
CountedPtr<ThreadLocalVariables>
tlv(newThreadLocalVariables)
constintSZ=5
ThreadedExecutorexecutor
for(inti=0i<SZi++)
executor.execute(newAccessor(tlv,i))
cin.get()
tlv>cancel()//AllAccessorswillquit
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
WhenyoucreateaThreadLocalobjectbyinstantiatingthetemplate,youareonlyabletoaccessthe
contentsoftheobjectusingtheget()andset()memberfunctions.Theget()functionreturnsacopyof
theobjectthatisassociatedwiththatthread,andset()insertsitsargumentintotheobjectstoredforthat
thread,returningtheoldobjectthatwasinstorage.Youcanseethisisuseinincrement()andget()in
ThreadLocalVariables.
SincetlvissharedbymultipleAccessorobjects,itiswrittenasCancelablesothattheAccessorscan
besignaledwhenwewanttoshutthesystemdown.
Whenyourunthisprogram,youllseeevidencethattheindividualthreadsareeachallocatedtheirown
storage.
Terminatingtasks
Inpreviousexamples,wehaveseentheuseofaquitflagortheCancelableinterfaceinorderto
terminateatask.Thisisareasonableapproachtotheproblem.However,insomesituationsthetaskmust
beterminatedmoreabruptly.Inthissection,youlllearnabouttheissuesandproblemsofsuch
termination.
First,letslookatanexamplethatnotonlydemonstratestheterminationproblembutisalsoanadditional
exampleofresourcesharing.Topresentthisexample,wellfirstneedtosolvetheproblemofiostream
collision
Preventingiostreamcollision
Youmayhavenoticedinpreviousexamplesthattheoutputissometimesgarbled.C++iostreamswerenot
createdwiththreadinginmind,sotheresnothingtokeeponethreadsoutputfrominterferingwithanother
threadsoutput.Thus,youmustwriteyourapplicationssothattheysynchronizetheuseofiostreams.
Tosolvetheproblem,weneedtocreatetheentireoutputpacketfirstandthenexplicitlydecidewhento
trytosendittotheconsole.Onesimplesolutionistowritetheinformationtoanostringstreamandthen
useasingleobjectwithamutexasthepointofoutputamongallthreads,topreventmorethanonethread
fromwritingatthesametime:
//:C11:Display.h
//Preventsostreamcollisions.
#ifndefDISPLAY_H
#defineDISPLAY_H
#include<iostream>
#include<sstream>
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
classDisplay{//Shareoneoftheseamongallthreads
ZThread::Mutexiolock
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
405/458
5/3/2015
ThinkinginC++2ndedVolume2
public:
voidoutput(std::ostringstream&os){
ZThread::Guard<ZThread::Mutex>g(iolock)
std::cout<<os.str()
}
}
#endif//DISPLAY_H///:~
Thisway,thestandardoperator<<()functionsarepredefinedforusandtheobjectcanbebuiltin
memoryusingfamiliarostreamoperators.Whenataskwantstodisplayoutput,itcreatesatemporary
ostringstreamobjectthatitusestobuildupthedesiredoutputmessage.Whenitcallsoutput(),the
mutexpreventsmultiplethreadsfromwritingtothisDisplayobject.(YoumustuseonlyoneDisplay
objectinyourprogram,asyoullseeinthefollowingexamples.)
Thisjustshowsthebasicidea,butifnecessary,youcanbuildamoreelaborateframework.Forexample,
youcouldenforcetherequirementthattherebeonlyoneDisplayobjectinaprogrambymakingita
Singleton.(TheZThreadlibraryhasaSingletontemplatetosupportSingletons.)
Theornamentalgarden
Inthissimulation,thegardencommitteewouldliketoknowhowmanypeopleenterthegardeneachday
throughitsmultiplegates.Eachgatehasaturnstileorsomeotherkindofcounter,andaftertheturnstile
countisincremented,asharedcountisincrementedthatrepresentsthetotalnumberofpeopleinthe
garden.
//:C11:OrnamentalGarden.cpp{RunByHand}
//{L}ZThread
#include<vector>
#include<cstdlib>
#include<ctime>
#include"Display.h"
#include"zthread/Thread.h"
#include"zthread/FastMutex.h"
#include"zthread/Guard.h"
#include"zthread/ThreadedExecutor.h"
#include"zthread/CountedPtr.h"
usingnamespaceZThread
usingnamespacestd
classCount:publicCancelable{
FastMutexlock
intcount
boolpaused,canceled
public:
Count():count(0),paused(false),canceled(false){}
intincrement(){
//Commentthefollowinglinetoseecountingfail:
Guard<FastMutex>g(lock)
inttemp=count
if(rand()%2==0)//Yieldhalfthetime
Thread::yield()
return(count=++temp)
}
intvalue(){
Guard<FastMutex>g(lock)
returncount
}
voidcancel(){
Guard<FastMutex>g(lock)
canceled=true
}
boolisCanceled(){
Guard<FastMutex>g(lock)
returncanceled
}
voidpause(){
Guard<FastMutex>g(lock)
paused=true
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
406/458
5/3/2015
ThinkinginC++2ndedVolume2
boolisPaused(){
Guard<FastMutex>g(lock)
returnpaused
}
}
classEntrance:publicRunnable{
CountedPtr<Count>count
CountedPtr<Display>display
intnumber
intid
boolwaitingForCancel
public:
Entrance(CountedPtr<Count>&cnt,
CountedPtr<Display>&disp,intidn)
:count(cnt),display(disp),number(0),id(idn),
waitingForCancel(false){}
voidrun(){
while(!count>isPaused()){
++number
{
ostringstreamos
os<<*this<<"Total:"
<<count>increment()<<endl
display>output(os)
}
Thread::sleep(100)
}
waitingForCancel=true
while(!count>isCanceled())//Holdhere...
Thread::sleep(100)
ostringstreamos
os<<"Terminating"<<*this<<endl
display>output(os)
}
intgetValue(){
while(count>isPaused()&&!waitingForCancel)
Thread::sleep(100)
returnnumber
}
friendostream&
operator<<(ostream&os,constEntrance&e){
returnos<<"Entrance"<<e.id<<":"<<e.number
}
}
intmain(){
srand(time(0))//Seedtherandomnumbergenerator
cout<<"Press<ENTER>toquit"<<endl
CountedPtr<Count>count(newCount)
vector<Entrance*>v
CountedPtr<Display>display(newDisplay)
constintSZ=5
try{
ThreadedExecutorexecutor
for(inti=0i<SZi++){
Entrance*task=newEntrance(count,display,i)
executor.execute(task)
//Savethepointertothetask:
v.push_back(task)
}
cin.get()//Waitforusertopress<Enter>
count>pause()//Causestaskstostopcounting
intsum=0
vector<Entrance*>::iteratorit=v.begin()
while(it!=v.end()){
sum+=(*it)>getValue()
++it
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
407/458
5/3/2015
ThinkinginC++2ndedVolume2
ostringstreamos
os<<"Total:"<<count>value()<<endl
<<"SumofEntrances:"<<sum<<endl
display>output(os)
count>cancel()//Causesthreadstoquit
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
Countistheclassthatkeepsthemastercountofgardenvisitors.ThesingleCountobjectdefinedin
main()ascountisheldasaCountedPtrinEntranceandthusissharedbyallEntranceobjects.A
FastMutexcalledlockisusedinthisexampleinsteadofanordinaryMutexbecauseaFastMutexuses
thenativeoperatingsystemmutexandwillthusyieldmoreinterestingresults.
AGuardisusedwithlockinincrement()tosynchronizeaccesstocount.Thisfunctionusesrand()to
causeayield()roughlyhalfthetime,inbetweenfetchingcountintotempandincrementingandstoring
tempbackintocount.Becauseofthis,ifyoucommentouttheGuardobjectdefinition,youwillrapidly
seetheprogrambreakbecausemultiplethreadswillbeaccessingandmodifyingcountsimultaneously.
TheEntranceclassalsokeepsalocalnumberwiththenumberofvisitorsthathavepassedthroughthis
particularentrance.Thisprovidesadoublecheckagainstthecountobjecttomakesurethattheproper
numberofvisitorsisbeingrecorded.Entrance::run()simplyincrementsnumberandthecountobject
andsleepsfor100milliseconds.
Inmain,avector<Entrance*>isloadedwitheachEntrancethatiscreated.Aftertheuserpresses
<Enter>,thisvectorisusedtoiterateoveralltheindividualEntrancevaluesandtotalthem.
Thisprogramgoestoquiteabitofextratroubletoshuteverythingdowninastablefashion.Partofthe
reasonforthisistoshowjusthowcarefulyoumustbewhenterminatingamultithreadedprogram,and
partofthereasonistodemonstratethevalueofinterrupt(),whichyouwilllearnaboutshortly.
AllthecommunicationbetweentheEntranceobjectstakesplacethroughthesingleCountobject.When
theuserpresses<Enter>,main()sendsthepause()messagetocount.SinceeachEntrance::run()is
watchingthecountobjecttoseewhetheritispaused,thiscauseseachEntrancetomoveintothe
waitingForCancelstate,whereitisnolongercounting,butitisstillalive.Thisisessentialbecause
main()muststillbeabletosafelyiterateovertheobjectsinthevector<Entrance*>.Notethat
becausethereisaslightpossibilitythattheiterationmightoccurbeforeanEntrancehasfinishedcounting
andmovedintothewaitingForCancelstate,thegetValue()functioncyclesthroughcallstosleep()
untiltheobjectmovesintowaitingForCancel.(Thisisoneformofwhatiscalledabusywait,whichis
undesirable.Youllseethepreferredapproachofusingwait()laterinthechapter.)Oncemain()
completesitsiterationthroughthevector<Entrance*>,thecancel()messageissenttothecount
object,andonceagainalltheEntranceobjectsarewatchingforthisstatechange.Atthispoint,theyprint
aterminationmessageandexitfromrun(),whichcauseseachtasktobedestroyedbythethreading
mechanism.
Asthisprogramruns,youwillseethetotalcountandthecountateachentrancedisplayedaspeoplewalk
throughaturnstile.IfyoucommentouttheGuardobjectinCount::increment(),youllnoticethatthe
totalnumberofpeopleisnotwhatyouexpectittobe.Thenumberofpeoplecountedbyeachturnstilewill
bedifferentfromthevalueincount.AslongastheMutexistheretosynchronizeaccesstotheCounter,
thingsworkcorrectly.KeepinmindthatCount::increment()exaggeratesthepotentialforfailureby
usingtempandyield().Inrealthreadingproblems,thepossibilityforfailuremaybestatisticallysmall,
soyoucaneasilyfallintothetrapofbelievingthatthingsareworkingcorrectly.Justasintheexample
above,therearelikelytobehiddenproblemsthathaventoccurredtoyou,sobeexceptionallydiligent
whenreviewingconcurrentcode.
Atomicoperations
NotethatCount::value()returnsthevalueofcountusingaGuardobjectforsynchronization.This
bringsupaninterestingpointbecausethiscodewillprobablyworkfinewithmostcompilersandsystems
withoutsynchronization.Thereasonisthat,ingeneral,asimpleoperationsuchasreturninganintwillbe
anatomicoperation,whichmeansthatitwillprobablyhappeninasinglemicroprocessorinstructionthat
willnotgetinterrupted.(Themultithreadingmechanismisunabletostopathreadinthemiddleofa
microprocessorinstruction.)Thatis,atomicoperationsarenotinterruptiblebythethreadingmechanism
andthusdonotneedtobeguarded.[152]Infact,ifweremovedthefetchofcountintotempandremoved
theyield(),andinsteadsimplyincrementedcountdirectly,weprobablywouldntneedalockbecausethe
incrementoperationisusuallyatomic,aswell.[153]
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
408/458
5/3/2015
ThinkinginC++2ndedVolume2
TheproblemisthattheC++Standarddoesntguaranteeatomicityforanyoftheseoperations.Although
operationssuchasreturninganintandincrementinganintarealmostcertainlyatomiconmostmachines,
theresnoguarantee.Andbecausetheresnoguarantee,youhavetoassumetheworst.Sometimesyou
mightinvestigatetheatomicitybehavioronaparticularmachine(usuallybylookingatassemblylanguage)
andwritecodebasedonthoseassumptions.Thatsalwaysdangerousandilladvised.Itstooeasyforthat
informationtobelostorhidden,andthenextpersonthatcomesalongmayassumethatthiscodecanbe
portedtoanothermachineandthengomadtrackingdowntheoccasionalglitchcausedbythreadcollisions.
So,whileremovingtheguardonCount::value()seemstowork,itsnotairtight,andthusonsome
machinesyoumayseeaberrantbehavior.
Terminatingwhenblocked
Entrance::run()inthepreviousexampleincludesacalltosleep()inthemainloop.Weknowthat
sleep()willeventuallywakeupandthetaskwillreachthetopoftheloopwhereithasanopportunityto
breakoutofthatloopbycheckingtheisPaused()status.However,sleep()isjustonesituationwherea
threadisblockedfromexecuting,andsometimesyoumustterminateataskthatsblocked.
Threadstates
Athreadcanbeinanyoneoffourstates:
1.New:Athreadremainsinthisstateonlymomentarily,asitisbeingcreated.Itallocatesany
necessarysystemresourcesandperformsinitialization.Atthispointitbecomeseligibleto
receiveCPUtime.Theschedulerwillthentransitionthisthreadtotherunnableorblockedstate.
2.Runnable:ThismeansthatathreadcanberunwhenthetimeslicingmechanismhasCPUcycles
availableforthethread.Thus,thethreadmightormightnotberunningatanymoment,but
theresnothingtopreventitfrombeingruniftheschedulercanarrangeititsnotdeador
blocked.
3.Blocked:Thethreadcouldberun,butsomethingpreventsit.(ItmightbewaitingforI/Oto
complete,forexample.)Whileathreadisintheblockedstate,theschedulerwillsimplyskipit
andnotgiveitanyCPUtime.Untilathreadreenterstherunnablestate,itwontperformany
operations.
4.Dead:AthreadinthedeadstateisnolongerschedulableandwillnotreceiveanyCPUtime.Its
taskiscompleted,anditisnolongerrunnable.Thenormalwayforathreadtodieisby
returningfromitsrun()function.
Becomingblocked
Athreadisblockedwhenitcannotcontinuerunning.Athreadcanbecomeblockedforthefollowing
reasons:
Youveputthethreadtosleepbycallingsleep(milliseconds),inwhichcaseitwillnotberunforthe
specifiedtime.
Youvesuspendedtheexecutionofthethreadwithwait().Itwillnotbecomerunnableagainuntilthe
threadgetsthesignal()orbroadcast()message.Wellexaminetheseinalatersection.
ThethreadiswaitingforsomeI/Otocomplete.
Thethreadistryingtoenterablockofcodethatisguardedbyamutex,andthatmutexhasalready
beenacquiredbyanotherthread.
Theproblemweneedtolookatnowisthis:sometimesyouwanttoterminateathreadthatisinablocked
state.Ifyoucantwaitforittogettoapointinthecodewhereitcancheckastatevalueanddecideto
terminateonitsown,youhavetoforcethethreadoutofitsblockedstate.
Interruption
Asyoumightimagine,itsmuchmessiertobreakoutofthemiddleofaRunnable::run()functionthanit
istowaitforthatfunctiontogettoatestofisCanceled()(orsomeotherplacewheretheprogrammeris
readytoleavethefunction).Whenyoubreakoutofablockedtask,youmightneedtodestroyobjectsand
cleanupresources.Becauseofthis,breakingoutofthemiddleofatasksrun()ismorelikethrowingan
exceptionthananythingelse,soinZThreads,exceptionsareusedforthiskindofabort.(Thiswalksthe
fineedgeofbeinganinappropriateuseofexceptions,becauseitmeansyouareoftenusingthemfor
controlflow.)[154]Toreturntoaknowngoodstatewhenterminatingataskthisway,carefullyconsiderthe
executionpathsofyourcodeandproperlycleanupeverythinginsidethecatchclause.Welllookatthese
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
409/458
5/3/2015
ThinkinginC++2ndedVolume2
issuesinthissection.
Toterminateablockedthread,theZThreadlibraryprovidestheThread::interrupt()function.Thissets
theinterruptedstatusforthatthread.Athreadwithitsinterruptedstatussetwillthrowan
Interrupted_Exceptionifitisalreadyblockedoritattemptsablockingoperation.Theinterruptedstatus
willberesetwhentheexceptionisthrownorifthetaskcallsThread::interrupted().Asyoullsee,
Thread::interrupted()providesasecondwaytoleaveyourrun()loop,withoutthrowinganexception.
Heresanexamplethatshowsthebasicsofinterrupt():
//:C11:Interrupting.cpp
//Interruptingablockedthread.
//{L}ZThread
#include<iostream>
#include"zthread/Thread.h"
usingnamespaceZThread
usingnamespacestd
classBlocked:publicRunnable{
public:
voidrun(){
try{
Thread::sleep(1000)
cout<<"Waitingforget()inrun():"
cin.get()
}catch(Interrupted_Exception&){
cout<<"CaughtInterrupted_Exception"<<endl
//Exitthetask
}
}
}
intmain(intargc,char*argv[]){
try{
Threadt(newBlocked)
if(argc>1)
Thread::sleep(1100)
t.interrupt()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
Youcanseethat,inadditiontotheinsertionintocout,run()containstwootherpointswhereblockingcan
occur:thecalltoThread::sleep(1000)andthecalltocin.get().Bygivingtheprogramanycommand
lineargument,youtellmain()tosleeplongenoughthatthetaskwillfinishitssleep()andcall
cin.get().[155]Ifyoudontgivetheprogramanargument,thesleep()inmain()isskipped.Here,the
calltointerrupt()willoccurwhilethetaskissleeping,andyoullseethatthiswillcause
Interrupted_Exceptiontobethrown.Ifyougivetheprogramacommandlineargument,youlldiscover
thatataskcannotbeinterruptedifitisblockedonIO.Thatis,youcaninterruptoutofanyblocking
operationexceptIO.[156]
ThisisalittledisconcertingifyourecreatingathreadthatperformsIObecauseitmeansthatI/Ohasthe
potentialoflockingyourmultithreadedprogram.Theproblemisthat,again,C++wasnotdesignedwith
threadinginmindquitetheopposite,iteffectivelypretendsthatthreadingdoesntexist.Thus,the
iostreamlibraryisnotthreadfriendly.IfthenewC++Standarddecidestoaddthreadsupport,the
iostreamlibrarymayneedtobereconsideredintheprocess.
Blockedbyamutex
Ifyoutrytocallafunctionwhosemutexhasalreadybeenacquired,thecallingtaskwillbesuspendeduntil
themutexbecomesavailable.Thefollowingexampletestswhetherthiskindofblockingisinterruptible:
//:C11:Interrupting2.cpp
//Interruptingathreadblocked
//withasynchronizationguard.
//{L}ZThread
#include<iostream>
#include"zthread/Thread.h"
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
410/458
5/3/2015
ThinkinginC++2ndedVolume2
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
usingnamespaceZThread
usingnamespacestd
classBlockedMutex{
Mutexlock
public:
BlockedMutex(){
lock.acquire()
}
voidf(){
Guard<Mutex>g(lock)
//Thiswillneverbeavailable
}
}
classBlocked2:publicRunnable{
BlockedMutexblocked
public:
voidrun(){
try{
cout<<"Waitingforf()inBlockedMutex"<<endl
blocked.f()
}catch(Interrupted_Exception&e){
cerr<<e.what()<<endl
//Exitthetask
}
}
}
intmain(intargc,char*argv[]){
try{
Threadt(newBlocked2)
t.interrupt()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
TheclassBlockedMutexhasaconstructorthatacquirestheobjectsownMutexandneverreleasesit.
Forthatreason,ifyoutrytocallf(),youwillalwaysbeblockedbecausetheMutexcannotbeacquired.
InBlocked2,therun()functionwillbestoppedatthecalltoblocked.f().Whenyouruntheprogram
youllseethat,unliketheiostreamcall,interrupt()canbreakoutofacallthatsblockedbyamutex.[157]
Checkingforaninterrupt
Notethatwhenyoucallinterrupt()onathread,theonlytimethattheinterruptoccursiswhenthetask
enters,orisalreadyinside,ablockingoperation(except,asyouveseen,inthecaseofIO,whereyoure
juststuck).Butwhatifyouvewrittencodethatmayormaynotmakesuchablockingcall,dependingon
theconditionsinwhichitisrun?Ifyoucanonlyexitbythrowinganexceptiononablockingcall,youwont
alwaysbeabletoleavetherun()loop.Thus,ifyoucallinterrupt()tostopatask,yourtaskneedsa
secondopportunitytoexitintheeventthatyourrun()loopdoesnthappentobemakinganyblocking
calls.
Thisopportunityispresentedbytheinterruptedstatus,whichissetbythecalltointerrupt().Youcheck
fortheinterruptedstatusbycallinginterrupted().Thisnotonlytellsyouwhetherinterrupt()hasbeen
called,italsoclearstheinterruptedstatus.Clearingtheinterruptedstatusensuresthattheframeworkwill
notnotifyyoutwiceaboutataskbeinginterrupted.Youwillbenotifiedviaeitherasingle
Interrupted_Exception,orasinglesuccessfulThread::interrupted()test.Ifyouwanttocheckagain
toseewhetheryouwereinterrupted,youcanstoretheresultwhenyoucallThread::interrupted().
Thefollowingexampleshowsthetypicalidiomthatyoushoulduseinyourrun()functiontohandleboth
blockedandnonblockedpossibilitieswhentheinterruptedstatusisset:
//:C11:Interrupting3.cpp{RunByHand}
//Generalidiomforinterruptingatask.
//{L}ZThread
#include<iostream>
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
411/458
5/3/2015
ThinkinginC++2ndedVolume2
#include"zthread/Thread.h"
usingnamespaceZThread
usingnamespacestd
constdoublePI=3.14159265358979323846
constdoubleE=2.7182818284590452354
classNeedsCleanup{
intid
public:
NeedsCleanup(intident):id(ident){
cout<<"NeedsCleanup"<<id<<endl
}
~NeedsCleanup(){
cout<<"~NeedsCleanup"<<id<<endl
}
}
classBlocked3:publicRunnable{
volatiledoubled
public:
Blocked3():d(0.0){}
voidrun(){
try{
while(!Thread::interrupted()){
point1:
NeedsCleanupn1(1)
cout<<"Sleeping"<<endl
Thread::sleep(1000)
point2:
NeedsCleanupn2(2)
cout<<"Calculating"<<endl
//Atimeconsuming,nonblockingoperation:
for(inti=1i<100000i++)
d=d+(PI+E)/(double)i
}
cout<<"Exitingviawhile()test"<<endl
}catch(Interrupted_Exception&){
cout<<"ExitingviaInterrupted_Exception"<<endl
}
}
}
intmain(intargc,char*argv[]){
if(argc!=2){
cerr<<"usage:"<<argv[0]
<<"delayinmilliseconds"<<endl
exit(1)
}
intdelay=atoi(argv[1])
try{
Threadt(newBlocked3)
Thread::sleep(delay)
t.interrupt()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
TheNeedsCleanupclassemphasizesthenecessityofproperresourcecleanupifyouleavetheloopviaan
exception.NotethatnopointersaredefinedinBlocked3::run()because,forexceptionsafety,all
resourcesmustbeenclosedinstackbasedobjectssothattheexceptionhandlercanautomaticallyclean
themupbycallingthedestructor.
Youmustgivetheprogramacommandlineargumentwhichisthedelaytimeinmillisecondsbeforeitcalls
interrupt().Byusingdifferentdelays,youcanexitBlocked3::run()atdifferentpointsintheloop:in
theblockingsleep()call,andinthenonblockingmathematicalcalculation.Youllseethatifinterrupt()
iscalledafterthelabelpoint2(duringthenonblockingoperation),firsttheloopiscompleted,thenallthe
localobjectsaredestructed,andfinallytheloopisexitedatthetopviathewhilestatement.However,if
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
412/458
5/3/2015
ThinkinginC++2ndedVolume2
interrupt()iscalledbetweenpoint1andpoint2(afterthewhilestatementbutbeforeorduringthe
blockingoperationsleep()),thetaskexitsviatheInterrupted_Exception.Inthatcase,onlythestack
objectsthathavebeencreateduptothepointwheretheexceptionisthrownarecleanedup,andyouhave
theopportunitytoperformanyothercleanupinthecatchclause.
Aclassdesignedtorespondtoaninterrupt()mustestablishapolicythatensuresitwillremainina
consistentstate.Thisgenerallymeansthatallresourceacquisitionshouldbewrappedinsidestackbased
objectssothatthedestructorswillbecalledregardlessofhowtherun()loopexits.Correctlydone,code
likethiscanbeelegant.Componentscanbecreatedthatcompletelyencapsulatetheirsynchronization
mechanismsbutarestillresponsivetoanexternalstimulus(viainterrupt())withoutaddinganyspecial
functionstoanobjectsinterface.
Cooperationbetweenthreads
Asyouveseen,whenyouusethreadstorunmorethanonetaskatatime,youcankeeponetaskfrom
interferingwithanothertasksresourcesbyusingamutextosynchronizethebehaviorofthetwotasks.
Thatis,iftwotasksaresteppingoneachotheroverasharedresource(usuallymemory),youuseamutex
toallowonlyonetaskatatimetoaccessthatresource.
Withthatproblemsolved,youcanmoveontotheissueofgettingthreadstocooperate,sothatmultiple
threadscanworktogethertosolveaproblem.Nowtheissueisnotaboutinterferingwithoneanother,but
ratheraboutworkinginunison,sinceportionsofsuchproblemsmustbesolvedbeforeotherportionscan
besolved.Itsmuchlikeprojectplanning:thefootingsforthehousemustbedugfirst,butthesteelcanbe
laidandtheconcreteformscanbebuiltinparallel,andbothofthosetasksmustbefinishedbeforethe
concretefoundationcanbepoured.Theplumbingmustbeinplacebeforetheconcreteslabcanbepoured,
theconcreteslabmustbeinplacebeforeyoustartframing,andsoon.Someofthesetaskscanbedonein
parallel,butcertainstepsrequirealltaskstobecompletedbeforeyoucanmoveahead.
Thekeyissuewhentasksarecooperatingishandshakingbetweenthosetasks.Toaccomplishthis
handshaking,weusethesamefoundation:themutex,whichinthiscaseguaranteesthatonlyonetaskcan
respondtoasignal.Thiseliminatesanypossibleraceconditions.Ontopofthemutex,weaddawayfora
tasktosuspenditselfuntilsomeexternalstatechanges(theplumbingisnowinplace),indicatingthatits
timeforthattasktomoveforward.Inthissection,welllookattheissuesofhandshakingbetweentasks,
theproblemsthatcanarise,andtheirsolutions.
Waitandsignal
InZThreads,thebasicclassthatusesamutexandallowstasksuspensionistheCondition,andyoucan
suspendataskbycallingwait()onaCondition.Whenexternalstatechangestakeplacethatmight
meanthatataskshouldcontinueprocessing,younotifythetaskbycallingsignal(),towakeuponetask,
orbroadcast(),towakeupalltasksthathavesuspendedthemselvesonthatConditionobject.
Therearetwoformsofwait().Thefirstformtakesanargumentinmillisecondsthathasthesame
meaningasinsleep():pauseforthisperiodoftime.Thesecondformtakesnoargumentsthisversion
ismorecommonlyused.Bothformsofwait()releasetheMutexthatiscontrolledbytheCondition
objectandsuspendsthethreaduntilthatConditionobjectreceivesasignal()orbroadcast().Thefirst
formmayalsoterminateifittimesoutbeforeasignal()orbroadcast()isreceived.
Becausewait()releasestheMutex,itmeansthattheMutexcanbeacquiredbyanotherthread.Thus,
whenyoucallwait()youresayingIvedoneallIcanrightnowsoImgoingtowaitrighthere,butI
wanttoallowothersynchronizedoperationstotakeplaceiftheycan.
Typically,youusewait()whenyourewaitingforsomeconditiontochangethatisunderthecontrolof
forcesoutsidethecurrentfunction.(Often,thisconditionwillbechangedbyanotherthread.)Youdont
wanttoidlyloopwhiletestingtheconditioninsideyourthreadthisiscalledabusywait,anditsusually
abaduseofCPUcycles.Thus,wait()suspendsthethreadwhilewaitingfortheworldtochange,andonly
whenasignal()orbroadcast()occurs(suggestingthatsomethingofinterestmayhavehappened),
doesthethreadwakeupandcheckforchanges.Sowait()providesawaytosynchronizeactivities
betweenthreads.
Letslookatasimpleexample.WaxOMatic.cpphastwoprocesses:onetoapplywaxtoaCarandoneto
polishit.Thepolishingprocesscannotdoitsjobuntiltheapplicationprocessisfinished,andtheapplication
processmustwaituntilthepolishingprocessisfinishedbeforeitcanputonanothercoatofwax.Both
WaxOnandWaxOffusetheCarobject,whichcontainsaConditionthatitusestosuspendathread
insidewaitForWaxing()orwaitForBuffing():
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
413/458
5/3/2015
ThinkinginC++2ndedVolume2
//:C11:WaxOMatic.cpp{RunByHand}
//Basicthreadcooperation.
//{L}ZThread
#include<iostream>
#include<string>
#include"zthread/Thread.h"
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
#include"zthread/Condition.h"
#include"zthread/ThreadedExecutor.h"
usingnamespaceZThread
usingnamespacestd
classCar{
Mutexlock
Conditioncondition
boolwaxOn
public:
Car():condition(lock),waxOn(false){}
voidwaxed(){
Guard<Mutex>g(lock)
waxOn=true//Readytobuff
condition.signal()
}
voidbuffed(){
Guard<Mutex>g(lock)
waxOn=false//Readyforanothercoatofwax
condition.signal()
}
voidwaitForWaxing(){
Guard<Mutex>g(lock)
while(waxOn==false)
condition.wait()
}
voidwaitForBuffing(){
Guard<Mutex>g(lock)
while(waxOn==true)
condition.wait()
}
}
classWaxOn:publicRunnable{
CountedPtr<Car>car
public:
WaxOn(CountedPtr<Car>&c):car(c){}
voidrun(){
try{
while(!Thread::interrupted()){
cout<<"WaxOn!"<<endl
Thread::sleep(200)
car>waxed()
car>waitForBuffing()
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"EndingWaxOnprocess"<<endl
}
}
classWaxOff:publicRunnable{
CountedPtr<Car>car
public:
WaxOff(CountedPtr<Car>&c):car(c){}
voidrun(){
try{
while(!Thread::interrupted()){
car>waitForWaxing()
cout<<"WaxOff!"<<endl
Thread::sleep(200)
car>buffed()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
414/458
5/3/2015
ThinkinginC++2ndedVolume2
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"EndingWaxOffprocess"<<endl
}
}
intmain(){
cout<<"Press<Enter>toquit"<<endl
try{
CountedPtr<Car>car(newCar)
ThreadedExecutorexecutor
executor.execute(newWaxOff(car))
executor.execute(newWaxOn(car))
cin.get()
executor.interrupt()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
InCarsconstructor,asingleMutexiswrappedinaConditionobjectsothatitcanbeusedtomanage
intertaskcommunication.However,theConditionobjectcontainsnoinformationaboutthestateofyour
process,soyouneedtomanageadditionalinformationtoindicateprocessstate.Here,Carhasasingle
boolwaxOn,whichindicatesthestateofthewaxingpolishingprocess.
InwaitForWaxing(),thewaxOnflagischecked,andifitisfalse,thecallingthreadissuspendedby
callingwait()ontheConditionobject.Itsimportantthatthisoccurinsideaguardedclause,wherethe
threadhasacquiredthelock(here,bycreatingaGuardobject).Whenyoucallwait(),thethreadis
suspendedandthelockisreleased.Itisessentialthatthelockbereleasedbecause,tosafelychangethe
stateoftheobject(forexample,tochangewaxOntotrue,whichmusthappenifthesuspendedthreadis
toevercontinue),thatlockmustbeavailabletobeacquiredbysomeothertask.Inthisexample,when
anotherthreadcallswaxed()totellitthatitstimetodosomething,themutexmustbeacquiredinorder
tochangewaxOntotrue.Afterward,waxed()sendsasignal()totheConditionobject,whichwakes
upthethreadsuspendedinthecalltowait().Althoughsignal()maybecalledinsideaguardedclause
asitishereyouarenotrequiredtodothis.[158]
Inorderforathreadtowakeupfromawait(),itmustfirstreacquirethemutexthatitreleasedwhenit
enteredthewait().Thethreadwillnotwakeupuntilthatmutexbecomesavailable.
Thecalltowait()isplacedinsideawhileloopthatcheckstheconditionofinterest.Thisisimportantfor
tworeasons:[159]
Itispossiblethatwhenthethreadgetsasignal(),someotherconditionhaschangedthatisnot
associatedwiththereasonthatwecalledwait()here.Ifthatisthecase,thisthreadshouldbe
suspendedagainuntilitsconditionofinterestchanges.
Bythetimethisthreadawakensfromitswait(),itspossiblethatsomeothertaskhaschanged
thingssuchthatthisthreadisunableoruninterestedinperformingitsoperationatthistime.Again,
itshouldberesuspendedbycallingwait()again.
Becausethesetworeasonsarealwayspresentwhenyouarecallingwait(),alwayswriteyourcallto
wait()insideawhileloopthattestsforyourcondition(s)ofinterest.
WaxOn::run()representsthefirststepintheprocessofwaxingthecar,soitperformsitsoperation(a
calltosleep()tosimulatethetimenecessaryforwaxing).Itthentellsthecarthatwaxingiscomplete,
andcallswaitForBuffing(),whichsuspendsthisthreadwithawait()untiltheWaxOffprocesscalls
buffed()forthecar,changingthestateandcallingnotify().WaxOff::run(),ontheotherhand,
immediatelymovesintowaitForWaxing()andisthussuspendeduntilthewaxhasbeenappliedby
WaxOnandwaxed()iscalled.Whenyourunthisprogram,youcanwatchthistwostepprocessrepeat
itselfascontrolishandedbackandforthbetweenthetwothreads.Whenyoupressthe<Enter>key,
interrupt()haltsboththreadswhenyoucallinterrupt()foranExecutor,itcallsinterrupt()forall
thethreadsitiscontrolling.
Producerconsumerrelationships
Acommonsituationinthreadingproblemsistheproducerconsumerrelationship,whereonetaskis
creatingobjectsandothertasksareconsumingthem.Insuchasituation,makesurethat(amongother
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
415/458
5/3/2015
ThinkinginC++2ndedVolume2
things)theconsumingtasksdonotaccidentallyskipanyoftheproducedobjects.
Toshowthisproblem,consideramachinethathasthreetasks:onetomaketoast,onetobutterthetoast,
andonetoputjamonthebutteredtoast.
//:C11:ToastOMatic.cpp{RunByHand}
//Problemswiththreadcooperation.
//{L}ZThread
#include<iostream>
#include<cstdlib>
#include<ctime>
#include"zthread/Thread.h"
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
#include"zthread/Condition.h"
#include"zthread/ThreadedExecutor.h"
usingnamespaceZThread
usingnamespacestd
//Applyjamtobutteredtoast:
classJammer:publicRunnable{
Mutexlock
ConditionbutteredToastReady
boolgotButteredToast
intjammed
public:
Jammer():butteredToastReady(lock){
gotButteredToast=false
jammed=0
}
voidmoreButteredToastReady(){
Guard<Mutex>g(lock)
gotButteredToast=true
butteredToastReady.signal()
}
voidrun(){
try{
while(!Thread::interrupted()){
{
Guard<Mutex>g(lock)
while(!gotButteredToast)
butteredToastReady.wait()
++jammed
}
cout<<"Puttingjamontoast"<<jammed<<endl
{
Guard<Mutex>g(lock)
gotButteredToast=false
}
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"Jammeroff"<<endl
}
}
//Applybuttertotoast:
classButterer:publicRunnable{
Mutexlock
ConditiontoastReady
CountedPtr<Jammer>jammer
boolgotToast
intbuttered
public:
Butterer(CountedPtr<Jammer>&j)
:toastReady(lock),jammer(j){
gotToast=false
buttered=0
}
voidmoreToastReady(){
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
416/458
5/3/2015
ThinkinginC++2ndedVolume2
Guard<Mutex>g(lock)
gotToast=true
toastReady.signal()
}
voidrun(){
try{
while(!Thread::interrupted()){
{
Guard<Mutex>g(lock)
while(!gotToast)
toastReady.wait()
++buttered
}
cout<<"Butteringtoast"<<buttered<<endl
jammer>moreButteredToastReady()
{
Guard<Mutex>g(lock)
gotToast=false
}
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"Buttereroff"<<endl
}
}
classToaster:publicRunnable{
CountedPtr<Butterer>butterer
inttoasted
public:
Toaster(CountedPtr<Butterer>&b):butterer(b){
toasted=0
}
voidrun(){
try{
while(!Thread::interrupted()){
Thread::sleep(rand()/(RAND_MAX/5)*100)
//...
//Createnewtoast
//...
cout<<"Newtoast"<<++toasted<<endl
butterer>moreToastReady()
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"Toasteroff"<<endl
}
}
intmain(){
srand(time(0))//Seedtherandomnumbergenerator
try{
cout<<"Press<Return>toquit"<<endl
CountedPtr<Jammer>jammer(newJammer)
CountedPtr<Butterer>butterer(newButterer(jammer))
ThreadedExecutorexecutor
executor.execute(newToaster(butterer))
executor.execute(butterer)
executor.execute(jammer)
cin.get()
executor.interrupt()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
Theclassesaredefinedinthereverseorderthattheyoperatetosimplifyforwardreferencingissues.
JammerandButtererbothcontainaMutex,aCondition,andsomekindofinternalstateinformation
thatchangestoindicatethattheprocessshouldsuspendorresume.(Toasterdoesntneedthesesinceitis
theproduceranddoesnthavetowaitonanything.)Thetworun()functionsperformanoperation,seta
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
417/458
5/3/2015
ThinkinginC++2ndedVolume2
stateflag,andthencallwait()tosuspendthetask.ThemoreToastReady()and
moreButteredToastReady()functionschangetheirrespectivestateflagstoindicatethatsomethinghas
changedandtheprocessshouldconsiderresumingandthencallsignal()towakeupthethread.
Thedifferencebetweenthisexampleandthepreviousoneisthat,atleastconceptually,somethingisbeing
producedhere:toast.Therateoftoastproductionisrandomizedabit,toaddsomeuncertainty.Andyoull
seethatwhenyouruntheprogram,thingsarentgoingrightbecausemanypiecesoftoastappeartobe
gettingdroppedonthefloornotbuttered,notjammed.
Solvingthreadingproblemswithqueues
Often,threadingproblemsarebasedontheneedfortaskstobeserializedthatis,totakecareofthings
inorder.ToastOMatic.cppmustnotonlytakecareofthingsinorder,itmustbeabletoworkonone
pieceoftoastwithoutworryingthattoastisfallingonthefloorinthemeantime.Youcansolvemany
threadingproblemsbyusingaqueuethatsynchronizesaccesstotheelementswithin:
//:C11:TQueue.h
#ifndefTQUEUE_H
#defineTQUEUE_H
#include<deque>
#include"zthread/Thread.h"
#include"zthread/Condition.h"
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
template<classT>classTQueue{
ZThread::Mutexlock
ZThread::Conditioncond
std::deque<T>data
public:
TQueue():cond(lock){}
voidput(Titem){
ZThread::Guard<ZThread::Mutex>g(lock)
data.push_back(item)
cond.signal()
}
Tget(){
ZThread::Guard<ZThread::Mutex>g(lock)
while(data.empty())
cond.wait()
TreturnVal=data.front()
data.pop_front()
returnreturnVal
}
}
#endif//TQUEUE_H///:~
ThisbuildsontheStandardC++Librarydequebyadding:
1.Synchronizationtoensurethatnotwothreadsaddobjectsatthesametime.
2.wait()andsignal()sothataconsumerthreadwillautomaticallysuspendifthequeueisempty,
andresumewhenmoreelementsbecomeavailable.
Thisrelativelysmallamountofcodecansolvearemarkablenumberofproblems.[160]
HeresasimpletestthatserializestheexecutionofLiftOffobjects.TheconsumerisLiftOffRunner,which
pullseachLiftOffobjectofftheTQueueandrunsitdirectly.(Thatis,itusesitsownthreadbycalling
run()explicitlyratherthanstartingupanewthreadforeachtask.)
//:C11:TestTQueue.cpp{RunByHand}
//{L}ZThread
#include<string>
#include<iostream>
#include"TQueue.h"
#include"zthread/Thread.h"
#include"LiftOff.h"
usingnamespaceZThread
usingnamespacestd
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
418/458
5/3/2015
ThinkinginC++2ndedVolume2
classLiftOffRunner:publicRunnable{
TQueue<LiftOff*>rockets
public:
voidadd(LiftOff*lo){rockets.put(lo)}
voidrun(){
try{
while(!Thread::interrupted()){
LiftOff*rocket=rockets.get()
rocket>run()
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"ExitingLiftOffRunner"<<endl
}
}
intmain(){
try{
LiftOffRunner*lor=newLiftOffRunner
Threadt(lor)
for(inti=0i<5i++)
lor>add(newLiftOff(10,i))
cin.get()
lor>add(newLiftOff(10,99))
cin.get()
t.interrupt()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
ThetasksareplacedontheTQueuebymain()andaretakenofftheTQueuebytheLiftOffRunner.
NoticethatLiftOffRunnercanignorethesynchronizationissuesbecausetheyaresolvedbytheTQueue.
Propertoasting
TosolvetheToastOMatic.cppproblem,wecanrunthetoastthroughTQueuesbetweenprocesses.And
todothis,wewillneedactualtoastobjects,whichmaintainanddisplaytheirstate:
//:C11:ToastOMaticMarkII.cpp{RunByHand}
//SolvingtheproblemsusingTQueues.
//{L}ZThread
#include<iostream>
#include<string>
#include<cstdlib>
#include<ctime>
#include"zthread/Thread.h"
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
#include"zthread/Condition.h"
#include"zthread/ThreadedExecutor.h"
#include"TQueue.h"
usingnamespaceZThread
usingnamespacestd
classToast{
enumStatus{DRY,BUTTERED,JAMMED}
Statusstatus
intid
public:
Toast(intidn):status(DRY),id(idn){}
#ifdef__DMC__//Incorrectlyrequiresdefault
Toast(){assert(0)}//Shouldneverbecalled
#endif
voidbutter(){status=BUTTERED}
voidjam(){status=JAMMED}
stringgetStatus()const{
switch(status){
caseDRY:return"dry"
caseBUTTERED:return"buttered"
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
419/458
5/3/2015
ThinkinginC++2ndedVolume2
caseJAMMED:return"jammed"
default:return"error"
}
}
intgetId(){returnid}
friendostream&operator<<(ostream&os,constToast&t){
returnos<<"Toast"<<t.id<<":"<<t.getStatus()
}
}
typedefCountedPtr<TQueue<Toast>>ToastQueue
classToaster:publicRunnable{
ToastQueuetoastQueue
intcount
public:
Toaster(ToastQueue&tq):toastQueue(tq),count(0){}
voidrun(){
try{
while(!Thread::interrupted()){
intdelay=rand()/(RAND_MAX/5)*100
Thread::sleep(delay)
//Maketoast
Toastt(count++)
cout<<t<<endl
//Insertintoqueue
toastQueue>put(t)
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"Toasteroff"<<endl
}
}
//Applybuttertotoast:
classButterer:publicRunnable{
ToastQueuedryQueue,butteredQueue
public:
Butterer(ToastQueue&dry,ToastQueue&buttered)
:dryQueue(dry),butteredQueue(buttered){}
voidrun(){
try{
while(!Thread::interrupted()){
//Blocksuntilnextpieceoftoastisavailable:
Toastt=dryQueue>get()
t.butter()
cout<<t<<endl
butteredQueue>put(t)
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"Buttereroff"<<endl
}
}
//Applyjamtobutteredtoast:
classJammer:publicRunnable{
ToastQueuebutteredQueue,finishedQueue
public:
Jammer(ToastQueue&buttered,ToastQueue&finished)
:butteredQueue(buttered),finishedQueue(finished){}
voidrun(){
try{
while(!Thread::interrupted()){
//Blocksuntilnextpieceoftoastisavailable:
Toastt=butteredQueue>get()
t.jam()
cout<<t<<endl
finishedQueue>put(t)
}
}catch(Interrupted_Exception&){/*Exit*/}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
420/458
5/3/2015
ThinkinginC++2ndedVolume2
cout<<"Jammeroff"<<endl
}
}
//Consumethetoast:
classEater:publicRunnable{
ToastQueuefinishedQueue
intcounter
public:
Eater(ToastQueue&finished)
:finishedQueue(finished),counter(0){}
voidrun(){
try{
while(!Thread::interrupted()){
//Blocksuntilnextpieceoftoastisavailable:
Toastt=finishedQueue>get()
//Verifythatthetoastiscominginorder,
//andthatallpiecesaregettingjammed:
if(t.getId()!=counter++||
t.getStatus()!="jammed"){
cout<<">>>>Error:"<<t<<endl
exit(1)
}else
cout<<"Chomp!"<<t<<endl
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"Eateroff"<<endl
}
}
intmain(){
srand(time(0))//Seedtherandomnumbergenerator
try{
ToastQueuedryQueue(newTQueue<Toast>),
butteredQueue(newTQueue<Toast>),
finishedQueue(newTQueue<Toast>)
cout<<"Press<Return>toquit"<<endl
ThreadedExecutorexecutor
executor.execute(newToaster(dryQueue))
executor.execute(newButterer(dryQueue,butteredQueue))
executor.execute(
newJammer(butteredQueue,finishedQueue))
executor.execute(newEater(finishedQueue))
cin.get()
executor.interrupt()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
Twothingsareimmediatelyapparentinthissolution:first,theamountandcomplexityofcodewithineach
RunnableclassisdramaticallyreducedbytheuseoftheTQueuebecausetheguarding,communication,
andwait()/signal()operationsarenowtakencareofbytheTQueue.TheRunnableclassesdonthave
MutexesorConditionobjectsanymore.Second,thecouplingbetweentheclassesiseliminatedbecause
eachclasscommunicatesonlywithitsTQueues.Noticethatthedefinitionorderoftheclassesisnow
independent.Lesscodeandlesscouplingarealwaysgoodthings,whichsuggeststhattheuseofthe
TQueuehasapositiveeffecthere,asitdoesonmostproblems.
Broadcast
Thesignal()functionwakesuponethreadthatiswaitingonaConditionobject.However,multiple
threadsmaybewaitingonthesameconditionobject,andinthatcaseyoumightwanttowakethemallup
usingbroadcast()insteadofsignal().
Asanexamplethatbringstogethermanyoftheconceptsinthischapter,considerahypotheticalrobotic
assemblylineforautomobiles.EachCarwillbebuiltinseveralstages,andinthisexamplewelllookata
singlestage:afterthechassishasbeencreated,atthetimewhentheengine,drivetrain,andwheelsare
attached.TheCarsaretransportedfromoneplacetoanotherviaaCarQueue,whichisatypeof
TQueue.ADirectortakeseachCar(asarawchassis)fromtheincomingCarQueueandplacesitina
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
421/458
5/3/2015
ThinkinginC++2ndedVolume2
Cradle,whichiswherealltheworkisdone.Atthispoint,theDirectortellsallthewaitingrobots(using
broadcast())thattheCarisintheCradlereadyfortherobotstoworkonit.Thethreetypesofrobots
gotowork,sendingamessagetotheCradlewhentheyfinishtheirtasks.TheDirectorwaitsuntilallthe
tasksarecompleteandthenputstheCarontotheoutgoingCarQueuetobetransportedtothenext
operation.Here,theconsumeroftheoutgoingCarQueueisaReporterobject,whichjustprintstheCar
toshowthatthetaskshavebeenproperlycompleted.
//:C11:CarBuilder.cpp{RunByHand}
//Howbroadcast()works.
//{L}ZThread
#include<iostream>
#include<string>
#include"zthread/Thread.h"
#include"zthread/Mutex.h"
#include"zthread/Guard.h"
#include"zthread/Condition.h"
#include"zthread/ThreadedExecutor.h"
#include"TQueue.h"
usingnamespaceZThread
usingnamespacestd
classCar{
intid
boolengine,driveTrain,wheels
public:
Car(intidn):id(idn),engine(false),
driveTrain(false),wheels(false){}
//EmptyCarobject:
Car():id(1),engine(false),
driveTrain(false),wheels(false){}
//Unsynchronizedassumesatomicbooloperations:
intgetId(){returnid}
voidaddEngine(){engine=true}
boolengineInstalled(){returnengine}
voidaddDriveTrain(){driveTrain=true}
booldriveTrainInstalled(){returndriveTrain}
voidaddWheels(){wheels=true}
boolwheelsInstalled(){returnwheels}
friendostream&operator<<(ostream&os,constCar&c){
returnos<<"Car"<<c.id<<"["
<<"engine:"<<c.engine
<<"driveTrain:"<<c.driveTrain
<<"wheels:"<<c.wheels<<"]"
}
}
typedefCountedPtr<TQueue<Car>>CarQueue
classChassisBuilder:publicRunnable{
CarQueuecarQueue
intcounter
public:
ChassisBuilder(CarQueue&cq):carQueue(cq),counter(0){}
voidrun(){
try{
while(!Thread::interrupted()){
Thread::sleep(1000)
//Makechassis:
Carc(counter++)
cout<<c<<endl
//Insertintoqueue
carQueue>put(c)
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"ChassisBuilderoff"<<endl
}
}
classCradle{
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
422/458
5/3/2015
ThinkinginC++2ndedVolume2
Carc//Holdscurrentcarbeingworkedon
booloccupied
MutexworkLock,readyLock
ConditionworkCondition,readyCondition
boolengineBotHired,wheelBotHired,driveTrainBotHired
public:
Cradle()
:workCondition(workLock),readyCondition(readyLock){
occupied=false
engineBotHired=true
wheelBotHired=true
driveTrainBotHired=true
}
voidinsertCar(Carchassis){
c=chassis
occupied=true
}
CargetCar(){//Canonlyextractcaronce
if(!occupied){
cerr<<"NoCarinCradleforgetCar()"<<endl
returnCar()//"Null"Carobject
}
occupied=false
returnc
}
//Accesscarwhileincradle:
Car*operator>(){return&c}
//Allowrobotstoofferservicestothiscradle:
voidofferEngineBotServices(){
Guard<Mutex>g(workLock)
while(engineBotHired)
workCondition.wait()
engineBotHired=true//Acceptthejob
}
voidofferWheelBotServices(){
Guard<Mutex>g(workLock)
while(wheelBotHired)
workCondition.wait()
wheelBotHired=true//Acceptthejob
}
voidofferDriveTrainBotServices(){
Guard<Mutex>g(workLock)
while(driveTrainBotHired)
workCondition.wait()
driveTrainBotHired=true//Acceptthejob
}
//Tellwaitingrobotsthatworkisready:
voidstartWork(){
Guard<Mutex>g(workLock)
engineBotHired=false
wheelBotHired=false
driveTrainBotHired=false
workCondition.broadcast()
}
//Eachrobotreportswhentheirjobisdone:
voidtaskFinished(){
Guard<Mutex>g(readyLock)
readyCondition.signal()
}
//Directorwaitsuntilalljobsaredone:
voidwaitUntilWorkFinished(){
Guard<Mutex>g(readyLock)
while(!(c.engineInstalled()&&c.driveTrainInstalled()
&&c.wheelsInstalled()))
readyCondition.wait()
}
}
typedefCountedPtr<Cradle>CradlePtr
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
423/458
5/3/2015
ThinkinginC++2ndedVolume2
classDirector:publicRunnable{
CarQueuechassisQueue,finishingQueue
CradlePtrcradle
public:
Director(CarQueue&cq,CarQueue&fq,CradlePtrcr)
:chassisQueue(cq),finishingQueue(fq),cradle(cr){}
voidrun(){
try{
while(!Thread::interrupted()){
//Blocksuntilchassisisavailable:
cradle>insertCar(chassisQueue>get())
//Notifyrobotscarisreadyforwork
cradle>startWork()
//Waituntilworkcompletes
cradle>waitUntilWorkFinished()
//Putcarintoqueueforfurtherwork
finishingQueue>put(cradle>getCar())
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"Directoroff"<<endl
}
}
classEngineRobot:publicRunnable{
CradlePtrcradle
public:
EngineRobot(CradlePtrcr):cradle(cr){}
voidrun(){
try{
while(!Thread::interrupted()){
//Blocksuntiljobisoffered/accepted:
cradle>offerEngineBotServices()
cout<<"Installingengine"<<endl
(*cradle)>addEngine()
cradle>taskFinished()
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"EngineRobotoff"<<endl
}
}
classDriveTrainRobot:publicRunnable{
CradlePtrcradle
public:
DriveTrainRobot(CradlePtrcr):cradle(cr){}
voidrun(){
try{
while(!Thread::interrupted()){
//Blocksuntiljobisoffered/accepted:
cradle>offerDriveTrainBotServices()
cout<<"InstallingDriveTrain"<<endl
(*cradle)>addDriveTrain()
cradle>taskFinished()
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"DriveTrainRobotoff"<<endl
}
}
classWheelRobot:publicRunnable{
CradlePtrcradle
public:
WheelRobot(CradlePtrcr):cradle(cr){}
voidrun(){
try{
while(!Thread::interrupted()){
//Blocksuntiljobisoffered/accepted:
cradle>offerWheelBotServices()
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
424/458
5/3/2015
ThinkinginC++2ndedVolume2
cout<<"InstallingWheels"<<endl
(*cradle)>addWheels()
cradle>taskFinished()
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"WheelRobotoff"<<endl
}
}
classReporter:publicRunnable{
CarQueuecarQueue
public:
Reporter(CarQueue&cq):carQueue(cq){}
voidrun(){
try{
while(!Thread::interrupted()){
cout<<carQueue>get()<<endl
}
}catch(Interrupted_Exception&){/*Exit*/}
cout<<"Reporteroff"<<endl
}
}
intmain(){
cout<<"Press<Enter>toquit"<<endl
try{
CarQueuechassisQueue(newTQueue<Car>),
finishingQueue(newTQueue<Car>)
CradlePtrcradle(newCradle)
ThreadedExecutorassemblyLine
assemblyLine.execute(newEngineRobot(cradle))
assemblyLine.execute(newDriveTrainRobot(cradle))
assemblyLine.execute(newWheelRobot(cradle))
assemblyLine.execute(
newDirector(chassisQueue,finishingQueue,cradle))
assemblyLine.execute(newReporter(finishingQueue))
//Starteverythingrunningbyproducingchassis:
assemblyLine.execute(newChassisBuilder(chassisQueue))
cin.get()
assemblyLine.interrupt()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
YoullnoticethatCartakesashortcut:itassumesthatbooloperationsareatomic,which,aspreviously
discussed,issometimesasafeassumptionbutrequirescarefulthought.[161]EachCarbeginsasan
unadornedchassis,anddifferentrobotswillattachdifferentpartstoit,callingtheappropriateadd
functionwhentheydo.
AChassisBuildersimplycreatesanewCareverysecondandplacesitintothechassisQueue.A
DirectormanagesthebuildprocessbytakingthenextCaroffthechassisQueue,puttingitintothe
Cradle,tellingalltherobotstostartWork(),andsuspendingitselfbycalling
waitUntilWorkFinished().Whentheworkisdone,theDirectortakestheCaroutoftheCradleand
putsinintothefinishingQueue.
TheCradleisthecruxofthesignalingoperations.AMutexandaConditionobjectcontrolboththe
workingoftherobotsandindicatewhetheralltheoperationsarefinished.Aparticulartypeofrobotcan
offeritsservicestotheCradlebycallingtheofferfunctionappropriatetoitstype.Atthispoint,that
robotthreadissuspendeduntiltheDirectorcallsstartWork(),whichchangesthehiringflagsandcalls
broadcast()totellalltherobotstoshowupforwork.Althoughthissystemallowsanynumberofrobots
tooffertheirservices,eachoneofthoserobotshasitsthreadsuspendedbydoingso.Youcouldimaginea
moresophisticatedsystemwheretherobotsregisterthemselveswithmanydifferentCradleswithout
beingsuspendedbythatregistrationprocessandthenresideinapoolwaitingforthefirstCradlethat
needsataskcompleted.
Aftereachrobotfinishesitstask(changingthestateoftheCarintheprocess),itcallstaskFinished(),
whichsendsasignal()tothereadyCondition,whichiswhattheDirectoriswaitingonin
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
425/458
5/3/2015
ThinkinginC++2ndedVolume2
waitUntilWorkFinished().Eachtimethedirectorthreadawakens,thestateoftheCarischecked,and
ifitstillisntfinished,thatthreadissuspendedagain.
WhentheDirectorinsertsaCarintotheCradle,youcanperformoperationsonthatCarviathe
operator>().Topreventmultipleextractionsofthesamecar,aflagcausesanerrorreporttobe
generated.(ExceptionsdontpropagateacrossthreadsintheZThreadlibrary.)
Inmain(),allthenecessaryobjectsarecreatedandthetasksareinitialized,withtheChassisBuilder
begunlasttostarttheprocess.(However,becauseofthebehavioroftheTQueue,itwouldntmatterifit
werestartedfirst.)Notethatthisprogramfollowsalltheguidelinesregardingobjectandtasklifetime
presentedinthischapter,andsotheshutdownprocessissafe.
Deadlock
Becausethreadscanbecomeblockedandbecauseobjectscanhavemutexesthatpreventthreadsfrom
accessingthatobjectuntilthemutexisreleased,itspossibleforonethreadtogetstuckwaitingfor
anotherthread,whichinturnwaitsforanotherthread,andsoon,untilthechainleadsbacktoathread
waitingonthefirstone.Yougetacontinuousloopofthreadswaitingoneachother,andnoonecanmove.
Thisiscalleddeadlock.
Ifyoutryrunningaprogramanditdeadlocksrightaway,youimmediatelyknowyouhaveaproblem,and
youcantrackitdown.Therealproblemiswhenyourprogramseemstobeworkingfinebuthasthehidden
potentialtodeadlock.Inthiscase,youmaygetnoindicationthatdeadlockingisapossibility,soitwillbe
latentinyourprogramuntilitunexpectedlyhappenstoacustomer.(Andyouprobablywontbeableto
easilyreproduceit.)Thus,preventingdeadlockthroughcarefulprogramdesignisacriticalpartof
developingconcurrentprograms.
Letslookattheclassicdemonstrationofdeadlock,inventedbyEdsgerDijkstra:thediningphilosophers
problem.Thebasicdescriptionspecifiesfivephilosophers(buttheexampleshownherewillallowany
number).Thesephilosophersspendpartoftheirtimethinkingandpartoftheirtimeeating.Whiletheyare
thinking,theydontneedanysharedresources,buttheyeatusingalimitednumberofutensils.Inthe
originalproblemdescription,theutensilsareforks,andtwoforksarerequiredtogetspaghettifromabowl
inthemiddleofthetable,butitseemstomakemoresensetosaythattheutensilsarechopsticks.
Clearly,eachphilosopherwillrequiretwochopsticksinordertoeat.
Adifficultyisintroducedintotheproblem:asphilosophers,theyhaveverylittlemoney,sotheycanonly
affordfivechopsticks.Thesearespacedaroundthetablebetweenthem.Whenaphilosopherwantstoeat,
theymustpickupthechopsticktotheleftandtheonetotheright.Ifthephilosopheroneithersideis
usingadesiredchopstick,ourphilosophermustwaituntilthenecessarychopsticksbecomeavailable.
//:C11:DiningPhilosophers.h
//ClassesforDiningPhilosophers.
#ifndefDININGPHILOSOPHERS_H
#defineDININGPHILOSOPHERS_H
#include<string>
#include<iostream>
#include<cstdlib>
#include"zthread/Condition.h"
#include"zthread/Guard.h"
#include"zthread/Mutex.h"
#include"zthread/Thread.h"
#include"Display.h"
classChopstick{
ZThread::Mutexlock
ZThread::ConditionnotTaken
booltaken
public:
Chopstick():notTaken(lock),taken(false){}
voidtake(){
ZThread::Guard<ZThread::Mutex>g(lock)
while(taken)
notTaken.wait()
taken=true
}
voiddrop(){
ZThread::Guard<ZThread::Mutex>g(lock)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
426/458
5/3/2015
ThinkinginC++2ndedVolume2
taken=false
notTaken.signal()
}
}
classPhilosopher:publicZThread::Runnable{
Chopstick&left
Chopstick&right
intid
intponderFactor
ZThread::CountedPtr<Display>display
intrandSleepTime(){
if(ponderFactor==0)return0
returnrand()/(RAND_MAX/ponderFactor)*250
}
voidoutput(std::strings){
std::ostringstreamos
os<<*this<<""<<s<<std::endl
display>output(os)
}
public:
Philosopher(Chopstick&l,Chopstick&r,
ZThread::CountedPtr<Display>&disp,intident,intponder)
:left(l),right(r),id(ident),ponderFactor(ponder),
display(disp){}
virtualvoidrun(){
try{
while(!ZThread::Thread::interrupted()){
output("thinking")
ZThread::Thread::sleep(randSleepTime())
//Hungry
output("grabbingright")
right.take()
output("grabbingleft")
left.take()
output("eating")
ZThread::Thread::sleep(randSleepTime())
right.drop()
left.drop()
}
}catch(ZThread::Synchronization_Exception&e){
output(e.what())
}
}
friendstd::ostream&
operator<<(std::ostream&os,constPhilosopher&p){
returnos<<"Philosopher"<<p.id
}
}
#endif//DININGPHILOSOPHERS_H///:~
NotwoPhilosopherscantake()aChopstickatthesametime,sincetake()issynchronizedwitha
Mutex.Inaddition,ifthechopstickhasalreadybeentakenbyonePhilosopher,anothercanwait()on
theavailableConditionuntiltheChopstickbecomesavailablewhenthecurrentholdercallsdrop()
(whichmustalsobesynchronizedtopreventraceconditionsandensurememoryvisibilityin
multiprocessorsystems).
EachPhilosopherholdsreferencestotheirleftandrightChopsticksotheycanattempttopickthoseup.
ThegoalofthePhilosopheristothinkpartofthetimeandeatpartofthetime,andthisisexpressedin
main().However,youwillobservethatifthePhilosophersspendverylittletimethinking,theywillall
becompetingfortheChopstickswhiletheytrytoeat,anddeadlockwillhappenmuchmorequickly.So
youcanexperimentwiththis,theponderFactorweightsthelengthoftimethataPhilosophertendsto
spendthinkingandeating.AsmallerponderFactorwillincreasetheprobabilityofdeadlock.
InPhilosopher::run(),eachPhilosopherjustthinksandeatscontinuously.YouseethePhilosopher
thinkingforarandomizedamountoftime,thentryingtotake()therightandthentheleftChopstick,
eatingforarandomizedamountoftime,andthendoingitagain.Outputtotheconsoleissynchronizedas
seenearlierinthischapter.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
427/458
5/3/2015
ThinkinginC++2ndedVolume2
Thisproblemisinterestingbecauseitdemonstratesthataprogramcanappeartoruncorrectlybutactually
bedeadlockprone.Toshowthis,thecommandlineargumentadjustsafactortoaffecttheamountoftime
eachphilosopherspendsthinking.Ifyouhavelotsofphilosophersortheyspendalotoftimethinking,you
mayneverseethedeadlockeventhoughitremainsapossibility.Acommandlineargumentofzerotends
tomakeitdeadlockfairlyquickly:[162]
//:C11:DeadlockingDiningPhilosophers.cpp{RunByHand}
//DiningPhilosopherswithDeadlock.
//{L}ZThread
#include<ctime>
#include"DiningPhilosophers.h"
#include"zthread/ThreadedExecutor.h"
usingnamespaceZThread
usingnamespacestd
intmain(intargc,char*argv[]){
srand(time(0))//Seedtherandomnumbergenerator
intponder=argc>1?atoi(argv[1]):5
cout<<"Press<ENTER>toquit"<<endl
enum{SZ=5}
try{
CountedPtr<Display>d(newDisplay)
ThreadedExecutorexecutor
Chopstickc[SZ]
for(inti=0i<SZi++){
executor.execute(
newPhilosopher(c[i],c[(i+1)%SZ],d,i,ponder))
}
cin.get()
executor.interrupt()
executor.wait()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
NotethattheChopstickobjectsdonotneedinternalidentifierstheyareidentifiedbytheirpositioninthe
arrayc.EachPhilosopherisgivenareferencetoaleftandrightChopstickobjectwhenconstructed
thesearetheutensilsthatmustbepickedupbeforethatPhilosophercaneat.EveryPhilosopherexcept
thelastoneisinitializedbysituatingthatPhilosopherbetweenthenextpairofChopstickobjects.The
lastPhilosopherisgiventhezerothChopstickforitsrightChopstick,sotheroundtableiscompleted.
ThatsbecausethelastPhilosopherissittingrightnexttothefirstone,andtheybothsharethatzeroth
chopstick.Withthisarrangement,itspossibleatsomepointforallthephilosopherstobetryingtoeatand
waitingonthephilosophernexttothemtoputdowntheirchopstick,andtheprogramwilldeadlock.
Ifyourthreads(philosophers)arespendingmoretimeonothertasks(thinking)thaneating,thenthey
haveamuchlowerprobabilityofrequiringthesharedresources(chopsticks),andthusyoucanconvince
yourselfthattheprogramisdeadlockfree(usinganonzeropondervalue),eventhoughitisnt.
Torepairtheproblem,youmustunderstandthatdeadlockcanoccuriffourconditionsaresimultaneously
met:
1.Mutualexclusion.Atleastoneresourceusedbythethreadsmustnotbeshareable.Inthiscase,a
chopstickcanbeusedbyonlyonephilosopheratatime.
2.Atleastoneprocessmustbeholdingaresourceandwaitingtoacquirearesourcecurrentlyheldby
anotherprocess.Thatis,fordeadlocktooccur,aphilosophermustbeholdingonechopstickand
waitingforanotherone.
3.Aresourcecannotbepreemptivelytakenawayfromaprocess.Processesonlyreleaseresourcesas
anormalevent.Ourphilosophersarepoliteandtheydontgrabchopsticksfromotherphilosophers.
4.Acircularwaitcanhappen,wherebyaprocesswaitsonaresourceheldbyanotherprocess,which
inturniswaitingonaresourceheldbyanotherprocess,andsoon,untiloneoftheprocessesis
waitingonaresourceheldbythefirstprocess,thusgridlockingeverything.In
DeadlockingDiningPhilosophers.cpp,thecircularwaithappensbecauseeachphilosophertriesto
gettherightchopstickfirstandthentheleft.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
428/458
5/3/2015
ThinkinginC++2ndedVolume2
Becausealltheseconditionsmustbemettocausedeadlock,youneedtostoponlyoneofthemfrom
occurringtopreventdeadlock.Inthisprogram,theeasiestwaytopreventdeadlockistobreakcondition
four.Thisconditionhappensbecauseeachphilosopheristryingtopickuptheirchopsticksinaparticular
sequence:firstright,thenleft.Becauseofthat,itspossibletogetintoasituationwhereeachofthemis
holdingtheirrightchopstickandwaitingtogettheleft,causingthecircularwaitcondition.However,ifthe
lastphilosopherisinitializedtotrytogettheleftchopstickfirstandthentheright,thatphilosopherwill
neverpreventthephilosopherontheimmediaterightfrompickinguptheirleftchopstick.Inthiscase,the
circularwaitisprevented.Thisisonlyonesolutiontotheproblem,butyoucouldalsosolveitby
preventingoneoftheotherconditions(seeadvancedthreadingbooksformoredetails):
//:C11:FixedDiningPhilosophers.cpp{RunByHand}
//DiningPhilosopherswithoutDeadlock.
//{L}ZThread
#include<ctime>
#include"DiningPhilosophers.h"
#include"zthread/ThreadedExecutor.h"
usingnamespaceZThread
usingnamespacestd
intmain(intargc,char*argv[]){
srand(time(0))//Seedtherandomnumbergenerator
intponder=argc>1?atoi(argv[1]):5
cout<<"Press<ENTER>toquit"<<endl
enum{SZ=5}
try{
CountedPtr<Display>d(newDisplay)
ThreadedExecutorexecutor
Chopstickc[SZ]
for(inti=0i<SZi++){
if(i<(SZ1))
executor.execute(
newPhilosopher(c[i],c[i+1],d,i,ponder))
else
executor.execute(
newPhilosopher(c[0],c[i],d,i,ponder))
}
cin.get()
executor.interrupt()
executor.wait()
}catch(Synchronization_Exception&e){
cerr<<e.what()<<endl
}
}///:~
Byensuringthatthelastphilosopherpicksupandputsdowntheirleftchopstickbeforetheirright,the
deadlockisremoved,andtheprogramwillrunsmoothly.
Thereisnolanguagesupporttohelppreventdeadlockitsuptoyoutoavoiditbycarefuldesign.These
arenotcomfortingwordstothepersonwhostryingtodebugadeadlockingprogram.
Summary
Thegoalofthischapterwastogiveyouthefoundationsofconcurrentprogrammingwiththreads:
1.Youcanrunmultipleindependenttasks.
2.Youmustconsiderallthepossibleproblemswhenthesetasksshutdown.Objectsorothertasks
maydisappearbeforetasksarefinishedwiththem.
3.Taskscancollidewitheachotheroversharedresources.Themutexisthebasictoolusedtoprevent
thesecollisions.
4.Taskscandeadlockiftheyarenotcarefullydesigned.
However,therearemultipleadditionalfacetsofthreadingandtoolstohelpyousolvethreadingproblems.
TheZThreadslibrarycontainsanumberofthesetools,suchassemaphoresandspecialtypesofqueues,
similartotheoneyousawinthischapter.Explorethatlibraryaswellasotherresourcesonthreadingto
gainmoreindepthknowledge.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
429/458
5/3/2015
ThinkinginC++2ndedVolume2
Itisvitaltolearnwhentouseconcurrencyandwhentoavoidit.Themainreasonstouseitare:
Tomanageanumberoftaskswhoseinterminglingusethecomputermoreefficiently(includingthe
abilitytotransparentlydistributethetasksacrossmultipleCPUs).
Toallowbettercodeorganization.
Tobemoreconvenientfortheuser.
TheclassicexampleofresourcebalancingistousetheCPUduringI/Owaits.Theclassicexampleofuser
convenienceistomonitorastopbuttonduringlongdownloads.
Anadditionaladvantagetothreadsisthattheyprovidelightexecutioncontextswitches(ontheorderof
100instructions)ratherthanheavyprocesscontextswitches(thousandsofinstructions).Sinceallthreads
inagivenprocesssharethesamememoryspace,alightcontextswitchchangesonlyprogramexecution
andlocalvariables.Aprocesschangetheheavycontextswitchmustexchangethefullmemoryspace.
Themaindrawbackstomultithreadingare:
Slowdownoccurswhilewaitingforsharedresources.
AdditionalCPUoverheadisrequiredtomanagethreads.
Unrewardedcomplexityarisesfrompoordesigndecisions.
Opportunitiesarecreatedforpathologiessuchasstarving,racing,deadlock,andlivelock.
Inconsistenciesoccuracrossplatforms.Whendevelopingtheoriginalmaterial(inJava)forthis
chapter,wediscoveredraceconditionsthatquicklyappearedonsomecomputersbutwouldnt
appearonothers.TheC++examplesinthischapterbehaveddifferently(butusuallyacceptably)
underdifferentoperatingsystems.Ifyoudevelopaprogramonacomputerandthingsseemto
workright,youmightgetanunwelcomesurprisewhenyoudistributeit.
Oneofthebiggestdifficultieswiththreadsoccursbecausemorethanonethreadmightbesharinga
resourcesuchasthememoryinanobjectandyoumustmakesurethatmultiplethreadsdonttryto
readandchangethatresourceatthesametime.Thisrequiresjudicioususeofsynchronizationtools,which
mustbethoroughlyunderstoodbecausetheycanquietlyintroducedeadlocksituations.
Inaddition,theresacertainarttotheapplicationofthreads.C++isdesignedtoallowyoutocreateas
manyobjectsasyouneedtosolveyourproblematleastintheory.(Creatingmillionsofobjectsforan
engineeringfiniteelementanalysis,forexample,mightnotbepractical.)However,thereisusuallyan
upperboundtothenumberofthreadsyoullwanttocreate,becauseatsomenumber,threadsmay
becomebalky.ThiscriticalpointcanbedifficulttodetectandwilloftendependontheOSandthread
libraryitcouldbefewerthanahundredorinthethousands.Asyouoftencreateonlyahandfulofthreads
tosolveaproblem,thisistypicallynotmuchofalimitbutinamoregeneraldesignitbecomesa
constraint.
Regardlessofhowsimplethreadingcanseemusingaparticularlanguageorlibrary,consideritablackart.
Theresalwayssomethingyouhaventconsideredthatcanbiteyouwhenyouleastexpectit.(Forexample,
notethatbecausethediningphilosophersproblemcanbeadjustedsothatdeadlockrarelyhappens,you
cangettheimpressionthateverythingisOK.)AnappropriatequotecomesfromGuidovanRossum,
creatorofthePythonprogramminglanguage:
Inanyprojectthatismultithreaded,mostbugswillcomefromthreadingissues.Thisisregardlessof
programminglanguageitsadeep,asyetununderstoodpropertyofthreads.
Formoreadvanceddiscussionsofthreading,seeParallelandDistributedProgrammingUsingC++,by
CameronHughesandTraceyHughes,AddisonWesley2004.
Exercises
SolutionstoselectedexercisescanbefoundintheelectronicdocumentTheThinkinginC++Volume2AnnotatedSolutionGuide,
availableforasmallfeefromwww.MindView.net.
1.InheritaclassfromRunnableandoverridetherun()function.Insiderun(),printamessage,and
thencallsleep().Repeatthisthreetimes,andthenreturnfromrun().Putastartupmessagein
theconstructorandashutdownmessagewhenthetaskterminates.Makeseveralthreadobjectsof
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
430/458
5/3/2015
ThinkinginC++2ndedVolume2
thistype,andrunthemtoseewhathappens.
2.ModifyBasicThreads.cpptomakeLiftOffthreadsstartotherLiftOffthreads.
3.ModifyResponsiveUI.cpptoeliminateanypossibleraceconditions.(Assumebooloperationsare
notatomic.)
4.InIncrementer.cpp,modifytheCountclasstouseasingleintinsteadofanarrayofint.Explain
theresultingbehavior.
5.InEvenChecker.h,correctthepotentialproblemintheGeneratorclass.(Assumebooloperations
arenotatomic.)
6.ModifyEvenGenerator.cpptouseinterrupt()insteadofquitflags.
7.InMutexEvenGenerator.cpp,changethecodeinMutexEvenGenerator::nextValue()sothat
thereturnexpressionprecedestherelease()statementandexplainwhathappens.
8.ModifyResponsiveUI.cpptouseinterrupt()insteadofthequitFlagapproach.
9.LookuptheSingletondocumentationintheZThreadslibrary.ModifyOrnamentalGarden.cppso
thattheDisplayobjectiscontrolledbyaSingletontopreventmorethanoneDisplayfrombeing
accidentallycreated.
10.InOrnamentalGarden.cpp,changetheCount::increment()functionsothatitdoesadirect
incrementofcount(thatis,itjustdoesacount++).Nowremovetheguardandseeifthatcauses
afailure.Isthissafeandreliable?
11.ModifyOrnamentalGarden.cppsothatitusesinterrupt()insteadofthepause()mechanism.
Makesurethatyoursolutiondoesntprematurelydestroyobjects.
12.ModifyWaxOMatic.cppbyaddingmoreinstancesoftheProcessclasssothatitappliesand
polishesthreecoatsofwaxinsteadofjustone.
13.CreatetwoRunnablesubclasses,onewitharun()thatstartsandcallswait().Theotherclasss
run()shouldcapturethereferenceofthefirstRunnableobject.Itsrun()shouldcallsignal()
forthefirstthreadaftersomenumberofsecondshavepassedsothatfirstthreadcanprinta
message.
14.Createanexampleofabusywait.Onethreadsleepsforawhileandthensetsaflagtotrue.The
secondthreadwatchesthatflaginsideawhileloop(thisisthebusywait)and,whentheflag
becomestrue,setsitbacktofalseandreportsthechangetotheconsole.Notehowmuchwasted
timetheprogramspendsinsidethebusywait,andcreateasecondversionoftheprogramthat
useswait()insteadofthebusywait.Extra:runaprofilertoshowthetimeusedbytheCPUin
eachcase.
15.ModifyTQueue.htoaddamaximumallowableelementcount.Ifthecountisreached,further
writesshouldbeblockeduntilthecountdropsbelowthemaximum.Writecodetotestthisbehavior.
16.ModifyToastOMaticMarkII.cpptocreatepeanutbutterandjellyontoastsandwichesusingtwo
separateassemblylinesandanoutputTQueueforthefinishedsandwiches.UseaReporterobject
asinCarBuilder.cpptodisplaytheresults.
17.RewriteC07:BankTeller.cpptouserealthreadinginsteadofsimulatedthreading.
18.ModifyCarBuilder.cpptogiveidentifierstotherobots,andaddmoreinstancesofthedifferent
kindsofrobots.Notewhetherallrobotsgetutilized.
19.ModifyCarBuilder.cpptoaddanotherstagetothecarbuildingprocess,wherebyyouaddthe
exhaustsystem,body,andfenders.Aswiththefirststage,assumetheseprocessescanbe
performedsimultaneouslybyrobots.
20.ModifyCarBuilder.cppsothatCarhassynchronizedaccesstoalltheboolvariables.Because
Mutexescannotbecopied,thiswillrequiresignificantchangesthroughouttheprogram.
21.UsingtheapproachinCarBuilder.cpp,modelthehousebuildingstorythatwasgiveninthis
chapter.
22.CreateaTimerclasswithtwooptions:(1)aoneshottimerthatonlygoesoffonce(2)atimerthat
goesoffatregularintervals.UsethisclasswithC10:MulticastCommand.cpptomovethecallsto
TaskRunner::run()fromtheproceduresintothetimer.
23.ChangebothofthediningphilosophersexamplessothatthenumberofPhilosophersiscontrolled
onthecommandline,inadditiontothepondertime.Trydifferentvaluesandexplaintheresults.
24.ChangeDiningPhilosophers.cppsothatthePhilosophersjustpickthenextavailablechopstick.
(WhenaPhilosopherisdonewiththeirchopsticks,theydropthemintoabin.WhenaPhilosopher
wantstoeat,theytakethenexttwoavailablechopsticksfromthebin.)Doesthiseliminatethe
possibilityofdeadlock?Canyoureintroducedeadlockbysimplyreducingthenumberofavailable
chopsticks?
A:RecommendedReading
GeneralC++
TheC++ProgrammingLanguage,3rdedition,byBjarneStroustrup(AddisonWesley1997).Tosome
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
431/458
5/3/2015
ThinkinginC++2ndedVolume2
degree,thegoalofthebookthatyourecurrentlyholdingistoallowyoutouseBjarnesbookasa
reference.Sincehisbookcontainsthedescriptionofthelanguagebytheauthorofthatlanguage,its
typicallytheplacewhereyoullgotoresolveanyuncertaintiesaboutwhatC++isorisntsupposedtodo.
Whenyougettheknackofthelanguageandarereadytogetserious,youllneedit.
C++Primer,3rdEdition,byStanleyLippmanandJoseeLajoie(AddisonWesley1998).Notthatmuchof
aprimeranymoreitsevolvedintoathickbookfilledwithlotsofdetail,andtheonethatIreachforalong
withStroustrupswhentryingtoresolveanissue.ThinkinginC++shouldprovideabasisforunderstanding
theC++PrimeraswellasStroustrupsbook.
AcceleratedC++,byAndrewKoenigandBarbaraMoo(AddisonWesley,2000).TakesyouthroughC++
byprogrammingtopicinsteadoflanguagefeature.Excellentintroductorybook.
TheC++StandardLibrary,byNicolaiJosuttis(AddisonWesley,1999).
ReadabletutorialandreferencefortheentireC++library,includingSTL.Assumesfamiliaritywith
languageconcepts.
STLTutorialandReferenceGuide,2ndEdition,byDavidR.Musseretal(AddisonWesley,2001).
GentlebutthoroughintroductiontotheconceptsunderlyingSTL.ContainsanSTLreferencemanual.
TheC++ANSI/ISOStandard.Thisisnotfree,unfortunately(Icertainlydidntgetpaidformytimeand
effortontheStandardsCommitteeinfact,itcostmealotofmoney).Butatleastyoucanbuythe
electronicforminPDFforonly$18athttp://www.ncits.org/cplusplus.htm.
Brucesbooks
Listedinorderofpublication.Notallthesearecurrentlyavailable.
ComputerInterfacingwithPascal&C,(SelfpublishedviatheEisysimprint,1988.Onlyavailablevia
www.MindView.net).AnintroductiontoelectronicsfrombackwhenCP/MwasstillkingandDOSwasan
upstart.Iusedhighlevellanguagesandoftentheparallelportofthecomputertodrivevariouselectronic
projects.AdaptedfrommycolumnsinthefirstandbestmagazineIwrotefor,MicroCornucopia.(To
paraphraseLarryOBrien,longtimeeditorofSoftwareDevelopmentMagazine:Thebestcomputer
magazineeverpublishedtheyevenhadplansforbuildingarobotinaflowerpot!)Alas,MicroCbecame
lostlongbeforetheInternetappeared.Creatingthisbookwasanextremelysatisfyingpublishing
experience.
UsingC++,(Osborne/McGrawHill,1989).OneofthefirstbooksoutonC++.Thisisoutofprintand
replacedbyitssecondedition,therenamedC++Inside&Out.
C++Inside&Out,(Osborne/McGrawHill,1993).Asnoted,thisisactuallythesecondeditionofUsing
C++.TheC++inthisbookisreasonablyaccurate,butitscirca1992andThinkinginC++isintendedto
replaceit.Youcanfindoutmoreaboutthisbookanddownloadthesourcecodeatwww.MindView.net.
ThinkinginC++,1stEdition,(PrenticeHall,1995).WinneroftheSoftwareDevelopmentMagazineJolt
Awardforbestbookof1995.
ThinkinginC++,2ndEdition,Volume1,(PrenticeHall,2000).Downloadablefromwww.MindView.net.
BlackBeltC++:theMastersCollection,BruceEckel,editor(M&TBooks,1994).Outofprint(often
availablethroughoutofprintservicesontheWeb).AcollectionofchaptersbyvariousC++luminaries
basedontheirpresentationsintheC++trackattheSoftwareDevelopmentConference,whichIchaired.
Thecoveronthisbookstimulatedmetogaincontroloverallfuturecoverdesigns.
ThinkinginJava,1stEdition,(PrenticeHall,1998).ThefirsteditionofthisbookwontheSoftware
DevelopmentMagazineProductivityAward,theJavaDevelopersJournalEditorsChoiceAward,andthe
JavaWorldReadersChoiceAwardforbestbook.OntheCDROMinthebackofthisbook,and
downloadablefromwww.MindView.net.
ThinkinginJava,2ndEdition,(PrenticeHall,2000).ThiseditionwontheJavaWorldEditorsChoice
Awardforbestbook.OntheCDROMinthebackofthisbook,anddownloadablefromwww.MindView.net.
ThinkinginJava,3rdEdition,(PrenticeHall,2002).ThiseditionwontheSoftwareDevelopment
MagazineJoltAwardforbestbookof2002,andtheJavaDevelopersJournalEditorsChoiceAward.The
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
432/458
5/3/2015
ThinkinginC++2ndedVolume2
newCDROMinthebackofthisbooknowincludesthefirstsevenlecturesfromthe2ndeditionofthe
HandsOnJavaCDROM.
TheHandsOnJavaCDROM,3rdedition(MindView,2004).Over15hoursofBruceslecturesandslides
coveringthebasicsoftheJavalanguage,basedonThinkinginJava,3rdEdition.Availableonlyat
www.MindView.net.
Chucksbooks
C&C++CodeCapsules,byChuckAllison(PrenticeHall,1998).AninthetrenchesguideforpracticalC
andC++programming.Thoroughlycoversthe1998ISOC++standard,especiallylibraryfeatures,and
servesasabridgetomoreadvancedtopics.BasedonChuckswellknowncolumnintheC/C++Users
Journal.
ThinkinginC:FoundationsforJava&C++,byChuckAllison(notabook,butaMindView,Inc.
SeminaronCDROM,1999,bundledwithThinkinginJavaandThinkinginC++,Volume1).Amultimedia
courseincludinglecturesandslidesinthefoundationsoftheCLanguage,toprepareyoutolearnJavaor
C++.ThisisnotanexhaustivecourseinConlythenecessitiesformovingontotheotherlanguagesare
included.AnextrasectioncoveringfeaturesfortheC++programmerisincluded.Prerequisite:experience
withahighlevelprogramminglanguage,suchasPascal,BASIC,FORTRAN,orLISP.
IndepthC++
Booksthatgomoredeeplyintotopicsofthelanguage,andhelpyouavoidthetypicalpitfallsinherentin
developingC++programs.
LargeScaleC++SoftwareDesign,byJohnLakos(AddisonWesley,1996).Motivatesandpresentsin
thetrenchestechniquesforlargeC++projects.
EffectiveC++,2ndEdition,byScottMeyers(AddisonWesley,1997).
ClassicbookoftechniquestoimproveC++designs.Codifiesmanyofthethingsthatprogrammershaveto
learnthehardway.
MoreEffectiveC++,byScottMeyers(AddisonWesley,1995)
ContinuationofEffectiveC++(above).AnotherC++classic.
EffectiveSTL,byScottMeyers(AddisonWesley,2001).Extremelypractical,indepthcoverageofhowto
usetheSTL.Containsexpertadvicefoundnowhereelse.
GenericProgrammingandtheSTL,byMattAustern(AddisonWesley,1998).Explorestheconceptual
underpinningsofthedesignoftheSTL.Heavyontheory,butimpartsavisionarylookintothedesignof
genericlibraries.
ExceptionalC++,byHerbSutter(AddisonWesley,2000).Leadsthereaderthroughaprogressionof
problemsandtheirsolution.GiveseasytorememberadviceforsoliddesignofmodernC++programs.
MoreExceptionalC++,byHerbSutter(AddisonWesley,2001).
ContinuationofExceptionalC++(above).
C++FAQs,2ndEdition,byMarshallCline,GregLomow,andMikeGirou
(AddisonWesley,1998).NicelystructuredcompendiumofcommonC++questionsandtheiranswers.
Coversabroadrangeoftopics,frombeginnertoadvanced.
C++Gotchas,byStephenDewhurst(AddisonWesley,2002).Contemporarycatalogofeasytodiscover
buthardtoremedyC++quirksbyawidelyrenownedrecognizedC++expert.
C++Templates,TheCompleteGuide,byDaveedVandevoordeandNicolaiM.Josuttis(AddisonWesley,
2002).Thefirstandonlybookdevotedcompletelytotemplates.Thedefinitivereference.
StandardC++iostreamsandLocales,byAngelikaLangerandKlausKreft(AddisonWesley,2000).The
mostindepthcoverageofiostreamsavailable.Plumbsthedepthsofstreamsimplementationandidiomatic
use.Ahandyreferenceaswellastutorial.
Design&EvolutionofC++,byBjarneStroustrup(AddisonWesley,1994).Tracesthecompletehistory
ofC++,documentingthedesigndecisionsthatweremadealongtheway.IfyouwanttoknowwhyC++is
thewayitis,thisisthebookwiththeanswers,writtenbythedesignerofthelanguage.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
433/458
5/3/2015
ThinkinginC++2ndedVolume2
ModernC++Design,byAndreiAlexandrescu(AddisonWesley,2001).Thestandardtextonpolicybased
designinC++.Filledwithpractical,advancedusesoftemplates.
ParallelandDistributedProgrammingUsingC++,CameronHughesandTraceyHughes(Addison
Wesley,2004).Thoroughandreadablecoverageofallaspectsofconcurrency,includingbasicconcepts,
threads,andmultiprocessing.Forbeginnersandexpertsalike.
GenerativeProgramming,byKrzysztofCzarneckiandUlrichEisencecker,(AddisonWesley,2000).
GroundbreakingbookonhighlyadvancedC++techniques.Takessoftwareautomationtothenextlevel.
MultiParadigmDesignforC++,byJamesO.Coplien(AddisonWesley,1998).Advancedtextshowing
howtoharmonizetheuseofprocedural,objectoriented,andgenericprogrammingforeffectiveC++
designs.
DesignPatterns
DesignPatterns,byErichGammaet.al.(AddisonWesley,1995).
Therevolutionarybookthatintroduceddesignpatternstotheindustry.Catalogsaselectionofdesign
patternswithmotivationandexamples(usingC++andalittleSmallTalk).
PatternOrientedSystemArchitecture,Volume1:ASystemofPatterns,byFrankBuschmannetal
(JohnWiley&Son,1996).
Anotherlookatdesignpatternsinpractice.Introducesnewdesignpatterns.
B:Etc
ThisappendixcontainsfilesthatarerequiredtobuildtheexamplesinVolume2.
//::require.h
//Testforerrorconditionsinprograms.
#ifndefREQUIRE_H
#defineREQUIRE_H
#include<cstdio>
#include<cstdlib>
#include<fstream>
inlinevoidrequire(boolrequirement,
constchar*msg="Requirementfailed"){
//Local"usingnamespacestd"foroldcompilers:
usingnamespacestd
if(!requirement){
fputs(msg,stderr)
fputs("\n",stderr)
exit(EXIT_FAILURE)
}
}
inlinevoidrequireArgs(intargc,intargs,
constchar*msg="Mustuse%darguments"){
usingnamespacestd
if(argc!=args+1){
fprintf(stderr,msg,args)
fputs("\n",stderr)
exit(EXIT_FAILURE)
}
}
inlinevoidrequireMinArgs(intargc,intminArgs,
constchar*msg="Mustuseatleast%darguments"){
usingnamespacestd
if(argc<minArgs+1){
fprintf(stderr,msg,minArgs)
fputs("\n",stderr)
exit(EXIT_FAILURE)
}
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
434/458
5/3/2015
ThinkinginC++2ndedVolume2
inlinevoidassure(std::ifstream&in,
constchar*filename=""){
usingnamespacestd
if(!in){
fprintf(stderr,"Couldnotopenfile%s\n",filename)
exit(EXIT_FAILURE)
}
}
inlinevoidassure(std::ofstream&in,
constchar*filename=""){
usingnamespacestd
if(!in){
fprintf(stderr,"Couldnotopenfile%s\n",filename)
exit(EXIT_FAILURE)
}
}
inlinevoidassure(std::fstream&in,
constchar*filename=""){
usingnamespacestd
if(!in){
fprintf(stderr,"Couldnotopenfile%s\n",filename)
exit(EXIT_FAILURE)
}
}
#endif//REQUIRE_H///:~
//:C0B:Dummy.cpp
//Togivethemakefileatleast
//onetargetforthisdirectory.
intmain(){}///:~
TheDateclassfiles:
//:C02:Date.h
#ifndefDATE_H
#defineDATE_H
#include<string>
#include<stdexcept>
#include<iosfwd>
classDate{
intyear,month,day
intcompare(constDate&)const
staticintdaysInPrevMonth(intyear,intmon)
public:
//Aclassfordatecalculations
structDuration{
intyears,months,days
Duration(inty,intm,intd)
:years(y),months(m),days(d){}
}
//Anexceptionclass
structDateError:publicstd::logic_error{
DateError(conststd::string&msg="")
:std::logic_error(msg){}
}
Date()
Date(int,int,int)throw(DateError)
Date(conststd::string&)throw(DateError)
intgetYear()const
intgetMonth()const
intgetDay()const
std::stringtoString()const
friendDurationduration(constDate&,constDate&)
friendbooloperator<(constDate&,constDate&)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
435/458
5/3/2015
ThinkinginC++2ndedVolume2
friendbooloperator<=(constDate&,constDate&)
friendbooloperator>(constDate&,constDate&)
friendbooloperator>=(constDate&,constDate&)
friendbooloperator==(constDate&,constDate&)
friendbooloperator!=(constDate&,constDate&)
friendstd::ostream&operator<<(std::ostream&,
constDate&)
friendstd::istream&operator>>(std::istream&,Date&)
}
#endif//DATE_H///:~
//:C02:Date.cpp{O}
#include"Date.h"
#include<iostream>
#include<sstream>
#include<cstdlib>
#include<string>
#include<algorithm>//Forswap()
#include<ctime>
#include<cassert>
#include<iomanip>
usingnamespacestd
namespace{
constintdaysInMonth[][13]={
{0,31,28,31,30,31,30,31,31,30,31,30,31},
{0,31,29,31,30,31,30,31,31,30,31,30,31}
}
inlineboolisleap(inty){
returny%4==0&&y%100!=0||y%400==0
}
}
Date::Date(){
//Getcurrentdate
time_ttval=time(0)
structtm*now=localtime(&tval)
year=now>tm_year+1900
month=now>tm_mon+1
day=now>tm_mday
}
Date::Date(intyr,intmon,intdy)throw(Date::DateError){
if(!(1<=mon&&mon<=12))
throwDateError("BadmonthinDatector")
if(!(1<=dy&&dy<=daysInMonth[isleap(year)][mon]))
throwDateError("BaddayinDatector")
year=yr
month=mon
day=dy
}
Date::Date(conststd::string&s)throw(Date::DateError){
//AssumeYYYYMMDDformat
if(!(s.size()==8))
throwDateError("BadstringinDatector")
for(intn=8n>=0)
if(!isdigit(s[n]))
throwDateError("BadstringinDatector")
stringbuf=s.substr(0,4)
year=atoi(buf.c_str())
buf=s.substr(4,2)
month=atoi(buf.c_str())
buf=s.substr(6,2)
day=atoi(buf.c_str())
if(!(1<=month&&month<=12))
throwDateError("BadmonthinDatector")
if(!(1<=day&&day<=
daysInMonth[isleap(year)][month]))
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
436/458
5/3/2015
ThinkinginC++2ndedVolume2
throwDateError("BaddayinDatector")
}
intDate::getYear()const{returnyear}
intDate::getMonth()const{returnmonth}
intDate::getDay()const{returnday}
stringDate::toString()const{
ostringstreamos
os.fill('0')
os<<setw(4)<<year
<<setw(2)<<month
<<setw(2)<<day
returnos.str()
}
intDate::compare(constDate&d2)const{
intresult=yeard2.year
if(result==0){
result=monthd2.month
if(result==0)
result=dayd2.day
}
returnresult
}
intDate::daysInPrevMonth(intyear,intmonth){
if(month==1){
year
month=12
}
else
month
returndaysInMonth[isleap(year)][month]
}
booloperator<(constDate&d1,constDate&d2){
returnd1.compare(d2)<0
}
booloperator<=(constDate&d1,constDate&d2){
returnd1<d2||d1==d2
}
booloperator>(constDate&d1,constDate&d2){
return!(d1<d2)&&!(d1==d2)
}
booloperator>=(constDate&d1,constDate&d2){
return!(d1<d2)
}
booloperator==(constDate&d1,constDate&d2){
returnd1.compare(d2)==0
}
booloperator!=(constDate&d1,constDate&d2){
return!(d1==d2)
}
Date::Duration
duration(constDate&date1,constDate&date2){
inty1=date1.year
inty2=date2.year
intm1=date1.month
intm2=date2.month
intd1=date1.day
intd2=date2.day
//Computethecompare
intorder=date1.compare(date2)
if(order==0)
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
437/458
5/3/2015
ThinkinginC++2ndedVolume2
returnDate::Duration(0,0,0)
elseif(order>0){
//Makedate1precededate2locally
usingstd::swap
swap(y1,y2)
swap(m1,m2)
swap(d1,d2)
}
intyears=y2y1
intmonths=m2m1
intdays=d2d1
assert(years>0||
years==0&&months>0||
years==0&&months==0&&days>0)
//Dotheobviouscorrections(mustadjustdays
//beforemonths!)Thisisaloopincasethe
//previousmonthisFebruary,anddays<28.
intlastMonth=m2
intlastYear=y2
while(days<0){
//Borrowfrommonth
assert(months>0)
days+=Date::daysInPrevMonth(
lastYear,lastMonth)
months
}
if(months<0){
//Borrowfromyear
assert(years>0)
months+=12
years
}
returnDate::Duration(years,months,days)
}
ostream&operator<<(ostream&os,constDate&d){
charfillc=os.fill('0')
os<<setw(2)<<d.getMonth()<<
<<setw(2)<<d.getDay()<<
<<setw(4)<<setfill(fillc)<<d.getYear()
returnos
}
istream&operator>>(istream&is,Date&d){
is>>d.month
chardash
is>>dash
if(dash!='')
is.setstate(ios::failbit)
is>>d.day
is>>dash
if(dash!='')
is.setstate(ios::failbit)
is>>d.year
returnis
}///:~
Thefiletest.txtusedinChapter6:
//:C06:Test.txt
fafdAGfdFaAFhfAdffaa
///:~
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
438/458
5/3/2015
ThinkinginC++2ndedVolume2
Index
<
<cctype>252
<cstdlib>215
<ctime>212
<exception>38
<fstream>169
<functional>338
<iomanip>196
<iosfwd>163
<limits>181,203,285
<memory>35
<sstream>179
<stdexcept>38
<typeinfo>557
A
abort()27
AbstractFactorydesignpattern651
abstraction,inprogramdesign614
accumulatealgorithm413
activationrecordinstance58
adaptablefunctionobject341
Adapterdesignpattern636
adaptor:container433,487functionobject338functionpointer351iterator487
adjacent_differencealgorithm415
adjacent_findalgorithm378
aggregation,designpatterns616
Alexandrescu,Andrei294,305
algorithm:accumulate413adjacent_difference415adjacent_find378applicators405binary_search
395complexity333copy326,365copy_backward372count370count_if334,371counting370
creatingyourown419equal327,385equal_range396fill369fill_n369fillingandgenerating368find
334,377find_end379find_first_of378find_if378for_each355,405generalutilities417generate
369generate_n369generic325heapoperations403includes400inner_product414inplace_merge
399iter_swap419,457lexicographical_compare385lower_bound395make_heap404manipulating
sequences372max419max_element380merge399merging398min418min_element379
mismatch386next_permutation373nth_element394numeric413ordering393partial_sort394
partial_sort_copy394partial_sum414partition374pop_heap404predicate329prev_permutation
373push_heap404random_shuffle374rangeofsequencein326remove389remove_copy389
remove_copy_if329,339,350,390remove_if389removingelements389replace380replace_copy
380replace_copy_if330,380replace_if330,380reverse372reverse_copy372rotate373
rotate_copy373search379search_n379searchingandreplacing377setoperations400set_difference
401set_intersection401set_symmetric_difference402set_union401sort366,393sort_heap
404sorting393stable_partition374stable_sort366,393swap419swap_ranges373transform
347,349,355,405unique390unique_copy390upper_bound395utilities417
ANSI/ISOC++Committee9
applicatoralgorithms405
applicator,iostreamsmanipulator200
applyingafunctiontoacontainer255
argument_type342
argumentdependentlookup274,278disabling275
assertmacro66
assertion66sideeffectsinan67
Assignable337
associativecontainer433,513
atof()181
atoi()181
atomicoperation732
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
439/458
5/3/2015
ThinkinginC++2ndedVolume2
auto_ptr35notforcontainers437
automatedtesting71
automatictypeconversion,andexceptionhandling23
B
back_insert_iterator448,482
back_inserter()328,370,372,418,448
bad_castexceptionclass40,557
bad_exceptionclass44
bad_typeidexceptionclass40,559
badbit165
basic_istream158,216
basic_ostream158,217
basic_string134,217,241
Becker,Pete11
before(),RTTIfunction559
behavioraldesignpatterns616
bidirectionaliterator446
BidirectionalIterator364
binaryfiles172,214
binaryfunction337
binarypredicate337
binarysearch63
binary_function342,353first_argument_type342result_type342second_argument_type342
binary_negatefunctionobject341
binary_searchalgorithm395
bind1stfunctionobjectadaptor339
bind2ndfunctionobjectadaptor338,350,371
binder1stfunctionobject339
binder2ndfunctionobject339
bitset229,506,540to_string()241
blocking,andthreads734
bookerrors,reporting10
Bright,Walter8,11
broadcast(),threading734,742,757
buffering,stream173
Builderdesignpattern660
busywait,threading732,743
C
cancel(),ZThreadlibraryfunction717
Cancelable,ZThreadlibraryclass717
cast:downcast551dynamic_cast555runtime551runtimetypeidentification,castingtointermediatelevels560
catch20catchinganyexception25
cerr158
cfront574
ChainofResponsibilitydesignpattern642
chaining,iniostreams159
change,vectorofchange614
char_traits217,241,287
charactertraits217compare()217
cin158
class:hierarchiesandexceptionhandling24invariant69maintaininglibrarysource204wrapping151
classtemplate:partialordering263partialspecialization263
cleaningupthestackduringexceptionhandling28
clear()166,175
close()168
codebloat,oftemplates268
codeinvariant63
cohesion49
CollectingParameterdesignpattern(idiom)618
commandline,interface162
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
440/458
5/3/2015
ThinkinginC++2ndedVolume2
Commandpattern626decoupling628
Committee,ANSI/ISOC++9
compilation,oftemplates315
compiletime:assertions304errorchecking155looping299selection303
complexityofalgorithms333
composenonstandardfunctionobjectadaptor360
composition,anddesignpatterns614,615
concurrency691blocking734Commandpattern628whentouseit771
ConcurrentExecutor(Concurrency)704
Conditionclass,threading742
consoleI/O162
constructor:defaultconstructorsynthesizedbythecompiler620designpatterns616exceptionhandling29,30,57
failing57orderofconstructoranddestructorcalls562privateconstructor620protected581simulatingvirtual
constructors654virtualfunctionsinsideconstructors654
container429adaptor433,487associative433,513bitset506,540cleaningup437,534combiningSTL
containers530creatingcustom536deque434,465duplicatekeys523extendedSTLcontainers440list
434,471map513,521multimap513,523multiplemembershipproblem438multiset513,527ofpointers
436priority_queue496queue491reversible445sequence433sequenceoperations454set479,513
stack487valarray540valuebased434vector434,457vector<bool>506,511
contract,designby68
conversion,automatictypeconversionsandexceptionhandling23
cooperationbetweenthreads741
Coplien,James296,655
copyalgorithm326,365
copy_backwardalgorithm372
copyonwrite634
countalgorithm370
count_ifalgorithm334,371
CountedPtr,referencecountingtemplateinZThreadlibrary(Concurrency)714
countingalgorithms370
cout158
covariance,ofexceptionspecifications47
Crahen,Eric11,694
creationaldesignpatterns615
criticalsection,inthreadprogramming719
curiouslyrecurringtemplatepattern294,624
Cygwin,andZThreads696
Czarnecki,Krysztof300
D
datalogger211
deadthread734
deadlock720,764conditionsfor769
debugging87
dec187
declaration,forward163
defaultconstructor:synthesizedbythecompiler620
dependentbaseclass278
dependentname274
deque434,465
design:abstractioninprogramdesign614cohesion49decisions66exceptionneutral52exceptionsafe48
designbycontract68
designpatterns613AbstractFactory651Adapter636aggregation616behavioral616Builder660Chainof
Responsibility642CollectingParameteridiom618Command626constructors616creational615destructors
616DoubleDispatching679FactoryMethod581,645Messengeridiom617MultipleDispatching679Observer
667Proxy632simulatingvirtualconstructors654Singleton460,619State634Strategy640structural
615TemplateMethod639vectorofchange614Visitor683
destructor659designpatterns616exceptionhandling28,57explicitcall453orderofconstructoranddestructor
calls562virtual581
diamondinheritance588
difference_type370
diningphilosophers,threading764
dispatching:DoubleDispatchingdesignpattern679MultipleDispatchingdesignpattern679
distance()417
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
441/458
5/3/2015
ThinkinginC++2ndedVolume2
dividesfunctionobject341
documentation,library101
documentviewarchitecture667
domain_errorexceptionclass40
dominance601
DoubleDispatchingdesignpattern653,679
downcast551
dynamictype,ofanobject557
dynamic_cast555castingtointermediatelevels560differencebetweendynamic_castandtypeid,runtimetype
identification561forpolymorphictypes556
E
effectors201
efficiency:runtimetypeidentification565threadsand693
Eisenecker,Ulrich300
ellipses,withexceptionhandling25
endl195
envelope,andletterclasses655
eofbit166
epsilon()181
equalalgorithm327,385
equal_rangealgorithm396
equal_tofunctionobject339,341
EqualityComparable337
errno16
error:handling15handling,inC16recovery15reportingerrorsinbook10
eventdrivenprogramming,andtheCommandpattern628
exceptionclass38what()38
exceptionhandling15asynchronousevents53atomicallocationsforsafety32automatictypeconversions23
bad_castexceptionclass40,557bad_exceptionclass44bad_typeidexceptionclass40,559catchingan
exception20catchinganyexception25,26catchingbyreference23catchingviaaccessiblebase25class
hierarchies24cleaningupthestackduringathrow28constructors29,30,57destructors28,36,57
domain_errorexceptionclass40ellipses25exceptionclass38exceptionclass,what()38exceptionhandler
20exceptionhierarchies56exceptionmatching23exceptionneutral52exceptionsafety48exception
specifications40exceptiontype39incompleteobjects29inheritance24invalid_argumentexceptionclass
40length_errorexceptionclass40logic_errorclass38memoryleaks29multipleinheritance56naked
pointers30objectslicingand23out_of_rangeexceptionclass40overheadof58programmingguidelines52
references34,56resourcemanagement30rethrowinganexception26,52runtime_errorclass38
set_terminate()27set_unexpected()41specifications,andinheritance46specifications,covarianceof47
specifications,whennottouse47stackunwinding19StandardC++libraryexceptions38terminate()44
terminationvs.resumption22testing79throwing&catchingpointers57throwinganexception18,19typical
usesofexceptions54uncaughtexceptions26,28unexpected()41whentoavoid52zerocostmodel60
ZThreads(Concurrency)708
exceptionspecifications40covarianceof47inheritance46whennottouse47
exclusion,mutual,inthreads719
Executors,ZThread(Concurrency)702
explicitinstantiation,oftemplates316
exportkeyword319
exportedtemplates319
expressiontemplates308
extractor,stream158
ExtremeProgramming(XP)71,615
F
facet:locale220
FactoryMethoddesignpattern581,645
fail()175
failbit160,166
Fibonacci298,636
filestreams,close()168
file,iostreams156,162
FILE,stdio152
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
442/458
5/3/2015
ThinkinginC++2ndedVolume2
fillalgorithm369
fill_nalgorithm369
fillingandgeneratingalgorithms368
findalgorithm334,377
find_endalgorithm379
find_first_ofalgorithm378
find_ifalgorithm378
first_argument_type342
flock(),andSynchronousExecutor(Concurrency)705
flush,iostreams195
for_eachalgorithm355,405
formatfields188
formatflags:dec187hex187iosshowbase187showpoint187showpos187skipws187unitbuf
187uppercase187oct187
formattedI/O186
formatting:incore179manipulators,iostreams194outputstream186
forwarddeclaration163
forwarditerator446
forward_iterator_tag447
ForwardIterator363
framework,unittest75
friendtemplate284
friends,oftemplates279
front_insert_iterator448
front_inserter()418,448
fseek()176
fstream168simultaneousinputandoutput172
function:applyingafunctiontoacontainer255binary337unary337
functionobject335,626adaptable341adaptor338binary_negate341bind1stadaptor339bind2ndadaptor
338,350binder1st339binder2nd339classification336divides341equal_to339,341greater338,
341,371greater_equal341less341less_equal341logical_and341logical_not341logical_or
341minus340modulus341multiplies341negate341not_equal_to341not1adaptor339plus
340unary_negate341
functionobjectadaptor338bind2nd371not1352pointer_to_binary_function353
pointer_to_unary_function352
functionpointeradaptor351ptr_fun351
functiontemplate245addressof251explicitqualification246overloading249partialorderingof259
specialization261typedeductionparametersin245
functioncalloperator335
functionleveltryblocks36
functor626seefunctionobject335
G
GangofFour(GoF)613
generalutilityalgorithms417
generatealgorithm369
generate_nalgorithm369
generator337,369
genericalgorithms325
getpointer177
get()170overloadedversions165
getline()164,171
getline(),forstrings129
getPriority()711
GoF,GangofFour613
goodbit166
greaterfunctionobject338,341,371
greater_equalfunctionobject341
Guardtemplate,ZThread(concurrency)721
H
handler,exception20
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
443/458
5/3/2015
ThinkinginC++2ndedVolume2
handshaking,betweenconcurrenttasks742
hash_mapnonstandardcontainer539
hash_multimapnonstandardcontainer539
hash_multisetnonstandardcontainer539
hash_setnonstandardcontainer539
heapoperations403
hex187
hierarchy,objectbased573
I
I/O:console162interactive162raw165threads,blocking737
i18n,seeinternationalization216
ifstream156,168,174
ignore()170
imbue()220
implementationinheritance579
includesalgorithm400
inclusionmodel,oftemplatecompilation315
incompletetype163
incoreformatting179
inheritance:designpatterns614diamond588hierarchies573implementation579interface575
inheritance,multiple573,673avoiding603dominance601namelookup599runtimetypeidentification560,
563,570
initialization:controllinginitializationorder621lazy620object596ResourceAcquisitionIsInitialization(RAII)32,
36,582zeroinitialization522
innerclassidiom,adaptedfromJava671
inner_productalgorithm414
inplace_mergealgorithm399
inputiterator446
input_iterator_tag447
InputIterator363
insert()448
insert_iterator372,448,482
inserter()372,418,448
inserter,stream158
instantiation,template260
interactiveI/O162
interface:class576commandline162extendingan603inheritance575repairinganinterfacewithmultiple
inheritance603responsiveuser700
internationalization216
interrupt(),threading735
interruptedstatus,threading739
Interrupted_Exception,threading739
invalid_argumentexceptionclass40
invalidation,iterator463
invariant:class69code63loop64
ios:app172ate172basefield188beg176binary172,214cur176end176failbit160fill()
190in171out172precision()190showbase187showpoint187showpos187skipws187
trunc172unitbuf187uppercase187width()190
ios_base157
iostate168
iostreams156applicator200automatic189badbit165binarymode172,214buffering173clearfunction
166,175decmanipulator195endlmanipulator195eofbit166errors165exceptions167exceptions
function167extractor158failfunction175failbit166failureexceptiontype167files162fill()190
fixed196flags()186flush195fmtflagstype186formatfields188formatflags186formatting186
fseek()176get()170getline()171goodbit166hexmanipulator195ignore()170imbue()
220inserter158internal196ios::basefield188ios::dec189ios::fixed189ios::hex189
ios::internal190ios::left190ios::oct189ios::right190ios::scientific189iostatetype168left
196locales216manipulators194manipulators,creating199narrow216narrowfunction218noshowbase
195noshowpoint196noshowpos195noskipws196nouppercase195octmanipulator195open
modes171operator<<158operator>>158positioning175precision()190,213resetiosflags
manipulator196right196scientific196seekingin175setbasemanipulator197setf()187,188,213
setfillmanipulator197setiosflagsmanipulator196setprecisionmanipulator197setstatefunction166setw
manipulator197,213showbase195showpoint196showpos195skipws196smaniptype201string
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
444/458
5/3/2015
ThinkinginC++2ndedVolume2
I/Owith179textmode172threads,collidingoutput727unsetf()188uppercase195wide216widen
function218width()190write()213wsmanipulator195
istream156get()164getline()164read()165seekg()176tellg()176
istream_iterator333,446,450
istreambuf_iterator446,451,481
istringstream156,179
iter_swapalgorithm419,457
iterator429,615adaptingaclasstoproduce637adaptor487bidirectional446categories446forward446
input446invalidation463istream333ostream332output446pasttheend443randomaccess446
reverse445stream331streamiterator450tag447traits366
iterator_traits366
J
Josuttis,Nico101
K
King,Jamie10
Koenig,Andrew274
Kreft,Klaus314,780
L
Lajoie,Josee60
Langer,Angelika314,780
lazyinitialization620,634
length_errorexceptionclass40
lessfunctionobject341
less_equalfunctionobject341
LessThanComparable337
letter,envelopeandletterclasses655
lexicographical_comparealgorithm385
library:documentation101maintainingclasssource204
lineinput162
linearsearch377
Linux,andZThreads696
list434,471merge()474remove()474reverse()472sort()472unique()474vs.set476
locale216,218collatecategory219ctypecategory219facet220iostreams216messagescategory219
monetarycategory219money_getfacet220money_punctfacet220money_putfacet220numeric
category219timecategory219time_getfacet220time_putfacet220
localtime()213
logic_errorclass38
logical_andfunctionobject341
logical_notfunctionobject341
logical_orfunctionobject341
longjmp()16
loop:invariant64unrolling301
lower_boundalgorithm395
M
machineepsilon181
maintainingclasslibrarysource204
make_heapalgorithm404,499
make_pair()417
manipulatingsequences372
manipulators160creating199iostreamsformatting194witharguments196
map521keysandvalues521
maxalgorithm419
max_elementalgorithm380
mem_funmemberpointeradaptor355
mem_fun_refmemberpointeradaptor355
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
445/458
5/3/2015
ThinkinginC++2ndedVolume2
membertemplates242vs.virtual245
memoryleaks90
memorymanagement,andthreads711
mergealgorithm399
mergingalgorithms398
Messengerdesignpattern(idiom)617
metaprogramming297compiletimeassertions304compiletimelooping299compiletimeselection303loop
unrolling301Turingcompletenessof298
Meyer,Bertrand68
Meyers,Scott60,623
minalgorithm418
min_elementalgorithm379
minusfunctionobject340
mismatchalgorithm386
mixin:class579parameterized583
modelviewcontroller(MVC)667
modulusfunctionobject341
money_get220
money_punct220
money_put220
multimap523
MultipleDispatchingdesignpattern679
multipleinheritance573,673avoiding603dominance601duplicatesubobjects585exceptionhandling56name
lookup599repairinganinterface603runtimetypeidentification560,563,570
multipliesfunctionobject341
multiprocessormachine,andthreading692
multiset527equal_range()529
multitasking691
multithreading691drawbacks771ZThreadlibraryforC++694
mutex:simplifyingwiththeGuardtemplate721threading742ZThreadFastMutex731
mutualexclusion,inthreads719
Myers,Nathan11,251,285,452,481,482
N
nakedpointers,andexceptionhandling30
namelookup,andmultipleinheritance599
name(),RTTIfunction559
narrowstreams216
narrow()218
negatefunctionobject341
new,placement91
newline,differencesbetweenDOSandUnix172
next_permutationalgorithm373
not_equal_tofunctionobject341
not1functionobjectadaptor339,352
nth_elementalgorithm394
numericalgorithms413
numeric_limits203,285
O
object:initialization596objectbasedhierarchy573slicing,andexceptionhandling23
Observable668
Observerdesignpattern667
oct187
ofstream156,168
onedefinitionrule622
openmodes,iostreams171
operatornew()90
operatorvoid*(),forstreams167
operator()229,335,339
operator++()234
optimization,throughput,withthreading692
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
446/458
5/3/2015
ThinkinginC++2ndedVolume2
order:controllinginitialization621ofconstructoranddestructorcalls562
ordering:algorithms393strictweak337
ostream156fill()160manipulators160seekp()176setfill()160setw()160tellp176write()
165
ostream_iterator332,365,446,451
ostreambuf_iterator446,451
ostringstream156,179str()182
out_of_rangeexceptionclass40
output:iterator446streamformatting186
output_iterator_tag447
OutputIterator363
overhead,exceptionhandling58
overloading,functiontemplate249
P
parameter,template227
parameterizedmixin583
Park,Nick257
partialordering:classtemplates263functiontemplates259
partialspecialization,template263
partial_sortalgorithm394
partial_sort_copyalgorithm394
partial_sumalgorithm414
partitionalgorithm374
pasttheenditerator443
patterns,designpatterns613
perror()16
philosophers,dining,andthreading764
placementnew91
Plauger,P.J.101
plusfunctionobject340
pointertomemberadaptor:mem_fun355mem_fun_ref355
pointer,smart437
pointer_to_binary_functionfunctionobjectadaptor353
pointer_to_unary_functionfunctionobjectadaptor352
policies291
policyclass293
polymorphism564
PoolExecutor(Concurrency)703
pop_heapalgorithm404,499
POSIXstandard145
postcondition68
precision()213
precondition68
predicate329binary337unary337
prev_permutationalgorithm373
printf()154errorcode15
priority,thread709
priority_queue496asaheap499pop()500
privateconstructor620
process,threadingand691
producerconsumer,threading747
programmingparadigms573
protectedconstructor581
Proxydesignpattern632
ptr_funfunctionpointeradaptor351
purevirtualfunction576
push_back()434,448,482
push_front()434,448
push_heapalgorithm404,499
putpointer176
Q
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
447/458
5/3/2015
ThinkinginC++2ndedVolume2
qualifiedname274,278
queue491
queues,thread,forproblemsolving750
quicksort366
R
racecondition717
RAII32,36,582
raise()16
rand()215
RAND_MAX215
random_shufflealgorithm374
randomaccessiterator446
RandomAccessIterator364
range,ofsequence326
rawbyteI/O165
raw_storage_iterator446,452
rbegin()445,448
rdbuf()174
read()165
refactoring70
referencecounting582,634ZThreads(Concurrency)712
references:bad_cast557exceptionhandling34,56
removealgorithm389
remove_copyalgorithm389
remove_copy_ifalgorithm329,339,350,390
remove_ifalgorithm389
removingelements,algorithm389
rend()445,448
reordering,stableandunstable366
replacealgorithm380
replace_copyalgorithm380
replace_copy_ifalgorithm330,380
replace_ifalgorithm330,380
reportingerrorsinbook10
requirements70
reserve()458
resize()456
ResourceAcquisitionIsInitialization(RAII)32,36,582
responsiveuserinterfaces700
result_type342
resumption,vs.termination,exceptionhandling22
rethrow,exception26,52
reversealgorithm372
reverse_copyalgorithm372
reverse_iterator445,448,487
reversiblecontainer445
ropenonstandardstringclass539
rotatealgorithm373
rotate_copyalgorithm373
Runnable696
runtimecast551
runtimestack228
runtimetypeidentification551castingtointermediatelevels560constandvolatileand558differencebetween
dynamic_castandtypeid561efficiency565mechanism&overhead570misuse564multipleinheritance
560,563,570templatesand562type_info570type_infoclass557type_info::before()559
type_info::name()559typeidoperator557voidpointers561VTABLE570whentouseit564
runtime_errorclass38
S
Saks,Dan282
Schwarz,Jerry201
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
448/458
5/3/2015
ThinkinginC++2ndedVolume2
searchalgorithm379
search_nalgorithm379
searchingandreplacingalgorithms377
second_argument_type342
seekg()176
seekinginiostreams175
seekp()176
separationmodel,oftemplatecompilation319
sequence:at()470container433convertingbetweensequences467deque465erase()457expandingwith
resize()456insert()457list471operations454operator[]471randomaccess470swap()457
swappingsequences477vector457
serialization:object215thread750
set479,513find()480operations400orderingof480STLsetclassexample432vs.list476
set_differencealgorithm401
set_intersectionalgorithm401
set_symmetric_differencealgorithm402
set_terminate()27
set_unexpected()41
set_unionalgorithm401
setf()187,213
setjmp()16
setPriority()711
setw()213
SieveofEratosthenes119
signal()16,53threading734,742
Singleton460,619implementedwithcuriouslyrecurringtemplatepattern624MeyersSingleton623ZThreads
library(concurrency)728
sleep(),threading707,734
slice,valarray542
slicing,objectslicingandexceptionhandling23
slistnonstandardcontainer539
Smalltalk573
smanip201
smartpointer437
softwarequality63
sortalgorithm366,393
sort_heapalgorithm404
sortingalgorithms393
specialization:functiontemplate261template260
specification,exception40
srand()214
stablereordering366
stable_partitionalgorithm374
stable_sortalgorithm366,393
stack487exceptionsafetyof489pop()489push()489top()489
stackframe58
stackunwinding19
StandardC9
StandardC++9concurrency694exceptiontypes38
Statedesignpattern634
stdio151
STLextensions538
Strategydesignpattern640
strcmp()217
stream156errors165iterator331,450outputformatting186state165
streambuf173get()174rdbuf()174
streampos176
strictweakordering337
StrictWeakOrdering374,403
string103append()110at()132c_str()131capacity()111caseinsensitivesearch120character
traits134compare()131concatenation117empty()356erase()126find()115
find_first_not_of()118find_first_of()118find_last_not_of()118find_last_of()118getline()
129indexingoperations133insert()110iterator108length()111memorymanagement110,114npos
member114operator!=129operator[]132operator+117operator+=117operator<129
operator<=129operator==129operator>129operator>=129referencecounted104relational
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
449/458
5/3/2015
ThinkinginC++2ndedVolume2
operators129replace()112reserve()111resize()111rfind()118size()111streamI/O156
substr()107swap()132transformingstringstotypedvalues181
stringstreams179
stringbuf183
stringizing,preprocessoroperator193
Stroustrup,Bjarne101
structtm213
structuraldesignpatterns615
subobject,duplicatesubobjectsinmultipleinheritance585
subtasks691
suite,test79
surrogate,indesignpatterns631
swapalgorithm419
swap_rangesalgorithm373
synchronization:(concurrency)exampleofproblemfromlackofsynchronization732blocking734thread719
Synchronization_Exception,ZThreadlibrary698,703
synchronized,threading,wrapperforanentireclass723
SynchronousExecutor(Concurrency)705
T
tag,iteratortagclasses447
task,definingforthreading696
tellg()176
tellp()176
template:argumentdependentlookupin274codebloat,preventing268compilation274compilationmodels315
compilation,twophase274curiouslyrecurringtemplatepattern294defaultarguments230dependentnamesin
274explicitinstantiation316export319expressiontemplates308friendtemplate284friends279function
245idioms285inclusioncompilationmodel315instantiation260keyword240member242member,and
virtualkeyword245metaprogramming297namelookupissues273namesin273nontypeparameters228
parameters227partialorderingofclasstemplates263partialorderingoffunctiontemplates259partial
specialization263policybaseddesign291qualifiednamesin274,278runtimetypeidentificationand562
separationcompilationmodel319specialization260templatetemplateparameters232traits285
TemplateMethoddesignpattern639
terminate()27,44uncaughtexceptions26
terminatingthreads735
terminationproblem,concurrency727
termination,vs.resumption,exceptionhandling22
test:automatedunittesting71Booleanexpressionsintesting72framework75suite79testfirstprogramming
71unit70
Testclass76
TestSuiteframework75
textprocessing103
thread691atomicoperation732blocked734broadcast()734,742,757busywait732,743Cancelable,
ZThreadlibraryclass717collidingoverresources,improperlyaccessingsharedresources715concurrency691
Conditionclassforwait()andsignal()742cooperation741deadstate734deadlock720,764deadlock,and
priorities709diningphilosophers764drawbacks771exampleofproblemfromlackofsynchronization732
getPriority()711handshakingbetweentasks742I/Oandthreads,blocking737interrupt()735interrupted
status739Interrupted_Exception739iostreamsandcollidingoutput727memorymanagement711multiple,
forproblemsolving741mutex,forhandshaking742mutex,simplifyingwiththeGuardtemplate721newstate
734orderoftaskshutdown717orderofthreadexecution708priority709producerconsumer747queuessolve
problems750racecondition717referencecounting712referencecountingwithCountedPtr714runnable
state734serialization750setPriority()711sharingresources711signal()734,742sleep()707,734
states734synchronization719synchronizationandblocking734synchronizedwrapperforanentireclass723
termination735terminationproblem727threadlocalstorage724threadsandefficiency693TQueue,solving
threadingproblemswith750wait()734,742whentousethreads771yield()706ZThreadFastMutex731
ThreadedExecutor(Concurrency)702
throughput,optimize692
throw19
throwinganexception18
time()214
time_get220
time_put220
tolower252
toupper252
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
450/458
5/3/2015
ThinkinginC++2ndedVolume2
TQueue,solvingthreadingproblemswith750
trace:file88macro87
traits285iterator366
transformalgorithm252,347,349,355,405
transformingcharacterstringstotypedvalues181
try20
tryblock20functionlevel36
type:automatictypeconversionsandexceptionhandling23deduction,offunctiontemplateparameters245incomplete
163runtimetypeidentification(RTTI)551
type_info:namefunction244structure570
type_infoclass557
type_info::before()559
type_info::name()559
typeidoperator244,557differencebetweendynamic_castandtypeid,runtimetypeidentification561
typename:keyword237typedef240versusclass240
typing,weak579
U
unaryfunction337
unarypredicate337
unary_composernonstandardfunctionobject360
unary_function342,352argument_type342result_type342
unary_negatefunctionobject341
uncaughtexceptions26
uncaught_exception()52
unexpected()41
Unicode216
uniquealgorithm390
unique_copyalgorithm390
unitbuffering188
unittest70
unstablereordering366
upcast603
upper_boundalgorithm395
Urlocker,Zack608
userinterface,responsive,withthreading692,700
utilityalgorithms417
V
valarray540slice542
value_type450
vanRossum,Guido773
Vandevoorde,Daveed308
vector457reserve()458
vectorofchange614
vector<bool>263,506,511
Veldhuizen,Todd308
virtual:baseclass563,589base,initializationof592destructor581functiontable654purevirtualfunctions576
simulatingvirtualconstructors654virtualfunctionsinsideconstructors654
Visitordesignpattern683
void561
VPTR654
VTABLE654runtimetypeidentification570
W
wait(),threading734,742
wchar_t216
wcscmp()217
weaktyping579
webservers,multiprocessor692
wide:character216stream216streamfunction,wcscmp()217
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
451/458
5/3/2015
ThinkinginC++2ndedVolume2
widen()218
WillHarris,Daniel11
wrapping,class151
write()165,213
ws195
X
XP,ExtremeProgramming71,615
Y
yield(),threading706
Z
zeroinitialization522
Zolman,Leor320
ZThread:Cancelableclass717Executors702installingthelibrary695multithreadinglibraryforC++694
[1]
YoumightbesurprisedwhenyouruntheexamplesomeC++compilershaveextendedlongjmp()tocleanup
objectsonthestack.Thisbehaviorisnotportable.
[2]
TheBASIClanguagehaslongsupportedalimitedformofresumptiveexceptionhandlingwithitsONERROR
facility.
[3]
Youmightalwayswanttospecifyexceptionobjectsbyconstreferenceinexceptionhandlers.(Itsraretomodify
andrethrowanexception.)However,wearenotdogmaticaboutthispractice.
[4]
Onlyunambiguous,accessiblebaseclassescancatchderivedexceptions.Thisruleminimizestheruntime
overheadneededtovalidateexceptions.Rememberthatexceptionsarecheckedatruntime,notatcompiletime,and
thereforetheextensiveinformationavailableatcompiletimeisnotavailableduringexceptionhandling.
[5]
Notethattheresanexceptionclasscalledstd::out_of_rangeintheC++StandardLibrary,intendedtobeused
insituationssuchasthis.
[6]
Formoredetailonauto_ptr,seeHerbSuttersarticleentitled,Usingauto_ptrEffectivelyintheOctober1999
issueoftheC/C++UsersJournal,pp.6367.
[7]
Ifyoureinterestedinamoreindepthanalysisofexceptionsafetyissues,thedefinitivereferenceisHerbSutters
ExceptionalC++,AddisonWesley,2000.
[8]
Thelibraryfunctionuncaught_exception()returnstrueinthemiddleofstackunwinding,sotechnicallyyou
cantestuncaught_exception()forfalseandletanexceptionescapefromadestructor.Weveneverseena
situationinwhichthisconstitutedgooddesign,however,soweonlymentionitinthisfootnote.
[9]
Somecompilersdothrowexceptionsinthesecases,buttheyusuallyprovideacompileroptiontodisablethis
(unusual)behavior.
[10]
CheckouttheBoostsmartpointertypesathttp://www.boost.org/libs/smart_ptr/index.htm.Someoftheseare
beingconsideredforinclusioninthenextrevisionofStandardC++.
[11]
Thisdependsonhowmuchreturncodecheckingyouwouldhavetoinsertifyouwerentusingexceptions.
[12]
Borlandenablesexceptionsbydefaulttodisableexceptionsusethe
xcompileroption.Microsoftdisablessupportbydefaulttoturniton,usetheGXoption.Withbothcompilersuse
thecoptiontocompileonly.
[13]
TheGNUC++compilerusesthezerocostmodelbydefault.MetrowerksCodeWarriorforC++alsohasan
optiontousethezerocostmodel.
[14]
ThankstoScottMeyersandJoseeLajoiefortheirinsightsonthezerocostmodel.Youcanfindmoreinformation
onhowexceptionsworkinJoseesexcellentarticle,ExceptionHandling:BehindtheScenes,C++Gems,SIGS,
1996.
[15]
HeinventedQuicksort,amongotherthings.
[16]
AsquotedinProgrammingLanguagePragmatics,byMichaelL.Scott,MorganKaufmann,2000.
Seehisbook,ObjectOrientedSoftwareConstruction,PrenticeHall,1994.
[17]
[18]
Thisisstillanassertionconceptually,butsincewedontwanttohaltexecution,theassert()macroisnot
appropriate.Java1.4,forexample,throwsanexceptionwhenanassertionfails.
[19]
Thereisanicephrasetohelprememberthisphenomenon:Requirenomorepromisenoless,firstcoinedin
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
452/458
5/3/2015
ThinkinginC++2ndedVolume2
C++FAQs,byMarshallClineandGregLomow(AddisonWesley,1994).Sincepreconditionscanweakeninderived
classes,wesaythattheyarecontravariant,and,conversely,postconditionsarecovariant(whichexplainswhywe
mentionedthecovarianceofexceptionspecificationsinChapter1).
[20]
ThissectionisbasedonChucksarticle,TheSimplestAutomatedUnitTestFrameworkThatCouldPossibly
Work,C/C++UsersJournal,Sept.2000.
[21]
AgoodbookonthissubjectisMartinFowlersRefactoring:ImprovingtheDesignofExistingCode(Addison
Wesley,2000).Seealsohttp://www.refactoring.com.RefactoringisacrucialpracticeofExtremeProgramming(XP).
[22]
SeeExtremeProgrammingExplained:EmbraceChangebyKentBeck,AddisonWesley1999.Lightweight
methodologiessuchasXPhavejoinedforcesintheAgileAlliance(seehttp://www.agilealliance.org/home).
[23]
OurDateclassisalsointernationalized,inthatitsupportswidecharactersets.Thisisintroducedattheendof
thenextchapter.
[24]
Seehttp://sourceforge.net/projects/cppunitformoreinformation.
[25]
ThisisakeyprincipleofExtremeProgramming.
[26]
RuntimeTypeIdentification,discussedinchapter9.Specifically,weusethename()memberfunctionofthe
typeinfoclass.IfyoureusingMicrosoftVisualC++,youneedtospecifythecompileoption/GR.Ifyoudont,youll
getanaccessviolationatruntime.
[27]
Inparticular,weusestringizing(viathe#preprocessingoperator)andthepredefinedmacros__FILE__and
__LINE__.Seethecodelaterinthechapter.
[28]
Batchfilesandshellscriptsworkwellforthis.TheSuiteclassisaC++basedwayoforganizingrelatedtests.
[29]
Ourkeytechnicalreviewer,PeteBeckerofDinkumware.Ltd.,broughttoourattentionthatitisillegaltouse
macrostoreplaceC++keywords.Histakeonthistechniquewasasfollows:Thisisadirtytrick.Dirtytricksare
sometimesnecessarytofigureoutwhycodeisntworking,soyoumaywanttokeepthisinyourtoolbox,butdont
shipanycodewithit.Caveatprogrammer.
[30]
ThankstoRegCharneyoftheC++StandardsCommitteeforsuggestingthistrick.
[31]
SomeofthematerialinthischapterwasoriginallycreatedbyNancyNicolaisen.
[32]
Itsdifficulttomakereferencecountingimplementationsthreadsafe.(SeeHerbSutter,MoreExceptionalC++,
pp.10414).SeeChapter10formoreonprogrammingwithmultiplethreads.
[33]
Itisanabbreviationfornoposition,andisthelargestvaluethatcanberepresentedbythestringallocators
size_type(std::size_tbydefault).
[34]
DiscussedindepthinChapter6.
[35]
Tokeeptheexpositionsimple,thisversiondoesnothandlenestedtags,suchascomments.
Itistemptingtousemathematicsheretofactoroutsomeofthesecallstoerase(),butsinceinsomecases
[36]
oneoftheoperandsisstring::npos(thelargestunsignedintegeravailable),integeroverflowoccursandwrecksthe
algorithm.
[37]
Forthesafetyreasonsmentioned,theC++StandardsCommitteeisconsideringaproposaltoredefine
string::operator[]tobehaveidenticallytostring::at()forC++0x.
[38]
Yourimplementationcandefineallthreetemplateargumentshere.Becausethelasttwotemplateparameters
havedefaultarguments,suchadeclarationisequivalenttowhatweshowhere.
[39]
BewarethatsomeversionsofMicrosoftWorderroneouslyreplacesinglequotecharacterswithanextendedASCII
characterwhenyousaveadocumentastext,whichcausesacompileerror.Wehavenoideawhythishappens.Just
replacethecharactermanuallywithanapostrophe.
[40]
POSIX,anIEEEstandard,standsforPortableOperatingSystemInterfaceandisageneralizationofmanyofthe
lowlevelsystemcallsfoundinUNIXsystems.
[41]
ExplainedindepthinChapter5.
[42]
Forthisreason,youcanwriteios::failbitinsteadofios_base::failbittosavetyping.
[43]
Itiscustomarytouseoperatorvoid*()inpreferencetooperatorbool()becausetheimplicitconversions
frombooltointmaycausesurprises,shouldyouincorrectlyplaceastreaminacontextwhereanintegerconversion
canbeapplied.Theoperatorvoid*()functionwillonlybecalledimplicitlyinthebodyofaBooleanexpression.
[44]
Anintegraltypeusedtoholdsinglebitflags.
[45]
AmoreindepthtreatmentofstreambuffersandstreamsingeneralcanbefoundinLanger&Krefts,Standard
C++iostreamsandLocales,AddisonWesley,1999.
[46]
Formoreinformationonmachineepsilonandfloatingpointcomputationingeneral,seeChucksarticle,The
StandardCLibrary,Part3,C/C++UsersJournal,March1995,availableatwww.freshsources.com/1995006a.htm.
[47]
Beforeputtingnlintoaheaderfile,makeitaninlinefunction.
[48]
JerrySchwarzisthedesignerofiostreams.
[49]
SeetheLanger&Kreftbookmentionedearlierformoredetailedinformation.
See,forexample,DinkumwaresAbridgedlibraryathttp://www.dinkumware.com.Thislibraryomitslocale
[50]
support.andexceptionsupportisoptional.
[51]
VandevoordeandJosuttis,C++Templates:TheCompleteGuide,AddisonWesley,2003.NotethatDaveed
sometimesappearsasDavid.
[52]
TheC++StandardsCommitteeisconsideringrelaxingtheonlywithinatemplateruleforthesedisambiguation
hints,andsomecompilersallowtheminnontemplatecodealready.
SeeStroustrup,TheC++ProgrammingLanguage,3 rdEdition,AddisonWesley,pp.335336.
[53]
[54]
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
453/458
5/3/2015
ThinkinginC++2ndedVolume2
[54]
Technically,comparingtwopointersthatarenotinsidethesamearrayisundefinedbehavior,buttodays
compilersdontcomplainaboutthis.Allthemorereasontodoitright.
[55]
WeareindebtedtoNathanMyersforthisexample.
[56]
Suchastypeinformationencodedinadecoratedname.
C++compilerscanintroducenamesanywheretheywant,however.Fortunately,mostdontdeclarenamesthey
[57]
dontneed.
[58]
Ifyoureinterestedinseeingtheproposal,itsCoreIssue352.
[59]
AreferencetotheBritishanimatedshortfilmsbyNickParkfeaturingWallaceandGromit.
[60]
Wediscussvector<bool>indepthinChapter7.
[61]
Insteadofthis>youcoulduseanyvalidqualification,suchasSortable::at()orvector<T>::at().Thepoint
isthatitmustbequalified.
[62]
SeealsotheexplanationaccompanyingPriorityQueue6.cppinChapter7.
[63]
Sincetheforwardingfunctionsareinline,nocodeforStack<void*>isgeneratedatall!
[64]
AlsocalledKoeniglookup,afterAndrewKoenig,whofirstproposedthetechniquetotheC++Standards
Committee.ADLappliesuniversally,whethertemplatesareinvolvedornot.
[65]
FromapresentationbyHerbSutter.
[66]
Anumberofcompilersusethisfrontend,includingComeauC++.
AlsobasedonanexamplebyHerbSutter.
[67]
[68]
InatalkgivenatTheC++Seminar,Portland,OR,September,2001.
[69]
Anothertemplateidiom,mixininheritance,iscoveredinChapter9.
[70]
Thefactthatchar_traits<>::compare()maycallstrcmp()inoneinstancevs.wcscmp()inanother,for
example,isimmaterialtothepointwemakehere:thefunctionperformedbycompare()isthesame.
[71]
ModernC++Design:GenericProgrammingandDesignPatternsApplied,AddisonWesley,2001.
[72]
C++Gems,editedbyStanLippman,SIGS,1996.
[73]
Thesearetechnicallycompiletimeconstants,soyoucouldarguethattheidentifiersshouldbealluppercase
letterstofollowtheusualform.Weleftthemlowercasedbecausetheyaresimulationsofvariables.
[74]
In1966BhmandJacopiniprovedthatanylanguagesupportingselectionandrepetition,alongwiththeability
touseanarbitrarynumberofvariables,isequivalenttoaTuringmachine,whichisbelievedcapableofexpressing
anyalgorithm.
[75]
CzarneckiandEisenecker,GenerativeProgramming:Methods,Tools,andApplications,AddisonWesley,2000,p.
417.
[76]
Thereisamuchbetterwaytocomputepowersofintegers:theRussianPeasantAlgorithm.
[77]
ModernC++Design,pp.2326.
[78]
Youarenotallowedtopassobjecttypes(otherthanbuiltins)toanellipsisparameterspecification,butsincewe
areonlyaskingforitssize(acompiletimeoperation),theexpressionisneveractuallyevaluatedatruntime.
[79]
AreprintofToddsoriginalarticlecanbefoundinLippman,C++Gems,SIGS,1996.Itshouldalsobenotedthat
besidesretainingmathematicalnotationandoptimizedcode,expressiontemplatesalsoallowforC++librariesto
incorporateparadigmsandmechanismsfoundinotherprogramminglanguages,suchaslambdaexpressions.Another
exampleisthefantasticclasslibrarySpirit,whichisaparserthatmakesheavyuseofexpressiontemplates,allowing
for(anapproximate)EBNFnotationdirectlyinC++,resultinginextremelyefficientparsers.Visit
http://spirit.sourceforge.net/.
[80]
SeehisandNicosbook,C++Templates,bookcitedearlier.
[81]
Namely,Blitz++(http://www.oonumerics.org/blitz/),theMatrixTemplateLibrary
(http://www.osl.iu.edu/research/mtl/),andPOOMA(http://www.acl.lanl.gov/pooma/).
[82]
Wemeanvectorinthemathematicalsense,asafixedlength,onedimensional,numericalarray.
[83]
LangerandKreft,C++ExpressionTemplates,C/C++UsersJournal,March2003.Seealsothearticleon
expressiontemplatesbyThomasBeckerintheJune2003issueofthesamejournal(thatarticlewastheinspiration
forthematerialinthissection).
[84]
Asexplainedearlier,youmustexplicitlyinstantiateatemplateonlyonceperprogram.
[85]
Visithttp://www.bdsoft.com/tools/stlfilt.html.
Orsomethingthatiscallableasafunction,asyoullseeshortly.
[86]
[87]
ThisissimplyanEnglishrenditionofO(nlogn),whichisthemathematicalwayofsayingthatforlargen,the
numberofcomparisonsgrowsindirectproportiontothefunctionf(n)=nlogn.
[88]
Unlessyoudosomethingungainlyusingglobalvariables.
[89]
Functionobjectsarealsocalledfunctors,afteramathematicalconceptwithsimilarbehavior.
[90]
Thespellinghereisadaptor,followingtheuseintheC++Standard.Elsewhereyouwillseeitspelledadapter
whenusedinthecontextofdesignpatterns,followingthecommonspellingthere.Bothspellingsareconsidered
acceptablebydictionaries.
[91]
Theresacomplicationwithdifferentlibraryimplementations.Ifpow()hasClinkage,meaningitsnameisnot
mangledlikeC++functions,thenthisexamplewontcompile.ptr_funrequiresapointertoanormal,overloadable
C++function.
[92]
Ifacompilerweretodefinestring::emptywithdefaultarguments(whichisallowed),thentheexpression
&string::emptywoulddefineapointertoamemberfunctiontakingthetotalnumberofarguments.Sincethereis
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
454/458
5/3/2015
ThinkinginC++2ndedVolume2
nowayforthecompilertoprovidetheextradefaults,therewouldbeamissingargumenterrorwhenanalgorithm
appliedstring::emptyviamem_fun_ref.
[93]
STLPort,forinstance,whichcomeswithversion6ofBorlandC++BuilderandtheDigitalMarscompiler,andis
basedonSGISTL.
[94]
Thestable_sort()algorithmusesmergesort,whichisindeedstable,buttendstorunslowerthanquicksorton
average.
[95]
Iteratorsarediscussedinmoredepthinthenextchapter.
[96]
Algorithmscandeterminethetypeofaniteratorbyreadingitstag,discussedinthenextchapter.
[97]
Wereignoringthecopyconstructorandassignmentoperatorinthisexample,sincetheydontapply.
[98]
Withoutviolatinganycopyrightlaws,ofcourse.
[99]
ThiswouldbeanexampleoftheStatepattern,describedinChapter10.
Visithttp://www.dinkumware.com,http://www.sgi.com/tech/stl,orhttp://www.stlport.org.
[100]
[101]
Thisisabouttochange,asmoresmartpointertypesareabouttobeaddedtothenextversionoftheStandard.
Forapreliminarylookatthem,seethesmartpointersavailableatwww.boost.org.
[102]
Thecontaineradaptors,stack,queue,andpriority_queuedonotsupportiterators,sincetheydonotbehaveas
sequencesfromtheuserspointofview.
[103]
Itwillonlyworkforimplementationsofvectorthatuseapointer(aT*)astheiteratortype,likeSTLPortdoes.
[104]
Thesewereactuallycreatedtoabstractthelocalefacetsawayfromiostreamssothatlocalefacetscouldoperate
onanysequenceofcharacters,notjustiostreams.Localesallowiostreamstoeasilyhandleculturallydifferent
formatting(suchastherepresentationofmoney).
[105]
Youwillneedtoprovideachar_traitsspecializationforanyotherargumenttype.
[106]
WeareindebtedtoNathanMyersforexplainingthis.
[107]
SingletonisawellknowndesignpatternandisdiscussedindepthinChapter10.
[108]
ThisisanotherexamplecoachedbyNathanMyers.
[109]
WerevisitmultithreadingissuesinChapter11.
Thismeanstheydependinsomewayonatemplateparameter.SeeChapter5inthesectionentitledName
[110]
LookupIssues.
[111]
AsweexplainedinChapter5,anyvalidqualification,suchasPQV::,willdo.
[112]
Chuckdesignedandprovidedtheoriginalreferenceimplementationsforbitsetandalsobitstring,theprecursor
tovector<bool>,whileanactivememberoftheC++StandardsCommitteeintheearly1990s.
[113]
Technically,itisnotlegalforuserstoaddtothestandardnamespace,butitistheeasiestwaytoavoidthis
obscurenamelookupproblem,andissupportedbyallthecompilersweuse.
[114]
TheywilllikelyappearinthenextrevisionofStandardC++.
[115]
Availableathttp://www.sgi.com/tech/stl.
Asweexplainedearlier,thevector<bool>specializationisalsoanonSTLcontainertosomedegree.
[116]
[117]
WithMicrosoftscompilersyouwillhavetoenableRTTIitsdisabledbydefault.Thecommandlineoptionto
enableitis/GR.
[118]
CompilerstypicallyinsertapointertoaclasssRTTItableinsideitsvirtualfunctiontable.
[119]
Adynamic_cast<void*>alwaysgivestheaddressofthefullobjectnotasubobject.Thiswillbeexplained
morefullyinthenextchapter.
[120]
ThisisalsotrueofJava,andotherobjectorientedlanguages.
[121]
TheseversionnumbersareinternalAT&Tnumberings.
[122]
Evenmoreimportantly,wedontwantundefinedbehavior.Itisanerrorforabaseclassnottohaveavirtual
destructor.
[123]
Theactuallayoutisimplementationspecific.
[124]
Butnotdetectedasanerror.dynamic_cast,however,cansolvethisproblem.Seethepreviouschapterfor
details.
[125]
Thatis,5*sizeof(int).Compilerscanaddarbitrarypadding,sothesizeofanobjectmustbeatleastaslargeas
thesumofitsparts,butcanbelarger.
[126]
Weusethetermhierarchybecauseeveryoneelsedoes,butthegraphrepresentingmultipleinheritance
relationshipsisingeneraladirectedacyclicgraph(DAG),alsocalledalattice,forobviousreasons.
[127]
Thepresenceofthesepointersexplainswhythesizeofbismuchlargerthanthesizeoffourintegers.Thisis
(partof)thecostofvirtualbaseclasses.ThereisalsoVPTRoverheadduetothevirtualdestructor.
[128]
Onceagain,baseclassesmusthavevirtualdestructors,butmostcompilerswillletthisexperimentcompile.
[129]
Notethatvirtualinheritanceiscrucialtothisexample.IfTopwerenotavirtualbaseclass,therewouldbe
multipleTopsubobjects,andtheambiguitywouldremain.Dominancewithmultipleinheritanceonlycomesintoplay
withvirtualbaseclasses.
[130]
JerrySchwarz,theauthorofiostreams,hasremarkedtobothofusonseparateoccasionsthatifhehadittodo
overagain,hewouldprobablyremoveMIfromthedesignofiostreamsandusemultiplestreambuffersand
conversionoperatorsinstead.
[131]
WeveseenthisincommercialC++libraries,atleastinsomeoftheearlyones.
[132]
AphrasecoinedbyZackUrlocker.
[133]
Conveniently,theexamplesareinC++unfortunately,thedialectispreStandardC++whichsuffersfromthe
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
455/458
5/3/2015
ThinkinginC++2ndedVolume2
lackofmoremodernlanguagefeatureslikeSTLcontainers.
[134]
MuchofthismaterialwasderivedfromThinkinginPatterns:ProblemSolvingTechniquesusingJava,available
atwww.MindView.net.
[135]
Foruptodateinformation,visithttp://hillside.net/patterns.
[136]
BillVennersnameforityoumayseeitnameddifferentlyelsewhere.
[137]
TheC++Standardstates:Notranslationunitshallcontainmorethanonedefinitionofanyvariable,function,
classtype,enumerationtypeortemplateEveryprogramshallcontainexactlyonedefinitionofeverynoninline
functionorobjectthatisusedinthatprogram.
[138]
ThisisknownasMeyersSingleton,afteritscreator,ScottMeyers.
[139]
AndreiAlexandrescudevelopsasuperior,policybasedsolutiontoimplementingtheSingletonpatterninModern
C++Design.
[140]
Formoreinformation,seethearticleOnceisNotEnoughbyHyslopandSutterintheMarch2003issueof
CUJ.
[141]
Page235.
[142]
SeeThinkinginC++,Volume1formoredetailsaboutreferencecounting.
[143]
JamesO.Coplien,AdvancedC++ProgrammingStylesandIdioms,AddisonWesley,1992.
[144]
ItdiffersfromJavainthatjava.util.Observable.notifyObservers()doesntcallclearChanged()untilafter
notifyingalltheobservers
[145]
Thereissomesimilaritybetweeninnerclassesandsubroutineclosures,whichsavethereferenceenvironment
ofafunctioncallsoitcanbereproducedlater.
[146]
ThisexampleexistedforanumberofyearsinbothC++andJavaonwww.MindView.netbeforeitappeared,
withoutattribution,inarecentbookbyotherauthors.
[147]
ThemotivationforincludingVisitorinGoFwasprobablyexcessivecleverness.Ataworkshop,oneoftheGoF
authorstoldoneofusthatVisitorwashisfavoritepattern.
[148]
Thisistruewhenthesystemusestimeslicing(Windows,forexample).SolarisusesaFIFOconcurrencymodel:
unlessahigherprioritythreadisawakenedthecurrentthreadrunsuntilitblocksorterminates.Thatmeansthat
otherthreadswiththesameprioritydontrununtilthecurrentonegivesuptheprocessor.
[149]
AssumingyouvedesigneditformultipleCPUs.Otherwise,codethatseemstoworkfineonatimeslicedsingle
processorsystemcanfailwhenmovedtomultipleCPUsystem,sincetheadditionalCPUscanrevealproblemsthata
oneCPUsystemdoesnot.
MuchofthischapterbeganasatranslationfromtheConcurrencychapterinThinkinginJava,3 rdedition,
PrenticeHall2003,althoughithaschangedverysignificantlyintheprocess.
[151]
Thiscanbesignificant.Usuallyonlyasmallpartofafunctionneedstobeguarded.Puttingtheguardatthe
[150]
functionentrypointcanoftenmakethecriticalsectionlongerthanitneedstobe.
[152]
Thisisanoversimplification.Sometimesevenwhenitseemslikeanatomicoperationshouldbesafe,itmaynot
be,soyoumustbeverycarefulwhendecidingthatyoucangetawaywithoutsynchronization.Removing
synchronizationisoftenasignofprematureoptimizationthingsthatcancauseyoualotoftroublewithoutgaining
much.Oranything.
[153]
Atomicityisnttheonlyissue.Onmultiprocessorsystemsvisibilityismuchmoreofanissuethanonsingle
processorsystems.Changesmadebyonethread,eveniftheyreatomicinthesenseofnotbeinginterruptible,might
notbevisibletootherthreads(thechangesmightbetemporarilystoredinalocalprocessorcache,forexample),so
differentthreadswillhaveadifferentviewoftheapplicationsstate.Thesynchronizationmechanismforceschanges
byonethreadonamultiprocessorsystemtobevisibleacrosstheapplication,whereaswithoutsynchronizationits
indeterminatewhenchangesbecomevisible.
[154]
However,exceptionsareneverdeliveredasynchronouslyinZThreads.Thus,thereisnodangerofsomething
abortingmidinstruction/functioncall.AndaslongasyouusetheGuardtemplatetoacquiremutexes,themutexes
willbeautomaticallyreleasedifanexceptionisthrown.
[155]
Actually,sleep()onlyprovidesaminimumdelay,notaguaranteeddelay,soitspossible(althoughimprobable)
thatthesleep(1100)willwakeupbeforethesleep(1000).
[156]
ThereisnothingintheC++StandardthatsaysthatinterruptionscantoccurduringIOoperations.However,
mostimplementationsdontsupportit.
[157]
Notethat,althoughitsunlikely,thecalltot.interrupt()couldactuallyhappenbeforethecalltoblocked.f().
[158]
ThisisincontrasttoJava,whereyoumustholdthelockinordertocallnotify()(Javasversionofsignal()).
AlthoughPosixthreads,onwhichtheZThreadlibraryislooselybased,donotrequirethatyouholdthelockinorder
tocallsignal()orbroadcast(),itisoftenrecommended.
[159]
Onsomeplatformstheresathirdwaytocomeoutofawait(),thesocalledspuriouswakeup.Aspurious
wakeupessentiallymeansthatathreadmayprematurelystopblocking(whilewaitingonaconditionvariableor
semaphore)withoutbeingpromptedbyasignal()orbroadcast().Thethreadjustwakesup,seeminglybyitself.
SpuriouswakeupsexistbecauseimplementingPOSIXthreads,ortheequivalent,isntalwaysasstraightforwardasit
shouldbeonsomeplatforms.Byallowingspuriouswakeupsthejobofbuildingalibrarylikepthreadsiseasierfor
thoseplatforms.SpuriouswakeupsdonotoccurinZThreads,becausethelibrarycompensatesforandhidesthese
issuesfromtheuser.
[160]
Notethatifthereadersstopforsomereason,thewriterswillkeeponwritinguntilthesystemrunsoutof
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
456/458
5/3/2015
ThinkinginC++2ndedVolume2
memory.Ifthisisanissuewithyourprogramyoucanaddamaximumallowableelementcount,andwritersshould
thenblockifthequeueisfull.
[161]
Inparticular,refertotheearlierfootnoteinthischapteronmultiprocessorsandvisibility.
[162]
Atthetimeofthiswriting,Cygwin(www.cygwin.com)wasundergoingchangesandimprovementstoits
threadingsupport,butwewerestillunabletoobservedeadlockingbehaviorwiththisprogramundertheavailable
versionofCygwin.Theprogramdeadlockedquicklyunder,forexample,Linux.
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
457/458
5/3/2015
ThinkinginC++2ndedVolume2
file:///C:/Users/BERARDO/Downloads/TICPP2ndedVoltwo/html/TicV2.html#_Toc305593301
458/458