Your SlideShare is downloading. ×
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Mc graw hill herb schildts c plus plus programming cookbook apr 2008
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Mc graw hill herb schildts c plus plus programming cookbook apr 2008

668

Published on

Published in: Technology, News & Politics
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
668
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
141
Comments
0
Likes
3
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Herb Schildt's C++ Programming Cookbook
  • 2. About the Author Herbert Schildt is a leading authority on C++, C, Java, and C#, and is a master Windows programmer. His programming books have sold more than 3.5 million copies worldwide and have been translated into all major foreign languages. He is the author of numerous bestsellers on C++, including C++: The Complete Reference, C++: A Beginner’s Guide, C++ from the Ground Up, and STL Programming from the Ground Up. His other bestsellers include C#: The Complete Reference, Java: The Complete Reference, C: The Complete Reference, and Herb Schildt’s Java Programming Cookbook. Schildt holds both graduate and undergraduate degrees from the University of Illinois. He can be reached at his consulting office at (217) 586-4683. His website is www.HerbSchildt.com. About the Technical Editor Jim Keogh introduced PC programming nationally in his Popular Electronics Magazine column in 1982, four years after Apple Computer started in a garage. He was a team member who built one of the first Windows applications by a Wall Street firm, featured by Bill Gates in 1986. Keogh has spent about two decades developing computer systems for Wall Street firms, such as Salomon, Inc. and Bear Stearns, Inc. Keogh was on the faculty of Columbia University where he taught technology courses, including the Java Development lab. He developed and chaired the electronic commerce track at Columbia University. Keogh is presently on the faculty of New York University. He is the author of J2EE: The Complete Reference, J2ME: The Complete Reference, both published by McGraw-Hill, and more than 55 other titles, including five in the popular …For Dummies series. His other books include Linux Programming for Dummies, Unix Programming for Dummies, Java Database Programming for Dummies, Essential Guide to Networking, Essential Guide to Computer Hardware, The C++ Programmer's Notebook, and E-Mergers. Copyright © 2008 by The McGraw-Hill Companies. Click here for terms of use.
  • 3. Herb Schildt's C++ Programming Cookbook Herb Schildt New York Chicago San Francisco Lisbon London Madrid Mexico City Milan New Delhi San Juan Seoul Singapore Sydney Toronto
  • 4. Copyright © 2008 by The McGraw-Hill Companies. All rights reserved. Manufactured in the United States of America. Except as permitted under the United States Copyright Act of 1976, no part of this publication may be reproduced or distributed in any form or by any means, or stored in a database or retrieval system, without the prior written permission of the publisher. 0-07-164385-0 The material in this eBook also appears in the print version of this title: 0-07-148860-X. All trademarks are trademarks of their respective owners. Rather than put a trademark symbol after every occurrence of a trademarked name, we use names in an editorial fashion only, and to the benefit of the trademark owner, with no intention of infringement of the trademark. Where such designations appear in this book, they have been printed with initial caps. McGraw-Hill eBooks are available at special quantity discounts to use as premiums and sales promotions, or for use in corporate training programs. For more information, please contact George Hoare, Special Sales, at george_hoare@mcgraw-hill.com or (212) 904-4069. TERMS OF USE This is a copyrighted work and The McGraw-Hill Companies, Inc. (“McGraw-Hill”) and its licensors reserve all rights in and to the work. Use of this work is subject to these terms. Except as permitted under the Copyright Act of 1976 and the right to store and retrieve one copy of the work, you may not decompile, disassemble, reverse engineer, reproduce, modify, create derivative works based upon, transmit, distribute, disseminate, sell, publish or sublicense the work or any part of it without McGraw-Hill’s prior consent. You may use the work for your own noncommercial and personal use; any other use of the work is strictly prohibited. Your right to use the work may be terminated if you fail to comply with these terms. THE WORK IS PROVIDED “AS IS.” McGRAW-HILL AND ITS LICENSORS MAKE NO GUARANTEES OR WARRANTIES AS TO THE ACCURACY, ADEQUACY OR COMPLETENESS OF OR RESULTS TO BE OBTAINED FROM USING THE WORK, INCLUDING ANY INFORMATION THAT CAN BE ACCESSED THROUGH THE WORK VIA HYPERLINK OR OTHERWISE, AND EXPRESSLY DISCLAIM ANY WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. McGraw-Hill and its licensors do not warrant or guarantee that the functions contained in the work will meet your requirements or that its operation will be uninterrupted or error free. Neither McGraw-Hill nor its licensors shall be liable to you or anyone else for any inaccuracy, error or omission, regardless of cause, in the work or for any damages resulting therefrom. McGraw-Hill has no responsibility for the content of any information accessed through the work. Under no circumstances shall McGraw-Hill and/or its licensors be liable for any indirect, incidental, special, punitive, consequential or similar damages that result from the use of or inability to use the work, even if any of them has been advised of the possibility of such damages. This limitation of liability shall apply to any claim or cause whatsoever whether such claim or cause arises in contract, tort or otherwise. DOI: 10.1036/007148860X
  • 5. Professional Want to learn more? We hope you enjoy this McGraw-Hill eBook! If you’d like more information about this book, its author, or related books and websites, please click here.
  • 6. For more information about this title, click here Contents Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xvii 1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . What's Inside . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . How the Recipes Are Organized . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A Few Words of Caution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C++ Experience Required . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . What Version of C++? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Two Coding Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Returning a Value from main( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Using Namespace std? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 2 3 3 4 4 4 4 2 String Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Overview of Null-Terminated Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Overview of the string Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . String Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Perform Basic Operations on Null-Terminated Strings . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Search a Null-Terminated String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reverse a Null-Terminated String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ignore Case Differences When Comparing Null-Terminated Strings . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Create a Search-and-Replace Function for Null-Terminated Strings . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 8 11 16 16 17 17 18 19 20 21 21 21 22 23 23 24 24 25 27 27 28 29 31 31 32 32 v
  • 7. vi Herb Schildt's C++ Programming Cookbook Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Categorize Characters Within a Null-Terminated String . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: Word Count . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tokenize a Null-Terminated String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Perform Basic Operations on string Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Search a string Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: A Tokenizer Class for string Objects . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Create a Search-and-Replace Function for string Objects . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operate on string Objects Through Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Create Case-Insensitive Search and Search-and-Replace Functions for string Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Convert a string Object into a Null-Terminated String . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 36 39 39 40 40 41 43 44 45 45 45 47 51 52 52 55 58 59 60 60 61 63 65 66 67 67 67 69 70 71 71 73 75 76 77 77 78 81 83 83 83 83 85
  • 8. Contents Implement Subtraction for string Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 85 86 87 88 90 Working with STL Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . STL Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Containers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Allocators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Function Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Adaptors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Predicates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Binders and Negators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The Container Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Common Functionality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Performance Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Basic Sequence Container Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use deque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use the Sequence Container Adaptors: stack, queue, and priority_queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 94 94 94 94 95 95 96 96 96 96 98 101 102 103 103 105 109 111 111 112 115 118 118 119 119 120 124 124 125 125 127 130 132 132 133 135 vii
  • 9. viii Herb Schildt's C++ Programming Cookbook Bonus Example: Use stack to Create a Four-Function Calculator . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Store User-Defined Objects in a Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Basic Associative Container Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use multimap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use set and multiset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: Use multiset to Store Objects with Duplicate Keys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 137 140 140 140 141 141 144 145 146 147 150 155 156 157 157 159 162 163 163 163 165 167 169 170 170 172 Algorithms, Function Objects, and Other STL Components . . . . . . . . . . . Algorithm Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Why Algorithms? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Algorithms Are Template Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . The Algorithm Categories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Function Object Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Binders and Negators Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sort a Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Find an Element in a Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 182 182 182 183 184 188 189 189 189 190 191 192 193 193 174 178
  • 10. Contents Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: Extract Sentences from a Vector of Characters . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use search( ) to Find a Matching Sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Reverse, Rotate, and Shuffle a Sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: Use Reverse Iterators to Perform a Right-Rotate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Cycle Through a Container with for_each( ) . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use transform( ) to Change a Sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Perform Set Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Permute a Sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Copy a Sequence from One Container to Another . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Replace and Remove Elements in a Container . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 195 197 199 200 200 200 202 203 204 204 204 206 207 208 208 208 209 210 211 211 212 212 214 217 217 218 219 221 222 222 222 223 224 225 225 225 226 227 227 228 228 228 230 ix
  • 11. x Herb Schildt's C++ Programming Cookbook Merge Two Sorted Sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Create and Manage a Heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Create an Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: Use a Predicate with a Custom Algorithm . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use a Built-In Function Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Create a Custom Function Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: Use a Function Object to Maintain State Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use a Binder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use a Negator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use the Pointer-to-Function Adaptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 231 231 232 234 235 235 235 236 238 238 238 239 240 242 244 245 245 246 246 248 248 249 249 250 253 255 255 256 256 257 258 259 259 260 260 261 262 262 262 263 265
  • 12. Contents Use the Stream Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: Create an STL-Based File Filter . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use the Insert Iterator Adaptors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 265 266 266 269 272 273 274 274 275 275 277 Working with I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I/O Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C++ Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The C++ Stream Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The Stream Class Specializations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C++'s Predefined Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The Format Flags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The I/O Manipulators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Checking for Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Opening and Closing a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Write Formatted Data to a Text File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Read Formatted Data from a Text File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Write Unformatted Binary Data to a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Read Unformatted Binary Data from a File . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use get( ) and getline( ) to Read from a File . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 280 280 281 285 287 287 287 288 289 293 293 294 295 296 296 297 297 298 300 300 301 301 302 304 305 305 306 307 309 310 310 310 xi
  • 13. xii Herb Schildt's C++ Programming Cookbook Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Read from and Write to a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Detecting EOF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: A Simple File-Comparison Utility . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use Exceptions to Detect and Handle I/O Errors . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use Random-Access File I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: Use Random-Access I/O to Access Fixed-Size Records . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Look Ahead in a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use the String Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Create Custom Inserters and Extractors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Create a Parameterless Manipulator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311 313 314 314 315 316 317 317 318 318 318 320 322 322 323 323 324 326 326 327 327 328 329 332 332 333 333 334 336 337 337 338 338 340 341 341 342 343 344 344 345 345 346 347
  • 14. Contents Create a Parameterized Manipulator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Obtain or Set a Stream's Locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use the C-Based File System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Rename and Remove a File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 348 348 349 350 352 352 353 353 353 355 355 356 356 359 361 363 363 363 364 365 Formatting Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Formatting Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The Format Flags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The Field Width, Precision, and Fill Character . . . . . . . . . . . . . . . . . . Format-Related Stream Member Functions . . . . . . . . . . . . . . . . . . . . . The I/O Manipulators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Format Data Using the Localization Library . . . . . . . . . . . . . . . . . . . . The printf( ) Family of Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . The strftime( ) Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Facet Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Access the Format Flags via Stream Member Functions . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: Display the Format Flag Settings . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Display Numeric Values in Various Formats . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Set the Precision . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367 368 368 369 370 370 370 371 371 372 374 374 374 375 376 378 379 379 380 380 382 383 383 383 xiii
  • 15. xiv Herb Schildt's C++ Programming Cookbook Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Set the Field Width and Fill Character . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: Line Up Columns of Numbers . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Justify Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use I/O Manipulators to Format Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Format Numeric Values for a Locale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Format Monetary Values Using the money_put Facet . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use the moneypunct and numpunct Facets . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Format Time and Date Using the time_put Facet . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Format Data into a String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Format Time and Date Using strftime( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384 384 385 385 385 386 387 388 388 388 389 389 391 391 392 392 394 395 395 396 396 396 397 398 399 399 400 401 402 402 403 404 405 407 408 408 410 411 412 412 412 412 414 414 414 415
  • 16. Contents Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use printf( ) to Format Data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 415 417 418 419 419 422 424 Potpourri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Operator Overloading Basic Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Overload the Function Call Operator ( ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Overload the Subscripting Operator [ ] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Overload the –> Operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: A Simple Safe Pointer Class . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Overload new and delete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Overload the Increment and Decrement Operators . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Create a Conversion Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425 426 426 427 432 435 437 437 437 439 440 441 441 441 442 445 445 446 446 446 447 451 451 451 452 453 456 457 457 457 459 462 463 463 463 464 466 xv
  • 17. xvi Herb Schildt's C++ Programming Cookbook Create a Copy Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Bonus Example: A Safe Array that Uses Dynamic Allocation . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Determine an Object's Type at Runtime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use Complex Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use auto_ptr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Create an Explicit Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Step-by-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Discussion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Options and Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466 467 467 468 471 477 478 479 479 480 484 484 485 485 486 487 487 488 488 489 490 491 491 491 492 494 Index 495 ...........................................................
  • 18. xvii Preface O ver the years, friends and readers have asked me to write a programming cookbook, sharing some of the techniques and approaches that I use when I program. From the start, I liked the idea, but was unable to make time for it in my very busy writing schedule. As many readers know, I write extensively about several facets of programming, with a special focus on C++, Java, and C#. Because of the rapid revision cycles of those languages, I spend nearly all of my available time updating my books to cover the latest versions. Fortunately, early in 2007, a window of opportunity opened and I was finally able to devote time to the cookbook. The two most requested cookbooks were ones for Java and C++. I began with Java, with the result being my Java programming cookbook. As soon as I finished the Java book, I moved on to C++. The result is, of course, this book. I must admit that both projects were among my most enjoyable. Based on the format of a traditional food cookbook, this book distills the essence of many general-purpose C++ techniques into a collection of step-by-step recipes. Each recipe describes a set of key ingredients, such as classes, functions, and headers. It then shows the steps needed to assemble those ingredients into a code sequence that achieves the desired result. This organization makes it easy to find the technique in which you are interested and then put that technique into action. Actually, "into action" is an important part of this book. I believe that good programming books contain two elements: solid theory and practical application. In the recipes, the step-bystep instructions and discussions supply the theory. To put that theory into practice, each recipe includes a complete code example. The examples demonstrate in a concrete, unambiguous way how the recipes can be applied. In other words, the examples eliminate the "guess work" and save you time. Although no cookbook can include every recipe that one might desire (there is a nearly unbounded number of possible recipes), I tried to span a wide range of topics. My criteria for including a recipe are discussed in detail in Chapter 1, but briefly, I included recipes that would be useful to many programmers and that answered frequently asked questions. Even with these criteria, it was difficult to decide what to include and what to leave out. This was the most challenging part of writing this book. Ultimately, it came down to experience, judgment, and intuition. Hopefully, I have included something to satisfy every programmer's taste! HS xvii Copyright © 2008 by The McGraw-Hill Companies. Click here for terms of use.
  • 19. xviii Herb Schildt's C++ Programming Cookbook Example Code on the Web The source code for all of the examples in this book is available free-of-charge on the Web at www.mhprofessional.com. More from Herbert Schildt Herb Schildt's C++ Programming Cookbook is just one of Herb's many programming books. Here are some others that you will find of interest. To learn more about C++, you will find these books especially helpful: C++: The Complete Reference C++: A Beginner's Guide C++ from the Ground Up STL Programming from the Ground Up The Art of C++ To learn about Java, we recommend: Java: The Complete Reference Java: A Beginner's Guide The Art of Java Swing: A Beginner's Guide Herb Schildt's Java Programming Cookbook To learn about C#, we suggest the following Schildt books: C#: The Complete Reference C#: A Beginner's Guide If you want to learn about the C language, then the following title will be of interest: C: The Complete Reference When you need solid answers fast, turn to Herbert Schildt, the recognized authority on programming.
  • 20. 1 CHAPTER Overview T his book is a collection of techniques that show how to perform various programming tasks in C++. As the title implies, it uses the well-known "cookbook" format. Each "recipe" illustrates how to accomplish a specific operation. For example, there are recipes that read bytes from a file, reverse a string, sort the contents of a container, format numeric data, and so on. In the same way that a recipe in a food cookbook describes a set of ingredients and a sequence of instructions necessary to prepare a dish, each technique in this book describes a set of key program elements and the sequence of steps necessary to use them to accomplish a programming task. Ultimately, the goal of this book is to save you time and effort during program development. Many programming tasks consist of a set of standard functions and classes, which must be applied in a specific sequence. The trouble is that sometimes you don't know which functions to use or what classes are appropriate. Instead of having to wade through reams of documentation and online tutorials to determine how to approach some task, you can look up its recipe. Each recipe shows one way to craft a solution, describing the necessary elements and the order in which they must be used. With this information, you can design a solution that fits your specific need. What's Inside No cookbook is exhaustive. The author of a cookbook must make choices about what is and isn't included. The same is true for this cookbook. In choosing the recipes for this book, I focused on four main topic areas: string handling, the Standard Template Library (STL), I/O, and formatting data. These are core topics that relate to a wide range of programmers. They are also very large topics, which required many pages to explore in depth. As a result, each of these topics became the basis for one or more chapters. It is important to state, however, that the content of those chapters is not limited to only those topics. As most readers know, just about everything in C++ is interrelated. In the process of creating recipes for one aspect of C++, several others, such as localization, dynamic allocation, or operator overloading, are often involved. Thus, recipes for the preceding topics often illustrate other C++ techniques. In addition to the recipes related to the main topic areas, I had several others that I wanted to include but for which an entire chapter was not feasible. I grouped those recipes into the final chapter. Several of these recipes focus on overloading C++'s more specialized 1 Copyright © 2008 by The McGraw-Hill Companies. Click here for terms of use.
  • 21. 2 Herb Schildt's C++ Programming Cookbook operators, such as [ ], –>, new, and delete. Others illustrate the use of the auto_ptr and complex classes or show how to create a conversion function, a copy constructor, or an explicit constructor. There is also a recipe that demonstrates runtime type ID. Of course, choosing the topics was only the beginning of the selection process. Within each category, I had to decide what to include and what not to include. In general, I included a recipe if it met the following two criteria. 1. The technique is useful to a wide range of programmers. 2. It provides an answer to a frequently asked programming question. The first criterion is largely self-explanatory. I included recipes that describe how to accomplish a set of tasks that would commonly be encountered when creating C++ applications. Some of the recipes illustrate a general concept that can be adapted to solve several different types of problems. For example, Chapter 2 shows a recipe that searches for a substring within a string. This general procedure is useful in several contexts, such as finding an e-mail address or a telephone number within a sentence, or extracting a keyword from a database query. Other recipes describe more specific, yet widely used techniques. For example, Chapter 6 shows how to format the time and date. The second criterion is based on my experience as the author of programming books. Over the many years that I have been writing, I have been asked hundreds and hundreds of "how to" questions by readers. These questions come from all areas of C++ programming and range from the very easy to the quite difficult. I have found, however, that a central core of questions occurs again and again. Here is one example: "How do I format a number so that it has two decimal places?" Here is another: "How do I create a function object?" There are many others. These same types of questions also occur frequently on various programmer forums on the Web. I used these commonly asked "how to" questions to guide my selection of recipes. The recipes in this book span various skill levels. Some illustrate basic techniques, such as reading bytes from a file or overloading the << operator to output objects of a class that you create. Others are more advanced, such as using the localization library to format monetary values, tokenizing a string, or overloading the [ ] operator. Thus, the level of difficulty of an individual recipe can range from relatively easy to significantly advanced. Of course, most things in programming are easy once you know how to do them, but difficult when you don't. Therefore, don't be surprised if some recipe seems obvious. It just means that you already know how to accomplish that task. How the Recipes Are Organized Each recipe in this book uses the same format, which has the following parts: • A table of key ingredients used by the recipe. • A description of the problem that the recipe solves. • The steps necessary to complete the recipe. • An in-depth discussion of the steps. • A code example that puts the recipe into action. • Options and alternatives that suggest other ways to craft a solution.
  • 22. Chapter 1: Overview A recipe begins by describing the task to accomplish. The key ingredients used by the recipe are shown in a table. These include the functions, classes, and headers required to create a solution. Of course, putting a recipe into practice may imply the use of additional elements, but the key ingredients are those that are fundamental to the task at hand. Each recipe then presents step-by-step instructions that summarize the procedure. These are followed by an in-depth discussion of the steps. In many cases, the summary will be sufficient, but the details are there if you need them. Next, a code example is presented that shows the recipe in action. All code examples are presented in their entirety. This avoids ambiguity and lets you clearly see precisely what is happening without having to fill in additional details yourself. Occasionally, a bonus example is included that further illustrates how a recipe can be applied. Each recipe concludes with a discussion of various options and alternatives. This section is especially important because it suggests different ways to implement a solution or other ways to think about the problem. A Few Words of Caution There are a few important points that you should keep in mind when you use this book. First, a recipe shows one way to craft a solution. Other ways may (and often do) exist. Your specific application may require an approach that is different from the one shown. The recipes in this book can serve as starting points, they can help you choose a general approach to a solution, and they can spur your imagination. However, in all cases, you must determine what is and what isn't appropriate for your application. Second, it is important to understand that the code examples are not optimized for performance. They are optimized for clarity and ease of understanding. Their purpose is to clearly illustrate the steps of the recipe. In many cases, you will have little trouble writing tighter, more efficient code. Furthermore, the examples are exactly that: examples. They are simple uses that do not necessarily reflect the way that you will write code for your own application. In all circumstances, you must create your own solution that fits the needs of your application. Third, each code example contains error handling that is appropriate for that specific example, but may not be appropriate in other situations. In all cases, you must properly handle the various errors and exceptions that can result when adapting a recipe for use in your own code. Let me state this important point again: When implementing a solution, you must provide error handling appropriate to your application. You cannot simply assume that the way that errors or exceptions are handled (or not handled) by an example is sufficient or adequate for your use. Typically, additional error handling will be required in real-world applications. C++ Experience Required This book is for every C++ programmer, whether beginner or experienced pro. However, it does assume that you know the fundamentals of C++ programming, including the C++ keywords and syntax, and have a general familiarity with the core library functions and classes. You should also be able to create, compile, and run C++ programs. None of these things are taught by this book. (This book is about applying C++ to a variety of real-world 3
  • 23. 4 Herb Schildt's C++ Programming Cookbook programming problems. It is not about teaching the fundamentals of the C++ language.) If you need to improve your C++ skills, I recommend my books C++: The Complete Reference, C++ From the Ground Up, and C++: A Beginner's Guide. All are published by McGraw-Hill, Inc. What Version of C++? The code and discussions in this book are based on the ANSI/ISO International Standard for C++. Unless explicitly stated otherwise, no non-standard extensions are used. As a result, the majority of techniques presented here are portable and can be used with any C++ compiler that adheres to the International Standard for C++. The code in this book was developed and tested with Microsoft's Visual C++. Both Visual Studio and Visual C++ Express (which is available free of charge from Microsoft) were used. NOTE At the time of this writing, the International Standard for C++ is in the process of being updated. Many new features are being contemplated. However, none of them are formally part of C++ at this time and are, therefore, not used in this book. Of course, future editions of this book may make use of these new features. Two Coding Conventions Before moving on to the recipes, there are two issues to discuss that relate to how the code in this book is written. The first relates to returning a value from main( ). The second concerns the use of namespace std. The following explains the decisions that I made relating to these two features. Returning a Value from main( ) The code examples in this book always explicitly return an integer value from main( ). By convention, a return value of zero indicates successful termination. A non-zero return value indicates some form of error. Explicitly returning a value from main( ) is not technically necessary, however, because in the words of the International Standard for C++: "If control reaches the end of main without encountering a return statement, the effect is that of executing return 0;" For this reason, you will occasionally find code that does not explicitly return a value from main( ), relying instead upon the implicit return value of zero. But this is not the approach used by this book. Instead, all of the main( ) functions in this book explicitly return a value because of two reasons. First, some compilers issue a warning when a non-void method fails to explicitly return a value. To avoid this warning, main( ) must include a return statement. Second, it just seems good practice to explicitly return a value, given that main( ) is declared with an int return type! Using Namespace std ? One of the problems that a writer of a C++ book faces is whether or not to use the line: using namespace std;
  • 24. Chapter 1: Overview near the top of each program. This statement brings the contents of the std namespace into view. The std namespace contains the C++ standard library. Thus, by using the std namespace, the standard library is brought into the global namespace, and names such as cout can be referred to directly, rather than as std::cout. The use of using namespace std; is both very common and occasionally controversial. Some programmers dislike it, suggesting that it defeats the point of packaging the standard library into the std namespace and invites conflicts with third-party code, especially in large projects. While this is true, others point out that in short programs (such as the examples shown in this book) and in small projects, the convenience it offers easily offsets the remote chance of conflicts, which seldom (if ever) occur in these cases. Frankly, in programs for which the risk of conflicts is essentially zero, having to always write std::cout, std::cin, std::ofstream, std::string, and so on is tedious. It also makes the code more verbose. The foregoing debate notwithstanding, this book uses using namespace std; in the example programs for two reasons. First, it makes the code shorter, which means that more code can fit on a line. In a book, line-length is limited. Not having to constantly use std:: shortens lines, which means that more code can fit on a single line without causing the line to break. The fewer broken lines, the easier the code is to read. Second, it makes the code examples less verbose, which enhances their clarity on the printed page. It has been my experience that using namespace std is very helpful when presenting example programs shown in a book. However, its use in the examples is not meant as an endorsement of the technique in general. You must decide what is appropriate for your own programs. 5
  • 25. This page intentionally left blank
  • 26. 2 CHAPTER String Handling T here is almost always more than one way to do something in C++. This is one reason why C++ is such a rich and powerful language. It lets the programmer choose the best approach for the task at hand. Nowhere is this multifaceted aspect of C++ more evident than in strings. In C++, strings are based on two separate but interrelated subsystems. One type of string is inherited from C. The other is defined by C++. Together, they provide the programmer with two different ways to think about and handle sequences of characters. The first type of string supported by C++ is the null-terminated string. This is a char array that contains the characters that comprise a string, followed by a null. The null-terminated string is inherited from C and it gives you low-level control over string operations. As a result, the null-terminated string offers a very efficient way in which to handle character sequences. C++ also supports wide-character, null-terminated strings, which are arrays of type wchar_t. The second type of string is an object of type basic_string, which is a template class defined by C++. Therefore, basic_string defines a unique type whose sole purpose is to represent sequences of characters. Because basic_string defines a class type, it offers a highlevel approach to working with strings. For example, it defines many member functions that perform various string manipulations, and several operators are overloaded for string operations. There are two specializations of basic_string that are defined by C++: string and wstring. The string class operates on characters of type char, and wstring operates on characters of type wchar_t. Thus, wstring encapsulates a wide-character string. As just explained, both null-terminated strings and basic_string support strings of types char and wchar_t. The main difference between strings based on char and those based on wchar_t is the size of the character. Otherwise, the two types of strings are handled in essentially the same way. For the sake of convenience and because char-based strings are, by far, the most common, they are the type of strings used in the recipes in this chapter. However, the same basic techniques can be adapted to wide-character strings with little effort. The topic of C++ strings is quite large. Frankly, it would be easy to fill an entire book with recipes about them. Thus, limiting the string recipes to a single chapter presented quite a challenge. In the end, I selected recipes that answer common questions, illustrate key aspects of each string type, or demonstrate general principles that can be adapted to a wide variety of uses. 7 Copyright © 2008 by The McGraw-Hill Companies. Click here for terms of use.
  • 27. 8 Herb Schildt's C++ Programming Cookbook Here are the recipes contained in this chapter: • Perform Basic Operations on Null-Terminated Strings • Search a Null-Terminated String • Reverse a Null-Terminated String • Ignore Case Differences When Comparing Null-Terminated Strings • Create a Search-and-Replace Function for Null-Terminated Strings • Categorize Characters Within a Null-Terminated String • Tokenize a Null-Terminated String • Perform Basic Operations on string Objects • Search a string Object • Create a Search-and-Replace Function for string Objects • Operate on string Objects Through Iterators • Create Case-Insensitive Search and Search-and-Replace Functions for string Objects • Convert a string Object into a Null-Terminated String • Implement Subtraction for string Objects NOTE In-depth coverage of null-terminated strings and the string class is found in my book C++: The Complete Reference. Overview of Null-Terminated Strings The type of string most commonly used in a C++ program is the null-terminated string. As mentioned, a null-terminated string is an array of char that ends with a null character. Thus, a null-terminated string is not a unique type of its own. Rather, it is a convention that is recognized by all C++ programmers. The null-terminated string was defined by the C language and is still widely used by C++ programmers. It is also commonly referred to as a char * string, or sometimes as a C string. Although null-terminated strings are familiar territory to most C++ programmers, it is still useful to review their key attributes and capabilities. There are two main reasons why null-terminated strings are widely used in C++. First, all string literals are represented as null-terminated strings. Therefore, whenever you create a string literal, you are creating a null-terminated string. For example, in the statement const char *ptr = "Hello"; the literal "Hello" is a null-terminated string. This means that it is a char array that contains the characters Hello and is terminated by a null. In this statement, a pointer to the array is assigned to ptr. As a point of interest, notice that ptr is specified as const. Standard C++ specifies that string literals are arrays of type const char. Therefore, it is best to use a const char * pointer to point to one. However, the current standard also defines an automatic (but deprecated) conversion to char *, and it is quite common to see code in which the const is omitted.
  • 28. Chapter 2: String Handling The second reason why null-terminated strings are widely used is efficiency. Using an array terminated by a null to hold a string allows many string operations to be implemented in a streamlined fashion. (Essentially, null-terminated string operations are simply specialized array operations.) For example, here is one way to write the standard library function strcpy( ), which copies the contents of one string to another. // One way to implement the standard strcpy() function. char *strcpy(char *target, const char *source) { char *t = target; // Copy the contents of source into target. while(*source) *target++ = *source++; // Null-terminate the target. *target = '0'; // Return pointer to the start of target. return t; } Pay special attention to this line: while(*source) *target++ = *source++; Because the source string ends with a null character, a very efficient loop can be created that simply copies characters until the character pointed to by source is null. Recall that in C++, any non-zero value is true, but zero is false. Since the null character is zero, the while loop stops when the null terminator is encountered. Loops like the one just shown are common when working with null-terminated strings. The standard C++ library defines several functions that operate on null-terminated strings. These require the header <cstring>. These functions will, no doubt, be familiar to many readers. Furthermore, the recipes in this chapter fully explain the string functions that they employ. However, it is still helpful to briefly list the commonly used null-terminated string functions. Function Description char *strcat(char *str1, const char *str2) Concatenates the string pointed to by str2 to the end of the string pointed to by str1. Returns str1. If the strings overlap, the behavior of strcat( ) is undefined. char *strchr(const char *str, int ch) Returns a pointer to the first occurrence of the low-order byte of ch in the string pointed to by str. If no match is found, a null pointer is returned. int strcmp(const char *str1, const char str2) Lexicographically compares the string pointed to by str1 with the string pointed to by str2. Returns less than zero if str1 is less than str2, greater than zero if str1 is greater than str2, and zero if the two strings are the same. 9
  • 29. 10 Herb Schildt's C++ Programming Cookbook Function Description char *strcpy(char *target, const char *source) Copies the string pointed to by source to the string pointed to by target. Returns target. If the strings overlap, the behavior of strcpy( ) is undefined. size_t strcspn(const char *str1, const char *str2) Returns the index of the first character in the string pointed to by str1 that matches any character in the string pointed to by str2. If no match is found, the length of str1 is returned. size_t strlen(const char *str) Returns the number of characters in the string pointed to by str. The null terminator is not counted. char *strncat(char *str1, const char *str2, size_t count) Concatenates not more than count characters from the string pointed to by str2 to the end of str1. Returns str1. If the strings overlap, the behavior of strncat( ) is undefined. int strncmp(const char *str1, const char *str2, size_t count) Lexicographically compares not more than the first count characters in the string pointed to by str1 with the string pointed to by str2. Returns less than zero if str1 is less than str2, greater than zero if str1 is greater than str2, and zero if the two strings are the same. char *strncpy(char *target, const char *source, size_t count) Copies not more than count characters from the string pointed to by source to the string pointed to by target. If source contains less than count characters, null characters will be appended to the end of target until count characters have been copied. However, if source is longer than count characters, the resultant string will not be null-terminated. Returns target. If the strings overlap, the behavior of strcnpy( ) is undefined. char *strpbrk(const char *str1, const char *str2) Returns a pointer to the first character in the string pointed to by str1 that matches any character in the string pointed to by str2. If no match is found, a null pointer is returned. char *strrchr(const char *str, int ch) Returns a pointer to the last occurrence of the low-order byte of ch in the string pointed to by str. If no match is found, a null pointer is returned. size_t strspn(const char *str1, const char *str2) Returns the index of the first character in the string pointed to by str1 that does not match any of the characters in the string pointed to by str2. char *strstr(const char *str1, const char *str2) Returns a pointer to the first occurrence of the string pointed to by str2 in the string pointed to by str1. If no match is found, a null pointer is returned. char *strtok(char *str, const char *delims) Returns a pointer to the next token in the string pointed to by str. The characters in the string pointed to by delims specify the delimiters that determine the boundaries of a token. A null pointer is returned when there is no token to return. To tokenize a string, the first call to strtok( ) must have str point to the string to be tokenized. Subsequent calls must pass a null pointer to str.
  • 30. Chapter 2: String Handling Notice that several of the functions, such as strlen( ) and strspn( ), use the type size_t. This is some form of unsigned integer and it is defined by <cstring>. The <cstring> header also defines several functions that begin with the "mem" prefix. These functions operate on characters, but do not use the null-terminator convention. They are sometimes useful when manipulating strings and can also be used for other purposes. The functions are memchr( ), memcmp( ), memcpy( ), memmove( ), and memset( ). The first three operate similar to strchr( ), strcmp( ), and strcpy( ), respectively, except that they take an extra parameter that specifies the number of characters on which to operate. The memset( ) function sets a block of memory to a specified value. The memmove( ) function moves a block of characters. Unlike memcpy( ), memmove( ) can be used to move characters in overlapping arrays. It is the only "mem" function used in this chapter and is shown here: void *memmove(void *target, const void *source, size_t count) It copies count characters from the array pointed to by source into the array pointed to by target. It returns target. As mentioned, the copy takes place correctly, even if the arrays overlap. However, in this case, the array pointed to by source may be modified (even though source is specified as const). NOTE Microsoft's Visual C++ "deprecates" (no longer recommends the use of) several standard string functions, such as strcpy( ), for security reasons. For example, Microsoft recommends using strcpy_s( ) instead. However, these alternatives are not defined by Standard C++ and are non-standard. Therefore, this book will use the functions specified by the International Standard for C++. Overview of the string Class Although null-terminated strings are very efficient, they do suffer from two problems. First, they do not define a type. That is, representing a string as an array of characters terminated by a null is a convention. Although this convention is well understood and widely recognized, it is not a data type in the normal sense. (In other words, the null-terminated string is not part of C++'s type system.) As a result, null-terminated strings cannot be manipulated by operators. For example, you cannot concatenate two null-terminated strings by using the + operator or use = to assign one null-terminated string to another. Therefore, the following sequence won't work: // This sequence is in error. char strA[] = "alpha"; char strB[] = "beta"; char strC[10] = strA + strB; // Oops! Won't work! Instead, you must use calls to library functions to perform these operations, as shown next: // This sequence works. char strA[] = "alpha"; char strB[] = "beta"; char strC[10]; strcpy(strC, strA); strcat(strC, strB); 11
  • 31. 12 Herb Schildt's C++ Programming Cookbook This corrected sequence uses strcpy( ) and strcat( ) to assign strC a string that contains the concatenation of strA and strB. Although it does achieve the desired result, manipulating strings through the use of functions rather than operators makes even the most rudimentary operations a bit clumsy. The second problem with null-terminated strings is the ease with which errors can be created. In the hands of an inexperienced or careless programmer, it is very easy to overrun the end of the array that holds a string. Because C++ provides no boundary-checking on array (or pointer) operations, there is nothing that prevents the end of an array from being exceeded. For example, the standard strcpy( ) function has no way to know if the target array is being exceeded. Therefore, if the source string contains more characters than the target array can hold, the target array will be overrun. In the best case, an array overrun simply crashes the program. However, in the worst case, it results in a security breach based on the now notorious "buffer overrun" attack. Because of the desire to integrate strings into the overall C++ type system and to prevent array overruns, a string data type was added to C++. The string type is based on the template class basic_string, which is declared in the <string> header. As mentioned, there are two specializations of this class: string and wstring, which are also declared in <string>. The string class is for char-based strings. The wstring class is for wide character strings based on wchar_t. Other than the type of characters, the two specializations work essentially the same. Since char-based strings are, by far, the most commonly used, the following discussion and all of the recipes use string, but most of the information can be readily adapted to wstring. The string class creates a dynamic data type. This means that a string instance can grow as needed during runtime to accommodate an increase in the length of the string. Not only does this eliminate the buffer overrun problem, but it also frees you from having to worry about specifying the correct length for a string. The string class handles this for you automatically. The string class defines several constructors and many functions. Here are three commonly used constructors: string(const Allocator &alloc = Allocator( ) ) string(const char *str, const Allocator &alloc = Allocator( ) ) string(const string &str, size_type start_idx = 0, size_type num = npos, const Allocator &alloc = Allocator( ) ) The first form creates an empty string object. The second creates a string object from the null-terminated string pointed to by str. This form lets you create a string from a nullterminated string. The third form creates a string from another string. The string being created contains num characters from str, beginning at the index specified by start_idx. Frequently, in the third constructor, the parameters start_idx and num are allowed to default. In this case, start_idx contains zero (indicating the start of the string) and num contains the value of npos, which (in this case) indicates the length of the longest possible string. In all cases, notice that the constructors allow an allocator to be specified. This is an object of type Allocator and it provides memory allocation for the string. Most often, this argument is allowed to default, which results in the default allocator being used.
  • 32. Chapter 2: String Handling Here is the way the constructors look when the argument defaults are used, which is often the case: string( ) string(const char *str) string (const string &str) These all use the default allocator. The first creates an empty string. The second and third create a string that contains str. The string class defines many functions, with most having several overloaded forms. Thus, a full description of each string function is not practical. Instead, the individual recipes describe in detail the functions that they employ. However, to give you an idea of the power available within string, here is a list of its core functions, grouped into categories. The following functions search the contents of a string: find rfind find_first_of find_last_of find_first_not_of find_last_not_of Returns the index at which the first occurrence of a substring or character is found within the invoking string. Returns npos if no match is found. Returns the index at which the last occurrence of a substring or character is found within the invoking string. Returns npos if no match is found. Searches the invoking string for the first occurrence of any character contained within a second string and returns the index within the invoking string at which the match is found. Returns npos if no match is found. Searches the invoking string for the last occurrence of any character contained within a second string and returns the index within the invoking string at which the match is found. Returns npos if no match is found. Searches the invoking string for the first occurrence of any character not contained within a second string and returns the index within the invoking string at which the mismatch is found. Returns npos if no match is found. Searches the invoking string for the last occurrence of any character not contained within a second string and returns the index within the invoking string at which the mismatch is found. Returns npos if no match is found. The next set of string functions alters the contents of a string: append assign clear copy erase insert push_back replace resize swap Appends a string to the end of the invoking string. Assigns a new string to the invoking string. Removes all characters from the invoking string. Copies a range of characters from the invoking string into an array. Removes one or more characters from the invoking string. Inserts a string, substring, or one or more characters into the invoking string. Adds a character to the end of the invoking string. Replaces a portion of the invoking string. Shortens or lengthens the invoking string. When shortening, characters may be lost. Exchanges two strings. 13
  • 33. 14 Herb Schildt's C++ Programming Cookbook The next functions return information about a string object: capacity Returns the number of characters that the invoking string can hold without more memory being allocated. c_str Returns a pointer to a null-terminated string that contains the same characters as those contained in the invoking string. data Returns a pointer to an array that contains the characters in the invoking string. This array is not null-terminated. empty Returns true if the invoking string is empty. length Returns the number of characters currently held in the invoking string. max_size Returns the maximum size of a string. size Same as length. The next set of functions supports iterators: begin Returns an iterator to the start of the string. end Returns an iterator to the location that is one past the end of the string. rbegin Returns a reverse iterator to the end of the string. rend Returns a reverse iterator to the location that is one before the start of the string. The next two functions obtain a substring or a character from a string: at Returns a reference to the character at a specified index within the invoking string. substr Returns a string that is a substring of the invoking string. The starting index and number of characters in the substring are specified. In addition to the functions just shown, there are two more. You can compare two strings by calling compare( ). You can cause a string to allocate sufficient memory to hold a specific number of characters by calling reserve( ). Because a string is a dynamic data structure, pre-allocating memory in advance prevents the need for costly reallocations as the string grows in length. Of course, this is helpful only if you know in advance the size of the largest string. The string class also defines several types, including size_type, which is some form of unsigned integer that is capable of holding a value equal to the length of the largest string supported by the implementation. The type of character held by a string is defined by value_type. The string class also declares several iterator types, including iterator and reverse_iterator. The string class declares a static const variable, called npos, of type size_type. This value is then initialized to –1. This results in npos containing the largest unsigned value that size_type can represent. Thus, in all cases, npos represents a value that is at least one larger than the size of the longest string. The npos variable is typically used to indicate
  • 34. Chapter 2: String Handling the "end of string" condition. For example, if a search fails, npos is returned. It is also used to request that some operation take place through the end of a string. A number of operators have been overloaded to apply to string objects. They are shown here: Operator Meaning = Assignment + Concatenation += Concatenation assignment == Equality != Inequality < Less than <= Less than or equal to > Greater than >= Greater than or equal to [] Subscripting << Output >> Input These operators allow the use of string objects in expressions and eliminate the need for calls to functions like strcpy( ), strcat( ), or strcmp( ), which are required for null-terminated strings. For example, you can use a relational operator such as < to compare two string objects, assign one string object to another by use of the = operator, and concatenate two string objects with the + operator. In general, you can mix string objects with null-terminated strings within an expression, as long as the desired outcome is a string object. For example, the + operator can be used to concatenate a string object with another string object or a string object with a C-style string. That is, the following variations are supported: string + string string + C-string C-string + string Also, you can use the = to assign a null-terminated string to a string object or compare a string object with a null-terminated string by use of the relational operators. There is another important aspect to the string class: It is also an STL-compatible container. The string class supports iterators and functions such as begin( ), end( ), and size( ), which must be implemented by all containers. Because string is a container, it is compatible with the other standard containers, such as vector. It can also be operated on by the STL algorithms. This gives you extraordinary power and flexibility when handling strings. Taken as a whole, the string class makes string handling exceedingly convenient and trouble-free. You can perform most common string operations through operators, and string's rich assortment of member functions make tasks such as searching, replacing, and comparing 15
  • 35. 16 Herb Schildt's C++ Programming Cookbook strings easy and relatively error-free. You don't need to worry about overrunning an array, for example, when you assign one string to another. In general, the string type offers safety and convenience that far exceeds that of null-terminated strings. Despite the advantages of the string class, null-terminated strings are still widely used in C++. One reason (as explained earlier) is that string literals are null-terminated strings. Another reason is that all of the power of string comes at a price. In some cases, operations on string objects are slower than operations on null-terminated strings. Therefore, for applications in which high performance is a principal concern and the benefits of string are not needed, null-terminated strings are still a good choice. It is important to state, however, that for many other uses, the string class is the best choice. String Exceptions Although string handling via string avoids many of the mishaps that are common with null-terminated strings, it is still possible to generate errors. Fortunately, when an error occurs when manipulating a string, an exception results, rather than a program crash or a security breach. This gives you a chance to rectify the error, or at least perform an orderly shutdown. There are two exceptions that can be generated when working with string objects. The first is length_error. This exception is thrown when an attempt is made to create a string that is longer than the longest possible string. This could happen in a number of different cases, such as when concatenating strings or inserting one substring into another. The length of the longest possible string is found by calling the max_size( ) function. The second exception is out_of_range. It is thrown when an argument is out of range. Both of these exceptions are declared in <stdexcept>. Because none of the examples in this chapter generate these exceptions, the examples do not explicitly handle them. However, in your own applications, you might need to do so. Perform Basic Operations on Null-Terminated Strings Key Ingredients Headers Classes <cstring> Functions char *strcat(char *str1, const char *str2) int strcmp(const char *str1, const char *str2) char *strcpy(char *target, const char *source) size_t strlen(const char *str) This recipe shows how to perform the following basic null-terminated string operations: • Obtain the length of a string. • Copy a string.
  • 36. Chapter 2: String Handling • Concatenate one string to the end of another. • Compare two strings. These are the operations that are commonly needed whenever null-terminated strings are used in a C++ program. They will be familiar to many readers—especially those who have a background in C programming. We begin with them because they illustrate fundamental concepts related to working with null-terminated strings. They also illustrate why you must be careful to avoid buffer overrun errors when using null-terminated strings. Step-by-Step To perform the basic null-terminated string operations involves these steps: 1. Include the header <cstring>. 2. To obtain the length of a string, call strlen( ). 3. To copy one string to another, call strcpy( ). 4. To concatenate one string to the end of another, call strcat( ). 5. To compare two strings, call strcmp( ). Discussion The functions that support null-terminated strings are declared in the header <cstring>. Thus, a program that uses these (or the other functions that operate on null-terminated strings) must include this header. To obtain the length of a null-terminated string, call strlen( ), shown here: size_t strlen(const char *str) It returns the number of characters in the string pointed to by str. As explained in the overview, a null-terminated string is simply an array of characters that is terminated with a null. The value returned by strlen( ) does not include the null terminator. Thus, the string "test" has a length of 4. Understand, however, that the array that will hold "test" must be at least five characters long so that there is room for the null terminator. The type size_t is some form of unsigned integer that is capable of representing the result of the sizeof operations. Thus, it is a type that is capable of representing the length of the longest string. To copy one null-terminated string to another, use strcpy( ), shown next: char *strcpy(char *target, const char *source) This function copies the characters in the string pointed to by source into the array pointed to by target. The result is null-terminated. In all cases, you must ensure that the array pointed to by target is large enough to hold the characters pointed to by source. If you don't, the copy will overwrite the end of the target array. This will corrupt your program and is one way that the notorious "buffer overrun attack" can be generated. The function returns target. To concatenate one null-terminated string to the end of another, call strcat( ): char *strcat(char *str1, const char *str2) 17
  • 37. 18 Herb Schildt's C++ Programming Cookbook This function copies the characters in the string pointed to by str2 to the end of the string pointed to by str1. The resulting string is null-terminated. It is imperative that the array pointed to by str1 be large enough to hold the resulting string. If it isn't, an array overrun will occur. This will also corrupt your program and is another way that a buffer overrun attack can occur. The function returns str1. You can lexicographically compare (compare using dictionary order) two strings using strcmp( ), shown next: int strcmp(const char *str1, const char *str2) It returns zero if the two strings are the same. Otherwise, it returns less than zero if the string pointed to by str1 is less than the string pointed to by str2 and greater than zero if the string pointed to by str1 is greater than the string pointed to by str2. The comparison is case-sensitive. Example The following example shows strcpy( ), strcat( ), strcmp( ), and strlen( ) in action: // Demonstrate the basic null-terminated string functions. #include <iostream> #include <cstring> using namespace std; int main() { char strA[7] char strB[5] char strC[5] char strD[6] cout cout cout cout cout << << << << << = = = = "Up"; "Down"; "Left"; "Right"; "Here are the strings: " << endl; "strA: " << strA << endl; "strB: " << strB << endl; "strC: " << strC << endl; "strD: " << strD << "nn"; // Display the length of strA. cout << "Length of strA is " << strlen(strA) << endl; // Concatenate strB with strA. strcat(strA, strB); cout << "strA after concatenation: " << strA << endl; cout << "Length of strA is now " << strlen(strA) << endl; // Copy strC into strB. strcpy(strB, strC); cout << "strB now holds: " << strB << endl; // Compare strings. if(!strcmp(strB, strC)) cout << "strB is equal to strCn";
  • 38. Chapter 2: String Handling int result = strcmp(strC, strD); if(!result) cout << "strC is equal to strDn"; else if(result < 0) cout << "strC is less than strDn"; else if(result > 0) cout << "strC is greater than strDn"; return 0; } The output is shown here: Here are the strings: strA: Up strB: Down strC: Left strD: Right Length of strA is 2 strA after concatenation: UpDown Length of strA is now 6 strB now holds: Left strB is equal to strC strC is less than strD Notice how the array that holds strA was declared to be larger than needed to hold its initial string. This extra room allows it to accommodate the concatenation of strB. Also, notice how strB and strC are the same size. This makes it possible to copy the contents of strC into strB. Remember, in all cases, the array that receives the result of a string copy or concatenation must be large enough. For example, in the preceding program, attempting to copy strD into strC would cause an error, because strC is only five elements long, but strD requires six elements (five for the characters in Right and one for the null terminator). Options and Alternatives In cases in which you do not know at compile time whether the length of the target array is sufficient to hold the result of a string copy or concatenation, you will need to confirm that fact at runtime prior to attempting the operation. One way to do this is to use sizeof to determine the size of the target array. For example, assuming the preceding example program, here is one way to add a "safety check" that ensures that strA is large enough to hold the concatenation of both strA and strB: if(sizeof(strA) > strlen(strA) + strlen(strB)) strcat(strA, strB); Here, the size of the target array is obtained by calling sizeof on the array. This returns the length of the array in bytes, which for arrays of type char equals the number of characters in the array. This value must be greater than the sum of the two strings that will be concatenated. (Remember, one extra character is needed to hold the null terminator.) By using this approach, you ensure that the target array will not be overrun. 19
  • 39. 20 Herb Schildt's C++ Programming Cookbook NOTE The preceding technique for preventing an array overrun works for char strings, not for wchar_t strings. For wchar_t strings, you will need to use an expression like if(sizeof(strA) > wcslen(strA)*sizeof(wchar_t) + wcslen(strB)*sizeof(wchar_t)) // ... This takes into consideration the size of a wide character. Sometimes you may want to operate on only a portion of a string, rather than the entire string. For example, you might want to copy just part of one string to another or compare only a portion of two strings. C++ includes functions that handle these types of situations. They are strncpy( ), strncat( ), and strncmp( ). Each is described next. To copy only a portion of one string to another, use strncpy( ), shown here: char *strncpy(char *target, const char *source, size_t count) This function copies not more than count characters from source to target. If source contains less than count characters, null characters will be appended to the end of target until count characters have been copied. However, if the string pointed to by source is longer than count characters, the resultant string pointed to by target will not be null-terminated. It returns target. You can concatenate only a portion of a string to another by calling strncat( ), shown next: char *strncat(char *str1, const char *str2, size_t count) It concatenates not more than count characters from the string pointed to by str2 to the end of str1. It returns str1. To compare a portion of one string to another, use strncmp( ), shown next: int strncmp(const char *str1, const char *str2, size_t count) The strncmp( ) function compares not more than the first count characters in the string pointed to by str1 with the string pointed to by str2. It returns less than zero if str1 is less than str2, greater than zero if str1 is greater than str2, and zero if the two strings are the same. Search a Null-Terminated String Key Ingredients Headers <cstring> Classes Functions char *strchr(const char *str, int ch) char *strpbrk(const char *str1, const char *str2) char *strstr(const char *str1, const char *str2) Another common part of string handling involves searching. Here are three examples. You might want to know whether a string contains the substring ".com" or ".net" when
  • 40. Chapter 2: String Handling processing an Internet address. You might want to find the first period in a file name so that you can separate the file's name from its extension. You might want to scan a billing log for occurrences of the string "Past Due" so that you can count the number of past due accounts. To handle these types of tasks, C++ provides functions that search a null-terminated string. This recipe demonstrates several of them. Specifically, it shows how to search a string for a specific character, for any of a set of characters, or for a substring. Step-by-Step To search a string involves the following steps: 1. To search for a specific character, call strchr( ). 2. To search for any of a set of characters, call strpbrk( ). 3. To search for a substring, call strstr( ). Discussion To find the first occurrence of a given character within a string, call strchr( ), shown here: char *strchr(const char *str, int ch) It returns a pointer to the first occurrence of the low-order byte of ch in the string pointed to by str. If no match is found, a null pointer is returned. To find the first occurrence of any character within a set of characters, call strpbrk( ), shown next: char *strpbrk(const char *str1, const char *str2) This function returns a pointer to the first character in the string pointed to by str1 that matches any character in the string pointed to by str2. If no match is found, a null pointer is returned. To find the first occurrence of a given substring within a string, call strstr( ), shown here: char *strstr(const char *str1, const char *str2) It returns a pointer to the first occurrence of the string pointed to by str2 within the string pointed to by str1. If no match is found, a null pointer is returned. Example The following example demonstrates strchr( ), strpbrk( ), and strstr( ): // Search a null-terminated string. #include <iostream> #include <cstring> using namespace std; int main() { const char *url = "HerbSchildt.com"; const char *url2 = "Apache.org"; 21
  • 41. 22 Herb Schildt's C++ Programming Cookbook const char *emailaddr = "Herb@HerbSchildt.com"; const char *tld[] = { ".com", ".net", ".org" }; const char *p; // First, determine if url and url2 contain .com, .net, or .org. for(int i=0; i < 3; i++) { p = strstr(url, tld[i]); if(p) cout << url << " has top-level domain " << tld[i] << endl; p = strstr(url2, tld[i]); if(p) cout << url2 << " has top-level domain " << tld[i] << endl; } // Search for a specific character. p = strchr(emailaddr, '@'); if(p) cout << "Site name of e-mail address is: " << p+1 << endl; // Search for any of a set of characters. In this case, // find the first @ or period. p = strpbrk(emailaddr, "@."); if(p) cout << "Found " << *p << endl; return 0; } The output is shown here: HerbSchildt.com has top-level domain .com Apache.org has top-level domain .org Site name of e-mail address is: HerbSchildt.com Found @ Options and Alternatives In addition to the search functions used by this recipe, there are several others supported by C++. Two that are especially helpful in some cases are strspn( ) and strcspn( ). They are shown here: size_t strspn(const char *str1, const char *str2) size_t strcspn(const char *str1, const char *str2) The strspn( ) function returns the index of the first character in the string pointed to by str1 that does not match any of the characters in the string pointed to by str2. The strcspn( ) function returns the index of the first character in the string pointed to by str1 that matches any character in the string pointed to by str2. You can find the last occurrence of a character within a null-terminated string by calling strrchr( ): char *strrchr(const char *str, int ch)
  • 42. Chapter 2: String Handling It returns a pointer to the last occurrence of the low-order byte of ch in the string pointed to by str. If no match is found, a null pointer is returned. The strtok( ) function is also used to search a string. It is described in its own recipe. See Tokenize a Null-Terminated String. Reverse a Null-Terminated String Key Ingredients Headers Classes <cstring> Functions size_t strlen(char *str) This recipe shows how to perform one simple, yet useful task. It reverses a null-terminated string. Although reversing a string is an easy operation for the experienced programmer, it is a common source of questions from beginners. For this reason alone it merits inclusion in this book. However, there are several other reasons to include this recipe. First, there are many ways to reverse a string, and each variation illustrates a different technique for handling a null-terminated string. Second, the basic mechanism used to reverse a string can be adapted to other types of string manipulations. Finally, it demonstrates in very practical terms how handling null-terminated strings often relies on fairly low-level, hands-on code. Often, such code can be highly efficient, but it requires more work than using the string class. The recipe shown here reverses the string in place. This means that the original string is modified. This is often what is needed. However, a variation that creates a reverse copy of the string is shown in the Options and Alternatives section for this recipe. Step-by-Step There are many ways to approach the task of reversing a string. This recipe uses a simple, yet effective method that is based on swapping end-to-end corresponding characters in the string. It puts this code inside a function called revstr( ). 1. Create a function called revstr( ) that has this prototype: void revstr(char *str); The string to be reversed is passed to str. 2. Inside revstr( ), create a for loop that controls two variables that will be used to index the array that holds the string. Initialize the first variable to zero and increment it each time through the loop. Initialize the second variable to the index of the last character in the string and decrement it with each iteration. This value is obtained by calling strlen( ). 3. With each pass through the loop, swap the characters at the two indexes. 4. Stop the loop when the first index is equal to or greater than the second index. At this point, the string has been reversed. 23
  • 43. 24 Herb Schildt's C++ Programming Cookbook Discussion As most readers know, when an array name is used by itself, without an index, it represents a pointer to the array. Therefore, when you pass an array to a function, you are actually passing only a pointer to that array. This means that a function that will receive a nullterminated string as an argument must declare its parameter to be of type char *. This is why the str parameter to revstr( ) is declared as char *str. Although str is a pointer, it can be indexed like an array, using the normal array-indexing syntax. To reverse the contents of the string, create a for loop that controls two variables, which serve as indexes into the string. One index starts at zero and indexes from the beginning of the string. The other index starts at the last character in the string. Each time through the loop, the characters at the specified indexes are exchanged. Then, the first index is incremented and the second index is decremented. When the indexes converge (that is, when the first index is equal to or greater than the second index), the string is reversed. Here is one way to write this loop: int i, j; char t; for(i = 0, j = strlen(str)-1; i < j; ++i, --j) { // Exchange corresponding characters, front to back. t = str[i]; str[i] = str[j]; str[j] = t; } Notice that the index of the last character in the string is obtained by subtracting one from the value returned by strlen( ). Its prototype is shown here: size_t strlen(const char *str) The strlen( ) function returns the length of a null-terminated string, which is the number of characters in the string. However, the null terminator is not counted. Since array indexing in C++ begins at zero, 1 must be subtracted from this value to obtain the index of the last character in the string. Example Putting together the pieces, here is one way to write the revstr( ) function: // Reverse a string in place. void revstr(char *str) { int i, j; char t; for(i = 0, j = strlen(str)-1; i < j; ++i, --j) { // Exchange corresponding characters, front to back. t = str[i]; str[i] = str[j]; str[j] = t; } }
  • 44. Chapter 2: String Handling The following program shows revstr( ) in action: // Reverse a string in place. #include <iostream> #include <cstring> using namespace std; void revstr(char *str); int main() { char str[] = "abcdefghijklmnopqrstuvwxyz"; cout << "Original string: " << str << endl; revstr(str); cout << "Reversed string: " << str << endl; return 0; } // Reverse a string in place. void revstr(char *str) { int i, j; char t; for(i = 0, j = strlen(str)-1; i < j; ++i, --j) { t = str[i]; str[i] = str[j]; str[j] = t; } } The output is shown here: Original string: abcdefghijklmnopqrstuvwxyz Reversed string: zyxwvutsrqponmlkjihgfedcba Options and Alternatives Although reversing a null-terminated string is a simple task, it does allow a number of interesting variations. For example, the approach used in the recipe relies on array indexing, which is probably the clearest way to implement this function. It may not be, however, the most efficient. One alternative is to use pointers rather than array indexing. Depending upon what compiler you are using (and what optimizations are turned on), pointer operations can be faster than array indexing. Also, many programmers simply prefer the use of pointers rather than array indexing when cycling through an array in a strictly 25
  • 45. 26 Herb Schildt's C++ Programming Cookbook sequential fashion. Whatever the reason, the pointer version is easy to implement. Here is one way to rework revstr( ) so that it substitutes pointer operations for array indexing: // Reverse a string in place. Use pointers rather than array indexing. void revstr(char *str) { char t; char *inc_p = str; char *dec_p = &str[strlen(str)-1]; while(inc_p <= dec_p) { t = *inc_p; *inc_p++ = *dec_p; *dec_p-- = t; } } One of the more interesting approaches to reversing a string makes use of recursion. Here is one implementation: // Reverse a string in place by using recursion. void revstr_r(char *str) { revstr_recursive(str, 0, strlen(str)-1); } // This function is called with a pointer to the string to reverse // and the beginning and ending indexes of the characters to reverse. // Thus, its first call passes zero for start and strlen(str)-1 for // end. The position of the null terminator does not change. void revstr_recursive(char *str, int start, int end) { if(start < end) revstr_recursive(str, start+1, end-1); else return; char t = str[start]; str[start] = str[end]; str[end] = t; } Notice that revstr_r( ) calls revstr_recursive( ) to actually reverse the string. This lets revstr_r( ) be called with just a pointer to the string. Notice how the recursive calls reverse the string. When start is less than end, a recursive call to revstr_recursive( ) is made, with the beginning index incremented by one and the ending index decremented by one. When these two indexes meet, the return statement is executed. This causes the recursive calls to begin returning, with corresponding characters being exchanged. As a point of interest, the same general technique can be used to reverse the contents of any type of array. Its use on a nullterminated string is simply a special case. The last alternative presented here works differently from the previous approaches because it creates a copy of the original string that contains the reverse of the original string. Thus, it leaves the original string unchanged. This technique is useful when the original string must not be modified.
  • 46. Chapter 2: String Handling // Make a reverse copy of a string. void revstrcpy(char *rstr, const char *orgstr) { rstr += strlen(orgstr); *rstr-- = '0'; while(*orgstr) *rstr-- = *orgstr++; } This function is passed a pointer to the original string in orgstr and a pointer to a char array that will receive the reversed string in rstr. Of course, the array pointed to by rstr must be large enough to hold the reversed string plus the null terminator. Here is an example of how revstrcpy( ) can be called: char str[5] = "abcd"; char rev[5]; revstrcpy(rev, str); After the call, rev will contain the characters dcba and str will be unaltered. Ignore Case Differences When Comparing Null-Terminated Strings Key Ingredients Headers <cctype> Classes Functions int tolower(int ch) The standard strcmp( ) function is case-sensitive. Therefore, the two strings "test" and "Test" compare as different. Although a case-sensitive comparison is often what is needed, there are times when a case-insensitive approach is required. For example, if you are alphabetizing a list of entries for the index of a book, some of those entries might be proper nouns, such as the name of a person. Despite the case differences, you want the alphabetical order to be preserved. For example, you want "Stroustrup" to come after "class". The trouble is that the lowercase letters are represented by values that are 32 greater than the uppercase letters. Therefore, performing a case-sensitive comparison on "Stroustrup" and "class" yields a result that puts "class" after "Stroustrup". To solve this problem, you must use a comparison function that ignores case differences. This recipe shows one way to do this. Step-by-Step One way to ignore case differences when comparing null-terminated strings is to create your own version of the strcmp( ) function. This is quite easy to do, as this recipe shows. The key is to convert each set of characters to the same case and then compare the two. This 27
  • 47. 28 Herb Schildt's C++ Programming Cookbook recipe converts each character to lowercase using the standard function tolower( ), but the conversion to uppercase would work just as well. 1. Create a function called strcmp_ign_case( ) that has this prototype: int strcmp_ign_case(const char *str1, const char *str2); 2. Inside strcmp_ign_case( ), compare each corresponding character in the two strings. To do this, set up a loop that iterates as long as the null terminator of one of the strings has not been reached. 3. Inside the loop, first convert each character to lowercase by calling tolower( ). Then compare the two characters. Continue comparing characters until the end of one of the strings is reached or the two characters differ. Notice that tolower( ) requires the header <cctype>. 4. When the loop stops, return the result of subtracting the last character compared from the second string from the last character compared from the first string. This causes the function to return less than zero if str1 is less than str2, zero if the two are equal (in this case, the terminating null of str2 is subtracted from the terminating null of str1), or greater than zero if str1 is greater than str2. Discussion The standard function tolower( ) was originally defined by C and is supported by C++ in a couple of different ways. The version used here is declared within the header <cctype>. It converts uppercase to lowercase based on the character set defined by the current locale. It is shown here: int tolower(int ch) It returns the lowercase equivalent of ch, which must be an 8-bit value. Non-alphabetical characters are returned unchanged. To compare two null-terminated strings independently of case differences, you must compare corresponding characters in the strings after normalizing them to a common case. In this recipe, the characters are converted to lowercase. Here is an example of a loop that compares characters in two strings but ignores case differences: while(*str1 && *str2) { if(tolower(*str1) != tolower(*str2)) break; ++str1; ++str2; } Notice that the loop will stop when the end of either string is reached or when a mismatch is encountered. When the loop ends, you must return a value that indicates the outcome of the comparison. This is easy to do. Simply return the result of subtracting the last character pointed to by str2 from the last character pointed to by str1, as shown here: return tolower(*str1) - tolower(*str2);
  • 48. Chapter 2: String Handling This returns zero if the null terminator of both strings has been encountered, indicating equality. Otherwise, if the character pointed to by str1 is less than the one pointed to by str2, a negative value is returned, indicating that the first string is less than the second. If the character pointed to by str1 is greater than the character pointed to by str2, a positive value is returned, indicating that the first string is greater than the second. Thus, it produces the same outcome as strcmp( ), but in a case-insensitive manner. Example Putting it all together, here is one way to implement a case-insensitive string comparison function called strcmp_ign_case( ): // A simple string comparison function that ignores case differences. int strcmp_ign_case(const char *str1, const char *str2) { while(*str1 && *str2) { if(tolower(*str1) != tolower(*str2)) break; ++str1; ++str2; } return tolower(*str1) - tolower(*str2); } The following program puts the string_ign_case( ) function into action: // Ignore case differences when comparing strings. #include <iostream> #include <cctype> using namespace std; int strcmp_ign_case(const char *str1, const char *str2); void showresult(const char *str1, const char *str2, int result); int main() { char strA[]= "tesT"; char strB[] = "Test"; char strC[] = "testing"; char strD[] = "Tea"; int result; cout cout cout cout cout << << << << << "Here are the strings: " << endl; "strA: " << strA << endl; "strB: " << strB << endl; "strC: " << strC << endl; "strD: " << strD << "nn"; 29
  • 49. 30 Herb Schildt's C++ Programming Cookbook // Compare strings ignoring case. result = strcmp_ign_case(strA, strB); showresult(strA, strB, result); result = strcmp_ign_case(strA, strC); showresult(strA, strC, result); result = strcmp_ign_case(strA, strD); showresult(strA, strD, result); result = strcmp_ign_case(strD, strA); showresult(strD, strA, result); return 0; } // A simple string comparison function that ignores case differences. int strcmp_ign_case(const char *str1, const char *str2) { while(*str1 && *str2) { if(tolower(*str1) != tolower(*str2)) break; ++str1; ++str2; } return tolower(*str1) - tolower(*str2); } void showresult(const char *str1, const char *str2, int result) { cout << str1 << " is "; if(!result) cout << "equal to "; else if(result < 0) cout << "less than "; else cout << "greater than "; cout << str2 << endl; } The output is shown here: Here are the strings: strA: tesT strB: Test strC: testing strD: Tea tesT is equal to Test tesT is less than testing tesT is greater than Tea Tea is less than tesT
  • 50. Chapter 2: String Handling Options and Alternatives As explained, the version of tolower( ) declared in <cctype> converts characters based on the current locale. Frankly, this is often what you want, so it makes a good (and convenient) choice in most cases. However, tolower( ) is also declared within <locale>, which declares the members of C++'s localization library. (Localization aids in the creation of code that can be easily internationalized.) Here is this version of tolower( ): template <class charT> charT tolower(charT ch, const locale &loc) This version of tolower( ) makes it possible to specify a different locale when converting the case of a letter. For example: char ch; //... locale loc("French"); cout << tolower(ch, loc); This call to tolower( ) uses the locale information compatible with French. Although there is no advantage to doing so, it is also possible to convert each character in the string to uppercase (rather than lowercase) to eliminate case differences. This is done via the toupper( ) function, shown here: int toupper(int ch) It works just like tolower( ), except that it converts characters to uppercase. Create a Search-and-Replace Function for Null-Terminated Strings Key Ingredients Headers <ccstring> Classes Functions char *strncpy(char *target, const char *source, int count) void *memmove(void *target, const void *source, size_t count) When working with strings, it is not uncommon to need to substitute one substring for another. This operation involves two steps. First, you must find the substring to replace, and second, you must replace it with the new substring. This process is commonly referred to as "search and replace." This recipe shows one way to accomplish this for null-terminated strings. There are various ways to implement a "search-and-replace" function. This recipe uses an approach in which the replacement takes place on the original string, thus modifying it. Two other approaches are described in the Options and Alternatives section for this recipe. 31
  • 51. 32 Herb Schildt's C++ Programming Cookbook Step-by-Step One way to implement a "search-and-replace" function for a null-terminated string involves the following steps. It creates a function called search_and_replace( ) that replaces the first occurrence of one substring with another. 1. Create a function called search_and_replace( ) that has this prototype: bool search_and_replace(char *orgstr, int maxlen, const char *oldsubstr, const char *newsubstr); A pointer to the original string is passed via orgstr. The maximum number of characters that orgstr can hold is passed in maxlen. A pointer to the substring to search for is passed through oldsubstr, and a pointer to the replacement is passed in newsubstr. The function will return true if a substitution has been made. That is, it returns true if the string originally contained at least one occurrence of oldsubstr. It returns false if no substitution takes place. 2. Search for a substring by calling strstr( ). It returns a pointer to the beginning of the first matching substring or a null pointer if no match is found. 3. If the substring is found, shift the remaining characters in the string as needed to create a "hole" in the string that is exactly the size of the replacement substring. This can be most easily done by calling memmove( ). 4. Using strncpy( ), copy the replacement substring into the "hole" in the original string. 5. Return true if a substitution was made and false if the original string is unchanged. Discussion To find a substring within a string, use the strstr( ) function, shown here: char *strstr(const char *str1, const char *str2) It returns a pointer to the start of the first occurrence of the string pointed to by str2 in the string pointed to by str1. If no match is found, a null pointer is returned. Conceptually, when one substring is replaced by another, the old substring must be removed and its replacement inserted. In practice, there is no need to actually remove the old substring. Instead, you can simply overwrite the old substring with the new one. However, you must prevent the remaining characters in the string from being overwritten when the new substring is longer than the old substring. You must also ensure that there is no gap when the new substring is shorter than the old substring. Therefore, unless the new substring is the same size as the old one, you will need to move the remaining characters in the original string up or down so that you create a "hole" in the original string that is the same size as the new substring. An easy way to accomplish this is to use memmove( ), shown here: void *memmove(void *target, const void *source, size_t count) It copies count characters from the array pointed to by source into the array pointed to by target. It returns target. The copy takes place correctly even if the arrays overlap. This means that it can be used to move characters up or down in the same array.
  • 52. Chapter 2: String Handling After you have created the properly sized "hole" in the string, you can copy the new substring into the hole by calling strncpy( ), shown here: char *strncpy(char *target, const char *source, size_t count) This function copies not more than count characters from source to target. If the string pointed to by source contains less than count characters, null characters will be appended to the end of target until count characters have been copied. However, if the string pointed to by source is longer than count characters, the resultant string will not be null-terminated. It returns target. If the two strings overlap, the behavior of strncpy( ) is undefined. Have search_and_replace( ) return true when a substitution takes place and false if the substring is not found or if the modified string exceeds the maximum permissible length of the resulting string. Example Here is one way to implement the search_and_replace( ) function. It replaces the first occurrence of oldsubstr with newsubstr. // Replace the first occurrence of oldsubstr with newsubstr // in the string pointed to by str. This means that the string // pointed to by str is modified by this function. // // The maximum size of the resulting string is passed in maxlen. // This value must be less than the size of the array that holds // str in order to prevent an array overrun. // // It returns true if a replacement was made and false otherwise. bool search_and_replace(char *str, int maxlen, const char *oldsubstr, const char *newsubstr) { // Don't allow the null terminator to be substituted. if(!*oldsubstr) return false; // Next, check that the resulting string has a length // less than or equal to the maximum number of characters allowed // as specified by maxlen. If the maximum is exceeded, the // function ends by returning false. int len = strlen(str) - strlen(oldsubstr) + strlen(newsubstr); if(len > maxlen) return false; // See if the specified substring is in the string. char *p = strstr(str, oldsubstr); // If the substring is found, replace it with the new one. if(p) { // First, use memmove() to move the remainder of the // string so that the new substring can replace the old one. // In other words, this step either increases or decreases // the size of the "hole" that the new substring will fill. memmove(p+strlen(newsubstr), p+strlen(oldsubstr), strlen(p)-strlen(oldsubstr)+1); 33
  • 53. 34 Herb Schildt's C++ Programming Cookbook // Now, copy substring into str. strncpy(p, newsubstr, strlen(newsubstr)); return true; } // Return false if no replacement was made. return false; } Notice that the function will not put more than maxlen characters into str. The maxlen parameter is used to prevent array overruns. You must pass it a value that is, at most, one less than the size of the array pointed to by str. It must be one less than the size of the array because you must allow room for the null terminator. The following program shows the search_and_replace( ) function in action: // Implement "search and replace" for null-terminated strings. #include <iostream> #include <cstring> using namespace std; bool search_and_replace(char *orgstr, int maxlen, const char *oldsubstr, const char *newsubstr); int main() { char str[80] = "alpha beta gamma alpha beta gamma"; cout << "Original string: " << str << "nn"; cout << "First, replace all instances of alpha with epsilon.n"; // Replace all occurrences of alpha with epsilon. while(search_and_replace(str, 79, "alpha", "epsilon")) cout << "After a replacement: " << str << endl; cout << "nNext, replace all instances of gamma with zeta.n"; // Replace all occurrences of gamma with zeta. while(search_and_replace(str, 79, "gamma", "zeta")) cout << "After a replacement: " << str << endl; cout << "nFinally, remove all occurrences of beta.n"; // Replace all occurrences of beta with a null string. // This has the effect of removing beta from the string. while(search_and_replace(str, 79, "beta", "")) cout << "After a replacement: " << str << endl; return 0; }
  • 54. Chapter 2: String Handling // Replace the first occurrence of oldsubstr with newsubstr // in the string pointed to by str. This means that the string // pointed to by str is modified by this function. // // The maximum size of the resulting string is passed in maxlen. // This value must be less than the size of the array that holds // str in order to prevent an array overrun. // // It returns true if a replacement was made and false otherwise. bool search_and_replace(char *str, int maxlen, const char *oldsubstr, const char *newsubstr) { // Don't allow the null terminator to be substituted. if(!*oldsubstr) return false; // Next, check that the resulting string has a length // less than or equal to the maximum number of characters allowed // as specified by maxlen. If the maximum is exceeded, the // function ends by returning false. int len = strlen(str) - strlen(oldsubstr) + strlen(newsubstr); if(len > maxlen) return false; // See if the specified substring is in the string. char *p = strstr(str, oldsubstr); // If the substring is found, replace it with the new one. if(p) { // First, use memmove() to move the remainder of the // string so that the new substring can replace the old one. // In other words, this step either increases or decreases // the size of the "hole" that the new substring will fill. memmove(p+strlen(newsubstr), p+strlen(oldsubstr), strlen(p)-strlen(oldsubstr)+1); // Now, copy substring into str. strncpy(p, newsubstr, strlen(newsubstr)); return true; } // Return false if no replacement was made. return false; } The output is shown here: Original string: alpha beta gamma alpha beta gamma First, replace all instances of alpha with epsilon. After a replacement: epsilon beta gamma alpha beta gamma After a replacement: epsilon beta gamma epsilon beta gamma 35
  • 55. 36 Herb Schildt's C++ Programming Cookbook Next, replace all instances of gamma with zeta. After a replacement: epsilon beta zeta epsilon beta gamma After a replacement: epsilon beta zeta epsilon beta zeta Finally, remove all occurrences of beta. After a replacement: epsilon zeta epsilon beta zeta After a replacement: epsilon zeta epsilon zeta Options and Alternatives As it is written, the search_and_replace( ) function substitutes a substring within the original string. This means that the original string is modified. However, it is possible to take a different approach in which the original string is left unchanged and the substituted string is returned in another array. One way to do this is to pass a pointer to a string into which the result is copied. This technique leaves the original string unchanged. This alternative is shown here: // This replaces the first occurrence of oldsubstr with newsubstr. // The resulting string is copied into the string passed in // resultstr. This means that the original string is unchanged. // The result string must be large enough to hold the // string that results after replacing oldsubstr with newsubstr. // The maximum number of characters to copy into resultstr // is passed in maxlen. It returns true if a replacement was made // and false otherwise. bool search_and_replace_copy(const char *orgstr, char *resultstr, int maxlen, const char *oldsubstr, const char *newsubstr) { // Don't allow the null terminator to be substituted. if(!*oldsubstr) return false; // Next, check that the resulting string has a length // less than the maximum number of characters allowed // as specified by maxlen. If the maximum is exceeded, // the function ends by returning false. int len = strlen(orgstr) - strlen(oldsubstr) + strlen(newsubstr); if(len > maxlen) return false; // See if the specified substring is in the string. const char *p = strstr(orgstr, oldsubstr); // If the substring is found, replace it with the new one. if(p) { // Copy first part of original string. strncpy(resultstr, orgstr, p-orgstr); // Null-terminate the first part of resultstr so that it // can be operated on by the other string functions. *(resultstr + (p-orgstr)) = '0';
  • 56. Chapter 2: String Handling // Substitute the new substring. strcat(resultstr, newsubstr); // Add the remainder of the original string, // skipping over the old substring that was replaced. strcat(resultstr, p+strlen(oldsubstr)); return true; } // Return false if no replacement was made. return false; } The comments give a "play-by-play" description of how search_and_replace_copy( ) works. Here is a synopsis. The function begins by finding the first occurrence in orgstr of the substring passed in oldsubstring. It then copies the original string (orgstr) into the result string (resultstr) up to the point at which the substring was found. Next, it copies the replacement substring into resultstr. Finally, it copies the remainder of orgstr into resultstr. Thus, on return, resultstr contains a copy of orgstr, with the only difference being the substitution of newsubstr for oldsubstr. To prevent array overruns, search_and_replace_copy( ) will copy only up to maxlen characters into resultstr. Therefore, the array pointed to by resultstr must be at least maxlen+1 characters long. The extra character leaves room for the null terminator. Another alternative that is useful in some cases is to have the search_and_replace( ) function dynamically allocate a new string that holds the resulting string and return a pointer to it. This approach offers one big advantage: You don't need to worry about array boundaries being overrun because you can allocate a properly sized array. This means that you don't need to know the size of the resulting string in advance. The main disadvantage is that you must remember to delete the dynamically allocated string when it is no longer needed. Here is one way to implement such an approach: // Replace the first occurrence of oldsubstr with newsubstr // in str. Return a pointer to a new string that contains // the result. The string pointed to by str is unchanged. // Memory for the new string is dynamically allocated and must be // released when it is no longer needed. If no substitution is made, // a null pointer is returned. This function throws bad_alloc if // a memory allocation failure occurs. char *search_and_replace_alloc(const char *str, const char *oldsubstr, const char *newsubstr) throw(bad_alloc) { // Don't allow the null terminator to be substituted. if(!*oldsubstr) return 0; // Allocate an array that is large enough to hold the resulting string. int size = strlen(str) + strlen(newsubstr) - strlen(oldsubstr) + 1; char *result = new char[size]; 37
  • 57. 38 Herb Schildt's C++ Programming Cookbook const char *p = strstr(str, oldsubstr); if(p) { // Copy first part of original string. strncpy(result, str, p-str); // Null-terminate the first part of result so that // it can be operated on by other string functions. *(result+(p-str)) = '0'; // Substitute the new substring. strcat(result, newsubstr); // Add the remainder of the original string. strcat(result, p+strlen(oldsubstr)); } else { delete [] result; // release the unused memory return 0; } return result; } Notice that search_and_replace_alloc( ) throws bad_alloc if the allocation of the temporary array fails. Remember, memory is finite and it is possible to run out. This is especially true for embedded systems. Therefore, the caller of this version may need to handle this exception. For example, here is the basic framework that you can use to call search_and_replace_alloc( ): char *ptr; try { ptr = search_and_replace_alloc(str, old, new) } catch(bad_alloc exc) { // Take appropriate action here. } if(ptr) { // Use the string... // Delete the memory when no longer needed. delete [] ptr; }
  • 58. Chapter 2: String Handling Categorize Characters Within a Null-Terminated String Key Ingredients Headers Classes <cctype> Functions int int int int int int int int int int int isalnum(int ch) isalpha(int ch) iscntrl(int ch) isdigit(int ch) isgraph(int ch) islower(int ch) isprint(int ch) ispunct(int ch) isspace(int ch) isupper(int ch) isxdigit(int ch) Sometimes you will want to know what sorts of characters a string contains. For example, you might want to remove all whitespace (spaces, tabs, and newlines) from a file or display non-printing characters using some type of visual representation. To perform these tasks implies that you can categorize characters into different types, such as alphabetical, control, digits, punctuation, and so on. Fortunately, C++ makes it very easy to accomplish this by using one or more standard functions that determine a character's category. Step-by-Step The character functions make it quite easy to categorize a character. It involves these steps: 1. All of the character categorization functions are declared in <cctype>. Therefore, it must be included in your program. 2. To determine if a character is a letter or digit, call isalnum( ). 3. To determine if a character is a letter, call isalpha( ). 4. To determine if a character is a control character, call iscntrl( ). 5. To determine if a character is a digit, call isdigit( ). 6. To determine if a character is visible (excluding the space), call isgraph( ). 7. To determine if a character is a lowercase letter, call islower( ). 8. To determine if a character is printable (including the space), call isprint( ). 9. To determine if a character is punctuation, call ispunct( ). 10. To determine if a character is whitespace, call isspace( ). 11. To determine if a character is an uppercase letter, call isupper( ). 12. To determine if a character is a hexadecimal digit, call isxdigit( ). 39
  • 59. 40 Herb Schildt's C++ Programming Cookbook Discussion The character categorization functions were originally defined by C and are supported by C++ in a couple of different ways. The versions used here are declared within the header <cctype>. They all categorize characters based on the current locale. All of the is… functions work in essentially the same way. Each is briefly described here: int isalnum(int ch) Returns non-zero if ch is either a letter or a digit, and zero otherwise. int isalpha(int ch) Returns non-zero if ch is a letter and zero otherwise. int iscntrl(int ch) Returns non-zero if ch is a control character and zero otherwise. int isdigit(int ch) Returns non-zero if ch is a digit and zero otherwise. int isgraph(int ch) Returns non-zero if ch is a printable character other than a space and zero otherwise. int islower(int ch) Returns non-zero if ch is a lowercase letter and zero otherwise. int isprint(int ch) Returns non-zero if ch is printable (including a space) and zero otherwise. int ispunct(int ch) Returns non-zero if ch is punctuation and zero otherwise. int isspace(int ch) Returns non-zero if ch is whitespace (including spaces, tabs, newlines) and zero otherwise. int isupper(int ch) Returns non-zero if ch is an uppercase letter and zero otherwise. int isxdigit(int ch) Returns non-zero if ch is a hexadecimal digit (0-9, A-F, or a-f) and zero otherwise. Most of the functions are self-explanatory. However, notice the ispunct( ) function. It returns true for any character that is punctuation. This is defined as any character that is not a letter, a digit, or a space. Therefore, operators such as + and / are categorized as punctuation. Example The following example shows the isalpha( ), isdigit( ), isspace( ), and ispunct( ) functions in action. They are used to count the number of letters, spaces, and punctuation contained within a string. // Count spaces, punctuation, digits, and letters. #include <iostream> #include <cctype> using namespace std; int main() { const char *str = "I have 30 apples and 12 pears. Do you have any?"; int letters = 0, spaces = 0, punct = 0, digits = 0; cout << str << endl;
  • 60. Chapter 2: String Handling while(*str) { if(isalpha(*str)) ++letters; else if(isspace(*str)) ++spaces; else if(ispunct(*str)) ++punct; else if(isdigit(*str)) ++digits; ++str; } cout cout cout cout << << << << "Letters: " << letters << endl; "Digits: " << digits << endl; "Spaces: " << spaces << endl; "Punctuation: " << punct << endl; return 0; } The output is shown here: I have 30 apples and 12 pears. Do you have any? Letters: 31 Digits: 4 Spaces: 10 Punctuation: 2 Bonus Example: Word Count There is one well-known application in which the character categorization functions are used: a word-count utility. As a result, a word-count program makes the quintessential example for functions such as isalpha( ) and ispunct( ). The following example creates a very simple version of the word-count utility. The actual counting is handled by the wordcount( ) function. It is passed a pointer to a string. It then counts the words, lines, spaces, and punctuation in the string and returns the result. This version of wordcount( ) uses a fairly simple strategy: It counts only whole words that consist solely of letters. This means that a hyphenated word counts as two separate words. As a result, the sequence "null-terminated" counts as two words. Furthermore, a word must not contain any digits. For example, the sequence "testing123testing" will count as two words. The wordcount( ) function does, however, allow one non-letter character to be in a word: the apostrophe. This allows it to support possessives (such as Tom's) and contractions (such as it's). Each is counted as one word. // Count words, lines, spaces, and punctuation. #include <iostream> #include <cctype> using namespace std; // A structure to hold the word-count statistics. struct wc { int words; int spaces; 41
  • 61. 42 Herb Schildt's C++ Programming Cookbook int punct; int lines; wc() { words = punct = spaces = lines = 0; } }; wc wordcount(const char *str); int main() { const char *test = "By supplying a string class and also " "supporting null-terminated strings,nC++ " "offers a rich programming environment for " "string-intensive tasks.nIt's power programming."; cout << "Given: " << "nn"; cout << test << endl; wc wcd = wordcount(test); cout cout cout cout << << << << "nWords: " << wcd.words << endl; "Spaces: " << wcd.spaces << endl; "Lines: " << wcd.lines << endl; "Punctuation: " << wcd.punct << endl; return 0; } // // // wc A very simple "word count" function. It counts the words, lines, spaces, and punctuation in a string and returns the result in a wc structure. wordcount(const char *str) { wc data; // If the string is not null, then it contains at least one line. if(*str) ++data.lines; while(*str) { // Check for a word. if(isalpha(*str)) { // Start of word found. Now, look for the end of the word. // Allow apostrophes in words, as in "it's." while(isalpha(*str) || *str == ''') { if(*str == ''') ++data.punct; ++str; } data.words++; } else { // Count punctuation, spaces (including newlines), and lines.
  • 62. Chapter 2: String Handling if(ispunct(*str)) ++data.punct; else if(isspace(*str)) { ++data.spaces; // If there is any character after the newline, increment // the line counter. if(*str == 'n' && *(str+1)) ++data.lines; } ++str; } } return data; } The output is shown here: Given: By supplying a string class and also supporting null-terminated strings, C++ offers a rich programming environment for string-intensive tasks. It's power programming. Words: 24 Spaces: 21 Lines: 3 Punctuation: 8 There are a couple of points of interest in this program. First, notice that the wordcount( ) function returns the results in an object of type wc, which is a struct. I used a struct rather than a class because wc is, essentially, a data-only object. Although wc does contain a default constructor (which performs a simple initialization), it defines no member functions or parameterized constructors. Thus, I felt that struct better fit its purpose (which is to hold data) than did class. In general, I like to use class when there are member functions. I like to use struct for objects that simply house data. Of course, in C++, both create a class type and there is no hard and fast rule in this regard. Second, the line count is incremented when a newline character is found only if that newline is not immediately followed by the terminating null. This check is handled by this line: if(*str == 'n' && *(str+1)) ++data.lines; Basically, this ensures that the number of lines of text that one would see is equal to the line count returned by the function. This prevents a completely empty final line from being counted as a line. Of course, the line may still appear blank if all it contains is spaces. Options and Alternatives As mentioned, the character categorization functions defined in <cctype> operate relative to the default locale. Additional versions of these functions are also supported by <locale> and they allow you to specify a locale. 43
  • 63. 44 Herb Schildt's C++ Programming Cookbook Tokenize a Null-Terminated String Key Ingredients Headers Classes Functions <cstring> char *strtok(char *str, const char *delimiters); Tokenizing a string is one programming task that just about every programmer will face at one time or another. Tokenizing is the process of reducing a string into its individual parts, which are called tokens. Thus, a token represents the smallest indivisible element that can be meaningfully extracted from a string. Of course, what constitutes a token depends on what type of input is being processed and for what purpose For example, if you want to obtain the words in a sentence, then a token is a set of characters surrounded by either whitespace or punctuation. For example, given the sentence: I like apples, pears, and grapes. The individual tokens are I like apples pears and grapes Each token is delimited by the whitespace and/or punctuation that separates one from another. When tokenizing a string that contains a list of key/value pairs organized like this: key=value, key=value, key=value, … The tokens are the key and the value. The = sign and the comma are separators that delimit the tokens. For example, given price=10.15, quantity=4 The tokens are price 10.15 quantity 4 The point is that what constitutes a token will change, depending on the circumstance. However, the general process of tokenizing a string is the same in all cases. Because tokenizing a string is both an important and common task, C++ provides built-in support for it through the strtok( ) function. This recipe shows how to use it.
  • 64. Chapter 2: String Handling Step-by-Step To use strtok( ) to tokenize a string involves these steps: 1. Create a string that contains the characters that separate one token from another. These are the token delimiters. 2. To obtain the first token in the string, call strtok( ) with a pointer to the string to be tokenized and a pointer to the string that contains the delimiters. 3. To obtain the remaining tokens in the string, continue calling strtok( ). However, pass a null pointer for the first argument. You can change the delimiters as needed. 4. When strtok( ) returns null, the string has been fully tokenized. Discussion The strtok( ) function has the following prototype: char *strtok(char *str, const char *delimiters) A pointer to the string from which one or more tokens will be obtained is passed in str. A pointer to the string that contains the characters that delimit a token is passed in delimiters. Thus, delimiters contains the characters that divide one token from another. A null pointer is returned if there are no more tokens in str. Otherwise, a pointer to a string that contains the next token is returned. Tokenizing a string is a two-step process. The first call to strtok( ) passes a pointer to the string to be tokenized. Each subsequent call to strtok( ) passes a null pointer to str. This causes strtok( ) to continue tokenizing the string from the point at which the previous token was found. When no more tokens are found, a null pointer is returned. One useful aspect of strtok( ) is that you can change the delimiters as needed during the tokenization process. For example, consider a string that contains key/value pairs organized like this count = 10, max = 99, min = 12, name = "Tom Jones, jr.", … To read most of the keys and values in this string, the following delimiter set can be used: " =," However, to read a quoted string that can consist of any character, including commas, this delimiter is needed: """ Because strtok( ) lets you change delimiter sets "on the fly," you can specify which delimiters are needed at any point in time. This technique is illustrated by the following example. Example The following example shows how to use strtok( ) to tokenize a null-terminated string: // Demonstrate strtok(). #include <iostream> #include <cstring> using namespace std; 45
  • 65. 46 Herb Schildt's C++ Programming Cookbook int main() { // First, use strtok() to tokenize a sentence. // Create a string of delimiters for simple sentences. char delims[] = "., ?;!"; char str[] = "I like apples, pears, and grapes. Do you?"; char *tok; cout << "Obtain the words in a sentence.n"; // Pass the string to be tokenized and get the first token. tok = strtok(str, delims); // Get all remaining tokens. while(tok) { cout << tok << endl; // Each subsequent call to strtok() is passed NULL // for the first argument. tok = strtok(NULL, delims); } // Now, use strtok() to extract keys and values stored // in key/value pairs within a string. char kvpairs[] = "count=10, name="Tom Jones, jr.", max=100, min=0.01"; // Create a list of delimiters for key/value pairs. char kvdelims[] = " =,"; cout << "nTokenize key/value pairs.n"; // Get the first key. tok = strtok(kvpairs, kvdelims); // Get all remaining tokens. while(tok) { cout << "Key: " << tok << " "; // Get a value. // First, if the key is name, the value will be // a quoted string. if(!strcmp("name", tok)) { // Notice that this call uses only quotes as a delimiter. // This lets it read a quoted string that contains any character. tok = strtok(NULL, """); } else { // Otherwise, read a simple value. tok = strtok(NULL, kvdelims);
  • 66. Chapter 2: String Handling } cout << "Value: " << tok << endl; // Get the next key. tok = strtok(NULL, kvdelims); } return 0; } The output is shown here: Obtain the words in a sentence. I like apples pears and grapes Do you Tokenize key/value pairs. Key: count Value: 10 Key: name Value: Tom Jones, jr. Key: max Value: 100 Key: min Value: 0.01 Pay special attention to the way that key/value pairs are read. The delimiters used to read a simple value differ from the delimiters used to read a quoted string. Furthermore, the delimiters are changed during the tokenization process. As explained, when tokenizing a string, you can change the delimiter set as needed. Options and Alternatives Although strtok( ) is simple to use, and quite effective when applied in situations for which it is well suited, its use is inherently limited. The main trouble is that strtok( ) tokenizes a string based on a set of delimiters, and once a delimiter has been encountered, it is lost. This makes it difficult to use strtok( ) to tokenize a string in which the delimiters might also be tokens. For example, consider the following simple C++ statement: x = count+12; To parse this statement, the + must be handled as both a delimiter that terminates count and as a token that indicates addition. The trouble is that there is no easy way to do this using strtok( ). To obtain count, the + must be in the set of delimiters. However, once the + has been encountered, it is consumed. Thus, it cannot also be read as a token. A second problem with strtok( ) is that errors in the format of the string being tokenized are difficult to detect— at least until the end of the string is prematurely reached. Because of the problems with applying strtok( ) to a wide range of cases, other approaches to tokenization are often used. One such approach is to write your own "get token" function. This gives you full control over the tokenization process and lets you easily return tokens 47
  • 67. 48 Herb Schildt's C++ Programming Cookbook based on context rather than delimiters. A simple example of such an approach is shown here. The custom get token function is called gettoken( ). It tokenizes a string into the following token types: • Alphanumeric strings, such as count, indx27, or OverFlow. • Unsigned integer numbers, such as 2, 99, or 0. • Punctuation, which includes operators, such as + and /. Thus, gettoken( ) can be used to tokenize very simple expressions, such as x = count+12; or while(x<9) x = x – w; The gettoken( ) function is used much like strtok( ). On the first call, pass a pointer to the string to be tokenized. On subsequent calls, pass a null pointer. It returns a pointer to the next token in the string. It returns a null pointer when there are no more tokens. To tokenize a new string, simply start the process over by passing a pointer to the new string. The simple gettoken( ) function, along with a main( ) function to demonstrate its use, is shown here: // Demonstrate a custom gettoken() function that can // return the tokens that comprise very simple expressions. #include <iostream> #include <cstring> #include <cctype> using namespace std; const char *gettoken(const char *str); int main() { char sampleA[] = "max=12+3/89; count27 = 19*(min+floor);"; char sampleB[] = "while(i < max) i = counter * 2;"; const char *tok; // Tokenize the first string. tok = gettoken(sampleA); cout << "Here are the tokens in: " << sampleA << endl; while(tok) { cout << tok << endl; tok = gettoken(NULL); } cout << "nn"; // Restart gettoken() by passing the second string. tok = gettoken(sampleB); cout << "Here are the tokens in: " << sampleB << endl; while(tok) { cout << tok << endl;
  • 68. Chapter 2: tok String Handling = gettoken(NULL); } return 0; } // A very simple custom gettoken() function. The tokens are comprised // of alphanumeric strings, numbers, and single-character punctuation. // Although this function is quite limited, it demonstrates the basic // framework that can be expanded and enhanced to obtain other types // of tokens. // // On the first call, pass a pointer to the string to be tokenized. // On subsequent calls, pass a null pointer. // It returns a pointer to the current token, or a null // pointer if there are no more tokens. #define MAX_TOK_SIZE 128 const char *gettoken(const char *str) { static char token[MAX_TOK_SIZE+1]; static const char *ptr; int count; // holds the current character count char *tokptr; if(str) { ptr = str; } tokptr = token; count = 0; while(isspace(*ptr)) ptr++; if(isalpha(*ptr)) { while(isalpha(*ptr) || isdigit(*ptr)) { *tokptr++ = *ptr++; ++count; if(count == MAX_TOK_SIZE) break; } } else if(isdigit(*ptr)) { while(isdigit(*ptr)) { *tokptr++ = *ptr++; ++count; if(count == MAX_TOK_SIZE) break; } } else if(ispunct(*ptr)) { *tokptr++ = *ptr++; } else return NULL; // Null-terminate the token. *tokptr = '0'; return token; } 49
  • 69. 50 Herb Schildt's C++ Programming Cookbook The output from the program is shown here: Here are the tokens in: max=12+3/89; count27 = 19*(min+floor); max = 12 + 3 / 89 ; count27 = 19 * ( min + floor ) ; Here are the tokens in: while(i < max) i = counter * 2; while ( i < max ) i = counter * 2 ; The operation of gettoken( ) is straightforward. It simply examines the next character in the input string and then reads the type of token that starts with that type of character. For example, if the token is a letter, then gettoken( ) reads an alphanumeric token. If the next character is a digit, then gettoken( ) reads an integer. If the next character is punctuation, then the token consists of that character. Notice that gettoken( ) does not let the length of a token exceed the maximum token length as specified by MAX_TOK_SIZE. Also, notice that gettoken( ) does not modify the input string. This differs from strtok( ), which does modify the input string. Finally, notice that the pointer returned by gettoken( ) is const. This means that it can't be used to modify the static array token. Finally, although gettoken( ) is very simple, it can be easily adapted and enhanced to fit other, more sophisticated situations.
  • 70. Chapter 2: String Handling Perform Basic Operations on string Objects Key Ingredients Headers Classes Functions <string> string size_type capacity( ) const string &erase(size_type indx = 0, size_type len = npos) string &insert(size_type indx, const string &str) size_type max_size( ) const char &operator[ ](size_type indx) string &operator=(const string &str) void push_back (const char ch) void reserve(size_type num = 0) size_type size( ) const; string substr(size_type indx = 0, size_type len = npos) const <string> string operator+(const string &leftop, const string &rightop) bool operator==(const string &leftop, const string &rightop) bool operator<=(const string &leftop, const string &rightop) bool operator>(const string &leftop, const string &rightop) As explained at the start of this chapter, C++ provides two ways of handling strings. The first is the null-terminated string (also called a C-string). The null-terminated string was inherited from C and is still widely used in C++ programming. It is also the type of string used in the preceding recipes. The second type of string is an object of the template class basic_string. This class is defined by C++ and is part of the standard C++ class library. The remaining recipes in this chapter use basic_string. Strings of type basic_string have several advantages over null-terminated strings. Here are some of the most important: • basic_string defines a data type. (Recall that a null-terminated string is simply a convention.) • basic_string encapsulates the character sequence that forms the string, thus preventing improper operations. When using basic_string, it is not possible to generate an array overrun, for example. 51
  • 71. 52 Herb Schildt's C++ Programming Cookbook • basic_string objects are dynamic. They grow as needed to accommodate the size of the string that is being held. Therefore, it is not necessary to know in advance how large a string is needed. • basic_string defines operators that manipulate strings. This streamlines many types of string handling. • basic_string defines a complete set of member functions that simplify working with strings. You seldom have to write your own function to perform some string manipulation. There are two built-in specializations of basic_string: string (which is for characters of type char) and wstring (which is for wide characters). For convenience, all of the recipes in this book use string, but most of the information is applicable to any type of basic_string. This recipe demonstrates several of the basic operations that can be applied to objects of type string. It shows how to construct a string. It then demonstrates several of its operators and member functions. It also demonstrates how string objects adjust their size at runtime to accommodate an increase in the size of the character sequence. Step-by-Step To perform the basic string operations involves these steps: 1. The string class is declared within the header <string>. Thus, <string> must be included in any program that uses string. 2. Create a string by using one of its constructors. Three are demonstrated in this recipe. The first creates an empty string, the second creates a string initialized by a string literal, and the third creates a string that is initialized by another string. 3. To obtain the length of the longest possible string, call max_size( ). 4. To assign one string to another, use the = operator. 5. To concatenate two string objects, use the + operator. 6. To lexicographically compare two string objects, use the relational operators, such as > or ==. 7. To obtain a reference to a character at a specified index, use the [ ] indexing operator. 8. To obtain the number of characters currently held by a string, call size( ). 9. To obtain the current capacity of a string, call capacity( ). 10. To specify a capacity, call reserve( ). 11. To remove all or part of the characters from a string, call erase( ). 12. To add a character to the end of a string, call push_back( ). 13. To obtain a substring, call substr( ). Discussion The string class defines several constructors. The ones used by this recipe are shown here: explicit string(const Allocator &alloc = Allocator( )) string(const char *str, const Allocator &alloc Allocator( ))
  • 72. Chapter 2: String Handling string(const string &str, size_type indx = 0, size_type len=npos, const Allocator &alloc Allocator( )) The first constructor creates an empty string. The second creates a string that is initialized by the null-terminated string pointed to by str. The third creates a string that is initialized by a substring of str that begins at indx and runs for len characters. Although these look a bit intimidating, they are easy to use. Generally, the allocator (which controls how memory is allocated) is allowed to default. This means that normally you won't specify an allocator when creating a string. For example, the following creates an empty string and a string initialized with a string literal: string mystr; // empty string string mystr2("Hello"); // string initialized with the sequence Hello In the third constructor, the defaults for both indx and len are typically used, which means that the string contains a complete copy of str. Although string objects are dynamic, growing as needed at runtime, there is still a maximum length that a string can have. Although this maximum is typically quite large, it may be useful to know it in some cases. To obtain the maximum string length, call max_size( ), shown here: size_type max_size( ) const It returns the length of the longest possible string. You can assign one string to another by using the = operator. This operator is implemented as a member function. It has several forms. Here is one used by this recipe: string &operator=(const string &str) It assigns the character sequence in str to the invoking string. It returns a reference to the invoking object. Other versions of the assignment operator let you assign a null-terminated string or a character to a string object. You can concatenate one string with another by using the + operator. It is defined as a non-member function. It has several forms. Here is the one used by this recipe: string operator+(const string &leftop, const string &rightop) It concatenates rightop to leftop and returns a string object that contains the result. Other versions of the concatenation operator let you concatenate a string object with a nullterminated string or with a character. You can insert one string into another by using the insert( ) function. It has several forms. The one used here is: string &insert(size_type indx, const string &str) It inserts str into the invoking string at the index specified by indx. It returns a reference to the invoking object. All of the relational operators are defined for the string class by non-member operator functions. They perform lexicographical comparisons of the character sequences contained within two strings. Each operator has several overloaded forms. The operators used here 53
  • 73. 54 Herb Schildt's C++ Programming Cookbook are ==, <=, and >, but all of the relational operators work in the same basic way. Here are the versions of these operators that are used in this recipe: bool operator==(const string &leftop, const string &rightop) bool operator<=(const string &leftop, const string &rightop) bool operator>(const string &leftop, const string &rightop) In all cases, leftop refers to the left operand and rightop refers to the right operand. The result of the comparison is returned. Other versions of these operators let you compare a string object with a null-terminated string. You can obtain a reference to a specific element in a string by using the array indexing operator [ ]. It is implemented as a member function, as shown here: char &operator[ ](size_type indx) It returns a reference to the character at the zero-based index specified by indx. For example, given a string object called mystr, the expression mystr[2] returns a reference to the third character in mystr. A const version is also available. The number of characters contained in the string can be obtained by calling size( ), shown here: size_type size( ) const It returns the number of characters currently in the string. As explained in the overview at the start of this chapter, size_type is a typedef that represents some form of unsigned integer. The number of characters that a string object can hold is not predetermined. Instead, a string object will grow as needed to accommodate the size of the string that it needs to encapsulate. However, all string objects begin with an initial capacity, which is the maximum number of characters that can be held before more memory needs to be allocated. The capacity of a string object can be determined by calling capacity( ), shown here: size_type capacity( ) const It returns the current capacity of the invoking string. The capacity of a string object can be important because memory allocations are costly in terms of time. If you know in advance the number of characters that a string will hold, then you can set the capacity to that amount, thereby eliminating a memory reallocation. To do this, call reserve( ), shown next: void reserve(size_type num = 0) It sets the capacity of the invoking string so that it is equal to at least num. If num is less than or equal to the number of characters in the string, then the call to reserve( ) is a request to reduce the capacity to equal the size. This request can be ignored, however. You can remove one or more characters from a string by calling erase( ). There are three versions of erase( ). The one used by this recipe is shown here: string &erase(size_type indx = 0, size_type len = npos) Beginning at indx, it removes len characters from the invoking object. It returns a reference to the invoking object. One of the more interesting string member functions is push_back( ). It adds a character to the end of the string: void push_back (const char ch)
  • 74. Chapter 2: String Handling It adds ch to the end of the invoking string. It is quite useful when you want to create a queue of characters. You can obtain a portion of a string (i.e., a substring) by calling substr( ), shown here: string substr(size_type indx = 0, size_type len = npos) const It returns a substring of len characters, beginning at indx within the invoking string. Example The following example illustrates several of the fundamental string operations: // Demonstrate the basic string operations. #include <iostream> #include <string> using namespace std; int main() { // Create some string objects. Three are initialized // using the string literal passed as an argument. string str1("Alpha"); string str2("Beta"); string str3("Gamma"); string str4; // Output a string via cout. cout << "Here are the original strings:n"; cout << " str1: " << str1 << endl; cout << " str2: " << str2 << endl; cout << " str3: " << str3 << "nn"; // Display the maximum string length. cout << "The maximum string length is: " << str1.max_size() << "nn"; // Display the size of str1. cout << "str1 contains " << str1.size() << " characters.n"; // Display the capacity of str1. cout << "Capacity of str1: " << str1.capacity() << "nn"; // Display the characters in a string one at a time // by using the indexing operator. for(unsigned i = 0; i < str1.size(); ++i) cout << "str1[i]: " << str1[i] << endl; cout << endl; // Assign one string to another. str4 = str1; cout << "str4 after being assigned str1: " << str4 << "nn"; 55
  • 75. 56 Herb Schildt's C++ Programming Cookbook // Concatenate two strings. str4 = str1 + str3; cout << "str4 after begin assigned st1+str3: " << str4 << "nn"; // Insert one string into another. str4.insert(5, str2); cout << "str4 after inserting str2: " << str4 << "nn"; // Obtain a substring. str4 = str4.substr(5, 4); cout << "str4 after being assigned str4.substr(5, 3): " << str4 << "nn"; // Compare two strings. cout << "Compare strings.n"; if(str3 > str1) cout << "str3 > str1n"; if(str3 == str1+str2) cout << "str3 == str1+str2n"; if(str1 <= str2) cout << "str1 <= str2nn"; // Create a string object using another string object. cout << "Initialize str5 with the contents of str1.n"; string str5(str1); cout << "str5: " << str5 << "nn"; // Erase str4. cout << "Erasing str4.n"; str4.erase(); if(str4.empty()) cout << "str4 is now empty.n"; cout << "Size and capacity of str4 is " << str4.size() << " " << str4.capacity() << "nn"; // Use push_back() to add characters to str4. for(char ch = 'A'; ch <= 'Z'; ++ch) str4.push_back(ch); cout << "str4 after calls to push_back(): " << str4 << endl; cout << "Size and capacity of str4 is now " << str4.size() << " " << str4.capacity() << "nn"; // Set the capacity of str4 to 128. cout << "Setting the capacity of str4 to 128n"; str4.reserve(128); cout << "Capacity of str4 is now: " << str4.capacity() << "nn"; // Input a string via cin. cout << "Enter a string: "; cin >> str1; cout << "You entered: " << str1 << "nn"; return 0; }
  • 76. Chapter 2: String Handling The output is shown here: Here are the original strings: str1: Alpha str2: Beta str3: Gamma The maximum string length is: 4294967294 str1 contains 5 characters. Capacity of str1: 15 str1[i]: str1[i]: str1[i]: str1[i]: str1[i]: A l p h a str4 after being assigned str1: Alpha str4 after being assigned st1+str3: AlphaGamma str4 after inserting str2: AlphaBetaGamma str4 after being assigned str4.substr(5, 3): Beta Compare strings. str3 > str1 str1 <= str2 Initialize str5 with the contents of str1. str5: Alpha Erasing str4. str4 is now empty. Size and capacity of str4 is 0 15 str4 after calls to push_back(): ABCDEFGHIJKLMNOPQRSTUVWXYZ Size and capacity of str4 is now 26 31 Setting the capacity of str4 to 128 Capacity of str4 is now: 143 Enter a string: test You entered: test Perhaps the most important thing to notice in the example is that the size of the strings is not specified. As explained, string objects are automatically sized to hold the string that they are given. Thus, when assigning or concatenating strings, the target string will grow as needed to accommodate the size of the new string. It is not possible to overrun the end of the string. This dynamic aspect of string objects is one of the ways in which they are better than standard null-terminated strings, which are subject to boundary overruns. (As mentioned in 57
  • 77. 58 Herb Schildt's C++ Programming Cookbook the overview, an attempt to create a string that exceeds the longest possible string results in a length_error being thrown. Thus, it is not possible to overrun a string.) There is one other important thing to notice in the sample run. When the capacity of str4 is increased by calling reserve( ) with an argument of 128, the actual capacity becomes 143. Remember, a call to reserve( ) causes the capacity to be increased to at least the specified value. The implementation is free to set it to a higher value. This might happen because allocations might be more efficient in blocks of a certain size, for example. (Of course, because of differences between compilers, you might see a different capacity value when you run the sample program. Such differences are to be expected.) Options and Alternatives Even for the basic string operations, string offers many alternatives. Several are mentioned here. As explained, to obtain the number of characters currently held by a string, you can call size( ). However, you can also call length( ). It returns the same value and works the same way. In essence, size( ) and length( ) are simply two different names for the same function. The reason for the two names is historical. The size( ) method must be implemented by all STL containers. Although not always thought of as part of the STL, string meets all of the STL requirements for a container and is compatible with the STL. Part of those requirements is that a container must provide a size( ) function. Therefore, size( ) became part of string. The insert( ) function has several additional forms. For example, you can insert a portion of one string into another, one or more characters into a string, or a null-terminated string into a string. The erase( ) function has two additional forms that let you remove characters referred to by an iterator (see Operate on string Objects Through Iterators ). Although using the indexing operator [ ] is more straightforward, you can also obtain a reference to a specific character by calling the at( ) method. It is shown here as it is implemented for string: char &at(size_type indx) It returns a reference to the character at the zero-based index specified by indx. A const version is also available. As the recipe shows, you can perform simple assignments and concatenations using the = and + operators defined for string. In cases in which more sophisticated assignments or concatenations are needed, string supplies the assign( ) and append( ) functions. These functions have many forms that allow you to assign or append portions of a string, all or part of a null-terminated string, or one or more characters. There are also forms that support iterators. Although there are far too many to describe in this recipe, here is an example of each: string &assign(const string &str, size_type indx, size_type len) string &append(const string &str, size_type indx, size_type len) This version of assign( ) assigns a substring of str to the invoking string. The substring begins at indx and runs for len characters. This version of append( ) appends a substring of str onto the end of the invoking string. The substring begins at indx and runs for len characters. Both functions return a reference to the invoking object.
  • 78. Chapter 2: String Handling The relational operators are the easiest way to compare one string with another. In addition to the forms used in the recipe, other versions of these operators let you compare a string object with a null-terminated string. To provide added flexibility, string also supplies the compare( ) function, which lets you compare portions of two strings. Here is one example. It compares a string with a substring of the invoking string. int compare(size_type indx, size_type len, const string &str) const This function compares str to the substring within the invoking string that begins at indx and is len characters long. It returns less than zero if the sequence in the invoking string is less than str, zero if the two sequences are equal, and greater than zero if the sequence in the invoking string is greater than str. You can remove all characters from a string in two ways. First, as the recipe shows, you can use the erase( ) function, allowing the arguments to default. Alternatively, you can call clear( ), which is shown here: void clear( ) Search a string Object Key Ingredients Headers Classes Functions <string> string size_type find(const char *str, size_type indx = 0) const size_type find(const string &str, size_type indx = 0) const size_type find_first_of(const char *str, size_type indx = 0) const size_type find_first_of(const string &str, size_type indx = 0) const size_type find_first_not_of(const char *str, size_type indx = 0) const size_type find_last_of(const char *str, size_type indx = npos) const size_type find_last_not_of(const char *str, size_type indx = npos) const size_type rfind(const char *str, size_type indx = npos) const 59
  • 79. 60 Herb Schildt's C++ Programming Cookbook The string class defines a powerful assortment of functions that search a string. These functions let you find: • The first occurrence of a substring or character. • The last occurrence of a substring or character. • The first occurrence of any character in a set of characters. • The last occurrence of any character in a set of characters. • The first occurrence of any character that is not part of a set of characters. • The last occurrence of any character that is not part of a set of characters. This recipe demonstrates their use. Step-by-Step Searching a string involves these steps: 1. To find the first occurrence of a sequence or character, call find( ). 2. To find the last occurrence of a sequence or character, call rfind( ). 3. To find the first occurrence of any character in a set of characters, call find_first_of( ). 4. To find the last occurrence of any character in a set of characters, call find_last_of( ). 5. To find the first occurrence of any character that is not part of a set of characters, call find_first_not_of( ). 6. To find the last occurrence of any character that is not part of a set of characters, call find_last_not_of( ). Discussion All of the search functions have four forms, which allow you to specify the objective of the search as a string, a null-terminated string, a portion of a null-terminated string, or a character. The forms used by the examples in this recipe are described here. The find( ) function finds the first occurrence of a substring or character within another string. Here are the forms used in this recipe or by the Bonus Example: size_type find(const string &str, size_type indx = 0) const size_type find(const char *str, size_type indx = 0) const Both return the index of the first occurrence of str within the invoking string. The indx parameter specifies the index at which the search will begin within the invoking string. In the first form, str is a reference to a string. In the second form, str is a pointer to a nullterminated string. If no match is found, npos is returned. The rfind( ) function finds the last occurrence of a substring or character within another string. The form used here is: size_type rfind(const char *str, size_type indx = npos) const It returns the index of the last occurrence of str within the invoking string. The indx parameter specifies the index at which the search will begin within the invoking string. If no match is found, npos is returned.
  • 80. Chapter 2: String Handling To find the first occurrence of any character within a set of characters, call find_first_of( ). Here are the forms used in this recipe or by the Bonus Example: size_type find_first_of(const string &str, size_type indx = 0) const size_type find_first_of(const char *str, size_type indx = 0) const Both return the index of the first character within the invoking string that matches any character in str. The search begins at index indx. npos is returned if no match is found. The difference between the two is simply the type of str, which can be either a string or a nullterminated string. To find the first occurrence of any character that is not part of a set of characters, call find_first_not_of( ). Here are the forms used in this recipe or by the Bonus Example: size_type find_first_not_of(const string &str, size_type indx = 0) const size_type find_first_not_of(const char *str, size_type indx = 0) const Both return the index of the first character within the invoking string that does not match any character in str. The search begins at index indx. npos is returned if no match is found. The difference between the two is simply the type of str, which can be either a string or a null-terminated string. To find the last occurrence of any character within a set of characters, call find_last_of( ). The form used here is: size_type find_last_of(const char *str, size_type indx = npos) const It returns the index of the last character within the invoking string that matches any character in str. The search begins at index indx. npos is returned if no match is found. To find the last occurrence of any character that is not part of a set of characters, call find_last_not_of( ). The form used by this recipe is: size_type find_last_not_of(const char *str, size_type indx = npos) const It returns the index of the last character within the invoking string that does not match any character in str. The search begins at index indx. npos is returned if no match is found. NOTE As just described, the value npos is returned by the find… functions when no match is found. The npos variable is of type string::size_type, which is some form of unsigned integer. However, npos is initialized to –1. This causes npos to contain its largest possible unsigned value. Microsoft currently recommends that if you will be comparing the value of some variable to npos, then that variable should be declared to be of type string::size_type, rather than int or unsigned, to ensure that the comparison is handled correctly in all cases. This is the approach used in these recipes. However, it is not uncommon to see code in which npos is declared as an int or unsigned. Example The following example shows the search functions in action: // Search a string. #include <iostream> #include <string> using namespace std; 61
  • 81. 62 Herb Schildt's C++ Programming Cookbook void showresult(string s, string::size_type i); int main() { string::size_type indx; // Create a string. string str("one two three, one two three"); string str2; cout << "String to be searched: " << str << "nn"; cout << "Searching for the first occurrence of 'two'n"; indx = str.find("two"); showresult(str, indx); cout << "Searching for the last occurrence of 'two'n"; indx = str.rfind("two"); showresult(str, indx); cout << "Searching for the first occurrence of t or hn"; indx = str.find_first_of("th"); showresult(str, indx); cout << "Searching for the last occurrence of t or hn"; indx = str.find_last_of("th"); showresult(str, indx); cout << "Searching for the first occurrence of any character other " << "than o, n, e, or spacen"; indx = str.find_first_not_of("one "); showresult(str, indx); cout << "Searching for the last occurrence of any character other " << "than o, n, e, or spacen"; indx = str.find_last_not_of("one "); showresult(str, indx); return 0; } // Display the results of the search. void showresult(string s, string::size_type i) { if(i == string::npos) { cout << "No match found.n"; return; } cout << "Match found at index " << i << endl; cout << "Remaining string from point of match: " << s.substr(i) << "nn"; }
  • 82. Chapter 2: String Handling The output is shown here: String to be searched: one two three, one two three Searching for the first occurrence of 'two' Match found at index 4 Remaining string from point of match: two three, one two three Searching for the last occurrence of 'two' Match found at index 19 Remaining string from point of match: two three Searching for the first occurrence of t or h Match found at index 4 Remaining string from point of match: two three, one two three Searching for the last occurrence of t or h Match found at index 24 Remaining string from point of match: hree Searching for the first occurrence of any character other than o, n, e, or space Match found at index 4 Remaining string from point of match: two three, one two three Searching for the last occurrence of any character other than o, n, e, or space Match found at index 25 Remaining string from point of match: ree Bonus Example: A Tokenizer Class for string Objects The C++ standard library contains the function strtok( ), which can be used to tokenize a null-terminated string (see Tokenize a Null-Terminated String). However, the string class does not define a corresponding equivalent. Fortunately, it is quite easy to create one. Before beginning, it is important to state that there are several different ways to approach this task. This example shows just one of many. The following program creates a class called tokenizer that encapsulates tokenization. To tokenize a string, first construct a tokenizer, passing the string as an argument. Next, call get_token( ) to obtain the individual tokens in the string. The delimiters that define the boundaries of each token are passed to get_token( ) as a string. The delimiters can be changed with each call to get_token( ). The get_token( ) function returns an empty string when there are no more tokens to return. Notice that get_token( ) makes use of the find_first_of( ) and find_first_not_of( ) functions. // Create a class called tokenizer that tokenizes a string. #include <iostream> #include <string> using namespace std; // // // // The tokenizer class is used to tokenize a string. Pass the constructor the string to be tokenized. To obtain the next token, call get_token(), passing in a string that contains the delimiters. 63
  • 83. 64 Herb Schildt's C++ Programming Cookbook class tokenizer { string s; string::size_type startidx; string::size_type endidx; public: tokenizer(const string &str) { s = str; startidx = 0; } // Return a token from the string. string get_token(const string &delims); }; // Return a token from the string. Return an // empty string when no more tokens are found. // Pass the delimiters in delims. string tokenizer::get_token(const string &delims) { // Return an empty string when there are no more // tokens to return. if(startidx == string::npos) return string(""); // Beginning at startidx, find the next delimiter. endidx = s.find_first_of(delims, startidx); // Construct a string that contains the token. string tok(s.substr(startidx, endidx-startidx)); // Find the start of the next token. This is a // character that is not a delimiter. startidx = s.find_first_not_of(delims, endidx); // Return the next token. return tok; } int main() { // Strings to be tokenized. string strA("I have four, five, six tokens. "); string strB("I might have more tokens!nDo You?"); // This string contains the delimiters. string delimiters(" ,.!?n"); // This string will hold the next token. string token; // Create two tokenizers. tokenizer tokA(strA); tokenizer tokB(strB);
  • 84. Chapter 2: String Handling // Display the tokens in strA. cout << "The tokens in strA:n"; token = tokA.get_token(delimiters); while(token != "") { cout << token << endl; token = tokA.get_token(delimiters); } cout << endl; // Display the tokens in strB. cout << "The tokens in strB:n"; token = tokB.get_token(delimiters); while(token != "") { cout << token << endl; token = tokB.get_token(delimiters); } return 0; } Here is the output: The tokens in strA: I have four five six tokens The tokens in strB: I might have more tokens Do You There is one easy enhancement that you might want to try making to tokenize: a reset( ) function. This function could be called to enable a string to be retokenized from the start. This is easy to do. Simply set startidx to zero, as shown here: void reset() { startidx = 0; } Options and Alternatives As mentioned, each of the find… functions has four forms. For example, here are all of the forms of find( ): size_type find(const string &str, size_type indx = 0) const size_type find(const char *str, size_type indx = 0) const size_type find(const char *str, size_type indx, size_type len) const size_type find(char ch, size_type indx = 0) const 65
  • 85. 66 Herb Schildt's C++ Programming Cookbook The first two forms were described earlier. The third form searches for the first occurrence of the first len characters of str. The fourth form searches for the first occurrence of ch. In all cases, the search begins at the index specified by indx within the invoking string, and the index at which a match is found is returned. If no match is found, npos is returned. The other find… functions have similar forms. As mentioned in the overview at the start of this chapter, the string class fulfills the general requirements for being an STL-compatible container. This means that it can be operated on by the algorithms declared in <algorithm>. Therefore, a string object can be searched by using the search algorithms, such as search( ), find( ), find_first_of( ), and so on. The one advantage that the algorithms offer is the ability to supply a user-defined predicate that lets you specify when one character in the string matches another. This feature is used by the recipe Create Case-Insensitive Search and Search-and-Replace Functions for string Objects to implement a search function that ignores case differences. (The STL and algorithms are covered in depth in Chapters 3 and 4.) Create a Search-and-Replace Function for string Objects Key Ingredients Headers Classes Functions <string> string size_type find(const string &str, size_type indx = 0) const string &replace(size_type indx, size_type len, const string &str) The string class provides very rich support for the replacement of one substring with another. This operation is provided by the replace( ) function, of which there are ten forms. These ten forms give you great flexibility in specifying how the replacement process will take place. For example, you can specify the replacement string as a string object or as a null-terminated string. You can specify what part of the invoking string is replaced by specifying indexes or through the use of iterators. This recipe makes use of replace( ) along with the find( ) function demonstrated by the preceding recipe to implement a search-and-replace function for string objects. As you will see, because of the support that string provides through find( ) and replace( ), the implementation of search-and-replace is straightforward. It is also a much cleaner implementation than is the same function implemented for null-terminated strings. (See Create a Search-and-Replace Function for Null-Terminated Strings.)
  • 86. Chapter 2: String Handling Step-by-Step To create a search-and-replace function for string objects involves these steps: 1. Create a function called search_and_replace( ) that has this prototype: bool search_and_replace(string &str, const string &oldsubstr, const string &newsubstr); The string to be changed is passed via str. The substring to replace is passed in oldsubstr. The replacement is passed in newsubstr. 2. Use the find( ) function to find the first occurrence of oldsubstr. 3. Use the replace( ) function to substitute newsubstr. 4. Return true if a replacement was made and false otherwise. Discussion The find( ) method is described by the preceding recipe and that discussion is not repeated here. Once the substring has been found, it can be replaced by calling replace( ). There are ten forms of replace( ). The one used by this recipe is shown here: string &replace(size_type indx, size_type len, const string &str) Beginning at indx within the invoking string, this version replaces up to len characters with the string in str. The reason that it replaces "up to" len characters is that it is not possible to replace past the end of the string. Thus, if len + indx exceeds the total length of the string, then only those characters from indx to the end will be replaced. The function returns a reference to the invoking string. Example Here is one way to implement the search_and_replace( ) function: // In the string referred to by str, replace oldsubstr with newsubstr. // Thus, this function modifies the string referred to by str. // It returns true if a replacement occurs and false otherwise. bool search_and_replace(string &str, const string &oldsubstr, const string &newsubstr) { string::size_type startidx; startidx = str.find(oldsubstr); if(startidx != string::npos) { str.replace(startidx, oldsubstr.size(), newsubstr); return true; } return false; } If you compare this version of search_and_replace( ) with the one created for null-terminated strings, you will see that this version is substantially smaller and simpler. There are two reasons for this. First, because objects of type string are dynamic, they can grow or shrink as needed. 67
  • 87. 68 Herb Schildt's C++ Programming Cookbook Therefore, it is easy to replace one substring with another. There is no need to worry about overrunning an array boundary when the length of the string is increased, for example. Second, string supplies a replace( ) function that automatically handles the removal of the old substring and the insertion of the new substring. This does not need to be handled manually, as is the case when inserting into a null-terminated string. The following example shows the search_and_replace( ) function in action. // Implement search-and-replace for string objects. #include <iostream> #include <string> using namespace std; bool search_and_replace(string &str, const string &oldsubstr, const string &newsubstr); int main() { string str = "This is a test. So is this."; cout << "Original string: " << str << "nn"; cout << "Replacing 'is' with 'was':n"; // The following replaces is with was. Notice that // it passes string literals for the substrings. // These are automatically converted into string objects. while(search_and_replace(str, "is", "was")) cout << str << endl; cout << endl; // Of course, you can explicitly pass string objects, too. string oldstr("So"); string newstr("So too"); cout << "Replace 'So' with 'So too'" << endl; search_and_replace(str, oldstr, newstr); cout << str << endl; return 0; } // In the string referred to by str, replace oldsubstr with newsubstr. // Thus, this function modifies the string referred to by str. // It returns true if a replacement occurs and false otherwise. bool search_and_replace(string &str, const string &oldsubstr, const string &newsubstr) { string::size_type startidx; startidx = str.find(oldsubstr);
  • 88. Chapter 2: String Handling if(startidx != string::npos) { str.replace(startidx, oldsubstr.size(), newsubstr); return true; } return false; } The output is shown here: Original string: This is a test. So is this. Replacing 'is' with 'was': Thwas is a test. So is this. Thwas was a test. So is this. Thwas was a test. So was this. Thwas was a test. So was thwas. Replace 'So' with 'So too' Thwas was a test. So too was thwas. Options and Alternatives The replace( ) function has several other forms. Three more commonly used forms are described here. All return a reference to the invoking string. The following form of replace( ) takes a null-terminated string as the replacement string: string &replace(size_type indx, size_type len, const char *str) Beginning at indx within the invoking string, it replaces up to len characters with the string in str. To replace a substring with a portion of another string, use this form: string &replace(size_type indx1, size_type len1, const string &str, size_type indx2, size_type len2) It replaces up to len1 characters in the invoking string, beginning at indx1, with the len2 characters from the string in str, beginning at indx2. The next form of replace( ) operates on iterators: string &replace(iterator start, iterator end, const string &str) The range specified by start and end is replaced with the characters in str. The search_and_replace( ) function operates in a case-sensitive manner. It is possible to perform a case-insensitive search-and-replace, but it takes a little work. One way is to implement a case-insensitive search function that uses the standard search( ) STL algorithm. This algorithm lets you specify a binary predicate that can be tailored to test two characters for equality in a case-independent manner. You can then use this function to find the location of the substring to be removed. To see this approach in action, see Create Case-Insensitive Search and Search-and-Replace Functions for string Objects. 69
  • 89. 70 Herb Schildt's C++ Programming Cookbook Operate on string Objects Through Iterators Key Ingredients Headers Classes Functions <string> string iterator begin( ) iterator end( ) reverse_iterator rbegin( ) reverse_iterator rend( ) iterator erase(iterator start, iterator end) template <class InIter> void insert(iterator itr, InIter start, InIter end) string &replace(iterator start, iterator end, const char *str) <algorithm> template <class InIter, class T> InIter find(InIter start, InIter end, const T &val) template <class InIter, class OutIter, class Func> OutIter transform(InIter start, InIter end, OutIter result, Func unaryFunc) This recipe shows how to use iterators with objects of type string. As most readers will know, iterators are objects that act much like pointers. They give you the ability to refer to the contents of a container by using a pointer-like syntax. They are also the mechanism that lets different types of containers be handled in the same way and enables different types of containers to exchange data. They are one of C++'s most powerful concepts. As explained in the overview of string near the start of this chapter, basic_string fulfills the basic requirements of a container. Therefore, the string specialization of basic_string is, essentially, a container for characters. One of the requirements of all containers is that they support iterators. By supporting iterators, string gains three important benefits: 1. Iterators can streamline some types of string operations. 2. Iterators enable string objects to be operated on by the various STL algorithms. 3. Iterators enable string to be compatible with other STL containers. For example, through iterators, you can copy the characters in a string into a vector or construct a string from characters stored in a deque.
  • 90. Chapter 2: String Handling The string class supports all basic iterator operations. It also provides versions of several of the functions, such as insert( ) and replace( ), that are designed to work through iterators. This recipe demonstrates the basic iterator operations and three iterator-enabled functions, and shows how iterators enable string to be integrated into the overall framework of the STL. NOTE For a detailed discussion of iterators, see Chapter 3, which presents STL-based recipes. Step-by-Step To operate on a string through iterators involves these steps: 1. Declare a variable that will hold an iterator. To do this, you must use one of the iterator types defined by string, such as iterator or reverse_iterator. 2. To obtain an iterator to the start of a string, call begin( ). 3. To obtain an iterator to the end of a string, call end( ). 4. To obtain a reverse iterator to the start of the reversed string, call rbegin( ). 5. To obtain a reverse iterator to the end of the reversed string, call rend( ). 6. You can cycle through the characters in a string through an iterator in much the same way that you can use a pointer to cycle through the elements of an array. 7. You can create a string object that is initialized with the characters pointed to by a range of iterators. Among other uses, this lets you construct a string that contains elements from another type of container, such as a vector. 8. Many of the functions defined by string define versions that operate through iterators. The ones demonstrated by this recipe are erase( ), insert( ), and replace( ). They enable you to remove, insert, or replace characters within a string given iterators to the endpoints of the characters. 9. Because the STL algorithms work through iterators, you can use any of the algorithms on objects of type string. Two are demonstrated here: find( ) and transform( ). They require the <algorithm> header. Discussion A general overview of iterators is presented in Chapter 3, and that information is not repeated here. However, it is useful to review a few key points. First, the object pointed to by an iterator is accessed via the * operator in just the way that the * is used to access the object pointed to by a pointer. As it applies to string, the object pointed to by an iterator is a char value. Second, when an iterator is incremented, it points to the next object in the container. When it is decremented, it points to the previous object. For string, this means that the iterator points to the next or previous character. There are two basic styles of iterators supported by string: forward and reverse. When incremented, a forward iterator moves towards the end of the string and when decremented, it moves towards the start of the string. A reverse iterator works oppositely. When a reverse iterator is incremented, it moves towards the start of the string and when 71
  • 91. 72 Herb Schildt's C++ Programming Cookbook decremented, it moves towards the end of the string. Of these two basic iterators, the string class declares four basic types of iterators that have the following type names: iterator Forward-moving iterator that can read and write what it points to. const_iterator Forward-moving iterator that is read-only. reverse_iterator Reverse-moving iterator that can read and write what it points to. const_reverse_iterator Reverse-moving iterator that is read-only. This recipe uses only iterator and reverse_iterator, but the other two work in the same way, except that the object to which they point cannot be written. In the discussions that follow, the generic type names InIter and OutIter are used by some of the functions. In this book, InIter is an iterator type that is, at minimum, capable of read operations. OutIter is an iterator type that is, at minimum, capable of write operations. (Other types of iterators are discussed in Chapter 3.) To declare an iterator to a string, use one of the aforementioned types. For example: string::iterator itr; declares a non-const forward iterator that can be used with a string object. To obtain an iterator to the start of a string (which is the first character in the string), call begin( ). To obtain an iterator that points one past the end of the string, call end( ). Thus, the last character in the string is at end( ) –1. These functions are shown here: iterator begin( ) iterator end( ) The advantage to having end( ) return an iterator to one past the last character is that very efficient loops can be written that cycle through all the characters in a string. Here is an example: string::iterator itr; for(itr = str.begin(); itr != str.end(); ++itr) { // ... } When itr equals end( ), all of the characters in str have been examined. When using a reverse iterator, you can obtain a reverse iterator to the last character in the string by calling rbegin( ). To obtain a reverse iterator to one before the first character in the string, call rend( ). They are shown here: reverse_iterator rbegin( ) reverse_iterator rend( ) A reverse iterator is used in just the same way that you use a regular iterator. The only difference is that it moves through the string in the reverse direction. The string class provides a constructor that lets you create a string that is initialized by characters pointed to by iterators. It is shown here: template <class InIter> string(InIter start, InIter end, const Allocator &alloc = Allocator( ))
  • 92. Chapter 2: String Handling The range of characters is specified by start and end. The type of these iterators is specified by the generic type InIter, which indicates that the iterators must support read operations. However, they do not have to be of type string::iterator. This means that you can use this constructor to create a string that contains characters from another container, such as a vector. Several of string's functions have overloaded forms that use iterators to access the contents of the string. Three representative ones are used by this recipe: insert( ), erase( ), and replace( ). The versions used by this recipe are shown here: iterator erase(iterator start, iterator end) string &replace(iterator start, iterator end, const char *str) template <class InIter> void insert(iterator itr, InIter start, InIter end) The erase( ) method removes the characters in the range pointed to by start to end. It returns an iterator to the character that follows the last character removed. The replace( ) function replaces the characters in the range specified by start and end with str. It returns a reference to the invoking object. (Other iterator-enabled versions of replace( ) let you pass a string to str.) The insert( ) method inserts the characters in the range pointed to by start and end immediately before the element specified by itr. In insert( ), notice that start and end are of the generic type InIter, which means that the iterators must support read operations. All string iterator types satisfy this constraint. So do many other iterators. Thus, you can insert characters from another type of container into a string. This is one of the advantages of iterators. Because the STL algorithms work through iterators, you can use these algorithms on strings. The STL algorithms are declared in <algorithm>, and they perform various operations on containers. This recipe demonstrates the use of two algorithms, find( ) and transform( ), which are shown here: template <class InIter, class T> InIter find(InIter start, InIter end, const T &val) template <class InIter, class OutIter, class Func> OutIter transform(InIter start, InIter end, OutIter result, Func unaryFunc) The find( ) algorithm searches the range pointed to by start and end for the value specified by val. It returns an iterator to the first occurrence of the element or to end if the value is not in the sequence. The transform( ) algorithm applies a function to a range of elements specified by start and end, putting the outcome in result. The function to be applied is specified by unaryFunc. This function receives a value from the sequence and must return its transformation. Thus, both the parameter type and the return type must be compatible with the type of objects stored in the container, which in the case of string is char. The transform( ) algorithm returns an iterator to the end of the resulting sequence. Notice that result is of type OutIter, which means that it must support write operations. Example The following example shows how to use iterators with string objects. It also demonstrates iterator versions of string's member functions insert( ), replace( ), and find( ). The STL algorithms find( ) and transform( ) also are used. 73
  • 93. 74 Herb Schildt's C++ Programming Cookbook // Demonstrate iterators with strings. #include <iostream> #include <string> #include <cctype> #include <algorithm> #include <vector> using namespace std; int main() { string strA("This is a test."); // Create an iterator to a string. string::iterator itr; // Use an iterator to cycle through the characters // of a string. cout << "Display a string via an iterator.n"; for(itr = strA.begin(); itr != strA.end(); ++itr) cout << *itr; cout << "nn"; // Use a reverse iterator to display the string in reverse. cout << "Display a string in reverse using a reverse iterator.n"; string::reverse_iterator ritr; for(ritr = strA.rbegin(); ritr != strA.rend(); ++ritr) cout << *ritr; cout << "nn"; // Insert into a string via an iterator. // First, use the STL find() algorithm to obtain // an iterator to the start of the first 'a'. itr = find(strA.begin(), strA.end(), 'a'); // Next, increment the iterator so that it points to the // character after 'a', which in this case is a space. ++itr; // Insert into str by using the iterator version of insert(). cout <<"Insert into a string via an iterator.n"; string strB(" bigger"); strA.insert(itr, strB.begin(), strB.end()); cout << strA << "nn"; // Now, replace 'bigger' with 'larger'. cout << "Replace bigger with larger.n"; itr = find(strA.begin(), strA.end(), 'b'); strA.replace(itr, itr+6, "larger"); cout << strA << "nn";
  • 94. Chapter 2: String Handling // Now, remove ' larger'. cout << "Remove ' larger'.n"; itr = find(strA.begin(), strA.end(), 'l'); strA.erase(itr, itr+7); cout << strA << "nn"; // Use an iterator with the STL transform() algorithm to convert // a string to uppercase. cout << "Use the STL transform() algorithm to convert a " << "string into uppercase.n"; transform(strA.begin(), strA.end(), strA.begin(), toupper); cout << strA << "nn"; // Create a string from a vector<char>. vector<char> vec; for(int i=0; i < 10; ++i) vec.push_back('A'+i); string strC(vec.begin(), vec.end()); cout << "Here is strC, which is constructed from a vector:n"; cout << strC << endl; return 0; } The output is shown here: Display a string via an iterator. This is a test. Display a string in reverse using a reverse iterator. .tset a si sihT Insert into a string via an iterator. This is a bigger test. Replace bigger with larger. This is a larger test. Remove ' larger'. This is a test. Use the STL transform() algorithm to convert a string into uppercase. THIS IS A TEST. Here is strC, which is constructed from a vector: ABCDEFGHIJ Options and Alternatives As mentioned, several of the member functions defined by string have forms that operate on or return iterators. In addition to insert( ), erase( ), and replace( ) used by this recipe, 75
  • 95. 76 Herb Schildt's C++ Programming Cookbook string provides iterator-enabled versions of the append( ) and assign( ) functions. They are shown here: template<class InIter> string &append(InIter start, InIter end) template<class InIter> string &assign(InIter start, InIter end) This version of append( ) adds the sequence specified by start and end onto the end of the invoking string. This version of assign( ) assigns the sequence specified by start and end to the invoking string. Both return a reference to the invoking string. Create Case-Insensitive Search and Search-and-Replace Functions for string Objects Key Ingredients Headers Classes <cctype> <string> <algorithm> Functions int tolower(int ch) string iterator begin( ) iterator end( ) string &replace(iterator start, iterator end, const string &newsubtr) template <class ForIter1, class ForIter2, class BinPred> ForItrer1 search(ForIter1 start1, ForIter1 end1, ForIter2 start2, ForIter2 end2, BinPred pfn) Although string is very powerful, it does not directly support two very useful functions. The first is a search function that ignores case differences. As virtually all readers know, caseinsensitive searching is both a common and valuable feature in many contexts. For example, when searching a document for occurrences of the word "this", you usually want to find "This", too. The second function is a case-insensitive search-and-replace function, which replaces one substring with another independently of case differences. You could use such a function, for example, to replace all instances of "www" or "WWW" with the words "World Wide Web" in a single step. Whatever the purpose, it is easy to create case-insensitive searchand-replace functions that operate on string objects. This recipe shows one way. The functions developed by this recipe rely on iterators to access the characters within a string. Because string is an STL-compatible container, it provides support for iterators. This support is particularly important because it enables a string to be operated on by the STL algorithms. This ability significantly expands the ways in which strings can be manipulated.
  • 96. Chapter 2: String Handling It also enables you to create streamlined solutions to what would otherwise be more challenging tasks. (See the preceding recipe for information on using iterators with string.) Step-by-Step One way to create a search function that ignores case differences involves these steps: 1. Create a comparison function called comp_ign_case( ) that performs a case-insensitive comparison of two char values. Here is its prototype: bool comp_ign_case(char x, char y); Have the function return true if the two characters are equal and false otherwise. 2. Create a function called search_ign_case( ) that has this prototype: string::iterator search_ign_case(string &str, const string &substr); The string to be searched is passed in str. The substring to search for is passed in substr. 3. Inside search_ign_case( ), use the STL algorithm search( ) to search a string for a substring. This algorithm searches one sequence for an occurrence of another. The sequences are specified by ranges of iterators. Specify the comp_ign_case( ) function created in Step 1 as the binary predicate that determines when one character equals another. This enables search( ) to ignore case differences when searching. Note that search( ) is declared in the <algorithm> header, which must be included. 4. Have search_ign_case( ) return an iterator to the start of the first match or str.end( ) if no match is found. To create a search-and-replace function that ignores case differences, follow these steps: 1. You will need the search_ign_case( ) function described by the preceding steps. Therefore, if you have not yet created search_ign_case( ), you must do so at this time. 2. Create a function called search_and_replace_ign_case( ) that has this prototype: bool search_and_replace_ign_case(string &str, const string &oldsubstr, const string &newsubstr); The string to be modified is passed in str. The sequence to be replaced is passed in oldsubstr. The string to substitute is passed in newsubstr. 3. Use search_ign_case( ) to find the first occurrence of oldsubstr within str. 4. Use the iterator version of string's replace( ) function to replace the first occurrence of oldsubstr with newsubstr. 5. Have search_and_replace_ign_case( ) return true if the replacement is made and false if str did not contain an occurrence of oldsubstr. Discussion Before you can use the search( ) algorithm to perform a case-insensitive search, you must create a function that compares two char values in a case-independent manner. It must return true if the characters are equal and false otherwise. In the language of the STL, such a function is called a binary predicate. (See Chapter 3 for a discussion of binary predicates.) 77
  • 97. 78 Herb Schildt's C++ Programming Cookbook This function is used by the search( ) algorithm to compare two elements. By having this function ignore case differences, the search will be conducted independently of case. Here is one way to code this function: bool comp_ign_case(char x, char y) { return tolower(x) == tolower(y); } Notice that this uses the standard tolower( ) function to obtain the lowercase equivalent of each character. (See Ignore Case Differences When Comparing Null-Terminated Strings for details on tolower( ).) By converting each argument to lowercase, case differences are eliminated. To find a substring, call the search( ) algorithm. The version used by this recipe is shown here: template <class ForIter1, class ForIter2, class BinPred> ForItrer1 search(ForIter1 start1, ForIter1 end1, ForIter2 start2, ForIter2 end2, BinPred pfn) It searches for an occurrence of the sequence specified by start2 and end2 within the range of the sequence specified by start1 and end1. In this book, the generic type names ForIter1 and ForIter2 indicate iterators that have read/write capabilities and that can move in the forward direction. The binary predicate pfn determines when two elements are equal. (In this book, the generic type name BinPred indicates a binary predicate.) For the purposes of this recipe, pass comp_ign_case to this parameter. If a match is found, the function returns an iterator to the start of the matching sequence. Otherwise, end1 is returned. The search_and_replace_ign_case( ) function uses the iterator returned by search_ign_case( ) to find the location at which to substitute one substring for another. To handle the actual replacement, you can use this version of string's replace( ) function, which operates through iterators: string &replace(iterator start, iterator end, const string &newsubstr) It replaces the range specified by start and end with newsubstr. Thus, the invoking string is modified. It returns a reference to the invoking string. Example Here is one way to create the search_ign_case( ) function. It uses comp_ign_case( ) to determine when two characters are equal. // Ignore case when searching for a substring. // The string to search is passed in str. The substring to search // for is passed in substr. It returns an iterator to the start of // the match or str.end() if no match is found. // // Notice that it uses the search() algorithm and specifies the // binary predicate comp_ign_case(). string::iterator search_ign_case(string &str, const string &substr) { return search(str.begin(), str.end(), substr.begin(), substr.end(), comp_ign_case); }
  • 98. Chapter 2: String Handling As the comments indicate, search_ign_case( ) finds (independently of case differences) the first occurrence of substr and returns an iterator to the start of the matching sequence. It returns str.end( ) if no match is found. Here is one way to implement search_and_replace_ign_case( ). Notice that it uses search_ign_case( ) to find the substring to replace. // This function replaces the first occurrence of oldsubstr with // newsubstr in the string passed in str. It returns true if a // replacement occurs and false otherwise. // // Notice that this function modifies the string referred to by str. // Also notice that it uses search_ign_case() to find the substring // to replace. bool search_and_replace_ign_case(string &str, const string &oldsubstr, const string &newsubstr) { string::iterator startitr; startitr = search_ign_case(str, oldsubstr); if(startitr != str.end()) { str.replace(startitr, startitr+oldsubstr.size(), newsubstr); return true; } return false; } This function replaces the first occurrence of oldsubstr with newsubstr. It returns true if a replacement occurs (that is, if str contains oldsubstr) and false otherwise. As the comments indicate, this function modifies str in the process. It uses search_ign_case( ) to find the first occurrence of oldsubstr. Therefore, the search is performed independently of case differences. The following example shows both search_ign_case( ) and search_and_replace_ign_case( ) in action: // Implement case-insensitive search and search-and-replace // for string objects. #include <iostream> #include <string> #include <cctype> #include <algorithm> using namespace std; bool comp_ign_case(char x, char y); string::iterator search_ign_case(string &str, const string &substr); bool search_and_replace_ign_case(string &str, const string &oldsubstr, const string &newsubstr); int main() { string strA("This is a test of case-insensitive searching."); string strB("test"); 79
  • 99. 80 Herb Schildt's C++ Programming Cookbook string strC("TEST"); string strD("testing"); cout << "First, demonstrate search_ign_case().n"; cout << "String to be searched:n" << strA << "nn"; cout << "Searching for " << strB << ". "; if(search_ign_case(strA, strB) != strA.end()) cout << "Found!n"; cout << "Searching for " << strC << ". "; if(search_ign_case(strA, strC) != strA.end()) cout << "Found!n"; cout << "Searching for " << strD << ". "; if(search_ign_case(strA, strD) != strA.end()) cout << "Found!n"; else cout << "Not Found.n"; // Use the iterator returned by search_ign_case() to display // the remainder of the string. cout << "nRemainder of string after finding 'of':n"; string::iterator itr = search_ign_case(strA, "of"); while(itr != strA.end()) cout << *itr++; cout << "nn"; // Now, demonstrate search and replace. strA = "Alpha Beta Gamma alpha beta gamma"; cout << "Now demonstrate search_and_replace_ign_case().n"; cout << "String that will receive replacements:n" << strA << "nn"; cout << "Replacing all occurrences of alpha with zeta:n"; while(search_and_replace_ign_case(strA, "alpha", "zeta")) cout << strA << endl; return 0; } // Ignore case when searching for a substring. // The string to search is passed in str. The substring to search // for is passed in substr. It returns an iterator to the start of // the match or str.end() if no match is found. // // Notice that it uses the search() algorithm and specifies the // binary predicate comp_ign_case(). string::iterator search_ign_case(string &str, const string &substr) { return search(str.begin(), str.end(), substr.begin(), substr.end(), comp_ign_case); } // Ignore case when comparing two characters for equality.
  • 100. Chapter 2: String Handling // Return true if the characters are equal, independently // of case differences. bool comp_ign_case(char x, char y) { return tolower(x) == tolower(y); } // This function replaces the first occurrence of oldsubstr with // newsubstr in the string passed in str. It returns true if a // replacement occurs and false otherwise. // // Note that this function modifies the string referred to by str. // Also note that it uses search_ign_case() to find the substring // to replace. bool search_and_replace_ign_case(string &str, const string &oldsubstr, const string &newsubstr) { string::iterator startitr; startitr = search_ign_case(str, oldsubstr); if(startitr != str.end()) { str.replace(startitr, startitr+oldsubstr.size(), newsubstr); return true; } return false; } The output is shown here: First, demonstrate search_ign_case(). String to be searched: This is a test of case-insensitive searching. Searching for test. Found! Searching for TEST. Found! Searching for testing. Not Found. Remainder of string after finding 'of': of case-insensitive searching. Now demonstrate search_and_replace_ign_case(). String that will receive replacements: Alpha Beta Gamma alpha beta gamma Replacing all occurrences of alpha with zeta: zeta Beta Gamma alpha beta gamma zeta Beta Gamma zeta beta gamma Options and Alternatives Although I personally favor implementing a case-insensitive search through the use of the STL search( ) algorithm as this recipe does, there is another approach. You can implement 81
  • 101. 82 Herb Schildt's C++ Programming Cookbook such a search function yourself, working character by character and manually attempting to find a matching substring. Here is one way to do this: // Implement search_ign_case() manually. // Like the original version, the string to search is passed in str // and the substring to search for is passed in substr. // It returns an iterator to the start of the match or str.end() // if no match is found. string::iterator search_ign_case(string &str, const string &substr) { string::iterator start1, found_at; string::const_iterator start2; // If the string to match is null, return an iterator to // the start of str. if(substr.begin() == substr.end()) return str.begin(); start1 = found_at = str.begin(); while(start1 != str.end()) { start2 = substr.begin(); while(tolower(*start1) == tolower(*start2)) { ++start1; ++start2; if(start2 == substr.end()) return found_at; if(start1 == str.end()) return str.end(); } ++found_at; start1 = found_at; } return str.end(); } As you can see, the manual approach involves much more code. Furthermore, developing and testing this function takes more time than does using the STL search( ) algorithm. Finally, no attempt was made to optimize the preceding code. Optimization also takes a significant amount of time. For these reasons, I prefer the STL algorithms over a "home grown" approach in most cases. The tolower( ) function converts characters based on the current locale. To compare characters for a different locale, you can use a version of tolower( ) that is declared within <locale>. Although there is no advantage to doing so, it is also possible to convert each character in the string to uppercase (rather than lowercase) to eliminate case differences. This is done via the toupper( ) function, shown here: int toupper(int ch) It works just like tolower( ), except that it converts characters to uppercase.
  • 102. Chapter 2: String Handling Convert a string Object into a Null-Terminated String Key Ingredients Headers Classes Functions <string> string const char *c_str( ) const The string class provides easy mechanisms that convert a null-terminated string into a string object. For example, you can construct a string that is initialized with a null-terminated string. You can also assign a null-terminated string to a string object. Unfortunately, the reverse procedure is not quite as easy. The reason is that a null-terminated string is not a data type, but a convention. This means that you cannot initialize a null-terminated string with a string or assign a string to a char * pointer, for example. However, string does provide the c_str( ) function that converts a string object into a null-terminated string. This recipe shows the process. Step-by-Step To obtain a null-terminated string that contains the same character sequence as that encapsulated by a string object, follow these steps: 1. Create an array of char that is large enough to hold the characters contained in the string object, plus the null terminator. This can be a statically declared array or an array that is dynamically allocated via new. 2. To obtain a pointer to a null-terminated string that corresponds to the string contained in a string object, call c_str( ). 3. Copy the null-terminated string obtained in Step 2 into the array created in Step 1. Discussion To obtain a null-terminated string representation of the character sequence stored in a string object, call c_str( ), shown here: const char *c_str( ) const Although the character sequence in a string is not necessarily null-terminated, the pointer returned by a call to c_str( ) is guaranteed to point to a null-terminated character array that contains the same sequence. Notice, however, that the returned pointer is const. Thus, it cannot be used to modify the string. Furthermore, this pointer is valid only until a nonconst member function is called on the same string object. As a result, you will usually want to copy the null-terminated string into another array. Example The following example shows how to convert a string object into a null-terminated string: // Convert a string object into a null-terminated string. #include <iostream> #include <string> 83
  • 103. 84 Herb Schildt's C++ Programming Cookbook #include <cstring> using namespace std; int main() { string str("This is a test."); char cstr[80]; cout << "Here is the original string:n"; cout << str << "nn"; // Obtain a pointer to the string. const char *p = str.c_str(); cout << "Here is the null-terminated version of the string:n"; cout << p << "nn"; // Copy the string into a statically allocated array. // // First, confirm that the array is long enough // to hold the string. if(sizeof(cstr) < str.size() + 1) { cout << "Array is too small to hold the string.n"; return 0; } strcpy(cstr, p); cout << "Here is the string copied into cstr:n" << cstr << "nn"; // Next,copy the string into a dynamically allocated array. try { // Dynamically allocate the array. char *p2 = new char[str.size()+1]; // Copy the string into the array. strcpy(p2, str.c_str()); cout << "String after being copied into dynamically-allocated array:n"; cout << p2 << endl; delete [] p2; } catch(bad_alloc ba) { cout << "Allocation Failuren"; return 1; } return 0; } The output is shown here: Here is the original string: This is a test.
  • 104. Chapter 2: String Handling Here is the null-terminated version of the string: This is a test. Here is the string copied into cstr: This is a test. String after being copied into dynamically-allocated array: This is a test. Options and Alternatives As explained, the c_str( ) function returns a pointer to a null-terminated array of char. If you only need access to the characters that comprise the sequence encapsulated by a string, without the null terminator, then you can use the data( ) function. It returns a pointer to an array of char that contains the characters, but that array is not null-terminated. It is shown here: const char *data( ) const Because a const pointer is returned, you cannot use it to modify the underlying characters in the array. If you want to modify the character sequence, copy it into another array. Although the pointer returned by c_str( ) is const, it is possible to override this by using a const_cast, as shown here: char *p = const_cast<char *> (str.c_str()); After this statement executes, it would be possible to modify the character sequence pointed to by p. However, doing this is not recommended! Changing the character sequence controlled by a string object from code outside the object could easily cause the object to become corrupted, possibly leading to a program crash or a security breach. Therefore, changes to a string object must always take place through string member functions. You should never attempt to change the underlying sequence through a pointer returned by c_str( ) or data( ). If you see a construct like this, you should consider it invalid code and take steps to remedy the situation. Implement Subtraction for string Objects Key Ingredients Headers Classes Functions <string> string string &erase(size_type indx = 0, size_type len = npos) size_type find(const string &str, size_type indx = 0) const 85
  • 105. 86 Herb Schildt's C++ Programming Cookbook As you know, the + operator is overloaded for objects of type string, and it concatenates two strings and returns the result. However, the – operator is not overloaded for string. Some programmers find this a bit surprising because, intuitively, one would expect the – operator to be used to remove a substring from a string, as illustrated by this sequence: string strA("one two three"); string strB; strB = strA – "two"; At this point, one would expect strB to contain the sequence "one three", which is the original sequence with the word "two" removed. Of course, this is not possible using only the operators defined for string by the standard library, because subtraction is not one of them. Fortunately, it is quite easy to remedy this situation, as this recipe shows. To support substring subtraction, this recipe implements both the – and the – = operators for objects of type string. Each removes the first occurrence of the string on the left from the string on the right. In the case of –, the result is returned but neither operand is modified. For – =, the substring is removed from the left operand. Thus, the left operand is modified. Step-by-Step To overload operator–( ) for objects of type string involves these steps: 1. Create a version of operator –( ) that has the following prototype: string operator-(const string &left, const string &right); When one string is subtracted from another, the string on the left will be referred to by left and the string on the right will be referred to by right. 2. Inside operator –( ), create a string that will hold the result of the subtraction, and initialize that string with the character sequence in left. 3. Use find( ) to find the first occurrence of right in the result string. 4. If a matching substring is found, use erase( ) to remove the substring from the result string. 5. Return the resulting string. To overload operator – =( ) for objects of type string involves these steps: 1. Create a version of operator – =( ) that has the following prototype: string operator-=(string &left, const string &right); Here, the string on the left will be referred to by left and the string on the right will be referred to by right. Furthermore, left will receive the result of the subtraction. 2. Inside operator –( ), use find( ) to find the first occurrence of right in the string referred to by left. 3. If a matching substring is found, use erase( ) to remove the substring from left. This results in left being modified. 4. Return left.
  • 106. Chapter 2: String Handling Discussion When binary operators are overloaded by non-member functions, the operand on the left is always passed in the first parameter and the operand on the right is always passed in the second parameter. Therefore, given an operator–( ) function with this prototype: string operator-(const string &left, const string &right); the expression strA – strB causes a reference to strA to be passed to left and a reference to strB to be passed to right. Furthermore, given an operator–=( ) function with this prototype: string operator-=(string &left, const string &right) The statement strA -= strB; causes a reference to strA to be passed to left and a reference to strB to be passed to right. Although there is no mechanism that enforces it, usually it is better to overload operators in a manner consistent with their normal meaning and effects. Therefore, typically, when a binary operator such as – is overloaded, the result is returned but neither operand is modified. This is in keeping with the normal usage of the – in expressions such as 10–3. In this case, the result is 7, but neither 10 nor 3 is modified. Of course, the situation is different for the – = operation. In this case, the operand on the left receives the outcome of the operation. Therefore, typically, an overloaded operator–=( ) modifies the left operand. This recipe follows these conventions. The actual process of removing the first occurrence of a substring is quite easy, involving only two main steps. First, string's find( ) function is called to locate the start of the first match. The find( ) function is detailed in Search a string Object, but here is a brief summary. The find( ) function has several forms. The one used here is: size_type find(const string &str, size_type indx = 0) const It returns the index of the first occurrence of str within the invoking string. The search begins at the index specified by indx. npos is returned if no match is found. Assuming a match is found, the substring is removed by calling erase( ). This function is discussed in Perform Basic Operations on string Objects. Here is a quick recap. The erase( ) function has three forms. The one used by this recipe is shown here: string &erase(size_type indx = 0, size_type len = npos) Beginning at indx, it removes len characters from the invoking string. It returns a reference to the invoking string. When implementing operator–( ), neither operand should be modified. Therefore, a temporary string that will hold the result of the subtraction must be used. Initialize this string with the character sequence in the left operand. Then, remove the substring specified by the right operand. Finally, return the result. 87
  • 107. 88 Herb Schildt's C++ Programming Cookbook When implementing operator – =( ), the left operand must contain the result of the subtraction. Therefore, remove the substring specified by the right operand from the string referred to by the left operand. Even though the left operand contains the result, you should also return the resulting string. This enables the – = operator to be used as part of a larger expression. Example Here is one way to implement operator–( ) and operator–=( ) for objects of type string: // Overload - (subtraction) for string objects so that it removes // the first occurrence of the substring on the right from the // string on the left and returns the result. Neither // operand is modified. If the substring was not found, the // result contains the same string as the left operand. string operator-(const string &left, const string &right) { string::size_type i; string result(left); i = result.find(right); if(i != string::npos) result.erase(i, right.size()); return result; } // Overload -= for string objects. It removes the first // occurrence of the substring on the right from the string // on the left. Thus, the string referred to by left is modified. // The resulting string is also returned. string operator-=(string &left, const string &right) { string::size_type i; i = left.find(right); if(i != string::npos) left.erase(i, right.size()); return left; } The following example shows these operators in action: // Implement operator-() and operator-=() for strings. #include <iostream> #include <string> using namespace std; string operator-(const string &left, const string &right); string operator-=(string &left, const string &right); int main() {
  • 108. Chapter 2: String Handling string str("This is a test."); string res_str; cout << "Contents of str: " << str << "nn"; // Subtract "is" from str and put the result in res_str. res_str = str - "is"; cout << "Result of str - "is": " << res_str << "nn"; // Use -= to subtract "is" from res_str. This puts the result // back into res_str. res_str -= "is"; cout << "Result of res_str -= "is": " << res_str << "nn"; cout << "Here is str again: " << str << "nNotice that str is unchanged by the preceding " << "operations." << "nn"; cout << "Here are some more examples:nn"; // Attempt to subtract "xyz". This causes no change. res_str = str - "xyz"; cout << "Result of str - "xyz": " << res_str << "nn"; // Remove the last three characters from str. res_str = str - "st."; cout << "Result of str - "st.": " << res_str << "nn"; // Remove a null string, which results in no change. res_str = str - ""; cout << "Result of str - "": " << res_str << "nn"; return 0; } // Overload - (subtraction) for string objects so that it removes // the first occurrence of the substring on the right from the // string on the left and returns the result. Neither // operand is modified. If the substring was not found, the // result contains the same string as the left operand. string operator-(const string &left, const string &right) { string::size_type i; string result(left); i = result.find(right); if(i != string::npos) result.erase(i, right.size()); return result; } // Overload -= for string objects. It removes the first // occurrence of the substring on the right from the string // on the left. Thus, the string referred to by left is modified. 89
  • 109. 90 Herb Schildt's C++ Programming Cookbook // The resulting string is also returned. string operator-=(string &left, const string &right) { string::size_type i; i = left.find(right); if(i != string::npos) left.erase(i, right.size()); return left; } The output is shown here: Contents of str: This is a test. Result of str - "is": Th is a test. Result of res_str -= "is": Th a test. Here is str again: This is a test. Notice that str is unchanged by the preceding operations. Here are some more examples: Result of str - "xyz": This is a test. Result of str - "st.": This is a te Result of str - "": This is a test. Options and Alternatives The versions of operator–( ) and operator–=( ) described by the recipe remove only the first occurrence of the substring on the right from the string on the left. However, with a bit of work, you can change their operation so that they remove all occurrences of the substring. Here is one approach: // Overload - (subtraction) for string objects so that it removes // ALL occurrences of the substring on the right from the // string on the left. The result is returned. Neither operand // is modified. string operator-(const string &left, const string &right) { string::size_type i; string result(left); if(right != "") { do { i = result.find(right); if(i != string::npos) result.erase(i, right.size()); } while(i != string::npos); }
  • 110. Chapter 2: String Handling return result; } // Overload -= for string objects so that it removes // ALL occurrences of the substring on the right from the string // on the left. The result is contained in the string referred // to by the left operand. Thus, the left operand is modified. // The resulting string is also returned. string operator-=(string &left, const string &right) { string::size_type i; if(right != "") { do { i = left.find(right); if(i != string::npos) left.erase(i, right.size()); } while(i != string::npos); } return left; } Another option that you may find helpful in some cases is to implement string subtraction so that it operates independently of case differences. To do this, use the approach described in Create Case-Insensitive Search and Search-and-Replace Functions for string Objects to perform a case-insensitive search to find the substring to remove. 91
  • 111. This page intentionally left blank
  • 112. 3 CHAPTER Working with STL Containers T his is the first of two chapters that present recipes that use the Standard Template Library (STL). Two chapters are needed because the STL is an extraordinarily large and important part of C++. Not only does it provide off-the-shelf solutions to some of programming's most challenging problems, it also redefines the way in which one approaches many common tasks. For example, instead of having to provide your own code for a linked list, you can use the STL's list class. If your program needs to associate a key with a value and provide a means of finding that value given the key, it can use the map class. Because the STL provides solid, debugged implementations of the most commonly used "data engines," you can use one whenever it is needed, without going through the time and trouble to develop your own. This chapter starts with an overview of the STL and then presents recipes that demonstrate the core of the STL: its containers. In the process, it shows how iterators are used to access and cycle through the contents of a container. The following chapter shows how to use algorithms and several other key components of the STL. Here are the recipes contained in this chapter: • Basic Sequence Container Techniques • Use vector • Use deque • Use list • Use the Sequence Container Adaptors: stack, queue, and priority_queue • Store User-Defined Objects in a Container • Basic Associative Container Techniques • Use map • Use multimap • Use set and multiset NOTE For an in-depth description of the STL, see my book STL Programming from the Ground Up. Much of the overview and descriptions in this chapter are adapted from that work. The STL also receives extensive coverage in my book C++: The Complete Reference. 93 Copyright © 2008 by The McGraw-Hill Companies. Click here for terms of use.
  • 113. 94 Herb Schildt's C++ Programming Cookbook STL Overview At its core, the Standard Template Library is a sophisticated set of template classes and functions that implements many popular and commonly used data structures and algorithms. For example, it includes support for vectors, lists, queues, and stacks. It also supplies many algorithms—such as sorting, searching, and merging—that operate on them. Because the STL is constructed from template classes and functions, the data structures and algorithms can be applied to nearly any type of data. This is, of course, part of its power. The STL is organized around three foundational items: containers, algorithms, and iterators. Put simply, algorithms act on containers through iterators. More than anything else, the design and implementation of these features determine the nature of the STL. In addition to containers, algorithms, and iterators, the STL relies on several other standard elements for support: allocators, adaptors, function objects, predicates, binders, and negators. A brief description of each follows. Containers As the name implies, a container is an object that can hold other objects. There are several different types of containers. For example, the vector class defines a dynamic array, deque creates a double-ended queue, and list provides a linked list. These containers are called sequence containers because in STL terminology, a sequence is a linear list. The STL also defines associative containers, which allow efficient retrieval of values based on keys. Thus, the associative containers store key/value pairs. A map is an example. It stores key/value pairs in which each key is unique. This makes it easy to retrieve a specific value given its key. Algorithms Algorithms act on containers. Their capabilities include initializing, sorting, searching, merging, replacing, and transforming the contents of a container. Many algorithms operate on a range of elements within a container. Iterators Iterators are objects that act, more or less, like pointers. They give you the ability to cycle through the contents of a container in much the same way that you would use a pointer to cycle through an array. There are five types of iterators: Iterator Access Allowed Random Access Store and retrieve values. Elements may be accessed randomly. Bidirectional Store and retrieve values. Forward- and backward-moving. Forward Store and retrieve values. Forward-moving only. Input Retrieve, but not store, values. Forward-moving only. Output Store, but not retrieve, values. Forward-moving only. In general, an iterator that has greater access capabilities can be used in place of one that has lesser capabilities. For example, a forward iterator can be used in place of an input iterator.
  • 114. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s Iterators are handled just like pointers. You can increment and decrement them. You can apply the * and –> operators to them. Iterators are declared using the iterator type defined by the various containers. The STL also supports reverse iterators. Reverse iterators are either bidirectional or random-access iterators that move through a sequence in the reverse direction. Thus, if a reverse iterator points to the end of a sequence, incrementing that iterator will cause it to point to one element before the end. All iterators must support the types of pointer operations allowed by their category. For example, an input iterator class must support –>, ++, *, ==, and !=. Further, the * operator cannot be used to assign a value. By contrast, a random-access iterator must support –>, +, ++, –, – –, *, <, >, <=, >=, –=, +=, ==, !=, and [ ]. Also, the * must allow assignment. The operations that are supported for each type of iterator are shown here: Iterator Operations Supported Random Access *, –>, =, +, –, ++, – –, [ ], <, >, <=, >=, –=, +=, ==, != Bidirectional *, –>, =, ++, – –, ==, != Forward *, –>, =, ++, ==, != Input *, –>, =, ++, ==, != Output *, =, ++ When referring to the various iterator types in template descriptions, this book will use the following terms: Term Represents BiIter Bidirectional iterator ForIter Forward iterator InIter Input iterator OutIter Output iterator RandIter Random-access iterator Allocators Each container has defined for it an allocator. Allocators manage memory allocation for a container. The default allocator is an object of class allocator, but you can define your own allocators, if needed, for specialized applications. For most uses, the default allocator is sufficient. Function Objects Function objects are instances of classes that define operator( ). There are several predefined function objects, such as less( ), greater( ), plus( ), minus( ), multiplies( ), and divides( ). Perhaps the most widely used function object is less( ), which determines when one object is less than another. Function objects can be used in place of function pointers in the 95
  • 115. 96 Herb Schildt's C++ Programming Cookbook STL algorithms. Function objects increase the efficiency of some types of operations and provide support for certain operations that would not otherwise be possible using only a function pointer. Adaptors In the most general sense, an adaptor transforms one thing into another. There are container adaptors, iterator adaptors, and function adaptors. An example of a container adaptor is queue, which adapts the deque container for use as a standard queue. Predicates Several of the algorithms and containers use a special type of function called a predicate. There are two variations of predicates: unary and binary. A unary predicate takes one argument. A binary predicate has two arguments. These functions return true/false results, but the precise conditions that make them return true or false are defined by you. In this book, when a unary predicate function is required, it will be notated using the type UnPred. When a binary predicate is required, the type BinPred will be used. In a binary predicate, the arguments are always in the order of first, second. For both unary and binary predicates, the arguments will contain values of the type of objects being stored by the container. Some algorithms use a special type of binary predicate that compares two elements. Comparison functions return true if their first argument is less than their second. In this book, comparison functions will be notated using the type Comp. Binders and Negators Two other entities that populate the STL are binders and negators. A binder binds an argument to a function object. A negator returns the complement of a predicate. Both increase the versatility of the STL. The Container Classes At the core of the STL are its containers. They are shown in Table 3-1. Also shown are the headers necessary to use each container. As one might expect, each container has different capabilities and attributes. Containers are implemented using template classes. For example, the template specification for the deque container is shown here. All containers use similar specifications. template <class T, class Allocator = allocator<T> > class deque Here, the generic type T specifies the type of objects held by the deque. The allocator used by the deque is specified by Allocator, which defaults to the standard allocator class. For the vast majority of applications, you will simply use the default allocator, and that is what all of the code in this chapter does. However, it is possible to define your own allocator class if a special allocation scheme is ever needed. If you are not familiar with default arguments in templates, just remember that they work in much the same way as default arguments in functions. If the generic type argument is not specified explicitly when an object is created, then the default type is used.
  • 116. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s Container Description Required Header deque A double-ended queue. <deque> list A linear list. <list> map Stores key/value pairs in which each key is associated with only one value. <map> multimap Stores key/value pairs in which one key may be associated with two or more values. <map> multiset A set in which each element is not necessarily unique. <set> priority_queue A priority queue. <queue> queue A queue. <queue> set A set in which each element is unique. <set> stack A stack. <stack> vector A dynamic array. <vector> TABLE 3-1 Containers Defined by the STL Each container class includes several typedefs that create a set of standard type names. Several of these typedef names are shown here: size_type Some type of unsigned integer. reference A reference to an element. const_reference A const reference to an element. iterator An iterator. const_iterator A const iterator. reverse_iterator A reverse iterator. const_reverse_iterator A const reverse iterator. value_type The type of value stored in a container. Same as T for sequence containers. allocator_type The type of the allocator. key_type The type of a key. As mentioned, there are two broad categories of containers: sequence and associative. The sequence containers are vector, list, and deque. The associative containers are map, multimap, set, and multiset. The sequence containers operate on sequences, which are essentially linear lists of objects. The associative containers operate on lists of keys. Associative containers that implement maps operate on key/value pairs and allow the retrieval of a value given its key. The stack, queue, and priority_queue classes are called container adaptors because they use (i.e., adapt) one of the sequence containers to hold their elements. Thus, one of the 97
  • 117. 98 Herb Schildt's C++ Programming Cookbook sequence containers underlies the functionality provided by stack, queue, and priority_queue. From the programmer's perspective, the container adaptors look and act like the other containers. Common Functionality The STL specifies a set of requirements that all containers must satisfy. By specifying a common functionality, the STL ensures that all containers can be acted on by algorithms and that all containers can be used in a well-understood, consistent manner that is independent of the details of each container implementation. This is another major strength of the STL. All containers must support the assignment operator. They must also support all of the logical operators. In other words, all containers must support these operators: =, ==, <, <=, !=, >, >= All containers must supply a constructor that creates an empty container and a copy constructor. They must supply a destructor that releases all memory used by the container and calls the destructor for every element in the container. All containers must also support iterators. Among other advantages, this ensures that all containers can be operated on by algorithms. All containers must provide the following functions: iterator begin( ) const_iterator begin( ) const bool empty( ) const iterator end( ) const_iterator end( ) const size_type max_size( ) const size_type size( ) const void swap(ContainerType c) Returns an iterator to the first element in the container. Returns a const iterator to the first element in the container. Returns true if the container is empty. Returns an iterator to one past the last element in the container. Returns a const iterator to one past the last element in the container. Returns the maximum number of elements that the container can hold. Returns the number of elements currently stored in the container. Exchanges the contents of two containers. A container that supports bidirectional access to its elements is called a reversible container. In addition to the basic container requirements, a reversible container must also provide reverse iterators and the following functions: reverse_iterator rbegin( ) Returns a reverse iterator to the last element in the container. const_reverse_iterator rbegin( ) const Returns a const reverse iterator to the last element in the container. reverse_iterator rend( ) Returns a reverse iterator to one before the first element in the container. const_reverse_iterator rend( ) const Returns a const reverse iterator to one before the first element in the container.
  • 118. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s Sequence Container Requirements In addition to the functionality common to all containers, a sequence container adds the following functions: void clear( ) Removes all elements in the container. iterator erase(iterator i) Removes the element pointed to by i. Returns an iterator to the element after the one removed. iterator erase(iterator start, iterator end) Removes elements in the range specified by start and end. Returns an iterator to the element that follows the last element removed. iterator insert(iterator i, const T &val) Inserts val immediately before the element specified by i. Returns an iterator to the element. void insert(iterator i, size_type num, const T &val) Inserts num copies of val immediately before the element specified by i. template <class InIter> void insert(iterator i, InIter start, InIter end) Inserts the sequence defined by start and end immediately before the element specified by i. The STL also defines a set of functions for sequence containers that are optional, but often implemented. These are shown here: reference at(size_type idx) Returns a reference to the element specified by idx. const_reference at(size_type idx) const Returns a const reference to the element specified by idx. reference back( ) Returns a reference to the last element in the container. const_reference back( ) const Returns a const reference to the last element in the container. reference front( ) Returns a reference to the first element in the container. const_reference front( ) const Returns a const reference to the first element in the container. reference operator[ ](size_type idx) Returns a reference to the element specified by idx. const_reference operator[ ](size_type idx) const Returns a const reference to the element specified by idx. void pop_back( ) Removes the last element in the container. void pop_front( ) Removes the first element in the container. void push_back(const T &val) Adds an element with the value specified by val to the end of the container. void push_front(const T &val) Adds an element with the value specified by val to the beginning of the container. 99
  • 119. 100 Herb Schildt's C++ Programming Cookbook Sequence containers must also supply constructors that enable a container to be initialized by elements specified by a pair of iterators or with a specified number of a specified element. Of course, a sequence container is free to supply additional functionality. Associative Container Requirements In addition to the functionality required of all containers, associative containers have several other requirements. First, all associative containers must support the following functions: void clear( ) Removes all elements from the container. size_type count(const key_type &k) const Returns the number of times k occurs in the container. void erase(iterator i) Removes the element pointed to by i. void erase(iterator start, iterator end) Removes the elements in the range start to end. size_type erase(const key_type &k) Removes elements that have keys with the value k. Returns the number of elements that have been removed. pair<iterator, iterator> equal_range(const key_type &k) Returns a pair of iterators that point to the upper bound and the lower bound in the container for the specified key. pair<const_iterator, const_iterator> equal_range(const key_type &k) const Returns a pair of const iterators that point to the upper bound and the lower bound in the container for the specified key. iterator find(const key_type &k) Returns an iterator to the specified key. If the key is not found, then an iterator to the end of the container is returned. const_iterator find(const key_type &k) const Returns a const iterator to the specified key. If the key is not found, then an iterator to the end of the container is returned. pair<iterator, bool> insert(const value_type &val) Inserts val into the container. If the container requires unique keys, then val is inserted only if it does not already exist. If the element is inserted, pair<iterator, true> is returned. Otherwise, pair<iterator, false> is returned. iterator insert(iterator start, const value_type &val) Inserts val. The search for the proper insertion point begins at the element specified by start. For containers that require unique keys, elements are inserted only if they do not already exist. An iterator to the element is returned. template <class InIter> void insert(InIter start, InIter end) Inserts a range of elements. For containers that require unique keys, elements are inserted only if they do not already exist. key_compare key_comp( ) const Returns the function object that compares two keys. iterator lower_bound(const key_type &k) Returns an iterator to the first element with a key equal to or greater than k. const_iterator lower_bound(const key_type &k) const Returns a const iterator to the first element with a key equal to or greater than k.
  • 120. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s iterator upper_bound(const key_type &k) Returns an iterator to the first element with a key greater than k. const_iterator upper_bound(const key_type &k) const Returns a const iterator to the first element with a key greater than k. value_compare value_comp( ) const Returns the function object that compares two values. Notice that some of the functions return a pair object. This is a class that encapsulates two objects. For associative containers that are maps, value_type represents a pair that encapsulates a key and value. The pair class is explained in detail in Basic Associative Container Techniques. Associative containers must supply constructors that enable a container to be initialized by elements specified by a pair of iterators. They must also support constructors that let you specify the comparison function used to compare two keys. Of course, an associative container is free to supply additional functionality. Performance Issues There is one other important aspect to the STL that adds to its power and general applicability: performance guarantees. Although a compiler manufacturer is free to implement the underlying mechanism used by each container and algorithm in its own way, all implementations must conform to the performance guarantees specified by the STL. The following general performance categories are defined: constant linear logarithmic Since different containers store their contents differently, they will have different performance guarantees. For example, insertion into the middle of a vector takes linear time. By contrast, insertion into a list takes constant time. Different algorithms might also behave differently. For example, the sort( ) algorithm executes proportional to N log N, but the find( ) algorithm runs in linear time. In some cases, an operation will be said to take amortized constant time. This is the term used to describe a situation in which an operation usually takes constant time, but occasionally requires longer. (For example, insertions onto the end of a vector normally occur in constant time, but if more memory must be allocated, then the insertion requires linear time.) If the longer operation is rare enough, then it can be thought of as being amortized over a number of shorter operations. In general, the STL specification requires that the containers and algorithms be implemented using techniques that ensure (loosely speaking) optimal runtime performance. This is important because it guarantees to you, the programmer, that the STL building blocks meet a certain level of efficiency no matter what implementation of the STL you are using. Without such a guarantee, the performance of STL-based code would depend entirely upon each individual implementation and could vary widely. 101
  • 121. 102 Herb Schildt's C++ Programming Cookbook Basic Sequence Container Techniques Key Ingredients Headers Classes Functions <vector> vector iterator begin( ) void clear( ) bool empty( ) const iterator end( ) iterator erase(iterator i) iterator insert(iterator i, const T &val) reverse_iterator rbegin( ) reverse_iterator rend( ) size_type size( ) const void swap(vector<T, Allocator> &ob) <vector> template <class T, class Allocator> bool operator==(const vector<T, Allocator> &leftop, const vector<T, Allocator> &rightop) template <class T, class Allocator> bool operator<(const vector<T, Allocator> &leftop, const vector<T, Allocator> &rightop) template <class T, class Allocator> bool operator>(const vector<T, Allocator> &leftop, const vector<T, Allocator> &rightop) All sequence containers share a common functionality. For example, all allow you to add elements to the container, remove elements from the container, or cycle through the container via an iterator. All support the assignment operator and the logical operators, and all sequence containers are constructed in the same way. This recipe describes this common functionality, showing the basic techniques that apply to all sequence containers. This recipe shows how to: • Create a sequence container. • Add elements to the container. • Determine the size of the container. • Use an iterator to cycle through the container.
  • 122. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s • Assign one container to another. • Determine when one container is equivalent to another. • Remove elements from the container. • Exchange the elements in one container with another. • Determine if a container is empty. This recipe uses the vector container class, but only those methods common to all sequence containers are employed. Therefore, the same general principles can be applied to any sequence container type. Step-by-Step To create and use a sequence container involves these steps: 1. Create an instance of the desired container. In this recipe, vector is used, but any other sequence container could be substituted. 2. Add elements to the container by calling insert( ). 3. Obtain the number of elements in the container by calling size( ). 4. Determine if the container is empty (i.e., contains no elements) by calling empty( ). 5. Remove elements from the container by calling erase( ). 6. Remove all elements from a container by calling clear( ). 7. Obtain an iterator to the start of the sequence by calling begin( ). Obtain an iterator to one past the end of the sequence by calling end( ). 8. For reversible sequence containers, obtain a reverse iterator to the end of the sequence by calling rbegin( ). Obtain a reverse iterator to one before the start of the sequence by calling rend( ). 9. Cycle through the elements in the container via an iterator. 10. Exchange the contents of one container with another via swap( ). 11. Determine when one container is equal to, less than, or greater than another. Discussion Although the internal operation of the STL is quite sophisticated, using the STL is actually quite easy. In many ways, the hardest part of using the STL is deciding what type of container to use. Each offers certain benefits and trade-offs. For example, vector is very good when a random-access, array-like object is required and not too many insertions or deletions are required. A list offers low-cost insertion and deletion, but trades away speedy look-ups. A double-ended queue is supported by deque. This recipe uses vector to demonstrate the basic sequence container operations, but the program will work with either list or deque. This is one of the major advantages of the STL; all sequence containers support a base level of common functionality. The template specification for vector is shown here: template <class T, class Allocator = allocator<T> > class vector 103
  • 123. 104 Herb Schildt's C++ Programming Cookbook Here, T is the type of data being stored and Allocator specifies the allocator, which defaults to the standard allocator. To use vector, you must include the header <vector>. The vector class supports several constructors. The two used in this recipe are those required by all sequence containers. They are shown here: explicit vector(const Allocator &alloc = Allocator( ) ) vector(const vector<T, Allocator> &ob) The first form constructs an empty vector. The second form is vector's copy constructor. After a container has been created, objects can be added to it. One way to do this that works for all sequence containers is to call insert( ). All sequence containers support at least three versions of insert( ). The one used here is: iterator insert(iterator i, const T &val) It inserts val into the invoking container at the point specified by i. It returns an iterator to the inserted element. A sequence container will automatically grow as needed when elements are added to it. You can remove one or more elements from a sequence container by calling erase( ). It has at least two forms. The one used by this recipe is shown here: iterator erase(iterator i) It removes the element pointed to by i. It returns an iterator to the element after the one removed. To remove all elements in a container, call clear( ). It is shown here: void clear( ) You can determine the number of elements in a container by calling size( ). To determine if a container is empty, call empty( ). Both functions are shown here: bool empty( ) const size_type size( ) const You can obtain an iterator to the start of the sequence by calling begin( ). An iterator to one past the last element in the sequence is obtained by calling end( ). These functions are shown here: iterator begin( ) iterator end( ) There are also const versions of these functions. To declare a variable that will be used as an iterator, you must specify the iterator type of the container. For example, this declares an iterator that can point to elements within a vector<double>: vector<double>::iterator itr; It is useful to emphasize that end( ) does not return an iterator that points to the last element in a container. Instead, it returns an iterator that points to one past the last element.
  • 124. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s Thus, the last element in a container is pointed to by end( ) – 1. This feature lets you write very efficient algorithms that cycle through all of the elements of a container, including the last one, using an iterator. When the iterator has the same value as the one returned by end( ), you know that all elements have been accessed. For example, here is a loop that cycles through all elements in a sequence container called cont: for(itr = cont.begin(); itr != cont.end(); ++itr) // ... The loop runs until itr equals cont.end( ). Thus, all elements in cont will have been processed. As explained, a reversible container is one in which the elements can be traversed in reverse order (back to front). All of the built-in sequence containers are reversible. For a reversible container, you can obtain a reverse iterator to the end of the sequence by calling rbegin( ). An iterator to one before the first element in the sequence is obtained by calling rend( ). These functions are shown here: reverse_iterator rbegin( ) reverse_iterator rend( ) There are also const versions of these functions. A reverse iterator is declared just like a regular iterator. For example, vector<double>::reverse_iterator ritr; You can use a reverse iterator to cycle through a vector in reverse order. For example, given a reverse iterator called ritr, here is a loop that cycles through all elements in a reversible sequence container called cont from back to front: for(ritr = cont.rbegin(); ritr != cont.rend(); ++ritr) // ... The reverse iterator ritr starts at the element pointed to by rbegin( ), which is the last element in the sequence. It runs until it equals rend( ), which points to an element that is one before the start of the sequence. (It is sometimes helpful to think of rbegin( ) and rend( ) returning iterators to the start and end of a reversed sequence.) Each time a reverse iterator is incremented, it points to the previous element. Each time it is decremented, it points to the next element. The contents of two sequence containers can be exchanged by calling swap( ). Here is the way that it is defined for vector: void swap(vector<T, Allocator> &ob) The contents of the invoking container are exchanged with those specified by ob. Example The following example demonstrates the basic sequence container operations: // Demonstrate the basic sequence container operations. // // This example uses vector, but the same techniques can be // applied to any sequence container. 105
  • 125. 106 Herb Schildt's C++ Programming Cookbook #include <iostream> #include <vector> using namespace std; void show(const char *msg, vector<char> vect); int main() { // Declare an empty vector that can hold char objects. vector<char> v; // Declare an iterator to a vector<char>. vector<char>::iterator itr; // Obtain an iterator to the start of v. itr = v.begin(); // Insert characters into v. An iterator to the inserted // object is returned. itr = v.insert(itr, 'A'); itr = v.insert(itr, 'B'); v.insert(itr, 'C'); // Display the contents of v. show("The contents of v: ", v); // Declare a reverse iterator. vector<char>::reverse_iterator ritr; // Use a reverse iterator to show the contents of v in reverse. cout << "Here is v in reverse: "; for(ritr = v.rbegin(); ritr != v.rend(); ++ritr) cout << *ritr << " "; cout << "nn"; // Create another vector that is the same as the first. vector<char> v2(v); show("The contents of v2: ",v2); cout << "n"; // Show the size of v, which is the number of elements // currently held by v. cout << "Size of v is " << v.size() << "nn"; // Compare two containers. if(v == v2) cout << "v and v2 are equivalent.nn"; // Insert more characters into v and v2. This time, // insert them at the end. cout << "Insert more characters into v and v2.n"; v.insert(v.end(), 'D'); v.insert(v.end(), 'E'); v2.insert(v2.end(), 'X'); show("The contents of v: ", v);
  • 126. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s show("The contents of v2: ", v2); cout << "n"; // Determine if v is less than v2. This is a // lexicographical compare. Therefore, the first // non-matching element determines which // container is less than another. if(v < v2) cout << "v is less than v2.nn"; // Now, insert Z at the start of v. cout << "Insert Z at the start of v.n"; v.insert(v.begin(), 'Z'); show("The contents of v: ", v); cout << "n"; // Now, compare v to v2 again. if(v > v2) cout << "Now, v is greater than v2.nn"; // Remove the first element from v2. v2.erase(v2.begin()); show("v2 after removing the first element: ", v2); cout << "n"; // Create another vector. vector<char> v3; v3.insert(v3.end(), 'X'); v3.insert(v3.end(), 'Y'); v3.insert(v3.end(), 'Z'); show("The contents of v3: ", v3); cout << "n"; // Exchange the contents of v and v3. cout << "Exchange v and v3.n"; v.swap(v3); show("The contents of v: ", v); show("The contents of v3: ", v3); cout << "n"; // Clear v. v.clear(); if(v.empty()) cout << "v is now empty."; return 0; } // Display the contents of a vector<char> by using // an iterator. void show(const char *msg, vector<char> vect) { vector<char>::iterator itr; cout << msg; for(itr=vect.begin(); itr != vect.end(); ++itr) cout << *itr << " "; cout << "n"; } 107
  • 127. 108 Herb Schildt's C++ Programming Cookbook The output is shown here: The contents of v: C B A Here is v in reverse: A B C The contents of v2: C B A Size of v is 3 v and v2 are equivalent. Insert more characters into v and v2. The contents of v: C B A D E The contents of v2: C B A X v is less than v2. Insert Z at the start of v. The contents of v: Z C B A D E Now, v is greater than v2. v2 after removing the first element: B A X The contents of v3: X Y Z Exchange v and v3. The contents of v: X Y Z The contents of v3: Z C B A D E v is now empty. Although much of the program is self-explanatory, there are several points of interest that warrant closer examination. First, notice that no allocator is specified when the containers in the program (v, v2, and v3) are declared. As explained, for most uses of the STL, the default allocator is the right choice. Next, notice how the iterator itr is declared by this statement: vector<char>::iterator itr; This declares an iterator that can be used with objects of type vector<char>. Each container class creates a typedef for iterator. Iterators to other types of vectors or other containers are declared in the same general way. For example, vector<double>::iterator itrA; deque<string>::iterator itrB; Here, itrA is an iterator that can be used on vector<double> containers, and itrB applies to containers of type deque<string>. In general, you must declare an iterator in a way that matches both the type of the container and the type of objects contained in the container. The same goes for reverse iterators.
  • 128. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s Next, an iterator to the start of the container is obtained by calling begin( ), and then the following set of calls to insert( ) puts elements into v: itr = v.insert(itr, 'A'); itr = v.insert(itr, 'B'); v.insert(itr, 'C'); Each call inserts the value immediately before the element pointed to by the iterator passed in itr. An iterator to the inserted item is returned. Thus, these three calls cause v to contain the sequence CBA. Now, look at the show( ) function. It is used to display the contents of a vector<char>. Pay special attention to the following loop: for(itr=vect.begin(); itr != vect.end(); ++itr) cout << *itr << " "; It cycles through the vector passed to vect, beginning with the first element and stopping when the last element has been encountered. Remember, end( ) returns an iterator that points one element past the end of the container. Therefore, when itr equals vect.end( ), the end of the container has been reached. These types of loops are extremely common when working with the STL. Also, notice how itr is dereferenced via the * operator in just the same way you would dereference a pointer. In general, iterators work like pointers and are handled in essentially the same way. Next, in main( ), notice how the reverse iterator ritr is used to cycle through the contents of v in reverse order. A reverse iterator works just like a normal iterator, except that it accesses the elements of the container in reverse order. Now, notice how two containers are compared by use of the == and < operators. For sequence containers, comparisons are conducted using a lexicographical comparison of the elements. Although the term "lexicographical" literally means "dictionary order," its meaning is generalized as it relates to the STL. For container comparisons, two containers are equal if they contain the same number of elements, in the same order, and all corresponding elements are equal. Otherwise, the result of a lexicographical comparison is based on the first nonmatching elements. For example, given these two sequences: seq1: 7, 8, 9 seq2: 7, 8, 11 seq1 is less than seq2 because the first mismatch is 9 and 11, and 9 is less than 11. Because the comparison is lexicographical, seq1 is still less than seq2, even if the length of seq1 is increased to 7, 8, 9, 10, 11, 12. The first non-matching elements (in this case, 9 and 11) determine the outcome. Options and Alternatives In addition to the version of insert( ) used in this recipe, all sequence containers support the two forms shown here: void insert(iterator i, size_type num, const T &val) template <class InIter> void insert(iterator i, InIter start, InIter end) 109
  • 129. 110 Herb Schildt's C++ Programming Cookbook The first form inserts num copies of val immediately before the element specified by i. The second form inserts the sequence that runs from start to end–1 immediately before the element specified by i. Notice that start and end do not need to point into the invoking container. Thus, this form can be used to insert elements from one container into another. Furthermore, the containers do not need to be of the same kind. As long as the elements are compatible, you can insert elements from a deque into a list, for example. There is a second form of erase( ) that is supported by all sequence containers. It is shown here: iterator erase(iterator start, iterator end) This version removes elements in the range start to end–1 and returns an iterator to the element after the last element removed. In addition to the ==, <, and > operators, all sequence containers support the <=, >=, and != logical operators. You can find the maximum number of elements that a container can hold by calling max_size( ), shown here: size_type max_size( ) const Understand that the maximum size will vary, depending on the type of data the container holds. Also, different types of containers may (probably will) have differing maximum capacities. As mentioned, the preceding example works for all sequence containers. To prove this, try substituting list or deque for vector. As you will see, the program produces the same output as before. Of course, choosing the right container is an important part of using the STL successfully. Remember, different containers have different performance guarantees. For example, inserting an element into the middle of a deque takes linear time. Inserting into a list takes constant time. Inserting into the middle of a vector uses linear time, but inserting on the end can occur in constant time (if no reallocation is required). In general, if there is no compelling reason to choose one container over another, the vector is usually the best choice because it implements what is, in essence, a dynamic array (see Use vector). In some cases, you will want to use one of the sequence container adaptors, such as queue, stack, or priority_queue, that provides a specific functionality that you desire. For example, if you want a container that implements a classic stack, then use stack. For a single-ended queue, use queue. For a queue that is ordered according to priority, use priority_queue.
  • 130. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s Use vector Key Ingredients Headers Classes Functions <vector> vector template <class InIter> void assign(InIter start, InIter end) reference at(size_type i) reference back( ) size_type capacity( ) const reference front( ) reference operator[ ](size_type i) void pop_back( ) void push_back(const T &val) void reserve(size_type num) void resize(size_type num, T val = T( )) This recipe demonstrates vector, which is probably the most widely used sequence container because it implements a dynamic array. Unlike a static array, whose dimensions are fixed at compile time, a dynamic array can grow as needed during program execution. This makes vector an excellent choice for situations in which you need an array but don't know in advance how large it needs to be. Even though the array created by vector is dynamic, its elements can still be accessed using the normal array-subscripting operator [ ]. This makes it easy to drop vector into situations that would otherwise require an array. NOTE The focus of this recipe is on the attributes and features of vector that make it unique. See Basic Sequence Container Techniques for information that applies to all sequence containers. Step-by-Step Using vector involves the following steps: 1. Create a vector instance of the desired type and initial size. 2. Assign or obtain values to elements via the subscripting operator. 3. Use the at( ) function as an alternative to the subscripting operator. 4. Add elements to the vector using either insert( ) or push_back( ). 5. Remove elements from the end by calling pop_back( ). 6. Obtain a reference to the first element in the vector by calling front( ). 111
  • 131. 112 Herb Schildt's C++ Programming Cookbook 7. Obtain a reference to the last element in the vector by calling back( ). 8. Assign a range of elements to a vector by calling assign( ). 9. To obtain the current capacity of a vector, call capacity( ). To specify a capacity, call reserve( ). 10. To change the size of a vector, call resize( ). Discussion The template specification for vector is shown here: template <class T, class Allocator = allocator<T> > class vector Here, T is the type of data being stored and Allocator specifies the allocator, which defaults to the standard allocator. To use vector, you must include the <vector> header. Here are vector's constructors: explicit vector(const Allocator &alloc = Allocator( ) ) explicit vector(size_type num, const T &val = T ( ), const Allocator &alloc = Allocator( )) vector(const vector<T, Allocator> &ob) template <class InIter> vector(InIter start, InIter end, const Allocator &alloc = Allocator( )) The first form constructs an empty vector. The second form constructs a vector that has num elements with the value val. The third form is vector's copy constructor. The fourth form constructs a vector that contains the elements in the range start to end–1. The allocator used by the vector is specified by alloc, which is typically allowed to default. The vector class supports random-access iterators, and the [ ] is overloaded. This allows a vector object to be indexed like an array. The vector class implements all required sequence container functions and operations, such as erase( ), insert( ), swap( ), and the logical operators. It also provides all functions required for a reversible container. It supplies most of the optional sequence container functions. The only optional functions that it does not implement are push_front( ) and pop_front( ). The elements within a vector can be accessed in two ways. First, and most convenient, is through the use of the [ ] subscripting operator. It is shown here: reference operator[ ](size_type i) It returns a reference to the element at the index specified by i. The reference type is a typedef for T &. (A const version of the function is also supplied that returns a const_reference.) This operator can be used to set or get the value at a specified index. Of course, the index you specify must be within the current range of the vector. Like arrays, indexing begins at zero. Another way to access the elements in a vector is to use the at( ) method. It is shown here: reference at(size_type i) It returns a reference to the element at the index specified by i. (A const version of the function is also supplied that returns a const_reference.) This reference can be used to set or get the
  • 132. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s value at a specified index. Of course, the index you specify must be within the current range of the vector. Like the [ ] operator, indexing using at( ) also begins at zero. Although the [ ] operator is more convenient to use, the at( ) function does offer one benefit. If an attempt is made to access an element that is outside the current bounds of the vector, at( ) will throw an out_of_range exception. Thus, it provides bounds checking. The [ ] does not. Although all vectors have an initial size (which can be zero), it is possible to increase that size by adding elements to the vector. There are two easy ways to do this: insert elements using the insert( ) function and add elements to the end by calling push_back( ). The insert( ) function is described in Basic Sequence Container Techniques and is not described further here. The push_back( ) function is shown here: void push_back(const T &val) It adds an element with the value specified by val to the end of the vector. The vector is automatically increased in size to accommodate the addition. The complement to push_back( ) is pop_back( ). It removes an element from the end of the vector. It is shown here: void pop_back( ) After pop_back( ) executes, the size of the vector is reduced by one. You can obtain a reference to the last element in the vector by calling back( ). A reference to the first element is returned by front( ). These functions are shown here: reference back( ) reference front( ) The vector class also supplies const versions of these functions. The iterator type provided by vector is random-access. This means that an integer value can be added to or subtracted from an iterator, enabling the iterator to point to any arbitrary element within the container. It also allows an iterator to traverse a vector in either the forward or reverse direction. The vector class defines two iterator types: forward and reverse iterators. Forward iterators are objects of type iterator or const_iterator. Reverse iterators are of type reverse_iterator or const_reverse_iterator. A forward iterator to the start of a vector is obtained by calling begin( ), and an iterator to the end of the vector is obtained by calling end( ). A reverse iterator to the end of the vector is obtained by calling rbegin( ). A reverse iterator to one before the start of a vector is obtained by calling rend( ). These functions and the basic procedure required to cycle through a sequence container are described in Basic Sequence Container Techniques. You can assign a new set of values to a vector by using the assign( ) function. It has two forms. The one used by this recipe is shown here: template <class InIter> void assign(InIter start, InIter end) It replaces the entire contents of the invoking vector with the values specified in the range start to end–1. Notice that start and end can be any type of input iterator. This means that you can use assign( ) to assign values from another vector or any other type of container. The only rule is that the values must be compatible with the invoking object. 113
  • 133. 114 Herb Schildt's C++ Programming Cookbook All vectors are created with an initial capacity. This is the number of elements that the vector can hold before more memory needs to be allocated. You can obtain the current capacity by calling capacity( ), shown here: size_type capacity( ) const It is important not to confuse capacity with size. The size of a vector, which is available by calling the standard container function size( ), is the number of elements that it currently holds. Capacity is how many it can hold before a reallocation must occur. You can reserve memory for a specific number of elements by calling reserve( ), shown here: void reserve(size_type num) The reserve( ) function reserves memory for at least the number of elements specified by num. In other words, its sets the capacity of the invoking vector equal to or greater than num. (Thus, a compiler is free to adjust the capacity upward in the interest of efficiency.) Since increasing the capacity may cause a memory reallocation, it might invalidate any pointers or references to elements within the vector. If you know in advance that a vector will be holding a specific number of elements, then using reserve( ) will prevent unnecessary reallocations, which are costly in terms of time. You can change the size of a vector by calling resize( ), shown here: void resize(size_type num, T val = T( )) It sets the size of the vector to that specified by num. If the size of the vector is increased, then elements with the value specified by val are added to the end. Notice that val defaults to the default value of the T. If the vector is decreased in size, then elements are removed from the end. The vector class has the following performance characteristics. Inserting or deleting elements at the end of a vector takes place in amortized constant time. When occurring at the beginning or in the middle, insertions or deletions take place in linear time. As just explained, it is possible to reserve additional space in a vector by using the reserve( ) function. By pre-allocating extra memory, you will prevent reallocations from occurring. Thus, if you manage your vectors correctly, most insertions can occur in constant time. Access of an element via the subscripting operator takes place in constant time. In general, element access in a vector is faster than it is with any other sequence containers defined by the STL. This is why vector is used for dynamic arrays. In all cases, when an insertion occurs, references and iterators to elements after the point of the insertion will be invalid. However, in some cases, including those in which the element is added to the end via a call to push_back( ), all references and iterators to elements may be invalid. This situation occurs only if the vector needs to allocate more memory. In this case, a reallocation occurs, and the contents of the vector may have to be moved to a new location. If the vector is physically moved, previous iterators and references are no longer valid. Thus, for all practical purposes, it is best to assume that iterators and references are not valid after insertions. When an element is deleted from a vector, iterators and references to elements that are after the point of the erasure are invalid.
  • 134. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s Example The following example shows vector in action: // Demonstrate vector. #include <iostream> #include <vector> using namespace std; void show(const char *msg, vector<int> vect); int main() { // Declare a vector that has an initial capacity of 10. vector<int> v(10); // Assign its elements some values. Notice how this is // done using the standard array-subscripting syntax. // Notice that the number of elements in the vector is // obtained by calling size(). for(unsigned i=0; i < v.size(); ++i) v[i] = i*i; show("Contents of v: ", v); // Compute the average of the values. Again, notice // the use of the subscripting operator. int sum = 0; for(unsigned i=0; i < v.size(); ++i) sum += v[i]; double avg = sum / v.size(); cout << "The average of the elements is " << avg << "nn"; // Add elements to the end of v. v.push_back(100); v.push_back(121); show("v after pushing elements onto the end: ", v); cout << endl; // Now use pop_back() to remove one element. v.pop_back(); show("v after back-popping one element: ", v); cout << endl; cout << "The first and last element in v as" << " pointed to by begin() and end()-1:n" << *v.begin() << ", " << *(v.end()-1) << "nn"; cout << "The first and last element in v as" << " pointed to by rbegin() and rend()-1:n" << *v.rbegin() << ", " << *(v.rend()-1) << "nn"; // Declare an iterator to a vector<int>. vector<int>::iterator itr; 115
  • 135. 116 Herb Schildt's C++ Programming Cookbook // Now, declare reverse iterator to a vector<int> vector<int>::reverse_iterator ritr; // Cycle through v in the forward direction using an iterator. cout << "Cycle through the vector in the forward direction:n"; for(itr = v.begin(); itr != v.end(); ++itr) cout << *itr << " "; cout << "nn"; cout << "Now, use a reverse iterator to cycle through in the" << " reverse direction:n"; // Cycle through v in the reverse direction using a reverse_iterator. for(ritr = v.rbegin(); ritr != v.rend(); ++ritr) cout << *ritr << " "; cout << "nn"; // Create another vector that contains a subrange of v. vector<int> v2(v.begin()+2, v.end()-4); // Display the contents of v2 by using an iterator. show("v2 contains a subrange of v: ", v2); cout << endl; // Change the values of some of v2's elements. v2[1] = 100; v2[2] = 88; v2[4] = 99; show("After the assignments, v2 now contains: ", v2); cout << endl; // Create an empty vector and then assign it a sequence // that is the reverse of v. vector<int> v3; v3.assign(v.rbegin(), v.rend()); show("v3 contains the reverse of v: ", v3); cout << endl; // Show the size and capacity of v. cout << "Size of v is " << v.size() << ". The capacity is " << v.capacity() << ".n"; // Now, resize v. v.resize(20); cout << "After calling resize(20), the size << v.size() << " and the capacity is " << v.capacity() << ".n"; of v is " // Now, reserve space for 50 elements. v.reserve(50); cout << "After calling reserve(50), the size of v is " << v.size() << " and the capacity is " << v.capacity() << ".n"; return 0; }
  • 136. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s // Display the contents of a vector<int>. void show(const char *msg, vector<int> vect) { cout << msg; for(unsigned i=0; i < vect.size(); ++i) cout << vect[i] << " "; cout << "n"; } The output is shown here: Contents of v: 0 1 4 9 16 25 36 49 64 81 The average of the elements is 28 v after pushing elements onto the end: 0 1 4 9 16 25 36 49 64 81 100 121 v after back-popping one element: 0 1 4 9 16 25 36 49 64 81 100 The first and last element in v as pointed to by begin() and end()-1: 0, 100 The first and last element in v as pointed to by rbegin() and rend()-1: 100, 0 Cycle through the vector in the forward direction: 0 1 4 9 16 25 36 49 64 81 100 Now, use a reverse iterator to cycle through in the reverse direction: 100 81 64 49 36 25 16 9 4 1 0 v2 contains a subrange of v: 4 9 16 25 36 After the assignments, v2 now contains: 4 100 88 25 99 v3 contains the reverse of v: 100 81 64 49 36 25 16 9 4 1 0 Size of v is 11. The capacity is 15. After calling resize(20), the size of v is 20 and the capacity is 22. After calling reserve(50), the size of v is 20 and the capacity is 50. Most of the program is self-explanatory, but a couple of points merit further discussion. First, notice that the subscripting operator is used to assign a value to an element of a vector or to obtain the current value of an element. Thus, it works in the same way that it does when applied to an array. A key point to understand is that you can only use subscripting to access an existent element. For example, in the program, v initially has 10 elements. Therefore, you cannot assign a value to v[15], for example. If you need to expand a vector after it is created, you should use either the push_back( ) method, which adds a value to the end, or the insert( ) method, which can be used to insert one or more elements anywhere in the sequence. Secondly, notice that reverse iterators are used in two places: first, to cycle through a vector in the reverse direction, and second, in the call to assign( ) to assign v3 a sequence that is the reverse of the one that is in v. It is this second use that is of most interest. By using a reverse iterator, it is possible to obtain a reversed sequence in one step, rather than the two steps that would be required if the sequence were first copied as-is and then reversed. Reverse iterators can often streamline operations that would otherwise be somewhat cumbersome. 117
  • 137. 118 Herb Schildt's C++ Programming Cookbook Options and Alternatives There is another form of assign( ) that lets you assign a value to a vector. It is shown here: void assign(size_type num, const T& val) This version removes any elements previously held by the vector and then assigns num copies of val to the vector. This version of assign( ) is useful when you want to re-initialize a vector to a known value, for example. The vector container does not store elements in sorted order. However, it is possible to sort a vector by using the sort( ) algorithm. See Sort a Container in Chapter 4. In some cases, the deque container is a good alternative to vector. It has similar capabilities, such as allowing its elements to be accessed via the subscripting operator, but it has different performance characteristics. See Use deque for details. The STL also contains a specialization of vector for bool values: vector<bool>. It includes all of the functionality of vector and adds these two members: void flip( ) Reverses all bits in the vector. static void swap(reference i, reference j) Exchanges the bits specified by i and j. By specializing for bool, vector can pack true/false values into individual bits. The vector<bool> specialization defines a class called reference, which is used to emulate a reference to a bit. Use deque Key Ingredients Headers Classes Functions <deque> deque template <class InIter> void assign(InIter start, InIter end) reference at(size_type i) reference back( ) reference front( ) reference operator[ ](size_type i) void pop_back( ) void pop_front( ) void push_back(const T &val) void push_front(const T &val) void resize(size_type num, T val = T( ))
  • 138. Chapter 3: Wo r k i n g w i t h S T L C o n t a i n e r s Perhaps the second most commonly used container is deque. There are two reasons for this. First, deque supports all of the optional functions defined for sequence containers. This makes it the STL's most full-featured sequence container. Second, deque is the default container that underlies the queue and stack container adaptors. (The default container used by priority_queue is vector). This recipe shows how to put deque into action. NOTE The focus of this recipe is on the attributes and features of deque that make it unique. See Basic Sequence Container Techniques for information that applies to all sequence containers. Step-by-Step To use a deque involves these steps: 1. Create a deque instance of the desired type and initial size. 2. Assign or obtain values to elements via the subscripting