Sunteți pe pagina 1din 458

5/3/2015

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,"&lt","<")
replaceAll(s,"&gt",">")
replaceAll(s,"&amp","&")
replaceAll(s,"&nbsp","")
//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,"&lt","<")
replaceAll(s,"&gt",">")
replaceAll(s,"&amp","&")
replaceAll(s,"&nbsp","")
//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=&copyconstructorOK
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

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