Seventh Edition
Starting Out with
C++ Early Objects
Tony Gaddis Judy Walters Godfrey Muganda
Editor-in-Chief: Michael Hirsch Editorial Assistant: Stephanie Sellinger Director of Marketing: Margaret Whaples Marketing Coordinator: Kathryn Ferranti Managing Editor: Jeffrey Holcomb Production Project Manager: Heather McNally Senior Manufacturing Buyer: Carol Melville Media Manufacturing Buyer: Ginny Michaud Art Director: Linda Knowles Cover and Interior Designer: Joyce Cosentino Wells
Cover
Art:
©
2010
Dmitriy
Ustyujanin/iStockphoto
Media Project Manager: Katelyn Boller Full-Service Project Management: Peggy Kellar, Aptara®, Inc. Composition: Aptara ®, Inc. Copyeditor: Evelyn Perricone Credits and acknowledgments borrowed from other sources and reproduced, with permission, in this textbook appear on appropriate page within text. Microsoft® and Windows® are registered trademarks of the Microsoft Corporation in the U.S.A. and other countries. Screen shots and icons reprinted with permission from the Microsoft Corporation. This book is not sponsored or endorsed by or affiliated with the Microsoft Corporation. The programs and applications presented in this book have been included for their instructional value. They have been tested with care, but are not guaranteed for any particular purpose. The publisher does not offer any warranties or representations, nor does it accept any liabilities with respect to the programs or applications.
Copyright © 2011, 2008, 2006, 2005. Pearson Education, Inc., publishing as Addison-Wesley, 501 Boylston Street, Suite 900, Boston, Massachusetts #02116. All rights reserved. Manufactured in the United States of America. This publication is protected by Copyright, and permission should be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise. To obtain permission(s) to use material from this work, please submit a written request to Pearson Education, Inc., Permissions Department, 501 Boylston Street, Suite 900, Boston, Massachusetts #02116. Many of the designations by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and the publisher was aware of a trademark claim, the designations have been printed in initial caps or all caps.
Library of Congress Publication Data
Cataloging-in-
Gaddis, Tony. Starting out with C++ : early objects / Tony Gaddis, Judy Walters, Godfrey Muganda. —Seventh ed. p. cm. Includes bibliographical references and index. ISBN 978-0-13-607774-9 (alk. paper) 1. C++ (Computer program language) I. Walters, Judy. II. Muganda, Godfrey. III. Title. QA76.73.C153G33 2010 005.13'3—dc22 2010004498 10 9 8 7 6 5 4 3 2 1—EB—14 13 12 11 10
www.pearsonhighered.com ISBN 10: 0-13-607774-9 ISBN 13: 978-0-13-607774-9
Contents at a Glance Preface CHAPTER 1 Introduction to Computers and Programming CHAPTER 2 Introduction to C++ CHAPTER 3 Expressions and Interactivity CHAPTER 4 Making Decisions CHAPTER 5 Looping CHAPTER 6 Functions CHAPTER 7 Introduction to Classes and Objects CHAPTER 8 Arrays CHAPTER 9 Searching, Algorithm Analysis
Sorting,
and
CHAPTER 10 Pointers CHAPTER 11 More About Classes and Object-Oriented Programming CHAPTER 12 More About Characters, Strings, and the string Class CHAPTER 13 Advanced File and I/O Operations CHAPTER 14 Recursion CHAPTER 15 Polymorphism and Virtual Functions CHAPTER 16 Exceptions, Templates, and the Standard Template Library (STL)
CHAPTER 17 Linked Lists CHAPTER 18 Stacks and Queues CHAPTER 19 Binary Trees Appendix A: The ASCII Character Set Appendix B: Operator Precedence and Associativity Index Student CD The following appendices are on the accompanying Student CD.
Appendix C: A Brief Introduction to ObjectOriented Programming Appendix D: Using UML in Class Design Appendix E: Namespaces Appendix F: Arguments
Passing
Command
Line
Appendix G: Header File and Library Function Reference Appendix H: Binary Numbers and Bitwise Operations Appendix I: C++ Casts and Run-Time Type Identification Appendix J: Multi-Source File Programs Appendix K: Multiple and Virtual Inheritance Appendix L: Introduction to the MinGW C++ Compiler and the wxDev-C++ IDE
Appendix M: Introduction to Microsoft Visual C++ 2008 Express Edition Appendix N: .NET and Managed C++ Appendix O: Introduction to Flowcharting Appendix P: Answers to Checkpoints Appendix Q: Answers to Odd-Numbered Review Questions
Contents Preface CHAPTER 1 Introduction to Computers and Programming 1.1 Why Program? 1.2 Computer Systems: Hardware and Software 1.3 Programs and Programming Languages 1.4 What Is a Program Made of? 1.5 Input, Processing, and Output 1.6 The Programming Process 1.7 Tying It All Together: Hi! It’s Me
CHAPTER 2 Introduction to C++ 2.1 The Parts of a C++ Program 2.2 The cout Object 2.3 The #include Directive 2.4 Standard and Prestandard C++ 2.5 Variables, Constants, and the Assignment Statement 2.6 Identifiers 2.7 Integer Data Types 2.8 The char Data Type 2.9 The C++ string Class 2.10 Floating-Point Data Types
2.11 The bool Data Type 2.12 Determining the Size of a Data Type 2.13 More on Variable Assignments and Initialization 2.14 Scope 2.15 Arithmetic Operators 2.16 Comments 2.17
Focus
on
Software
Engineering:
Programming Style 2.18 Tying It All Together: Smile!
CHAPTER 3 Expressions and Interactivity 3.1 The cin Object 3.2 Mathematical Expressions 3.3 Implicit Type Conversion 3.4 Explicit Type Conversion 3.5 Overflow and Underflow 3.6 Named Constants 3.7 Multiple and Combined Assignment 3.8 Formatting Output 3.9 Working with Characters and String Objects 3.10 Using C-Strings 3.11 More Mathematical Library Functions 3.12 Introduction to Files 3.13 Focus on Debugging: Hand Tracing a
Program 3.14 Green Fields Landscaping Case Study —Part 1 3.15 Tying It All Together: Word Game
CHAPTER 4 Making Decisions 4.1 Relational Operators 4.2 The if Statement 4.3 The if/else Statement 4.4 The if/else if Statement 4.5 Menu-Driven Programs 4.6 Nested if Statements 4.7 Logical Operators 4.8 Validating User Input 4.9 More About Variable Definitions and Scope 4.10 Comparing Characters and Strings 4.11 The Conditional Operator 4.12 The switch Statement 4.13 Enumerated Data Types 4.14 Testing for File Open Errors 4.15 Focus on Testing and Debugging: Validating
Output Results 4.16 Green Fields Landscaping Case Study —Part 2 4.17 Tying It All Together: Fortune Teller
CHAPTER 5 Looping 5.1 The Increment and Decrement Operators 5.2 Introduction to Loops: The while Loop 5.3 Using the while Loop for Input Validation 5.4 Counters 5.5 The do-while Loop 5.6 The for Loop 5.7 Keeping a Running Total 5.8 Sentinels 5.9 Using a Loop to Read Data from a File 5.10 Focus on Software Engineering: Deciding
Which Loop to Use 5.11 Nested Loops 5.12 Breaking Out of a Loop 5.13 The continue Statement 5.14 Focus on Testing and Debugging: Creating
Good Test Data 5.15 Central Mountain Credit Union Case Study 5.16 Tying It All Together: What a Colorful World
CHAPTER 6 Functions 6.1 Modular Programming 6.2 Defining and Calling Functions 6.3 Function Prototypes
6.4 Sending Data into a Function 6.5 Passing Data by Value 6.6 The return Statement 6.7 Returning a Value from a Function 6.8 Returning a Boolean Value 6.9 Using Functions in a Menu-Driven Program 6.10 Local and Global Variables 6.11 Static Local Variables 6.12 Default Arguments 6.13 Using Reference Variables as Parameters 6.14 Overloading Functions 6.15 The exit() Function 6.16 Stubs and Drivers 6.17 Little Lotto Case Study 6.18 Tying It All Together: Glowing Jack-o-lantern
CHAPTER 7 Introduction to Classes and Objects 7.1 Abstract Data Types 7.2 Object-Oriented Programming 7.3 Introduction to Classes 7.4 Introduction to Objects 7.5 Defining Member Functions 7.6 Constructors 7.7 Destructors
7.8 Private Member Functions 7.9 Passing Objects to Functions 7.10 Object Composition 7.11 Focus on Software Engineering: Separating
Class Specification, Implementation, and Client Code 7.12 Input Validation Objects 7.13 Structures 7.14 Home Software Company OOP Case Study 7.15 Introduction to Object-Oriented Analysis and Design 7.16 Screen Control 7.17 Tying It All Together: Yoyo Animation
CHAPTER 8 Arrays 8.1 Arrays Hold Multiple Values 8.2 Accessing Array Elements 8.3 Inputting and Displaying Array Contents 8.4 Array Initialization 8.5 Processing Array Contents 8.6 Using Parallel Arrays 8.7 The typedef Statement 8.8 Arrays as Function Arguments 8.9 Two-Dimensional Arrays 8.10 Arrays with Three or More Dimensions
8.11 Vectors 8.12 Arrays of Class Objects 8.13 National Commerce Bank Case Study 8.14 Tying It All Together: Rock, Paper, Scissors
CHAPTER 9 Searching, Sorting, and Algorithm Analysis 9.1 Introduction to Search Algorithms 9.2 Searching an Array of Objects 9.3 Introduction to Sorting Algorithms 9.4 Sorting an Array of Objects 9.5 Sorting and Searching Vectors 9.6 Introduction to Analysis of Algorithms 9.7 Case Studies 9.8 Tying It All Together: Secret Messages
CHAPTER 10 Pointers 10.1 Pointers and the Address Operator 10.2 Pointer Variables 10.3 The Relationship Between Arrays and Pointers 10.4 Pointer Arithmetic 10.5 Initializing Pointers 10.6 Comparing Pointers 10.7 Pointers as Function Parameters 10.8 Pointers to Constants and Constant Pointers
10.9 Focus on Software Engineering: Dynamic
Memory Allocation 10.10 Focus on Software Engineering: Returning
Pointers from Functions 10.11 Pointers to Class Objects and Structures 10.12 Focus on Software Engineering: Selecting
Members of Objects 10.13 United Cause Relief Agency Case Study 10.14 Tying It All Together: Pardon Me, Do You
Have the Time? CHAPTER 11 More About Classes and ObjectOriented Programming 11.1 The this Pointer and Constant Member Functions 11.2 Static Members 11.3 Friends of Classes 11.4 Memberwise Assignment 11.5 Copy Constructors 11.6 Operator Overloading 11.7 Type Conversion Operators 11.8 Convert Constructors 11.9 Aggregation and Composition 11.10 Inheritance 11.11 Protected Members and Class Access 11.12 Constructors, Destructors, and Inheritance
11.13 Overriding Base Class Functions 11.14 Tying It All Together: Putting Data on the
World Wide Web CHAPTER 12 More About Characters, Strings, and the string Class 12.1 C-Strings 12.2 Library Functions for Working with C-Strings 12.3 Conversions Between Numbers and Strings 12.4 Character Testing 12.5 Character Case Conversion 12.6 Writing Your Own C-String Handling Functions 12.7 More About the C++ string Class 12.8 Creating Your Own String Class 12.9 Advanced Software Enterprises Case Study 12.10 Tying It All Together: Program Execution
Environments CHAPTER 13 Advanced File and I/O Operations 13.1 Files 13.2 Output Formatting 13.3 Passing File Stream Objects to Functions 13.4 More Detailed Error Testing 13.5 Member Functions for Reading and Writing Files 13.6 Binary Files
13.7 Creating Records with Structures 13.8 Random-Access Files 13.9 Opening a File for Both Input and Output 13.10 Online Friendship Connections Case Study 13.11 Tying It All Together: File Merging and
Color-Coded HTML CHAPTER 14 Recursion 14.1 Introduction to Recursion 14.2 The Recursive Factorial Function 14.3 The Recursive gcd Function 14.4 Solving Recursively Defined Problems 14.5 A Recursive Binary Search Function 14.6 Focus on Problem Solving and Program Design: The QuickSort Algorithm 14.7 The Towers of Hanoi 14.8 Focus on Problem Solving: Exhaustive and
Enumeration Algorithms 14.9 Focus on Software Engineering: Recursion
Versus Iteration 14.10 Tying It All Together: Infix and Prefix
Expressions CHAPTER Functions
15
Polymorphism
and
Virtual
15.1 Type Compatibility in Inheritance Hierarchies 15.2 Polymorphism and Virtual Member Functions
15.3 Abstract Base Classes and Pure Virtual Functions 15.4 Focus on Object-Oriented Programming:
Composition Versus Inheritance 15.5 Secure Encryption Systems, Inc., Case Study 15.6 Tying It All Together: Let’s Move It
CHAPTER 16 Exceptions, Templates, and the Standard Template Library (STL) 16.1 Exceptions 16.2 Function Templates 16.3 Class Templates 16.4 Class Templates and Inheritance 16.5 Introduction to the Standard Template Library 16.6 Tying It All Together: Word Transformers
Game CHAPTER 17 Linked Lists 17.1 Introduction to the Linked List ADT 17.2 Linked List Operations 17.3 A Linked List Template 17.4 Recursive Linked List Operations 17.5 Variations of the Linked List 17.6 The STL list Container 17.7 Reliable Software Systems, Inc., Case Study 17.8 Tying It All Together: More on Graphics and
Animation CHAPTER 18 Stacks and Queues 18.1 Introduction to the Stack ADT 18.2 Dynamic Stacks 18.3 The STL Stack Container 18.4 Introduction to the Queue ADT 18.5 Dynamic Queues 18.6 The STL deque and queue Containers 18.7 Focus on Problem Solving and Program Design: Eliminating Recursion 18.8 Tying It All Together: Converting Postfix
Expressions to Infix CHAPTER 19 Binary Trees 19.1 Definition and Applications of Binary Trees 19.2 Binary Search Tree Operations 19.3 Template Considerations for Binary Search Trees 19.4 Tying It All Together: Genealogy Trees
Appendix A: The ASCII Character Set Appendix B: Operator Precedence and Associativity Index Student CD The following appendices are on the accompanying Student CD.
Appendix C: A Brief Introduction to Object-
Oriented Programming Appendix D: Using UML in Class Design Appendix E: Namespaces Appendix F: Arguments
Passing
Command
Line
Appendix G: Header File and Library Function Reference Appendix H: Binary Numbers and Bitwise Operations Appendix I: C++ Casts and Run-Time Type Identification Appendix J: Multi-Source File Programs Appendix K: Multiple and Virtual Inheritance Appendix L: Introduction to the MinGW C++ Compiler and the wxDev-C++ IDE Appendix M: Introduction to Microsoft Visual C++ 2008 Express Edition Appendix N: .NET and Managed C++ Appendix O: Introduction to Flowcharting Appendix P: Answers to Checkpoints Appendix Q: Answers to Odd-Numbered Review Questions
Preface Welcome to Starting Out with C++: Early Objects, 7th Edition. This book is intended for use in a two-term or three-term C++ programming sequence, or an accelerated one-term course. Students new to programming, as well those with prior course work in other languages, will find this text beneficial. The fundamentals of programming are covered for the novice, while the details, pitfalls, and nuances of the C++ language are explored in-depth for both the beginner and more experienced student. The book is written with clear, easy-to-understand language and it covers all the necessary topics for an introductory programming course. This text is rich in example programs that are concise, practical, and real world oriented, ensuring that the student not only learns how to implement the features and constructs of C++, but why and when to use them.
What’s New in the Seventh Edition This book’s pedagogy, organization, and clear writing style remain the same as in the previous edition. However, many improvements have been made to make it even more student-friendly and to keep it state of the art for introductory programming using the C++ programming language.
• Updated Material Material has been updated throughout the book to reflect changes in technology, operating systems, and software development environments, as well as to improve clarity and incorporate best practices in object-oriented programming.
• New Material New material has been added on a number of topics including embedding operating system calls in program code, using object composition and aggregation, and creating text-based graphics.
• Completely Revised Chapter 7 Chapter 7, Introduction to Classes and Objects, has
been reorganized and almost entirely rewritten to start right in with classes and objects, instead of introducing structures first.
• Greater Focus on Object-Oriented Programming Many examples throughout the text have been rewritten to incorporate appropriate use of classes and objects.
• Reusability Material has been added illustrating how to create general classes that can be appropriately reused in multiple applications.
• Improved Diagrams Many diagrams have been improved and new diagrams added to better illustrate important concepts.
• Online VideoNotes An extensive set of online videos have been developed to accompany this text. Throughout the book, VideoNotes icons alert the student to videos covering specific topics they are studying. Additionally, one Programming Challenge at the end of each chapter now has an accompanying video explaining how to develop the problem’s solution. The videos are available at http://www.pearsonhighered.com/gaddis/
• New Tying It All Together Sections A new Tying It All Together section has been added at the end of every chapter that shows the student how to do something clever and fun with the material covered in that chapter.
• New Programming Challenges New Programming Challenges have been added to every chapter, including a number of Challenges that ask students to develop object-oriented solutions and to create solutions that reuse, modify, and build on previously written code.
• New Compiler and IDE Bundled with the Book The MinGW C++ Compiler and wxDev-C++ Software Development Environment now come bundled, for free, with the book.
• New Appendices An Appendix has been added on using the MinGW C++ Compiler and wxDev-C++ IDE that accompany
the book. Additional new appendices cover the Microsoft Visual C++ 2008 Express Edition IDE and Multiple and Virtual Inheritance.
Organization of the Text This text teaches C++ in a step-by-step fashion. Each chapter covers a major set of topics and builds knowledge as the student progresses through the book. Although the chapters can be easily taught in their existing sequence, flexibility is provided. The following dependency diagram (Figure P-1) suggests possible sequences of instruction. Chapter 1 covers fundamental hardware, software, and programming concepts. The instructor may choose to skip this chapter if the class has already mastered those topics. Chapters 2 through 6 cover basic C++ syntax, data types, expressions, selection structures, repetition structures, and functions. Each of these chapters builds on the previous chapter and should be covered in the order presented. Chapter 7 introduces object-oriented programming. It can be covered any time after Chapter 6, but before Chapter 11. Instructors who prefer to introduce arrays before classes can cover Chapter 8 before Chapter 7. In this case it is only necessary to postpone section 8.12 (Arrays of Class Objects) until Chapter 7 has been covered. As Figure P-1 illustrates, in the second half of the book Chapters 11, 12, 13, and 14 can be covered in any order. Chapters 11, 15, and 16, however, should be done in sequence. Instructors who wish to introduce data structures at an earlier point in the course, without having first covered advanced C++ and OOP features, can cover Chapter 17 (Linked Lists), followed by Chapters 18 and 19 (Stacks & Queues and Binary Trees), any time after Chapter 14 (Recursion). In this case it is necessary to simply omit the sections in Chapters 17–19 that deal with templates and the
Standard Template Library.
Figure P-1
Brief Overview of Each Chapter Chapter 1: Introduction to Computers and
Programming This chapter provides an introduction to the field of computer science and covers the fundamentals of hardware, software, operating systems, programming, problem solving, and software engineering. The components of programs, such as key words, variables, operators, and punctuation are covered. The tools of the trade, such as hierarchy charts and pseudocode, are also presented. The new Tying It All Together section shows students how to use the cout statement to create a personalized output message. Two new Programming Challenges help students see how the same basic input, processing, and output structure can be used to create multiple programs.
Chapter 2: Introduction to C++ This chapter gets the student started in C++ by introducing the basic parts of a C++ program, data types, variable definitions, assignment statements, constants, comments, program output, and simple arithmetic operations. The C++ string class is presented and string objects are used from this point on in the book as the primary method of handling strings. Programming style conventions are introduced and good programming style is modeled here, as it is throughout the text. An optional section explains the difference between ANSI standard and prestandard C++ programs. The new Tying It All Together section lets the student play with simple text-based graphics.
Chapter 3: Expressions and Interactivity In this chapter the student learns to write programs that input and handle numeric, character, and string data. The use of arithmetic operators and the creation of mathematical expressions are covered, with emphasis on operator precedence. Debugging is introduced, with a section on hand tracing a program. Sections are also included on using random numbers, on reading
and writing sequential files, on simple output formatting, on data type conversion and type casting, and on using library functions that work with numbers. For those who wish to cover them, there is also a section on C-strings. The new Tying It All Together section shows students how to create a simple interactive word game.
Chapter 4: Making Decisions Here the student learns about relational expressions and how to control the flow of a program with the if, if/else, and if/else if statements. Logical operators, the conditional operator, and the switch statement are also covered. Applications of these constructs, such as menu-driven programs, are illustrated. This chapter also continues the theme of debugging with a section on validating output results. The new Tying It All Together section uses random numbers and branching statements to create a fortune telling game.
Chapter 5: Looping This chapter covers C++’s repetitive control mechanisms. The while loop, do-while loop, and for loop are taught, along with a variety of methods to control them. These include using counters, user input, end sentinels, and end-of-file testing. Applications utilizing loops, such as keeping a running total and performing data validation, are covered. The emphasis on testing and debugging continues, with a section on creating good test data. The new Tying It All Together section introduces students to Windows commands to create colorful output and uses a loop to create a multicolored display.
Chapter 6: Functions In this chapter the student learns how and why to modularize programs, using both void and value-
returning functions. Parameter passing is covered, with emphasis on when arguments should be passed by value versus when they need to be passed by reference. Scope of variables is covered and sections are provided on local versus global variables and on static local variables. Overloaded functions are also introduced and demonstrated. The new Tying It All Together section includes a modular, menu-driven program that emphasizes the versatility of functions, illustrating how their behavior can be controlled by the arguments sent to them.
Chapter 7: Introduction to Classes and Objects In this chapter the text begins to focus on the objectoriented paradigm. Students learn how to define classes and to create and use objects. Careful attention is paid to illustrating which functions belong in a class versus which functions belong in a client program that uses the class. Good object-oriented practices are discussed and modeled, such as protecting member data through carefully constructed accessor and mutator functions and hiding class implementation details from client programs. Once students are comfortable working with classes and objects, the chapter provides a brief introduction to the topic of object-oriented analysis and design. The chapter also introduces structures and uses them in this chapter’s Tying It All Together section, where students learn to use screen control techniques to create an animation that simulates the motion of a yoyo.
Chapter 8: Arrays In this chapter the student learns to create and work with single and multidimensional arrays. Many examples of array processing are provided, including functions to compute the sum, average, highest and lowest values in an array. Students also learn to create
tables using two-dimensional arrays, and to analyze the array data by row or by column. Programming techniques using parallel arrays are also demonstrated, and the student is shown how to use a data file as an input source to populate an array. STL vectors are introduced and compared to arrays. Sections on arrays of objects and structures are located at the end of the chapter, so they can be covered now or saved for later if the instructor wishes to cover this chapter before Chapter 7. The new Tying It All Together section uses arrays to create a game of Rock, Paper, Scissors between a human player and the computer.
Chapter 9: Searching, Sorting, and Algorithm Analysis Here the student learns the basics of searching for information stored in arrays and of sorting arrays, including arrays of objects. The chapter covers the Linear Search, Binary Search, Bubble Sort, and Selection Sort algorithms, and has an optional section on sorting and searching STL vectors. A brief introduction to algorithm analysis is included and students are shown how to determine which of two algorithms is more efficient. The new Tying It All Together section uses both a table lookup and a searching algorithm to encode and decode secret messages.
Chapter 10: Pointers This chapter explains how to use pointers. The topics include pointer arithmetic, initialization of pointers, comparison of pointers, pointers and arrays, pointers and functions, dynamic memory allocation, and more. The new Tying It All Together section demonstrates the use of pointers to access library data structures and functions that return calendar and wall clock time.
Chapter 11: More about Classes and
Object-Oriented Programming This chapter continues the study of classes and objectoriented programming. It covers object aggregation and composition, as well as inheritance, and illustrates the difference between is-a and has-a relations. Constant member functions, static members, friends, memberwise assignment, copy constructors, object type conversion operators, convert constructors, and a newly rewritten section on operator overloading are also included. The new Tying It All Together section brings together the concepts of inheritance and convert constructors to build a program that formats the contents of an array to form an HTML table for display on a Web site.
Chapter 12: More about Characters, Strings, and the string Class This chapter covers standard library functions for working with characters and C-strings, covering topics such as passing C-strings to functions and using the C++ sstream classes to convert between numeric and string forms of numbers. Additional material about the C++ string class and its member functions and operators is presented and a new, improved program illustrates how to write your own string class. The new Tying It All Together section shows students how to access string-based program environments to obtain information about the computer and the network on which the program is running.
Chapter 13: Advanced File and I/O Operations This chapter covers sequential access, random access, text, and binary files. Various modes for opening files are discussed, as well as the many methods for reading and writing file contents. Advanced output formatting is also covered. The new Tying It All Together program applies many of the
techniques covered in the chapter to merge two text files into an HTML document for display on the Web, with different colors used to illustrate which file each piece of data came from.
Chapter 14: Recursion In this chapter recursion is defined and demonstrated. A visual trace of recursive calls is provided, and recursive applications are discussed. Many recursive algorithms are presented, including recursive functions for computing factorials, finding a greatest common denominator (GCD), performing a binary search, sorting QuickSort, and solving the famous Towers of Hanoi problem. For students who need more challenge, there is a section on exhaustive and enumeration algorithms. The new Tying It All Together section uses recursion to evaluate prefix expressions.
Chapter 15: Polymorphism and Virtual Functions The study of classes and object-oriented programming continues in this chapter with the introduction of more advanced concepts such as polymorphism and virtual functions. Information is also presented on abstract base classes, pure virtual functions, type compatibility within an inheritance hierarchy, and virtual inheritance. The material on multiple inheritance previously in the chapter has been rewritten and moved to an appendix. The new Tying It All Together section illustrates the use of inheritance and polymorphism to display and animate graphical images.
Chapter 16: Exceptions, Templates, and the Standard Template Library (STL) Here the student learns to develop enhanced error trapping techniques using exceptions. Discussion then turns to function and class templates as a method for writing generic code. Finally, the student is introduced
to the containers, iterators, and algorithms offered by the Standard Template Library (STL). The new Tying It All Together section uses various containers in the Standard Template Library to create an educational children’s game.
Chapter 17: Linked Lists This chapter introduces concepts and techniques needed to work with lists. A linked list ADT is developed and the student is taught to code operations such as creating a linked list, appending a node, traversing the list, searching for a node, inserting a node, deleting a node, and destroying a list. A linked list class template is also demonstrated. The new Tying It All Together section brings together many of the most important concepts of OOP by using objects, inheritance, and polymorphism to animate a collection of images.
Chapter 18: Stacks and Queues In this chapter the student learns to create and use static and dynamic stacks and queues. The operations of stacks and queues are defined, and templates for each ADT are demonstrated. The static array-based stack uses exception-handling to handle stack overflow and underflow, providing a realistic and natural example of defining, throwing, and catching exceptions. The new Tying It All Together section discusses strategies for evaluating postfix expressions and for converting them to infix.
Chapter 19: Binary Trees This chapter covers the binary tree ADT and demonstrates many binary tree operations. The student learns to traverse a tree, insert an element, delete an element, replace an element, test for an element, and destroy a tree. The new Tying It All Together section introduces a tree structure versatile
enough to create genealogy trees.
Appendices Appendix A: The ASCII Character Set A list of the ASCII and extended ASCII characters and their codes. Appendix B: Operator Precedence and Associativity A list of the C++ operators with their precedence and associativity.
The following appendices are on the accompanying student CD Appendix C: A Brief Introduction to ObjectOriented Programming An introduction to the concepts and terminology of object-oriented programming. Appendix D: Using UML in Class Design A brief introduction to the Unified Modeling Language (UML) class diagrams with examples of their use. Appendix E: Namespaces An explanation of namespaces and their purpose, with examples provided on how to define a namespace and access its members. Appendix F: Passing Command Line Arguments An introduction to writing C++ programs that accept command-line arguments. This appendix will be useful to students working in a command-line environment, such as UNIX or Linux. Appendix G: Header File and Library Function Reference A reference for the C++ library functions and header files used in the book. Appendix H: Binary Numbers and Bitwise Operations A guide to the binary number system and the C++ bitwise operators, as well as a tutorial on the internal storage of integers.
Appendix I: C++ Casts and Run-Time Type Identification An introduction to the different ways of doing type casting in C++ and to run-time type identification. Appendix J: Multi-Source File Programs A tutorial on how to create, compile, and link programs with multiple source files. Includes the use of function header files, class specification files, and class implementation files. Appendix K: Multiple and Virtual Inheritance A
self-contained discussion of the C++ concepts of multiple and virtual inheritance for anyone already familiar with single inheritance. Appendix L: Introduction to the MinGW C++ Compiler and the wxDev-C++ IDE A tutorial on how
to start a wxDev-C++ project, compile and run a program, save source files, and more. Appendix M: Introduction to Microsoft Visual C++ 2008 Express Edition A tutorial on how to start a
project using Microsoft Visual C++ 2008, compile and run a program, save source files, and more. Appendix N: .NET and Managed C++ A short
introduction to Microsoft .NET and managed C++. Appendix O: Introduction to Flowcharting A tutorial
that introduces flowcharting and its symbols. Includes handling sequence, selection, case, repetition, and calls to other modules. Sample flowcharts for several of the book’s example programs are presented. Appendix P: Answers to Checkpoints A tool
students can use to assess their understanding by comparing their answers to the Checkpoint exercises found throughout the book. The answers to all Checkpoint exercises are included. Appendix Q: Answers to Odd-Numbered Review
Questions Another tool students can use to gauge their understanding and progress.
Features of the Text Each major section of the text starts with a concept statement. This statement summarizes the key idea of the section. The text has over 350 complete example programs, each designed to highlight the topic currently being studied. In most cases, these are practical, real-world examples. Source code for these programs is provided so that students can run the programs themselves. After each example program there is a sample of its screen output. This immediately shows the student how the program should function. This special section, found at the end All of every chapter, shows the student how to do something clever and fun with the material covered in that chapter. A series of online videos, developed specifically for this book, are available for viewing at
Concept Statements
Example Programs
Program Output
Tying It Together
VideoNotes
http://www.pearsonhighered.com/ gaddis/. VideoNotes icons appear
throughout the text, alerting the student to videos about specific topics. Checkpoints are questions placed throughout each chapter as a self-test study aid. Answers for all Checkpoint Checkpoints questions are provided on the student CD so students can check how well they have learned a new topic. Notes appear at appropriate places
throughout the text. They are short explanations of interesting or often misunderstood points relevant to the topic at hand. Warnings caution the student about certain C++ features, programming Warnings techniques, or practices that can lead to malfunctioning programs or lost data. Case studies that simulate real-world applications appear in many chapters throughout the text, with complete code provided for each one. Case Studies Additional case studies are provided on the student CD. These case studies are designed to highlight the major topics of the chapter in which they appear. Each chapter presents a thorough and diverse set of review questions, such as fill-in-the-blank and short answer, that check the student’s mastery of the basic material presented in the chapter. These are followed by exercises requiring problem solving Review and analysis, such as the Algorithm Questions Workbench, Predict the Output, and and Find the Errors sections. Each Exercises chapter ends with a Soft Skills exercise that focuses on communication and group process skills. Answers to the odd numbered review questions and review exercises are provided on the student CD. Each chapter offers a pool of programming exercises designed to solidify the student’s knowledge of the Programming topics currently being studied. In most Challenges cases the assignments present realworld problems to be solved. When Notes
applicable, these exercises include input validation rules. There are several group programming projects throughout the text, intended to be constructed by a team of students. One student might build the program’s user interface, while Group another student writes the Projects mathematical code, and another designs and implements a class the program uses. This process is similar to the way many professional programs are written and encourages team work within the classroom. C++ Quick For easy access, a quick reference Reference guide to the C++ language is printed Guide on the inside front and back covers.
Supplements Student CD This CD includes: • MinGW C++ Compiler • wxDev-C++ IDE • Answers to all Checkpoint questions (Appendix P) • Answers to all odd-numbered Review Questions and Exercises (Appendix Q) • Complete source code for every program included in the book • Additional case studies, complete with source code • Serendipity Booksellers ongoing software development project • A full set of appendices (including several tutorials) that accompany the book If a CD did not come with your book or you can’t locate your CD, you can access most of these items at
items
at
http://www.pearsonhighered.com/cssupport
Other CDs Upon Request Professors should contact their campus Pearson Education/ Addison-Wesley representative for the specific ISBN to order this book packaged with Microsoft Visual C++.
MyCodeMate—Your Own T.A. Just a Click Away Addison-Wesley’s MyCodeMate is a book-specific Web resource that provides tutorial help and evaluation of student programs. Example programs throughout the book and selected Programming Challenges from every chapter have been integrated into MyCodeMate. Using this tool, a student is able to write and compile programs from any computer with Internet access and receive guidance and feedback on how to proceed and on how to address compiler error messages. Instructors can track each student’s progress on Programming Challenges from the text or can develop projects of their own. A complimentary subscription to MyCodeMate is offered when the access code is ordered in a package with a new copy of this text. Subscriptions can also be purchased online. For more information visit www.mycodemate.com, or contact your campus Pearson Education/Addison-Wesley representative.
Instructor Resources The following supplements are available to qualified instructors only. • Answers to all Review Questions in the text • Solutions for all Programming Challenges in the text • PowerPoint presentation slides for every chapter
• A computerized test bank • A collection of lab materials • Source code files Visit the Pearson Education Instructor Resource Center ( http://www.pearsonhighered.com/irc) or send an email to
[email protected] for information on how to access them.
Textbook Web Site A Web site for the Starting Out with C++ series of books is located at the following URL: http://www.pearsonhighered.com/gaddis/
Get This Book the Way You Want It! This book is part of the Pearson Custom Computer Science Library. Use our online PubSelect system to select just the chapters you need from this, and other, Pearson Education CS textbooks. You can edit the sequence to exactly match your course organization and teaching approach. Visit www.pearsoncustom.com/cs for details.
Which Gaddis C++ Book Is Right for You? The Starting Out with C++ Series includes three books, one of which is sure to fit your course: • Starting Out with C++: Control Structures through Objects; • Starting Out with C++: Early Objects; • Starting Out with C++: Brief Version . The following chart will help you determine which book is right for your course.
▪ FROM CONTROL STRUCTURES ▪ EARLY OBJECTS THROUGH OBJECTS
▪ BRIEF VERSION EARLIER LATE INTRODUCTION INTRODUCTION OF OBJECTS OBJECTS
OF
Classes are introduced in Classes are introduced Chapter 13 of the standard in Chapter 7, after control text and Chapter 11 of the structures and functions, brief text, after control but before arrays and structures, functions, arrays, pointers. Their use is and pointers. Advanced then integrated into the OOP topics, such as remainder of the text. inheritance and Advanced OOP topics, polymorphism, are covered such as inheritance and in the following two polymorphism, are chapters. covered in Chapters 11 and 15.
USE OF C-STRINGS
USE OF OBJECTS
string
Null-terminated C-strings are used throughout, with Standard library string the C++ string class class objects are used throughout, with C-strings covered briefly. covered briefly.
INTRODUCTION OF DATA STRUCTURES AND RECURSION Linked lists, stacks and INTRODUCTION OF queues, and binary trees DATA STRUCTURES are introduced in the final AND RECURSION chapters of the standard Linked lists, stacks and text. Recursion is covered queues, and binary trees after stacks and queues, are introduced in the final but before binary trees. chapters of the text, after These topics are not the chapter on recursion. covered in the brief text, though it does have appendices dealing with linked lists and recursion.
Acknowledgments
There have been many helping hands in the development and publication of this text. We would like to thank the following faculty reviewers for their helpful suggestions and expertise. Reviewers of the Seventh Edition or Its Previous Versions Ahmad Abuhejleh University of Wisconsin, River Falls David Akins El Camino College Steve Allan Utah State University Ijaz A. Awan Savannah State University John Bierbauer North Central College Don Biggerstaff Fayetteville Technical Community College Paul Bladek Spokane Falls Community College Chuck Boehm Dean Foods, Inc. Bill Brown Pikes Peak Community College Richard Cacace Pensacola Junior College Randy Campbell Morningside College Stephen P. Carl Wright State University
Wayne Caruolo Red Rocks Community College Thomas Cheatham Middle Tennessee State University James Chegwidden Tarrant County College John Cigas Rockhurst University John Cross Indiana University of Pennsylvania Joseph DeLibero Arizona State University Dennis Fairclough Utah Valley State College Larry Farrer Guilford Technical Community College Richard Flint North Central College Sheila Foster California State University Long Beach David E. Fox American River College Cindy Fry Baylor University Peter Gacs Boston University Cristi Gale Sterling College James Gifford
University of Wisconsin, Stevens Point Leon Gleiberman Touro College Simon Gray Ashland University—Ohio Margaret E. Guertin Tufts University Jamshid Haghighi Guilford Technical Community College Ranette H. Halverson Midwestern State University, Wichita Falls, TX Dennis Heckman Portland Community College Ric Heishman Northern Virginia Community College Patricia Hines Brookdale Community College Mike Holland Northern Virginia Community College Lister Wayne Horn Pensacola Junior College Richard Hull Lenoir-Rhyne College Norman Jacobson University of California, Irvine Eric Jiang San Diego State University David Kaeli Northeastern University
Chris Kardaras North Central College Eugene Katzen Montgomery College—Rockville Willard Keeling Blue Ridge Community College A. J. Krygeris Houston Community College Ray Larson Inver Hills Community College Stephen Leach Florida State University Parkay Louie Houston Community College Zhu-qu Lu University of Maine, Presque Isle Tucjer Maney George Mason University Bill Martin Central Piedmont Community College Debbie Mathews J. Sargeant Reynolds Ron McCarty Penn State Erie, The Behrend College Robert McDonald East Stroudsburg University James McGuffee Austin Community College M. Dee Medley Augusta State University
Cathi Chambley-Miller Aiken Technical College Sandeep Mitra SUNY Brockport Frank Paiano Southwestern Community College Theresa Park Texas State Technical College Mark Parker Shoreline Community College Robert Plantz Sonoma State University Tino Posillico SUNY Farmingdale M. Padmaja Rao Francis Marion University Timothy Reeves San Juan College Ronald Robison Arkansas Tech University Caroline St. Clair North Central College Dolly Samson Weber State University Kate Sanders Rhode Island College Lalchand Shimpi Saint Augustine’s College Sung Shin South Dakota State University
Garth Sorenson Snow College Daniel Spiegel Kutztown University Ray Springston University of Texas at Arlington Kirk Stephens Southwestern Community College Cherie Stevens South Florida Community College Hong Sung University of Central Oklahoma Mark Swanson Red Wing Technical College Martha Tillman College of San Mateo Delores Tull Itawamba Community College Rober Tureman Paul D. Camp Community College Jane Turk LaSalle University Sylvia Unwin Bellevue Community College Stewart Venit California State University, Los Angeles Doug White University of Northern Colorado Chris Wild
Old Dominion University Catherine Wyman DeVry Institute of Technology, Phoenix Sherali Zeadally University of the District of Columbia The authors would like to thank their students at Haywood Community College and North Central College for inspiring them to write studentfriendly books. They would also like to thank their families for their tremendous support throughout this project, as well as North Central College for providing Prof. Walters and Muganda with the sabbatical term during which they worked on this book. An especially big thanks goes to our terrific editorial, production, and marketing team at Addison-Wesley. In particular we want to thank our editor Michael Hirsch and our production project manager Heather McNally, who have been instrumental in guiding the production of this book. We also want to thank our project manager, Peggy Kellar, who helped everything run smoothly, and our meticulous and knowledgable copyeditor, Evelyn Perricone, who dedicated many hours to making this book the best book it could be. You are great people to work with!
About the Authors Tony Gaddis is the principal author of the Starting Out With . . . series of textbooks. He is a highly acclaimed instructor with twenty years of experience teaching computer science courses at Haywood Community College. Tony was previously selected as the North Carolina Community College “Teacher of the Year” and has received the Teaching Excellence award from the National Institute for Staff and Organizational Development. The
Starting Out With . . . series includes introductory books covering C++, Java™, Microsoft® Visual Basic®, Microsoft® C#, and Alice, all published by Addison-Wesley. Judy Walters is an Associate Professor of Computer Science at North Central College in Naperville, Illinois. In addition to her many computer science courses, she also teaches two film-related courses she developed for the college’s interdisciplinary freshman seminar program. She recently returned from her second semester teaching in Costa Rica, where she hopes to retire some day. Godfrey Muganda is an Associate Professor of Computer Science at North Central College. He teaches a wide variety of courses at both the undergraduate and graduate levels, including courses in Algorithms, Computer Organization, Web Applications, and Web Services. His primary research interests are in the area of Fuzzy Sets and Systems.
CHAPTER 1 Introduction to Computers and Programming TOPICS 1.1 Why Program? 1.2 Computer Systems: Hardware and Software 1.3 Programs and Programming Languages 1.4 What Is a Program Made of? 1.5 Input, Processing, and Output 1.6 The Programming Process 1.7 Tying It All Together: Hi! It’s Me
1.1 Why Program? CONCEPT: Computers can do many different jobs because they are programmable. Every profession has tools that make its job easier to do. Carpenters use hammers, saws, and measuring tapes. Mechanics use wrenches, screwdrivers, and ratchets. Electronics technicians use probes, scopes, and meters. Some tools are unique and can be categorized as belonging to a single profession. For example, surgeons have certain tools that are designed specifically for surgical operations. Those tools probably aren’t used by anyone other than surgeons. There are some tools, however, that are used in several professions. Screwdrivers, for instance, are used by mechanics, carpenters, and many others.
The computer is a tool that is used by so many professions, it cannot be easily categorized. It can perform so many different jobs that it is perhaps the most versatile tool ever made. For the accountant, computers balance books, analyze profits and losses, and prepare tax reports. For the factory worker, computers control manufacturing machines and track production. For the mechanic, computers analyze the various systems in an automobile and pinpoint hard-tofind problems. What makes the computer so useful? Quite simply, the computer can do such a wide variety of tasks because it can be programmed. It is a machine specifically designed to follow instructions. Because of the computer’s programmability, it doesn’t belong to any single profession. Computers are designed to do whatever job their programs, or software, tell them to do. Computer programmers do a very important job. They create software that transforms computers into the specialized tools of many trades. Without programmers, the users of computers would have no software, and without software, computers would not be able to do anything. Computer programming is both an art and a science. It is an art because every aspect of a program should be designed with care and judgment. Listed below are a few of the things that must be designed for any real-world computer program: • The logical flow of the instructions • The mathematical procedures • The appearance of the screens • The way information is presented to the user • The program’s “user-friendliness”
• Manuals and other forms of written documentation There is also a scientific, or engineering side to programming. Because programs rarely work right the first time they are written, a lot of experimentation, correction, and redesigning is required. This demands patience and persistence of the programmer. Writing software demands discipline as well. Programmers must learn special languages like C++ because computers do not understand English or other human languages. Languages such as C++ have strict rules that must be carefully followed. Both the artistic and scientific nature of programming makes writing computer software like designing a car. Both cars and programs should be functional, efficient, powerful, easy to use, and pleasing to look at.
1.2 Computer Systems: Hardware and Software CONCEPT: All computer systems consist of similar hardware devices and software components. This section provides an overview of standard computer hardware and software organization.
Hardware Hardware refers to the physical components that a computer is made of. A computer, as we generally think of it, is not an individual device, but a system of devices. Like the instruments in a symphony orchestra, each device plays its own part. A typical computer system consists of the following major components: 1. The central processing unit (CPU) 2. Main memory (random-access memory, or RAM)
RAM) 3. Secondary storage devices 4. Input devices 5. Output devices The organization of a computer system is depicted in Figure 1-1. Figure 1-1
The CPU At the heart of a computer is its central processing unit, or CPU. The CPU’s job is to fetch instructions, follow the instructions, and produce some result. Internally, the central processing unit consists of two parts: the control unit and the arithmetic and logic unit (ALU) . The control unit coordinates all of the computer’s operations. It is responsible for determining where to get the next instruction and regulating the other major components of the computer with control signals.
The arithmetic and logic unit, as its name suggests, is designed to perform mathematical operations. The organization of the CPU is shown in Figure 1-2. Figure 1-2
A program is a sequence of instructions stored in the computer’s memory. When a computer is running a program, the CPU is engaged in a process known formally as the fetch/ decode/execute cycle. The steps in the fetch/decode/execute cycle are as follows: The CPU’s control unit fetches, from main Fetch memory, the next instruction in the sequence of program instructions. The instruction is encoded in the form of a number. The control unit decodes the Decode instruction and generates an electronic signal. The signal is routed to the appropriate component of the computer (such as the Execute ALU, a disk drive, or some other device). The signal causes the component to perform an operation. These steps are repeated as long as there are instructions to perform. Main Memory
Main Memory Commonly known as random-access memory, or RAM, the computer’s main memory is a device that holds information. Specifically, RAM holds the sequences of instructions in the programs that are running and the data those programs are using. Memory is divided into sections, or cells, that each hold an equal amount of data. Each cell typically contains eight “switches” that may be either on or off. A switch that is in the on position usually represents the number 1, while a switch in the off position usually represents the number 0. The computer stores data by setting the switches in a memory cell to a pattern that represents a piece of information. Each of these switches is known as a bit, which stands for binary digit. Each cell, which is a collection of eight bits, is known as a byte. Bytes are grouped together to make words. On most computers a word contains four bytes. Each word is assigned a unique number known as an address. The addresses are ordered from lowest to highest. A word is identified by its address in much the same way a post office box is identified by an address. Figure 1-3 shows a group of memory words with their addresses. In the illustration, sample data is stored in memory. The number 149 is stored in the word with the address 16, and the number 72 is stored at address 23. Figure 1-3
RAM is usually a volatile type of memory, used only for temporary storage. When the computer is turned off, the contents of RAM are erased. Secondary Storage Secondary storage is a type of memory that can hold data for long periods of time—even when there is no power to the computer. Frequently used programs are stored in secondary memory and loaded into main memory as needed. Important information, such as word processing documents, payroll data, and inventory figures, is saved to secondary storage as well. The most common type of secondary storage device is the disk drive. A disk drive stores data by magnetically encoding it onto a circular disk. Most computers have a disk drive mounted inside their case. External disk drives, which connect to one of the computer’s communication ports, are also available. External disk drives can be used to create backup copies of important data or to move data to another computer. In addition to external disk drives, many types of devices have been created for copying data and for moving it to other computers. For many years floppy disk drives were popular. A floppy disk drive records data onto a small, flexible (“floppy”) disk, which can be removed from the drive. The use of floppy disk drives has declined dramatically in recent years, in favor of superior devices such as USB flash drives. USB flash drives are small devices that plug into the computer’s USB (universal serial bus) port and appear to the system as a disk drive. These drives, which use flash memory to store data, are inexpensive, reliable, and small enough to be carried in your pocket. Optical devices such as the CD (compact disc) and the DVD (digital versatile disc) are also
popular for data storage. Data is not recorded magnetically on an optical disc, but rather is encoded as a series of pits on the disc surface. CD and DVD drives use a laser to detect the pits and thus read the encoded data. Optical discs hold large amounts of data, and because recordable CD and DVD drives are now commonplace, they are good media for creating backup copies of data. Input Devices Input is any information the computer collects from the outside world. The device that collects the information and sends it to the computer is called an input device. Common input devices are the keyboard, mouse, scanner, digital camera, and microphone. Disk drives, CD/DVD drives, and USB flash drives can also be considered input devices because programs and information are retrieved from them and loaded into the computer’s memory. Output Devices Output is any information the computer sends to the outside world. It might be a sales report, a list of names, or a graphic image. The information is sent to an output device, which formats and presents it. Common output devices are computer screens, printers, and speakers. Output sent to a computer screen is sometimes called soft copy, while output sent to a printer is called hard copy . Disk drives, USB flash drives, and CD/DVD recorders can also be considered output devices because the CPU sends information to them so it can be saved.
Software As previously mentioned, software refers to the programs that run on a computer. There are two
general categories of software: operating systems and application software. An operating system is a set of programs that manages the computer’s hardware devices and controls their processes. Operating systems fall into one of the following categories. A single tasking operating system is capable of running only one program at a time. The computer devotes all its Single hardware resources and CPU time to tasking each program as it executes. MS-DOS is an example of a single tasking operating system. A multitasking operating system is capable of running multiple programs at once. Through a technique called time sharing, the system divides the Multitasking allocation of hardware resources and the attention of the CPU among all the executing programs. UNIX, Windows XP, and Windows Vista are multitasking operating systems. In addition, operating systems fall into one of the following categories, which describe the number of users they can accommodate. This type of system allows only one user to Single operate the computer at a time. MS-DOS and older versions of Windows are single user user operating systems. Multiuser systems allow several users to run programs and operate the computer at Multiuser once. Most variations of the UNIX operating system are multiuser systems. Application software refers to programs that make the computer useful to the user. These programs solve specific problems or perform general operations that satisfy the needs of the user. Word processing, spreadsheet, and database programs are all examples of application
software.
Checkpoint 1.1 Why is the computer used by so many different people, in so many different professions? 1.2 List the five major hardware components of a computer system. 1.3 Internally, the CPU consists of what two units? 1.4 Describe the fetch/decode/execute cycle.
steps
in
the
1.5 What is a memory address? 1.6 Explain why computers have both main memory and secondary storage. 1.7 What are the two general categories of software? 1.8 What is the difference between a single tasking system and a multitasking system? 1.9 What is the difference between a single user system and a multiuser system?
1.3 Programs and Programming Languages CONCEPT: A program is a set of instructions a computer follows in order to perform a task. A programming language is a special language used to write computer programs.
What Is a Program? Computers are designed to follow instructions. A
computer program is a set of instructions that tells the computer how to solve a problem or perform a task. For example, suppose we want the computer to calculate someone’s gross pay. Here is a list of things the computer might do: 1. Display a message on the screen asking “How many hours did you work?” 2. Wait for the user to enter the number of hours worked. Once the user enters a number, store it in memory. 3. Display a message on the screen asking “How much do you get paid per hour?” 4. Wait for the user to enter an hourly pay rate. Once the user enters a number, store it in memory. 5. Multiply the number of hours by the amount paid per hour, and store the result in memory. 6. Display a message on the screen that tells the amount of money earned. The message must include the result of the calculation performed in step 5. Collectively, these instructions are called an algorithm. An algorithm is a set of well-defined steps for performing a task or solving a problem. Notice these steps are sequentially ordered. Step 1 should be performed before step 2, and so forth. It is important that these instructions be performed in their proper sequence. Although a person might easily understand the instructions in the pay-calculating algorithm, it is not ready to be executed on a computer. A computer’s CPU can only process instructions that are written in machine language. A machine language program consists of a sequence of binary numbers (numbers consisting of only 1s and 0s) which the CPU interprets as commands. Here is an example of what a machine language instruction might look like: 1011010000000101
As you can imagine, the process of encoding an algorithm in machine language is very tedious and difficult. In addition, each different type of CPU has its own machine language. If you wrote a machine language program for computer A and then wanted to run it on a computer B that has a different type of CPU, you would have to rewrite the program in computer B’s machine language. Programming languages, which use words instead of numbers, were invented to ease the task of programming. A program can be written in a programming language such as C++, which is much easier to understand than machine language. Programmers save their programs in text files, and then use special software to convert their programs to machine language. Program 1-1 shows how the pay-calculating algorithm might be written in C++.
NOTE: The line numbers shown in Program 1-1 are not part of the program. This book shows line numbers in all program listings to help point out specific parts of the program. Program 1-1
The “Program Output with Example Input Shown in Bold” shows what the program will display on the screen when it is running. In the example, the user enters 10 for the number of hours worked and 15 for the hourly pay. The program displays the earnings, which are $150.
Programming Languages In a broad sense, there are two categories of programming languages: low-level and high-level. A low-level language is close to the level of the computer, which means it resembles the numeric machine language of the computer more than the natural language of humans. The easiest languages for people to learn are high-level languages. They are called “high-level” because
they are closer to the level of human-readability than computer-readability. Figure 1-4 illustrates the concept of language levels. Figure 1-4
Many high-level languages have been created. Table 1-1 lists a few of the well-known ones. Table 1-1 Well-Known Programming Languages
High-Level
C++ is a widely used language because, in addition to the high-level features necessary for writing applications such as payroll systems and inventory programs, it also has many low-level features. C++ is based on the C language, which was invented for purposes such as writing operating systems and compilers. Because C++ evolved from C, it carries all of C’s low-level capabilities with it. C++ is also popular because of its portability. This means that a C++ program can be written on one type of computer and then run on many other types of systems. This usually requires that the program is recompiled on each type of system, but the program itself may need little or no
change.
NOTE: Programs written for specific graphical environments often require significant changes when moved to a different type of system. Examples of such graphical environments are Windows, the X-Window System, and the Mac OS X operating system.
Source Code, Object Code, and Executable Code When a C++ program is written, it must be typed into the computer and saved to a file. A text editor, which is similar to a word processing program, is used for this task. The statements written by the programmer are called source code, and the file they are saved in is called the source file. After the source code is saved to a file, the process of translating it to machine language can begin. During the first phase of this process, a program called the preprocessor reads the source code. The preprocessor searches for special lines that begin with the # symbol. These lines contain commands, or directives, that cause the preprocessor to amend or process the source code in some way. During the next phase the compiler steps through the preprocessed source code, translating each source code instruction into the appropriate machine language instruction. This process will uncover any syntax errors that may be in the program. Syntax errors are illegal uses of key words, operators, punctuation, and other language elements. If the program is free of syntax errors, the compiler stores the translated machine language instructions, which are called object code, in an object file.
Although an object file contains machine language instructions, it is not a complete program. Here is why. C++ is conveniently equipped with a library of prewritten code for performing common operations or sometimesdifficult tasks. For example, the library contains hardware-specific code for displaying messages on the screen and reading input from the keyboard. It also provides routines for mathematical functions, such as calculating the square root of a number. This collection of code, called the run-time library, is extensive. Programs almost always use some part of it. When the compiler generates an object file, however, it does not include machine code for any run-time library routines the programmer might have used. During the last phase of the translation process, another program called the linker combines the object file with the necessary library routines. Once the linker has finished with this step, an executable file is created. The executable file contains machine language instructions, or executable code, and is ready to run on the computer. Figure 1-5 illustrates the process of translating a C++ source file into an executable file. The entire process of invoking the preprocessor, compiler, and linker can be initiated with a single action. For example, on a Linux system, the following command causes the C++ program named hello.cpp to be preprocessed, compiled, and linked. The executable code is stored in a file named hello. g++ -o hello hello.cpp
Figure 1-5
Appendix M on the student CD explains how compiling works in Microsoft Visual C++. Many development systems, particularly those on personal computers, have integrated development environments (IDEs). These environments consist of a text editor, compiler, debugger, and other utilities integrated into a
package with a single set of menus. Preprocessing, compiling, linking, and even executing a program is done with a single click of a button, or by selecting a single item from a me n u . Figure 1-6 shows a screen from the Microsoft Visual C++ IDE. Figure 1-6
Checkpoint 1.10 What is an algorithm? 1.11 Why were computer programming languages invented? 1.12 What is the difference between a highlevel language and a low-level language?
1.13 What does portability mean? 1.14 Explain the operations carried out by the preprocessor, compiler, and linker. 1.15 Explain what is stored in a source file, an object file, and an executable file. 1.16 What is an integrated development environment?
1.4 What Is a Program Made of? CONCEPT: There are certain elements that are common to all programming languages.
Language Elements All programming languages have a few things in common. Table 1-2 lists the common elements found in almost every language. Table 1-2 Programming Language Elements
Let’s look at some specific parts of Program 1-1 (the pay-calculating program) to see examples of each element listed in the table above. For convenience, Program 1-1 is listed again.
Program 1-1
Key Words (reserved words) Three of C++’s key words appear on lines 3 and 5 : using , namespace, and int. The word double, which appears on line 7, is also a C++ key word. These words, which are always written in lowercase, each have a special meaning in C++ and can only be used for their intended purposes. As you will see, the programmer is allowed to make up his or her own names for certain things in a program. Key words, however, are reserved and cannot be used for anything other than their designated purposes. Part of learning a programming language is learning what the key words are, what they mean, and how to use them.
NOTE: The #include statement in line 2 is a preprocessor directive.
NOTE: In C++, key words are written in all lowercase. Programmer-Defined Identifiers The words hours, rate, and pay that appear in the program on lines 7, 11, 15, 18, and 21 are programmer-defined identifiers. They are not part of the C++ language but rather are names made up by the programmer. In this particular program, these are the names of variables. As you will learn later in this chapter, variables are the names of memory locations that may hold data. Operators On line 18 the following statement appears: pay = hours * rate;
The = and * symbols are both operators. They perform operations on pieces of data, known as operands. The * operator multiplies its two operands, which in this example are the variables hours and rate. The = symbol is called the assignment operator. It takes the value of the expression on the right and stores it in the variable whose name appears on the left. In this example, the = operator stores in the pay variable the result of the hours variable multiplied by the rate variable. In other words, the statement says, “Make the pay variable equal to hours times rate” or “pay is assigned the value of hours times rate.” Punctuation
Notice that many lines end with a semicolon. A semicolon in C++ is similar to a period in English. It marks the end of a complete sentence (or statement, as it is called in programming). Semicolons do not appear at the end of every line in a C++ program, however. There are rules that govern where semicolons are required and where they are not. Part of learning C++ is learning where to place semicolons and other punctuation symbols.
Lines and Statements Often, the contents of a program are thought of in terms of lines and statements. A line is just that —a single line as it appears in the body of a program. Program 1-1 is shown with each of its lines numbered. Most of the lines contain something meaningful; however some of the lines are empty. The blank lines are only there to make the program more readable. A statement is a complete instruction that causes the computer to perform some action. Here is the statement that appears in line 10 of Program 1-1: cout > hours; cin >> rate;
Once information is gathered from the outside world, a program usually processes it in some manner. In Program 1-1, the hours worked and hourly pay rate are multiplied in line 18 to produce the value assigned to the variable pay : pay = hours * rate;
Output is information that a program sends to the outside world. It can be words or graphics displayed on a screen, a report sent to the printer, data stored in a file, or information sent to any device connected to the computer. Lines 10, 14, and 21 in Program 1-1 all use the cout (pronounced “see out”) object to display messages on the computer’s screen. cout power; bigNum = num; while (count++ < power); bigNum *= num; cout provides parsing of numeric input. For example, consider the following program fragment: ofstream file("num.dat"); short x = 1297; file Avon, Martha
"color:black"> Bostrom, Andy
"color:blue"> Gomez, Diane
"color:blue"> Pistachio, Mary
"color:black"> Potus, Nicholas
"color:black"> Radon, Joseph
"color:blue"> Rhodes, Peter
"color:black"> Williams, Nancy
"color:blue"> Wilson, Zelda
"color:blue"> Zazinski, Pete
The
HTML element signifies a line break. Our solution to this problem will use a subclass of fstream that has a member function for writing a string inside of an HTML span element. The span element will specify the color the browser should use to display the string:
class ColorCodedStream : public fstream { public: void writeInColor(string str, string aColor) { *this next = add(aList->next, value); 36 return aList; 37 } 38 }
The Recursive remove Member Function The remove function ListNode *remove(ListNode *aList, double value)
takes as parameter an input list and a value, removes the value from the input list, and returns the resulting list. If the value to be removed is not on the list, the function returns the input list unchanged. The function works as follows. If the list is empty, the function returns NULL. if(aList == NULL) return NULL;
Otherwise, the function compares the value to what is stored in the first (head) node of the list. If the value is found there, the head node (pointed to by aList) is deleted and the function returns the tail: if (aList->value == value) { ListNode *tail = aList->next; delete aList; return tail; }
The last case considered is when the list is not empty and the head of the list does not contain the value to be removed. In this case, the function recursively removes the value from the tail of the list, reattaches the original head to the modified tail, and returns a pointer to the head of the (possibly) modified list. Using the same reasoning as in the add() function, we can write this case as aList->next = remove(aList->next, value); return aList;
Again putting it all together, we get the complete function as found lines 10–60 of the implementation file NumberList2.cpp.
The following program demonstrates the use of
these member functions. Program 17-8
17.5 Variations of the Linked List CONCEPT: There are many ways to link dynamically allocated data structures together. Two variations of the linked list are the doubly linked list and the circular linked list. The linked list examples are singly linked lists: a single other node. A the doubly linked list.
that we have discussed Each node is linked to variation of this is In this type of list,
each node not only points to the next node, but also to the previous one. This is illustrated in Figure 17-14. Figure 17-14
I n Figure 17-14, the last node and the first node in the list have pointers to the NULL address. When the program traverses the list it knows when it has reached either end. Another variation is the circular linked list. The last node in this type of list points to the first, as shown in Figure 17-15. Figure 17-15
17.6 The STL list Container* CONCEPT: The Standard Template provides a linked list container.
Library
T h e list container, found in the Standard Template Library, is a template version of a doubly linked list. STL lists can insert elements or add elements within the list more quickly than vectors can, because lists do not have to shift the other elements. Lists are also efficient at adding elements at their back because they have a built-in pointer to the last element in the list (no traversal required). Table 17-1 describes some of the list member
functions. Table 17-1 Selected List Member Functions
Program 17-9 demonstrates operations with the STL lists.
some
simple
Program 17-9
17.7 Reliable Software Systems, Inc., Case Study Problem Statement Reliable Software Systems, Inc., writes and markets C++ class libraries for use by programmers worldwide. One of the company’s products is a library package that includes the NumberList class introduced in section 2 of this chapter. Its customers need to use the class in programs in which copies and
assignment of NumberList objects will occur. You have been asked to modify the class to support a copy constructor and an assignment operator.
Planning for the Changes and Class Design At least two functions need to be added to the NumberList class. Rather than modifying the original class, you opt to use inheritance to create a new class with the requested enhancements. Both copy constructor and assignment need to make a copy of the linked list of nodes inside of the NumberList object being copied. To avoid duplication of code, we will add a member function ListNode *copyList(ListNode *aList);
that creates and returns a distinct copy of a list of nodes. In addition, the assignment operator, when applied as in the statement x = y;
will need to deallocate storage allocated to the linked list in the NumberList object x. Accordingly, we add a member function void destroyList(ListNode *aList);
to the class. The result of this design work is t h e ReliableNumberList class shown in the listing of the ReliableNumberList.h file. Contents of ReliableNumberList.h 1 #include "numberlist.h" 2 3 class ReliableNumberList : public NumberList 4 { 5 public: 6 // Copy constructor 7 ReliableNumberList(const ReliableNumberList& original); 8 // Now we need a default constructor 9 ReliableNumberList(){} 10 // Assignment operator 11 ReliableNumberList& operator=(ReliableNumberList right); 12 private: 13 static ListNode* copyList(ListNode *aList); 14 static void destroyList(ListNode *aList); 15 };
We have added a default constructor (Line 9) to
allow lists that are initially empty to be created. Notice that the auxiliary functions copyList and destroyList are declared static. This is because they are generic utility functions that do not require access to specific NumberList objects to do their job.
Implementation of Class Member Functions We adopt a recursive strategy for implementing copyList and destroyList. If a list is empty, copyList returns NULL. If a list is not empty, then the function creates a copy of the head node, attaches it to recursively created copy of the tail, and returns the resulting list. Consider now the working of destroyList. There is nothing to destroy if the argument list is empty. If the argument list is nonempty, the functions recursively destroys the tail and then deallocates the storage for the head node. The coding details for both copyList and destroyList can be seen in the listing of ReliableNumberList.cpp that follows. Having the copyList function makes writing the copy constructor almost trivial: the constructor simply copies the linked list in the existing object and assigns the result to the head pointer of the object being created: head = copylist(original.head);
Coding the assignment operator is not much harder. The operator first deallocates the storage for the linked list in the calling object, and then assigns a copy of the list in its right operand to the head member of the calling object. destroyList(head); head = copyList(right.head);
You can find the full implementation details and an illustration of the use of this new class in the following listing.
Program 17-10
17.8 Tying It All Together: More on Graphics and Animation In previous chapters you learned how to use text-based graphics to draw and animate simple geometric shapes like straight lines, rectangles, and triangles. The techniques you learned can be extended to more complex shapes and figures. Before you can draw a shape, you must determine the screen coordinates of the characters that will form both its outline and interior. For example, consider the slanted line segment shown in Figure 17-16. Figure 17-16
The line starts at (0, 0) and ends at (3, 3), and is drawn by placing asterisks at the screen coordinates (0, 0), (1, 1), (2, 2), and (3, 3).
Representing Shapes with Image Maps More generally, a figure or shape may consist of several parts. Each of the individual parts making up the figure may be a line segment, a geometric shape such as a rectangle or triangle, or some other type of shape. It is convenient to use an array of coordinates to specify a part of a multi-part figure, and then combine the arrays into a single list that defines the whole figure. We use the term image map to refer to the list of coordinates that specifies a shape to be drawn. Let us design the class that will be used represent image maps. An image map is a list coordinates, so we make the class a subclass the STL type list. In addition, define a member function
to of of we
void add(COORD coordArray[]);
to allow us to add the list. We mark array by storing a the last element of
an array of coordinates to the end of the coordinate COORD value of (-1, -1) as the array.
Here is a preliminary ImageMap class:
declaration
of
an
class ImageMap: list { public: // Add an array of coordinates to the image map void add(COORD coordArray[]) { for(int k = 0; coordArray[k].X != -1; k++) { push_back(coordArray[k]); } } }
As an example, the line from (0, 0) to (3, 3) would be represented by the code ImageMap line; COORD lineCoords[] = {{0,0}, {1,1}, {2,2}, {3,3}, {-1,-
COORD lineCoords[] = {{0,0}, {1,1}, {2,2}, {3,3}, {-1,1}}; line.add(lineCoords);
Initializing an array of coordinates in this manner and then adding it to the image map is very handy, and is a vast improvement over the alternative of using push_back to add the coordinates to the image map one at a time: ImageMap line; COORD pos; pos.X = 0; pos.Y = 0; line.push_back(pos); pos.X = 1; pos.Y = 1; line.push_back(pos); // rest of the code is omitted
The braces { } that go around a single COORD object in the initialization of the lineCoords array are tedious to insert, particularly when the array has a lot of elements. We will therefore consider an alternative notation for initializing the image map. The alternative will allow us to use an array of short integers to initialize an array of coordinates. An array of two short integers and a COORD object both consist of two short integers and C++ compilers store both in memory the same way. Once stored in memory, an array of 5 COORD objects is indistinguishable from an array of 10 short integers. If we write short int lineShorts[] = {0, 0, 1, 1, 2, 2, 3, 3, -1, -1};
then the array lineShorts is indistinguishable from lineCoords in the way the two arrays are stored in memory. We can use this fact to find an alternative way of initializing image maps that does not require as many braces. We add a second add member function to ImageMap, one that takes an array of short int as a parameter. The new add function uses a cast to convert its parameter to an array of COORD, and then calls the first add function. void add(short *coordAsShorts) { COORD *pCoord = *>(coordAsShorts); add(pCoord);
reinterpret_castnext != NULL) q = q->next; cout value;
Find the Errors 17. Each of the following member functions for performing an operation on a linked list of type NumberList has at least one error. Explain what is wrong and how to fix it. A) NumberList::printList( ) { while(head) { cout value; head = head->next; } }
B) NumberList::printList( ) { ListNode *p = head; while (p->next) { cout value; p = p->next; } }
C) NumberList::printList( ) { ListNode *p = head; while(p) { cout value; p++; } }
D) NumberList::˜NumberList() { ListNode *nodePtr, *nextNode; nodePtr = head; while (nodePtr != NULL) { nextNode = nodePtr->next; nodePtr->next = NULL; nodePtr = nextNode; } }
Soft Skills 18. You are the leader of a programming team. You want the programmers on your team to attend a two-day workshop on linked lists, stacks and queues. One of the managers points out that the STL already supplies each one of those data structures, making it unnecessary for your programmers to write their own. Write the manager a short memo that justifies the need for the workshop.
Programming Challenges 1. Simple Linked List Class
Using an appropriate definition of ListNode, design a simple linked list class with only two member functions and a default constructor: void add(double x); boolean isMember(double x); LinkedList( );
The add function adds a new node containing x to the front (head) of the list, while the isMember function tests to see if the list contains a node with the value x. Test your linked list class by adding various numbers to
the list and then testing for membership. 2. List Copy Constructor Modify your list class of Programming Challenge 1 to add a copy constructor. Test your class by making a copy of a list and then testing membership on the copy. 3. List Print
Modify the list class you created in the previous programming challenges to add a print member function. Test the class by starting with an empty list, adding some elements, and then printing the resulting list out. 4. Recursive Member Check Modify the list class you created in the previous programming challenges to use a recursive method to check for list membership. Test your class. 5. List Member Deletion Modify the list class you created in the previous programming challenges by adding a function to remove an item from the list, and by adding a destructor: void remove(double x); ˜LinkedList();
Test the class by adding by a sequence of instructions that mixes operations for adding items, removing items, and printing the list. 6. List Reverse Modify the list class you created in the previous programming challenges by adding a member function for reversing the list: void reverse();
The member function rearranges the nodes in the list so that their order is reversed. You should do this without creating or destroying nodes.
7. List Search
Modify the list class of Programming Challenge 1 (or later) to include a member function int search(double x)
that returns the position of a number x on the list. The first node in the list is at position 0, the second node is at position 1, and so on. I f x is not found on the list, the search should return -1. Test the new member function using an appropriate driver program. 8. Member Insertion By Position Modify the list class you created in the previous programming challenges by adding a member function for inserting a new item at a specified position: void insert(double x, int pos);
Solving the Member Insertion By Position Problem A position of 0 means that x will become the first item on the list, a position of 1 means that x will become the second item on the list, and so on. A position equal to, or greater than, the length of the list means that the x is placed at the end of the list. 9. Member Removal by Position Modify the list class you created in the previous programming challenges by adding a member function for deleting a node at a specified position: void remove(int pos);
A value of 0 for the position means that the
first node on the list (the current head) is deleted. The function does nothing if the value passed for pos is greater or equal to the length of the list. 10. List Sort Modify the list class you created in the previous programming challenges by adding a member function that will sort the list into ascending order by the numeric value of the item stored in the node. void sort(
);
You should sort the list by moving pointers rather than by copying or swapping the contents of the nodes. 11. List Template Program 17-10 destroys linked lists in two different functions: in the destructor for NumberList and in the destroyList method of the ReliableNumberList class. Improve the program by eliminating this duplication of code. 12. Running Back Program 17-11 makes a person run from across the screen, starting near the left edge of the screen and ending near the right edge. Modify the program so that the person turns around and runs back to the starting point.
CHAPTER 18 Stacks and Queues TOPICS 18.1
Introduction to the Stack ADT
18.2
Dynamic Stacks
18.3
The STL stack Container
18.4
Introduction to the Queue ADT
18.5
Dynamic Queues
18.6 Containers
The
STL deque
and queue
18.7 Focus on Problem Solving and Program Design: Eliminating Recursion 18.8 Tying It All Together: Converting Postfix Expressions to Infix
18.1 Introduction to the Stack ADT CONCEPT: A stack is a data structure that stores and retrieves items in a last-infirst-out manner.
Definition Like an array or a linked list, a stack is a data structure that holds a sequence of elements. Unlike arrays and lists, however, stacks are last-in-first-out (LIFO) structures. This means that when a program retrieves elements from a stack, the last element inserted into the stack is the first one retrieved (and likewise, the first element inserted is the last one retrieved). When visualizing the way a stack works, think of a stack of plates at the beginning of a cafeteria line. When a cafeteria worker replenishes the supply of plates, the first one he or she puts on the stack is the last one taken off. This is illustrated in Figure 18-1. Figure 18-1
The LIFO characteristic of a stack of plates in a cafeteria is also the primary characteristic of a stack data structure. The last data element placed on the stack is the first data retrieved from the stack.
Applications of Stacks Stacks are useful data structures for algorithms that work first with the last saved element of a series. For example, computer systems use stacks while executing programs. When a function is called, they save the program’s return address on a stack. They also create local variables on a stack. When the function terminates, the local variables are removed from the stack and the return address is retrieved. Also, some calculators use a stack for performing mathematical operations.
Static and Dynamic Stacks There are two types of stack data structure: static and dynamic. Static stacks have a fixed size and are implemented as arrays. Dynamic stacks grow in size as needed and are implemented as linked lists. In this section you will see examples of both static and dynamic stacks.
Stack Operations A stack has two primary operations: push and pop. The push operation causes a value to be stored, or pushed onto the stack. For example, suppose we have an empty integer stack that is capable of holding a maximum of three values. With that stack we execute the following push operations. push(5); push(10); push(15);
Figure 18-2 illustrates the state of the stack after each of these push operations. Figure 18-2
The pop operation retrieves (and hence, removes) a value from the stack. Suppose we execute three consecutive pop operations on the stack shown in Figure 18-2. Figure 18-3 depicts the results. Figure 18-3
As you can see from Figure 18-3, the last pop operation leaves the stack empty. For a static stack (one with a fixed size), we will need a boolean isFull operation. The isFull operation returns true if the stack is full, and false otherwise. This operation is necessary to prevent a stack overflow in the event a push operation is attempted when all the stack’s elements have values stored in them. For both static and dynamic stacks we will need a boolean isEmpty operation. The isEmpty operation returns true when the stack is empty, and false otherwise. This prevents an error from occurring when a pop operation is attempted on an empty stack.
A Static Stack Class
Now we examine a class IntStack that stores a static stack of integers and performs the stack operations we have discussed. The class has the member variables described in Table 18-1. Table 18-1 Members Variables of the Stack Class
The class’s member functions are listed in Table 18-2. Table 18-2 Members Functions of the Stack Class
NOTE: Even though the constructor dynamically allocates the stack array, it is still considered a static stack since the size of the stack does not change once it is allocated. The code for the class is shown here:
Contents of IntStack.h 1 class IntStack 2{ 3 private: 4 int *stackArray; 5 int capacity; 6 int top; 7 public: 8 IntStack(int capacity); // Constructor 9 ˜IntStack() { delete[] stackArray; } 10 void push(int value); 11 void pop(int &value); 12 bool isEmpty(); 13 14 // Stack Exceptions 15 class Overflow {}; 16 class Underflow {}; 17 };
In addition to the members of the stack described in Table 18-2, the IntStack class defines two inner classes named Overflow and Underflow to be used as stack exceptions. Exceptions are covered in Chapter 16, but we will briefly explain them here for the benefit of those who may have skipped that chapter. A section of code is said to cause an exception when, in the course of execution, it encounters conditions that make it impossible to perform the task the code was designed to do. In the case of a static stack, an overflow exception occurs during a call to push if there is no more room on the stack. Likewise, an underflow exception occurs in a call to pop if there is nothing on the stack for pop to return. Code that detects the occurrence of an exception can notify the rest of the program by creating a value that describes the exception and passing that value to the rest of the program using a throw statement. For example, the push function announces the occurrence of an overflow exception by executing the statement throw InstStack::Overflow();
and the pop function executes the statement throw IntStack::Underflow();
to
notify
the
program
that
the
underflow
exception has occurred. By default, a program terminates with an error message when any part of it throws an exception. This default behavior can be changed through a process known as catching the exception. You can learn more about exceptions in Chapter 16. The IntStack constructor allocates an array of a specified capacity and sets the member variable top to 0. All stack functions use top in such a way that it always points to the next available slot in the stack’s array. When top equals capacity, there are no more slots available to store values, and the next call to push throws an exception. Likewise, when top is zero, the stack is empty and a call to pop throws an exception. Because there are no provisions in the program to catch either exception, the occurrence of either one will terminate the program with an error message. Notice that push increments top after adding a value to the stack, and pop decrements top before returning the value stored at stackArray[top].
Program 18-1 illustrates the stack class and its member functions. Notice that the values pushed onto the stack come off in reverse order when they are popped. Program 18-1
I n Program 18-1, the constructor is called with the argument 5. This sets up the member variables, as shown in Figure 18-4. Since top is set to 0, the stack is empty. Figure 18-4
Figure 18-5 shows the state of the member variables after the push function is called the first time (with 5 as its argument). The value of top is now 1. Figure 18-5
Figure 18-6 shows the state of the member variables after all five calls to the push function. Now top has value 5, and the stack is full. Figure 18-6
Notice that the pop function uses a reference parameter, value. The value that is popped off the stack is copied into value so it can be used later in the program. Figure 18-7 depicts the state of the class members and the value parameter, just after the first value is popped off the stack. Figure 18-7
The program continues to call the pop function until all the values have been removed from the stack.
Handling Stack Exceptions
As you learned in Chapter 16, the C++ try/catch statement can be used to catch and recover from exceptions, thereby allowing the program to avoid being terminated. The following program shows how a program using t h e IntStack class can catch the exceptions that it throws. The program tries to store in the stack more values than the stack can handle, causing push to throw the Overflow exception. The main function catches the exception and prints an explanatory error message. Program 18-2
There is a significant difference between a stack filling up and a stack becoming empty,
and between the stack overflow and stack underflow exceptions. In stack overflow, a program has a value to push on a stack, but cannot continue execution because the stack is full. There is a sense in which overflow is unexpected, because a program does not normally expect to use up every slot in the stack. On the other hand, programs are usually written to remove and process all items stored on a stack, so they do expect that the stack will eventually become empty. Indeed, most algorithms that use a stack have a loop that continues to iterate as long as the stack is not empty. This is why a stack always needs an isEmpty function, but does not need an isFull function. A well-written stack-based algorithm will normally call isEmpty to make sure the stack is not empty before calling pop. The difference between overflow and underflow can be summarized as follows. Stack overflow in push notifies the caller that the stack has run out of resources, while stack underflow notifies the caller of an error in the program’s logic. Unlike overflow, stack underflow can be avoided by careful programming. Therefore, programs should not use try/catch to handle underflow. Instead, they should ensure that underflow cannot occur by calling isEmpty before calling pop.
Stack Templates The stack classes shown in this chapter work only with integers. A stack template can be easily designed to work with any data type. This is left as a Programming Challenge for you to complete.
18.2 Dynamic Stacks CONCEPT: A stack may be implemented as a linked list, and expand or shrink with each push or pop operation. A dynamic stack is built on a linked list instead of on an array. A stack based on a linked list offers two advantages over a stack based on an array. First, there is no need to specify the starting size of the stack. A
dynamic stack simply starts as an empty linked list, then expands by one node each time a value is pushed. Second, a dynamic stack will never be full, as long as the system has enough free memory. In this section we will look at a dynamic stack class, DynIntStack. This class is a dynamic version of the IntStack class previously discussed. The class declaration is shown here:
The StackNode class is the data type of each node in the linked list. Because it is easy to add and remove items at the beginning of the list, we make the beginning of the linked list the top of the stack, and use a pointer top to point to the first node in the list. This pointer is initialized to NULL by the stack constructor, to signify that the stack is created empty. The member functions of this stack class are shown here:
The push function is particularly simple. It simply creates a new node whose value is the
number to be pushed on successor pointer is currently the top of the the newly created node stack:
the stack, and whose the node that is stack, and then makes the new top of the
top = new StackNode(num, top);
Note that this works correctly even if the stack was empty previous to the push operation, because in that case the successor to the new node at the top of the stack will be correctly set to NULL. Now let’s look at the pop function. Just as the push function must insert nodes at the head of the list, pop must delete nodes at the head of the list. First, the function calls isEmpty to determine whether there are any nodes in the stack. If not, an error message is displayed and the program is terminated. if (isEmpty()) { cout value; temp = top; top = top->next; delete temp; }
First, a copy of the value member of the node at the top of the stack is saved in the num reference parameter. A temporary pointer temp is then set to point to the node that is to be deleted, that is, the node currently at the top of the stack. The top pointer is then set to point to the node after the one that is currently at the top. The same code will set top to NULL if there are no nodes after the one that is currently at the top of the stack. It is then safe to delete the top node through the temporary pointer.
Program 18-3 is a driver that demostrates the DynIntStack class. Program 18-3
18.3 The STL stack Container* CONCEPT:
The
Standard
Template
Library
offers a stack template, that may be implemented as a vector, a list, or a deque. So far, the STL containers you have learned about are vectors and lists. The STL stack container may be implemented as a vector or a list. (It may also be implemented as a deque, which you will learn about later in this chapter.) One class is said to adapt another class if it provides a new interface for it. The purpose of the new interface is to make it more convenient to use the class for specialized tasks. Because the stack container is used to adapt the list, vector, and deque containers, it is often referred to as a container adapter. Here are examples of how to declare a stack of ints, implemented as a vector, a list, and a deque. stack< int, vector > iStack; stack< int, list > iStack stack< int > iStack; default)
// Vector stack // List stack // Deque stack (the
Storing Objects in an STL Stack
NOTE: Be sure to put spaces between the angled brackets that appear next to each other. This will prevent the compiler from mistaking >> for the stream extraction operator, >>. Table 18-3 lists and describes some of the stack template’s member functions. Table 18-3 STL Stack Member Fiunctions
NOTE: The pop function in the stack template does not retrieve the value from the top of the stack, it only removes it. To retrieve the value, you must call the top function first. Program 18-4 is a driver that demonstrates an STL stack implemented as a vector. Program 18-4
Checkpoint 18.1 Describe what LIFO means. 18.2 What is the difference between static and dynamic stacks? What advantages do dynamic stacks have over static stacks? 18.3 What are the two primary operations? Describe them both.
stack
18.4 What STL types does the STL stack container adapt?
18.4
Introduction to the Queue ADT
CONCEPT: A queue is a data structure that stores and retrieves items in a first-infirst-out manner.
Definition Like a stack, a queue (pronounced “cue”) is a data structure that holds a sequence of elements. A queue, however, provides access to its elements in first-in, first-out (FIFO) order. The elements in a queue are processed like customers standing in a grocery checkout line: The first customer in line is the first one served.
Application of Queues Queue data structures are commonly used in computer operating systems. They are especially important in multiuser/multitasking environments where several users or tasks may be requesting the same resource simultaneously. Printing, for example, is controlled by a queue because only one document may be printed at a time. A queue is used to hold print jobs submitted by users of the system, while the printer services those jobs one at a time. Communications software also uses queues to hold information received over networks and dial-up connections. Sometimes information is transmitted to a system faster than it can be processed, so it is placed in a queue when it is received.
Static and Dynamic Queues Queues, like stacks, can be implemented as arrays or linked lists. Dynamic queues offer the same advantages over static queues that dynamic stacks offer over static stacks. In fact, the primary difference between queues and stacks is the way data elements are accessed in each structure.
Queue Operations A queue has a front and a rear like a checkout
line in a grocery store. This is illustrated in Figure 18-8. When an element is added to a queue, it is added to the rear. When an element is removed from a queue, it is removed from the front. The two primary queue operations are enqueuing and dequeuing. To enqueue means to insert an element at the rear of a queue, and to dequeue means to remove an element from the front of a queue. There are several algorithms for implementing these operations. We will begin by looking at the simplest. Figure 18-8
Suppose we have an empty static integer queue that is capable of holding a maximum of three values. With that queue we execute the following enqueue operations: enqueue(3); enqueue(6); enqueue(9);
Figure 18-9 illustrates the state of the queue after each of these enqueue operations. Figure 18-9
Notice that the front index (which is a variable holding a subscript or perhaps a pointer) always references the same physical
element. The rear index moves in the array as items are enqueued. Now let’s see how dequeue operations are performed. Figure 18-10 illustrates the state of the queue after each of three consecutive dequeue operations. Figure 18-10
In the dequeuing operation, the element at the front of the queue is removed. This is done by moving all the elements after it forward by one position. After the first dequeue operation, the value 3 is removed from the queue and the value 6 is at the front. After the second dequeue operation, the value 6 is removed and the value 9 is at the front. Notice that when only one value is stored in the queue, that value is at both the front and the rear. When the last dequeue operation is performed in Figure 18-10, the queue is empty. An empty queue can be signified by setting both front and rear indices to –1. The problem with this algorithm is its inefficiency. Each time an item is dequeued, the remaining items in the queue are copied forward to their neighboring element. The more items there are in the queue, the longer each successive dequeue operation will take. Here is one way to overcome the problem: Make both the front and rear indices move in the array. As before, when an item is enqueued, the rear index is moved to make room for it. But
in this design, when an item is dequeued, the front index moves by one element toward the rear of the queue. This logically removes the front item from the queue and eleminates the need to copy the remaining items to their neighboring elements. With this approach, as items are added and removed, the queue gradually “crawls” toward the end of the array. This is illustrated in Figure 18-11. The shaded squares represent the queue elements (between the front and rear). Figure 18-11
The problem with this approach is that the rear index cannot move beyond the last element in the array. The solution is to think of the array as circular instead of linear. When an item moves past the end of a circular array, it simply wraps around to the beginning. For example, consider the queue depicted in Figure 18-12. Figure 18-12
The value 3 is at the rear of the queue, and the value 7 is at the front of the queue. Now, suppose an enqueue operation is performed, inserting the value 4 into the queue. Figure 18-13 shows how the rear of the queue wraps around to the beginning of the array.
Figure 18-13
So, what is the code for wrapping the rear marker around to the opposite end of the array? One straightforward approach is to use an if statement such as if (rear == queueSize - 1) rear = 0; else rear++;
Another approach is with modular arithmetic: rear = (rear + 1) % queueSize;
This statement uses the % operator to adjust the value in rear to the proper position. Although this approach appears more elegant, the choice of which code to use is yours.
Detecting Full and Empty Queues with Circular Arrays In our implementation of a queue using a circular array, we have adopted the convention that the front and rear indices both reference items that are still in the queue, and that the front and rear indices will both be set to –1 to indicate an empty queue. To preserve this convention, the operation for dequeueing an element must set both front and rear to –1 after removing an element from a queue with only one item. The dequeuing operation can test for a queue with only one item by testing whether front is equal to rear. To avoid overflowing the queue, the operation for enqueuing must first check that the queue is not already full before adding another element. We can check to see if the queue is full by testing the expression
(rear + 1) % queueSize == front
to see if it is true. There is another way for detecting full and empty queues: A counter variable can be used to keep a count of the number of items currently stored in the queue. With this convention, the counter is incremented with each enqueue operation and decremented with each dequeue operation. The queue is empty when the counter is zero, and is full when the counter equals the size allocated for the queue. Because it might be helpful to keep a count of items in the queue anyway, we will use the second method in our implementation. Accordingly, we introduce the variables int int int int int
*queueArray; queueSize; front; rear; numItems;
with numItems being the counter variable, and queueArray the pointer to a dynamically allocated array of size queueSize. We adopt the following two conventions: • rear points to the place in the queue holding the item that was last added to the queue. • front points to the place in the queue that used to hold the item that was last removed from the queue. Because of the convention on where the rear index is pointing to, the enqueue operation must first (circularly) move rear one place to the right before adding a new item num: rear = (rear + 1) % queueSize; queueArray[rear] = num; numItems ++;
Similarly, because whatever is at front has already been removed, the dequeue operation must first move front before retrieving a queue item.
A Static Queue Class The declaration of the IntQueue class is as follows: Contents of IntQueue.h 1 #ifndef INTQUEUE_H 2 #define INTQUEUE_H 3 4 class IntQueue 5 { 6 private: 7 int *queueArray; 8 int queueSize; 9 int front; 10 int rear; 11 int numItems; 12 public: 13 IntQueue(int); 14 ˜IntQueue(); 15 void enqueue(int); 16 void dequeue(int &); 17 bool isEmpty(); 18 bool isFull(); 19 void clear(); 20 }; 21 #endif
Notice that in addition to the operations discussed in this section, the class also declares a member function named clear. This function clears the queue by resetting the front and rear indices, and setting the numItems member to 0. The member function definitions are listed here:
Program 18-5 is a driver that demonstrates the IntQueue class. Program 18-5
Overflow and Underflow Exceptions in a Static Queue The enqueue and dequeue functions in our queue class terminate the calling program when they cannot perform the task they are called to do. But terminating the caller is not always the right thing to do. A better course of action is to throw an exception and allow the caller who is prepared to handle such an exception to take appropriate action. Upon catching such an exception, some callers may indeed decide to terminate the program. Other callers, however, may be able to recover and continue execution. For example, a program that catches a queue overflow exception might be able to create a bigger queue and switch to the new queue. A better design for a static queue is to have enqueue and dequeue throw overflow and
underflow exceptions. Having enqueue throw overflow eliminates the need for a public isFull function because the caller can use a try/catch block to handle queue overflows if and when they occur. By putting all calls to enqueue within the try block, the caller is able to put the code to handle an exception thrown by any of those calls in a single place: the catch block. Without exception handling, every call to enqueue would have to be preceded by a call to isFull and have code attached to it to recover in the event that isFull returns true. One of the programming challenges at the end of this chapter asks you to modify the queue class to use exceptions.
18.5 Dynamic Queues CONCEPT: A queue may be implemented as a linked list, and expand or shrink with each enqueue or dequeue operation. Dynamic queues, which are built around linked lists, are much more intuitive to understand than static queues. A dynamic queue starts as an empty linked list. With the first enqueue operation, a node is added, which is pointed to by the front and rear pointers. As each new item is added to the queue, a new node is added to the rear of the list, and the rear pointer is updated to point to the new node. As each item is dequeued, front is made to point to the next mode in the list, and then the node that was previously at the front of the list is deleted. Figure 18-14 shows the structure of a dynamic queue. Figure 18-14
A dynamic integer queue class is listed here:
A dynamic integer queue class is listed here:
Program 18-6 is a driver that demonstrates the DynIntQueue class. Program 18-6
18.6 The STL deque and queue Containers* CONCEPT: The Standard Template Library provides two containers, deque and queue, for implementing queue-like data structures. In this section we will examine two ADTs offered by the Standard Template Library: deque a n d queue. A deque (pronounced “deck” or “deek”) is a double-ended queue. It similar to a vector, but allows efficient access to values at both the front and the rear. The queue ADT is like the stack ADT: It is actually a container adapter.
The deque Container
Think of the deque container as a vector that provides quick access to the element at its front as well as at the back. (Like vector, deque also provides access to its elements with the [] operator.) Programs that use the deque ADT must include the deque header. Since we are concentrating on its queue-like characteristics, we will focus our attention on the push_back, pop_front, and front member functions. Table 18-4 describes them. Table 18-4 deque Member Functions
Program 18-7 demonstrates the deque container. Program 18-7
The queue Container Adapter The queue container adapter can be built upon vectors, lists, or deques. By default, it uses a deque as its base. The insertion and removal operations supported by queue are similar to those supported by the stack ADT: push, pop, and front. There are differences in their behavior, however. The queue version of push always inserts an element at the rear of the queue. The queue version of pop always removes an element from the structure’s front. The front function returns the value of the element at the front of the
queue. Program 18-8 demonstrates a queue. Since the declaration of the queue does not specify which type of container is being adapted, the queue will be built on a deque.
Storing Objects in an STL Queue Program 18-8
18.7 Focus on Problem Solving and Program Design: Eliminating Recursion Although recursion is a very useful programming technique, it carries the overhead of the necessity to make numerous function calls during the process of solving the problem. The efficiency of a recursive solution can often be greatly improved by reformulating a recursive algorithm to eliminate the recursion. In this section, we look at how a stack can be used to eliminate recursion from the Quicksort algorithm. The main problem in Quicksort is that of sorting a range, or a segment of an array arr, between two indices start and end. Naturally, this has to be done only if start is less than end. As learned in Chapter 14, this is accomplished by calling a procedure partition, which determines an integer pivot such that 1. All array items in the segment to the left of the pivot are less than the element at the pivot: that is, arr[k] < arr[pivot] for all k in the range start .. pivot-1
2. All array items in the segment to the right of the pivot are greater than or equal to the element at the pivot: that is, arr[k] >= arr[pivot] for all k in the range pivot+1..end
Once this is done, the array item at the pivot is in its sorted position. Thus an important effect of the partition procedure is that it gets the pivot element in its final sorted position. By keeping track of the left and right subranges when we call partition, and then later calling partition on those subranges, we can sort the entire array without using recursion. We need to keep track of these subranges, and eventually partition them in the order in which the recursive calls to Quicksort would have done them. Because the recursive calls to Quicksort are invoked and return in LIFO (last-in-first-out) order, we
use a stack to keep track of the ranges that are waiting to be partitioned. The main idea of our solution is to define a class class Range { int start; int end; public: Range(int s, int e) { start = s; end = e; } };
to keep track of the ranges of the array that remain to be partitioned. Accordingly, we use the STL stack class to define a stack of these ranges:
stack qStack;
We then use a function qSort(int arr[ ], int size) that sorts the array arr by initially pushing the range from 0 to size-1 onto the stack, and then repeatedly removing ranges from the stack, partitioning the range, and putting the left and right subrange back onto the stack. Empty subranges removed from the stack are discarded. The algorithm is push Range(0, size-1) onto stack While stack not empty pop a range r from the stack If r is not empty partition the range r into two smaller ranges about the pivot push the two smaller ranges onto the stack End if End While
The complete solution, which reuses the partition function from Chapter 14, is given i n Program 18-9. Notice that we declare the qSort function to be a friend of Range, to allow access to the private members of Range.
NOTE: The statement qStack.push(Range(0, size-1)); creates a Range object by invoking the constructor. The Range object is then pushed onto the stack. Program 18-9
NOTE: The friend concept should be used with caution, since it circumvents the protection afforded the members of the class by declaring them private. Notice that in our case, the start and end members of the Range class are never modified by the friend function qSort.
18.8 Tying It All Together: Converting Postfix Expressions to Infix Stacks can be used to evaluate postfix expressions. Let’s see how this can be done. We confine ourselves to postfix expressions that contain only numbers and the binary operators +, -, *, and /. Recall from Chapter 14 that a postfix expression is either a single number or two postfix expressions followed by an operator. Evaluation of a single-number postfix expression is easy: we just return the number. For the nonsimple case, we must evaluate the two postfix expressions in order and save their values. Then, when we come to the operator, we retrieve the two previously saved values and apply the operator. To see how the method works consider the example 2 5 Because 2 and 5 are single-number postfix expressions, we simply save their values for later use. Then, when we encounter the minus operator, we retrieve the two saved values and apply the operator, yielding -3 as the value of the entire expression. In general, any postfix expression can be evaluated by reading it in left to right order. Whenever a value is encountered, it is pushed onto the stack to await application by an operator at a later stage. Whenever an operator is encountered, its two operands are popped off the stack, and the operator is applied to them to yield a value. This value is in turn pushed onto the stack. The procedure ends when all of the input expression has been read. At that time, there should be only one value on the stack. The value on the stack is the value of the postfix expression. This same idea can be used to convert postfix expressions to infix. Again, we read the input postfix expression from left to right. This time, though, we use a stack of strings instead of a stack of integer. Any number that is read
must be an operand: it is converted to a string and pushed onto the stack. If an operator is encountered, the two strings at the top of the stack are popped and the operator is placed between them. Parentheses are then placed around the resulting string, and the parenthesized string is pushed back onto the stack. Thus, for example, the above input postfix expression would result on the following sequence of pushes of strings onto the stack: "2" "2" "(2 – 5)"
"5"
These ideas are used in Program 18-10, which follows. Program 18-10
Review Questions and Exercises Short Answer 1. What does LIFO mean? 2. What element is retrieved from a stack by the pop operation? 3. What is the difference between a static stack and a dynamic stack? 4. Describe two operations that all stacks perform. 5. The STL stack is considered a
container adapter. What does that mean? 6. What types may the STL stack be based on? By default, what type is an STL stack based on? 7. What does FIFO mean? 8. When an element is added to a queue, where is it added? 9. When an element is removed from a queue, where is it removed from? 10. Describe two operations that all queues perform. 11. What two queue-like containers does the STL offer? 12. Suppose the following operations were performed on an empty stack: push(0); push(9); push(12); push(1);
Insert numbers in the following diagram to show what will be stored in the static stack after the operations have executed.
13. Suppose the following operations were performed on an empty stack: push(8); push(7); pop(); push(19); push(21); pop();
Insert numbers in the following diagram to show what will be stored in the static stack after the operations have executed.
14. Suppose the following operations are performed on an empty queue: enqueue(5); enqueue(7); enqueue(9); enqueue(12);
Insert numbers in the following diagram to show what will be stored in the static queue after the operations have executed.
15. Suppose the following operations are performed on an empty queue: enqueue(5); enqueue(7); dequeue(); enqueue(9); enqueue(12); dequeue(); enqueue(10);
Insert numbers in the following diagram to show what will be stored in the static queue after the operations have executed.
16. What problem is overcome by using a circular array for a static queue? Algorithm Workbench 17. Give pseudocode that implements a queue using two stacks. The queue operations enqueue, dequeue, and empty must be implemented in terms of the push, pop, and
empty stack operations. Soft Skills 18. A common real-life example used to explain stacks is the stack of plates in a cafeteria. Find at least two other reallife examples in which items are added and removed from a container in last-in-firstout order, and use these examples to explain the concept of a stack.
Programming Challenges 1. Static Stack Template
In this chapter you studied IntStack, a class that implements a static stack of integers. Write a template that will create a static stack of any data type. Demonstrate the class with a driver program. 2. Dynamic Stack Template In this chapter you studied DynIntStack, a class that implements a dynamic stack of integers. Write a template that will create a dynamic stack of any data type. Demonstrate the class with a driver program. 3. Static Queue Template In this chapter you studied IntQueue, a class that implements a static queue of integers. Write a template that will create a static queue of any data type. Demonstrate the class with a driver program. 4. Dynamic Queue Template In this chapter you studied DynIntQueue, a class that implements a dynamic queue of integers. Write a template that will create a dynamic queue of any data type. Demonstrate the class with a driver program. 5. Error Testing The DynIntStack and DynIntQueue classes shown
in this chapter are abstract data types using a dynamic stack and dynamic queue, respectively. The classes do not currently test for memory allocaton errors. Modify the classes so they determine if new nodes cannot be created, and handle the error condition in an appropriate way. (You will need to catch the predefined exception bad_alloc.)
NOTE: If you have already done Programming Challenges 2 and 4, modify the templates you created. 6. Dynamic String Queue Design a class that stores strings on a dynamic queue. The strings should not be fixed in length. Demonstrate the class with a driver program. 7. Queue Exceptions Modify the static queue class used in Program 18-5 as follows. 1. Make the isFull member private. 2. Define a queue overflow exception and modify enqueue so that it throws this exception when the queue runs out of space. 3. Define a queue underflow exception and modify dequeue so that it throws this exception when the queue is empty. 4. Rewrite the main program so that it catches overflow exceptions when they occur. The exception handler for queue overflow should print an appropriate error message and then terminate the program. 8. Evaluating Postfix Expressions Write a program that reads postfix expressions and prints their values. Each input expression should be entered on its own line, and the program should terminate when the user enters a blank line. Assume only binary operators, and that the expressions contain no variables. Note that you will need to use parentheses to indicate the
order of application of the operators in the expression. Here are sample input–output pairs:
9. File Reverser
Solving the File Reverser Problem Write a program that opens a text file and reads its contents into a stack of characters. The program should then pop the characters from the stack and save them in a second text file. The order of the characters saved in the second file should be the reverse of their order in the first file. 10. Balanced Parentheses
A string of characters has balanced parentheses if each right parentheses occurring in the string is matched with a preceding left parentheses in the same way each right brace in a C++ program is matched with a preceding left brace. Write a program that uses a stack to determine whether a string entered at the keyboard has balanced parentheses. 11. Balanced Multiple Delimiters A string may use more than one type of delimiter to bracket information into “blocks.” For example, A string may use braces { }, parentheses ( ), and brackets [ ] as delimiters. A string is properly delimited if each right delimiter is matched with a preceding left delimiter of the same type in such a way that the either the resulting blocks of information are disjoint, or one of
them is completely nested within the other. Write a program that uses a single stack to check whether a string containing braces, parentheses, and brackets is properly delimited. 12. Inventory Bin Stack Design an inventory class that stores the following members. serialNum: an integer that holds a part's serial number manufactDate: a member that holds the date the part was manufactured lotNum: an integer that holds the part's lot number
The class should have appropriate member functions for storing data into, and retrieving data from, these members. Next, design objects of the the template Challenge 1 or
a stack class that can hold class. If you wish, you may use you designed in Programming 2.
Last, design a program that uses the stack class. The program should have a loop that asks the user whether he or she wishes to add a part to inventory or take a part from inventory. The loop should repeat until the user is finished. If the user wishes to add a part to inventory, the program should ask for the serial number, date of manufacture, and lot number. The information should be stored in an inventory object and pushed onto the stack. If the user wishes to take a part from inventory, the program should pop the topmost part from the stack and display the contents of its member variables. When the user finishes, the program should display the contents of the member values of all the objects that remain on the stack 13. Inventory Bin Queue Modify the program you wrote for Programming Challenge 12 so it uses a queue instead of a stack. Compare the order in which the parts are
removed from the bin for each program.
CHAPTER 19 Binary Trees TOPICS 19.1 Definition and Applications of Binary Trees 19.2
Binary Search Tree Operations
19.3 Template Considerations for Binary Search Trees 19.4 Trees
Tying It All Together: Genealogy
19.1 Definition and Applications of Binary Trees CONCEPT: Binary trees differ from linked lists in that where a node in a linked list may have at most one successor, a node in a binary tree can have up to two successors. A binary tree is a collection of nodes in which each node is associated with up to two successor nodes, respectively called the left and right child. Not every node in the binary tree will have two children: one or both nodes may be omitted. A node in a binary tree that has no children is called a leaf node. A node that has children is said to be the parent of its children. For a nonempty collection of nodes to qualify as a binary tree, every node must have at most one parent, and there must be exactly one node with no parent. The one node that has no parent is called the root of the binary tree. An empty collection of nodes is regarded as constituting an empty binary tree. There is some similarity between a linked list and a binary tree. The root of a binary tree corresponds to the head of a list, a child of a binary tree node corresponds to a successor node in a list, and the parent of a binary tree node corresponds to the predecessor of a node in the list. And of course, the analog of the empty list is the empty binary tree.
Implementation of Binary Trees Binary trees are used to store values in their nodes. A node in a binary tree will therefore be a structure or class object that contains a member for storing the value, as well as two members that point to nodes that are the left and right children of that node: struct TreeNode { int value; TreeNode *left; TreeNode *right; };
A binary tree is itself represented by a pointer to the node that is the root of the tree. An example binary tree, with the values stored in the nodes not shown, is illustrated in Figure 19-1. The left or right pointer in a node is set to NULL if that node does not possess the corresponding child. Figure 19-1
Binary trees are called trees because they resemble an upside-down tree. Any nonempty tree can be partitioned into its root node, its
left subtree, and its right subtree. Intuitively, a subtree is an entire branch of the tree, from one particular node down. Figure 19-2 shows the left subtree of the binary tree shown in Figure 19-1.
Applications of Binary Trees Searching any linear data structure, such as an array or a standard linked list, is slow when the structure holds a large amount of information. This is because of the sequential nature of linear data structures. Binary trees and their generalizations are excellent data structures for searching large amounts of information. They are commonly used in database applications to organize key values that index database records. When used to facilitate searches, Figure 19-2
a binary tree is called a binary search tree. Binary search trees are the primary focus of this chapter.
Information is stored in binary search trees in a way that makes searching for information in the tree simple. For example, look at Figure 19-3. Figure 19-3
The figure depicts a binary search tree where each node stores a letter of the alphabet. Notice that the root node holds the letter M. The left child of the root node holds the letter F, and the right child holds R. Values are stored in a binary search tree so that a node’s left child holds data whose value is less than the node’s data, and the node’s right child holds data whose value is greater than the node’s data. This is true for all nodes in the tree that have children. In fact, in a binary search tree, all the nodes to the left of a node hold values less than the node’s value. Likewise, all the nodes to the right of a node hold values that are greater than the node’s data. When an application is searching a binary search tree, it starts at the root node. If the root node does not hold the search value, the application branches
either to the left or right child, depending on whether the search value is less than or greater than the value at the root node. This process continues until the value is found or it is determined that the search value is not in the tree. Figure 19-4 illustrates the search pattern for finding the letter P in the binary tree shown. Figure 19-4
This manner of searching a binary tree is reminiscent of the binary search technique that is used on sorted arrays. Assuming that the binary tree is balanced (meaning that at each node, the left and right subtrees have approximately the same number of nodes), the search will reduce the size of the tree remaining to be searched by one half at each step. This makes it possible to search trees with very large amounts of information in a relatively small number of steps.
Checkpoint 19.1 Describe the difference between a binary tree and a linked list.
binary tree and a linked list. 19.2
What is a root node?
19.3
What is a child node?
19.4
What is a leaf node?
19.5
What is a subtree?
19.6 Why are binary trees suitable for algorithms that must search large amounts of information?
19.2 Binary Search Tree Operations CONCEPT: There are many operations that may be performed on a binary search tree, including creating a binary search tree, inserting, finding, and deleting nodes. In this section you will learn some basic operations that may be performed on a binary search tree. We will study a simple class that implements a binary tree for storing integer values.
Creating a Binary Search Tree We will demonstrate the fundamental binary tree operations using a simple ADT: the IntBinaryTree class. The basis of our binary tree node is the following class declaration: class TreeNode { friend class IntBinaryTree; int value; TreeNode *left; TreeNode *right; TreeNode(int value1, TreeNode *left1 = NULL, TreeNode *right1 = NULL ) { value = value1; left = left1; right = right1; } };
Notice that each node of the tree has a value member, as well as two pointers to keep track of the left and right children of the node. The class will only be used by methods of IntBinaryTree, which is declared a friend of
TreeNode to allow it access to all of the members of TreeNode. The entire IntBinaryTree class follows:
Besides the TreeNode class declaration, the class has a root member. This is a pointer to the root node of the binary tree, and plays a role similar to that of the head pointer in the linked list class of Chapter 17. In many instances, it is useful to think of the pointer to the node that is the root of a binary tree as the binary tree itself. Thus, we may write TreeNode *tree;
or TreeNode *root;
and think of both as representing a binary tree because the root provides access to the entire tree. On the other hand, it is also useful to think of an object of the IntBinaryTree class as a binary tree, and write IntBinaryTree Tree;
To avoid confusion, we will use identifiers with an initial capital letter for a binary tree that is represented by an object of the IntBinaryTree class, and use identifiers with initial lowercase letters for a binary tree
represented by a pointer to its root node. The public member functions of IntBinaryTree include a constructor, a destructor, and member functions for inserting a new number into the tree, for searching a tree to determining whether a given number is in the tree, for removing a number from the tree, and for displaying the numbers stored in the tree according to different orders. All of these member functions are discussed in the sections that follow. Program 19-1 demonstrates the creation of an IntBinaryTree object, and the use of the public insert member function to build a binary search tree. The implementation code for the member functions are in the IntBinaryTree.cpp file; the contents of that file will be discussed later. The tree that results from the execution of Program 19-1 is shown in Figure 19-5. Program 19-1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// This program builds a binary tree with 5 nodes. #include #include "IntBinaryTree.h" using namespace std; int main() { IntBinaryTree tree; cout left and tree->right. A recursive operation might first process tree>value, and then the recursively operate on tree->left and tree->right.
Inserting an Element
Inserting an Element into a Binary Tree The work of inserting a number into a binary search tree is performed by the private member function insert(TreeNode *&tree, int num)
which is passed a pointer tree to the root node of a binary search tree and a number num to be inserted into the tree. It uses a recursive strategy: if the binary tree is empty (this is the base case for the recursion), it creates a new TreeNode object whose value member is the given number, and makes it the root of the tree: if (!tree) { tree = new TreeNode(num); return; }
If, however, the binary search tree is not empty, the insert function compares num to the tree->value, the value in the root node. Depending on the outcome of this comparison, the new value is recursively inserted into the left or right subtree: if (num < tree->value) insert(tree->left, num); else insert(tree->right, num);
The entire function is given here: void IntBinaryTree::insert(TreeNode * &tree, int num) { // If the tree is empty, make a new node and make it // the root of the tree. if (!tree) { tree = new TreeNode(num); return; }
// If num is already in tree: return. if (tree->value == num) return; // The tree is not empty: insert the new node into the // left or right subtree. if (num < tree->value) insert(tree->left, num); else insert(tree->right, num); }
Note that the function is passed a reference to a pointer because the pointer passed may need to be modified by the function. This is also the reason the remove and makeDeletion functions are passed their parameters by reference.
NOTE: The shape of the tree shown in Figure 19-5 is determined by the order in which the values are inserted. The root node holds the value 5 because that was the first value inserted. By stepping through the function, you can see how the other nodes came to appear in their depicted positions.
Traversing the Tree There are three common methods for traversing a nonempty binary tree and processing the value of each node: inorder, preorder, and postorder. Each of these methods is best implemented as a recursive function. The algorithms are described as follows. • Inorder traversal 1. The node’s left subtree is traversed. 2. The node’s data is processed. 3. The node’s right subtree is traversed. • Preorder traversal 1. The node’s data is processed. 2. The node’s left subtree is traversed.
3. The node’s right subtree is traversed. • Postorder traversal 1. The node’s left subtree is traversed. 2. The node’s right subtree is traversed. 3. The node’s data is processed. T h e IntBinaryTree class can display all the values in the tree using all three of these algorithms. The algorithms are initiated by the following inline public member functions: void showInOrder(void) { displayInOrder(root); } void showPreOrder() { displayPreOrder(root); } void showPostOrder() { displayPostOrder(root); }
Each of the public member functions calls a recursive private member function and passes the root pointer as an argument. The recursive functions are very simple and straightforward: void IntBinaryTree::displayInOrder(TreeNode *tree) { if (tree) { displayInOrder(tree->left); cout value right); } } void IntBinaryTree::displayPreOrder(TreeNode *tree) { if (tree) { cout value left); displayPreOrder(tree->right); } } void IntBinaryTree::displayPostOrder(TreeNode *tree) { if (tree) { displayPostOrder(tree->left); displayPostOrder(tree->right); cout value value == num) return true; else if (num < tree->value) tree = tree->left; else tree = tree->right; } return false; }
Program 19-3 demonstrates this function. Program 19-3
Removing an Element
Removing an Element from a Binary Tree To remove an element, we first locate the node containing the element, and then delete the node. The procedure for deleting a node X depends on the number of its children. If X has no children, we first find its parent, set the parent’s child pointer that links to X to NULL, and then free the memory allocated to X. If X is the root of the tree, the procedure we have just described will not work. In that case, we simply delete X and set the pointer to the root of the tree to NULL. A procedure for deleting a nonleaf node must ensure that the subtrees that the node links to
remain as parts of the tree. The procedure varies according to whether the node being deleted has one or two children. Figure 19-6 shows a tree in which we are about to delete a node with one subtree. Figure 19-6
Figure 19-7 shows how we will link the node’s subtree with its parent. Figure 19-7
The problem is not as easily solved, however, when the node we are about to delete has two subtrees. For example, look at Figure 19-8. Figure 19-8
Obviously, we cannot attach both of the node’s subtrees to its parent, so there must be an alternative solution. One way of addressing
this problem is to attach the node’s right subtree to the parent, then find a position in the right subtree to attach the left subtree. The result is shown in Figure 19-9. Note that in attaching the left subtree to the right subtree, we must take care to preserve the binary tree’s search property. Figure 19-9
The deletion of a value from an IntBinaryTree object is accomplished by calling the public member function remove, which in turn calls the private member function of the same name. This latter function is passed (the root of) a binary search tree tree, and a value num to be removed from the tree: remove(TreeNode *&tree, int num)
The function uses a recursive strategy. If the tree is empty, it returns immediately. Otherwise, if num is less than the value stored in the root node, the function recursively removes num from the left subtree; but if num is greater, the function recursively removes num from the right subtree. The case where num is found in the root node is handed off to a function
makeDeletion(TreeNode *&tree)
Here is the code for remove: void IntBinaryTree::remove(TreeNode *&tree, int num) { if (tree == NULL) return; if (num < tree->value) remove(tree->left, num); else if (num > tree->value) remove(tree->right,num); else // We have found the node to delete. makeDeletion(tree); }
The makeDeletion function is designed to remove the root node of the binary search tree passed to it as an argument, leaving a binary search tree consisting of the remaining nodes. Let us take a look at the logic behind makeDeletion. There are a number of cases to consider: 1. The root of the tree passed to makeDeletion has no children. In this case, we delete the root node and replace the tree with NULL. 2. The root of the tree has only one child. In this case we delete the root node and replace the tree with the child of the deleted root: TreeNode *nodeToDelete = tree; if (tree->right == NULL) tree = tree->left; else if (tree->left == NULL) tree = tree->right;
Note that this code works for the first case as well. 3. The tree passed to makeDelete has two children. The deletion of the root node would leave two subtrees, and we need to do something with both of them. The strategy we adopt is to combine the two subtrees into one binary search tree, and then replace the original tree with the tree built from the combined subtrees. As shown in Figure 19-9, we can do this by attaching the left subtree of the original tree as the left subtree of the least node in the right subtree of the original tree. Here is the code for the entire function.
Program 19-4 demonstrates these functions. Program 19-4
For your reference, the entire contents of IntBinaryTree file are shown here:
Checkpoint 19.7 Describe the sequence of events in an inorder traversal. 19.8 Describe the sequence of events in a preorder traversal. 19.9 Describe the sequence of events in a postorder traversal. 19.10 Describe the steps taken in deleting a leaf node. 19.11 Describe the steps taken in deleting a node with one child. 19.12 Describe the steps taken in deleting a node with two children.
19.3 Template Considerations for Binary Search Trees CONCEPT: Binary search trees may be implemented as templates, but any data types used with them must support the , and == operators. The actual implementation of a binary tree template has been left as a Programming Challenge for students who have covered Chapters 16 and 19. When designing your template, remember that any data types stored in the binary tree must support the , and == operators. If you use the tree to store class objects, these operators must be overridden.
19.4 Tying It All Together: Genealogy Trees Say we want to write a program that will trace peoples’ ancestries and build genealogy trees. To keep track of each person’s biological parents, we might use a class such as the following: class Person { string name;
string name; Person *father; Person *mother; };
This simple class is very similar to the “node” classes we have been using to build binary trees. In maintaining genealogies, however, we are interested in recording not only a person’s ancestors, but their descendants as well. We also want to keep track of gender information to enable people using the program to distinguish between maternal and paternal relatives. The Person class, as shown above, is not adequate for these needs. We therefore modify it as shown here: enum Gender {male, female}; class Person { string name; Gender gender; vector parents; vector children; };
We now have a “node” that can have any number of children and any number of parents. Because each person can have at most two parents, the size of the parents vector will never exceed two. We can make the Person class more useful by adding a constructor and several member functions. The method Person *addChild(string name, Gender g);
creates a Person object with the specified name and gender, adds a pointer p to the created object to the children of “this” Person object, and returns p to the caller. Another method, Person *addChild(Person *p);
adds a pointer to an already created Person object to the children vector of "this" Person object. The following code shows the use of these member functions to record the fact that a father f and a mother m have a child named "Charlie": Person f("Frank", male); Person m("Mary", female);
Person m("Mary", female); Person *pChild = m.addChild("Charlie", male); f.addChild(pChild);
There is also a method void addParent(Person *p);
that is used to record the fact that one person is the parent of another. The class also has an overloaded stream operator that outputs the data in a Person object using an XML-like format. Finally, the class has several methods that can be used to access information about various members of the class objects. These additional functions can be seen in Lines 29–34 of the program listing. Program 19-5
Review Questions and Exercises Fill-in-the-Blank and Short Answer 1. The first node in a binary tree is called the __________. 2. A binary tree node’s left and right pointers point to the node’s __________. 3. A node with no children is called a(n)
__________. 4. A(n)__________ is an entire branch of the tree, from one particular node down. 5. The three common types of traversal with a binary tree are __________, __________, and __________. 6. In what ways is a binary tree similar to a linked list? 7. A ternary tree is like a binary tree, except each node in a ternary tree may have three children: a left child, a middle child, and a right child. Write an analogue of the TreeNode declaration that can be used to represent the nodes of a ternary tree. 8. Imagine a tree in which each node can have up to a hundred children. Write an analog of the TreeNode declaration that can be used to represent the nodes of such a tree. A declaration such as TreeNode { int value; TreeNode *child1; TreeNode *child2; TreeNode *child3; . . . };
that simply lists all the pointers to the hundred children is not acceptable. Algorithm Workbench 9. Propose a definition of a preorder traversal for ternary trees, and give pseudocode for accomplishing such a traversal. 10. Propose a definition of a postorder traversal for ternary trees, and give pseudocode for accomplishing such a traversal. 11. What problems do you encounter when you try to define the concept of an inorder traversal for ternary trees? 12. Assume that data is stored in a binary tree, but that unlike in the case of binary search tree, no attempt is made to maintain any sort of order in the data stored. Give an algorithm for a function search that
searches a binary tree for a particular v a l u e num and returns true or false according to whether the value num is found in the tree. 13. Give an algorithm for a function int largest(TreeNode *tree)
that takes a pointer to a root of a binary search tree as parameter and returns the largest value stored in the tree. 14. Give an algorithm for a function int smallest(TreeNode *tree)
that takes a pointer to a root of a binary search tree as parameter and returns the smallest value stored in the tree. 15. Give an algorithm for a function void increment (TreeNode *tree)
that increments the value in every node of a binary tree by one. 16. Suppose the following values are inserted into a binary search tree, in the order given: 12, 7, 9, 10, 22, 24, 30, 18, 3, 14, 20
Draw a diagram of the resulting binary tree. 17. How would the values in the tree you sketched for queston 16 be displayed in an inorder traversal? 18. How would the values in the tree you sketched for queston 16 be displayed in a preorder traversal? 19. How would the values in the tree you sketched for queston 16 be displayed in a postorder traversal? Soft Skills 20. All three binary tree traversal methods studied in this chapter traverse the left subtree before the right subtree. This is an artifact of western culture, where people are accustomed to reading material printed on a page from left to right. In a
world of increasing globalization, products and services that will be offered in foreign markets must be designed so that they can be easily altered to target different markets. Discuss with your classmates some of the ways these internationalization considerations are affecting the design of computer software and hardware today. Discuss this with a friend who has had a course in International Business, or take such a course yourself, to become better aware of some of the problems businesses face when they enter international markets.
Programming Challenges 1. Simple Binary Search Tree Class
Write a class for implementing a simple binary search tree capable of storing numbers. The class should have member functions void insert(double x) bool search(double x) void inorder(vector <double> & v )
The insert function should not use recursion directly, or indirectly by calling a recursive function. The search function should work by calling a private recursive member function bool search(double x, BtreeNode *t)
The inorder function is passed an initially empty vector v: it fills v with the inorder list of numbers stored in the binary search tree. Demonstrate the operation of the class using a suitable driver program. 2. Tree Size Modify the binary search tree created in the previous programming challenge to add a member function int size()
that
returns
the
number
of
items
(nodes)
stored in the tree. Demonstrate the correctness of the new member function with a suitable driver program.
Solving the Tree Size Problem 3. Leaf Counter Modify the binary search tree you created in the preceding programming challenges to add a member function int leafCount()
that counts and returns the number of leaf nodes in the tree. Demonstrate that the function works correctly in a suitable driver program. 4. Tree Height Modify the binary search tree created in the preceding programming challenges by adding a member function that computes and returns the height of the tree. int height()
The height of the tree is the number of levels it contains. For example, the tree shown in Figure 19-10 has three levels. Demonstrate the function with a suitable driver program. Figure 19-10
5. Tree Width Modify the binary search tree created in the preceding programming challenges by adding a member function that computes the width of the tree. int width()
The width of a tree is the largest number of nodes at the same level. Demonstrate correctness in a suitable driver program. 6. Tree Copy Constructor Design and implement a copy constructor for the binary search tree created in the preceding programming challenges. Use a driver program to demonstrate correctness. 7. Tree Assignment Operator Design and implement an overloaded assignment operator for the binary search tree created in the preceding programming challenges. 8. Employee Tree Design an EmployeeInfo class that holds the following employee information: Employee ID Number: an integer Employee Name: a string Implement a binary tree whose nodes hold an instance of the EmployeeInfo class. The nodes should be sorted on the Employee ID number.
Test the binary tree by inserting nodes with the following information.
Your program should allow the user to enter an ID number, then search the tree for the number. If the number is found, it should display the employee’s name. If the node is not found, it should display a message indicating so. 9. Cousins Building on Program 19-5, write a function that takes a pointer to a Person object and produces a list of that person’s cousins.
APPENDIX A The ASCII Character Set
APPENDIX B Operator Precedence and Associativity The operators are shown in order precedence, from highest to lowest.
of
INDEX Symbols and Numerics # symbol, 10, 28, 30, 81, 99 $ (delimiter), 853 %= (combined assignment operator), 102 % (modulus operator), 61, 62, 83, 1088 && (AND operator), 187–189 & (address operator), 620, 628 & (reference variable), 351 *= (combined assignment operator), 102, 710 * (indirection operator), 621 * (multiplication operator), 61, 83 * (pointer variable), 621 ++ (increment operator), 243–248 += (combined assignment operator), 102, 807 + (addition operator), 61, 83, 712, 716 + (concatenation operator), 120, 807 () (parentheses), 30, 84 :: (scope resolution operator), 396 ; (semicolon), 30, 36, 100