Search for notes by fellow students, in your own course and all over the country.
Browse our notes for titles which look like what you need, you can preview any of the notes via a sample of the contents. After you're happy these are the notes you're after simply pop them into your shopping cart.
Title: Data structures C++
Description: complete notes of data structure using C++by Malik. Array, linked-list, stack, queue, binary search tree, sorting algorithm, graph, standard template library. for beginner all the way to professional level with detail explanation and concept and example.
Description: complete notes of data structure using C++by Malik. Array, linked-list, stack, queue, binary search tree, sorting algorithm, graph, standard template library. for beginner all the way to professional level with detail explanation and concept and example.
Document Preview
Extracts from the notes are below, to see the PDF you'll receive please use the links above
DATA STRUCTURES
USING C++
SECOND EDITION
D
...
MALIK
Australia Brazil Japan Korea Mexico Singapore Spain United Kingdom United States
Data Structures Using C++, Second Edition
D
...
Malik
Executive Editor: Marie Lee
Acquisitions Editor: Amy Jollymore
Senior Product Manager: Alyssa Pratt
Editorial Assistant: Zina Kresin
Marketing Manager: Bryant Chrzan
ª 2010 Course Technology, Cengage Learning
ALL RIGHTS RESERVED
...
Content Project Manager: Heather Furrow
Art Director: Faith Brosnan
Image credit: ª Fancy Photography/Veer
(Royalty Free)
Cover Designer: Roycroft Design
Compositor: Integra
For product information and technology assistance, contact us at
Cengage Learning Customer & Sales Support, 1-800-354-9706
For permission to use material from this text or product, submit
all requests online at cengage
...
com
ISBN-13: 978-0-324-78201-1
ISBN-10: 0-324-78201-2
Course Technology
20 Channel Center Street
Boston, MA 02210
USA
Cengage Learning is a leading provider of customized learning solutions
with office locations around the globe, including Singapore, the United
Kingdom, Australia, Mexico, Brazil, and Japan
...
cengage
...
For your lifelong learning solutions, visit
www
...
com/coursetechnology
Purchase any of our products at your local college store or at our
preferred online store www
...
com
Some of the product names and company names used in this book
have been used for identification purposes only and may be
trademarks or registered trademarks of their respective
manufacturers and sellers
...
At
the time this book was printed, any such data was fictional and not
belonging to any real persons or companies
...
The programs in this book are for instructional purposes only
...
The author and the
publisher do not offer any warranties or representations, nor do they
accept any liabilities with respect to the programs
...
Software Engineering Principles and C++ Classes
2
...
Pointers and Array-Based Lists
131
4
...
Linked Lists
265
6
...
Stacks
395
8
...
Searching and Hashing Algorithms
497
10
...
Binary Trees and B-Trees
599
12
...
Standard Template Library (STL) II
731
APPENDIX A
Reserved Words
807
APPENDIX B
Operator Precedence
809
APPENDIX C
Character Sets
811
APPENDIX D
Operator Overloading
815
APPENDIX E
Header Files
817
vi |
Data Structures Using C++, Second Edition
APPENDIX F
Additional C++ Topics
825
APPENDIX G
C++ for Java Programmers
833
APPENDIX H
References
857
APPENDIX I
INDEX
Answers to Odd-Numbered
Exercises
859
879
TABLE OF C ONTENTS
Preface
1
xxiii
SOFTWARE ENGINEERING PRINCIPLES
AND C++ CLASSES
1
Software Life Cycle
2
Software Development Phase
3
Analysis
Design
3
3
Implementation
Testing and Debugging
5
7
Algorithm Analysis: The Big-O Notation
Classes
Constructors
8
17
21
Unified Modeling Language Diagrams
Variable (Object) Declaration
22
23
Accessing Class Members
24
Implementation of Member Functions
Reference Parameters and Class Objects (Variables)
25
30
Assignment Operator and Classes
Class Scope
31
32
Functions and Classes
Constructors and Default Parameters
32
32
Destructors
33
Structs
33
viii |
Data Structures Using C++, Second Edition
Data Abstraction, Classes, and Abstract Data Types
Programming Example: Fruit Juice Machine
38
Identifying Classes, Objects, and Operations
48
Quick Review
49
Exercises
51
Programming Exercises
2
33
57
OBJECT-ORIENTED DESIGN (OOD) AND C++
59
Inheritance
Redefining (Overriding) Member Functions of the Base Class
60
63
Constructors of Derived and Base Classes
69
Header File of a Derived Class
Multiple Inclusions of a Header File
75
76
Protected Members of a Class
Inheritance as public, protected, or private
78
78
Composition
79
Polymorphism: Operator and Function Overloading
84
Operator Overloading
85
Why Operator Overloading Is Needed
85
Operator Overloading
Syntax for Operator Functions
86
86
Overloading an Operator: Some Restrictions
The Pointer this
87
87
Friend Functions of Classes
91
Operator Functions as Member Functions and Nonmember
Functions
94
Overloading Binary Operators
Overloading the Stream Insertion (<<) and Extraction (>>)
Operators
95
98
Operator Overloading: Member Versus Nonmember
102
Programming Example: Complex Numbers
103
Function Overloading
108
Table of Contents
Templates
|
108
Function Templates
Class Templates
109
111
Header File and Implementation File of a Class Template
112
Quick Review
Exercises
115
Programming Exercises
3
113
124
POINTERS AND ARRAY-BASED LISTS
131
The Pointer Data Type and Pointer Variables
132
Declaring Pointer Variables
132
Address of Operator (&)
Dereferencing Operator (*)
133
133
Pointers and Classes
Initializing Pointer Variables
137
138
Dynamic Variables
138
Operator new
Operator delete
138
139
Operations on Pointer Variables
Dynamic Arrays
145
147
Array Name: A Constant Pointer
148
Functions and Pointers
Pointers and Function Return Values
149
150
Dynamic Two-Dimensional Arrays
Shallow Vs
...
Designed for the CS2 C++ course,
this text will provide a breath of fresh air to you and your students
...
This text is a
culmination and development of my classroom notes throughout more than 50 semesters of
teaching successful programming and data structures to computer science students
...
The approach taken in this book to
present the material is similar to the one used in the CS1 book and therefore driven by the
students’ demand for clarity and readability
...
Most of the examples in this book resulted from student
interaction in the classroom
...
However, if you need to review these
concepts or you have taken Java as a first program language, you will find the relevant material
in Appendix G
...
In addition, some adequate mathematics
background such as college algebra is required
...
•
To create generic code to process data in linked lists, Chapter 5 uses the concept of
abstract classes to capture the basic properties of linked lists and then derive two
separate classes to process unordered and ordered lists
...
In Chapter 3, a section on creating and manipulating dynamic two-dimensional
arrays, a section on virtual functions, and a section on abstract classes is included
...
•
In Chapter 9, the discussion of hashing is expanded with additional examples illustrating how to resolve collisions
...
•
Throughout the book, new exercises and programming exercises have been added
...
Chapter 12, on graphs, contains a new section on how to find Euler circuits in a graph
...
These changes were implemented based on comments from the reviewers of the second
proposal and readers of the first edition
...
The programming examples given in this book effectively
use OOD techniques to solve and program a particular problem
...
After describing the life cycle of a
software, this chapter discusses why algorithm analysis is important and introduces the Big-O
notation used in algorithm analysis
...
Encapsulation in C++ is achieved via the use of classes
...
If you are familiar with how to create
and use your own classes, you can skip this section
...
Chapter 2 continues with the principles of OOD and discusses inheritance and two types of
polymorphism
...
The three basic data types in C++ are simple, structured, and pointers
...
The
structured data type class is introduced in Chapter 1
...
This chapter also discusses the relationship between pointers
and classes
...
Chapter 3 also discusses virtual functions and abstract classes
...
Among other things, the STL
provides code to process lists (contiguous or linked), stacks, and queues
...
In particular, this chapter discusses the sequence containers vector and
Preface to Second Edition |
xxv
deque
...
Chapter 5 discusses linked lists in detail, by first discussing the basic properties of linked lists
such as item insertion and deletion and how to construct a linked list
...
Doubly linked lists are also
discussed in some detail
...
This chapter also discusses the STL class list
...
Chapters 7 and 8 discuss stacks and queues in detail
...
The programming code developed in these chapters is generic
...
After analyzing the sequential search
algorithm, it discusses the binary search algorithm and provides a brief analysis of this
algorithm
...
Sorting algorithms such as selection sort, insertion sort, Shellsort, quicksort, mergesort, and
heapsort are introduced and discussed in Chapter 10
...
Chapter 12 introduces graphs and discusses graph algorithms such as shortest
path, minimum spanning tree, topological sorting, and how to find Euler circuits in a graph
...
In particular, it
introduces the STL associative containers and algorithms
...
Appendix B shows the precedence and associativity of the C++ operators
...
Appendix D lists the C++ operators that can be overloaded
...
Appendix F contains the detailed analysis of the
insertion sort and quicksort algorithms
...
One of its objectives is
to provide a quick review of the basic elements of C++
...
Therefore, if you have taken Java as a first programming language, Appendix G helps familiarize
you with these basic elements of C++
...
Appendix I gives the
answers to odd-numbered exercises in the text
...
To do so, the book discusses data structures such as
linked lists, stacks, queues, and binary trees
...
However, our emphasis is to
teach you how to develop your own code
...
Chapter 4 of this book introduces STL
...
The book can, therefore, be used in various ways
...
Chapter 6 discusses recursion
...
If you read Chapter 6 after these chapters, then you can skip the section ‘‘Removing
Recursion’’ in Chapter 7, and read this section after reading Chapter 6
...
Therefore, we recommend that you should study Chapter 6 before Chapter 9
...
Chapter 1
Chapter 2
Chapter 3
Chapter 4
Chapter 5
Chapter 6
Chapter 7
Chapter 8
Chapter 9
Chapter 10
Chapter 11
Chapter 13
Chapter 12
A dotted arrow means that the chapter is not essential to study the
following chapter
...
From beginning to end, the
concepts are introduced at an appropriate pace
...
The writing style of this book is simple and
straightforward
...
Here is a brief summary of the
various pedagogical features in each chapter:
•
Learning objectives offer an outline of C++ programming concepts that will be
discussed in detail within the chapter
...
•
•
Numbered Examples within each chapter illustrate the key concepts with relevant code
...
•
Programming Exercises challenge students to write C++ programs with a specified
outcome
...
The
book contains over 295 figures
...
These
examples contain the accurate, concrete stages of Input, Output, Problem Analysis
and Algorithm Design, and a Program Listing
...
Exercises further reinforce learning and ensure that students have, in fact, learned the
material
...
Before introducing a key
concept, we explain why certain elements are necessary
...
Each chapter contains two types of programs
...
Each line of the programming code in these
examples is numbered
...
The rationale behind each line is discussed in detail
...
These Programming Examples form the backbone of the book
...
Beginning with Problem Analysis, the
Programming Example is then followed by Algorithm Design
...
In addition to teaching problem-solving techniques, these detailed
programs show the user how to implement concepts in an actual C++ program
...
Quick Review sections at the end of each chapter reinforce learning
...
Many readers refer to the Quick Review as a way
to quickly review the chapter before an exam
...
Programs can be compiled with various compilers such as Microsoft Visual C++ 2008
...
All of the teaching tools available with this book are provided to the instructor on a
single CD-ROM
...
ExamView includes hundreds of questions that correspond to the topics covered in this text,
enabling students to generate detailed study guides that include page references for further
review
...
PowerPoint Presentations
This book comes with Microsoft PowerPoint slides for each chapter
...
Instructors can modify slides or add their own slides to
tailor their presentations
...
For more
information on how to bring distance learning to your course, contact your local Cengage
Learning sales representative
...
cengage
...
If an input file is needed to run a program, it is included
with the source code
...
cengage
...
If an input file is needed to run a
programming exercise, it is included with the solution file
...
Additionally, I
express thanks to the reviewers of the proposal package: Ted Krovetz, California State
University; Kenneth Lambert, Washington and Lee University; Stephen Scott, University
of Nebraska; and Deborah Silver, Rutgers, The State University of New Jersey
...
Next, I express thanks to Amy Jollymore, Acquisitions
Editor, for recognizing the importance and uniqueness of this project
...
I extend my
sincere thanks to Alyssa, as well as to Content Project Manager Heather Furrow
...
I would like to thank Chris Scriver and Serge Palladino of QA department of Cengage
Learning for patiently and carefully proofreading the text, testing the code, and discovering
typos and errors
...
Finally, I
would like to thank my wife Sadhana and my daughter Shelly
...
I welcome any comments concerning the text
...
edu
...
S
...
Learn about software engineering principles
...
Become aware of structured design and object-oriented design programming methodologies
...
Become aware of private, protected, and public members of a class
...
Become aware of Unified Modeling Language (UML) notation
...
Become aware of abstract data type (ADT)
...
Software are
computer programs designed to accomplish a specific task
...
This book, for example, was created with the help of a
´
´
word processor
...
Instead, they use word processing software to complete their term papers
...
Powerful, yet easy-to-use software has drastically changed the way we live and communicate
...
With the help of computers and the software running on them, you
can send letters to, and receive letters from, loved ones within seconds
...
You can watch how stocks perform in real time,
and instantly buy and sell them
...
It is the software that enables you to do things
that were, perhaps, fiction a few years ago
...
From the time a software program is conceived until it is delivered, it goes through
several phases
...
Most colleges and universities offer a course in software engineering
...
However, this chapter briefly describes some of the basic software engineering principles
that can simplify program design
...
The three fundamental stages through which a
program goes are development, use, and maintenance
...
The new program is created in the
software development stage
...
Once the program is considered complete, it is released for the user to use
...
The problems and/or ideas for improvements are conveyed to the software
developer, and the program goes through the maintenance phase
...
If there are serious/numerous changes, typically, a new
version of the program is created and released for use
...
Software Development Phase
| 3
The software development phase is the first and perhaps most important phase of the
software life cycle
...
The next section describes this phase
...
Analysis
Analyzing the problem is the first and most important step
...
Understand the problem requirements
...
Suppose that you need to develop a program to make an automated teller
machine (ATM) operational
...
Here, you determine the necessary operations performed by the machine, such as withdraw money, deposit
money, transfer money, check account balance, and so on
...
To make it user-friendly, you must understand their requirements and
add any necessary operations
...
That is, you need to look at sample data
...
• If the problem is complex, divide the problem into subproblems, analyze
each subproblem, and understand each subproblem’s requirements
...
If you broke the problem into subproblems, you need to design an
algorithm for each subproblem
...
STRUCTURED DESIGN
Dividing a problem into smaller subproblems is called structured design
...
In structured design, the problem is divided into smaller problems
...
The solutions of all the subproblems are then combined to solve the overall problem
...
OBJECT-ORIENTED DESIGN
In object-oriented design (OOD), the first step in the problem-solving process is to
identify the components called objects, which form the basis of the solution, and
determine how these objects interact with one another
...
The
two main objects in this problem are the video and the customer
...
For example, for a video object, the
data might include the movie name, starring actors, producer, production company,
number of copies in stock, and so on
...
This illustrates that each object consists of data and operations on that data
...
In OOD, the final program is
a collection of interacting objects
...
You will learn about the
many advantages of OOD in later chapters
...
How classes
are implemented in C++ is described later in this chapter
...
In object-oriented design, you decide what classes you need and their relevant data
members and member functions
...
Software Development Phase
| 5
Implementation
In the implementation phase, you write and compile programming code to implement the
classes and functions that were discovered in the design phase
...
It contains many case studies—called Programming
Examples—to solve real-world problems
...
Some
functions are part of the main program; others are used to implement various operations
on objects
...
To use a function, the user needs to know only how to use the function and
what the function does
...
Let us illustrate this with the help of the
following example
...
The conversion formula is 1 inch ¼ 2
...
The
following function accomplishes the job:
double inchesToCentimeters(double inches)
{
if (inches < 0)
{
cerr << "The given measurement must be nonnegative
...
0;
}
else
return 2
...
Unlike the
object cout (whose output first goes to the buffer), the output of cerr is immediately
sent to the standard error stream, which is usually the screen
...
0; otherwise, the function
returns the equivalent length in centimeters
...
However, the user must know that in order to get the
valid answer, the input must be a nonnegative number
...
0
...
Precondition: A statement specifying the condition(s) that must be true before the
function is called
...
The precondition and postcondition for the function inchesToCentimeters can be
specified as follows:
//Precondition: The value of inches must be nonnegative
...
0; otherwise, the function returns the
//
equivalent length in centimeters
...
" << endl;
return -1
...
54 * inches;
}
In certain situations, you could use C++’s assert statement to validate the input
...
//Postcondition: If the value of inches is < 0, the function
//
terminates; otherwise, the function returns the
//
equivalent length in centimeters
...
54 * inches;
}
However, if the assert statement fails, the entire program will terminate, which
might be appropriate if the remainder of the program depends on the execution of
the function
...
To use the assert function, you need to include the header file cassert in your
program
...
This directive must be placed before the statement #include
...
Because the user of a function need not be concerned with the details of the
function, the preconditions and postconditions are specified with the function prototype
...
//Postcondition: If the value of inches is < 0, the function
//
returns -1
...
As another example, to use a function that searches a list for a specific item, the list must
exist before the function is called
...
bool search(int list[], int listLength, int searchItem);
//Precondition: The list must exist
...
Testing and Debugging
The term testing refers to testing the correctness of the program; that is, making sure that
the program does what it is supposed to do
...
Once a function and/or an algorithm is written, the next step is to verify that it works
properly
...
Therefore, to increase the reliability of the program, errors must be discovered and fixed
before the program is released to the user
...
However, for large and complex programs, this technique
alone might not be enough because errors can be made in the proof
...
The program is run through a
series of specific tests, called test cases, in an attempt to find problems
...
Because a test case can be repeated several times, it must be properly
documented
...
It is, therefore,
impractical (although possible) to create test cases for all possible inputs
...
Clearly, it is not possible to create a test case
for each integer
...
An equivalence category is a set of input values that are likely to produce the
same output
...
In this case, you can
form two equivalence categories—one consisting of negative numbers and the other
consisting of nonnegative numbers
...
In black-box
testing, you do not know the internal working of the algorithm or function
...
Black-box testing is based on inputs and outputs
...
If a function works for one input in the equivalence category, it is
expected to work for other inputs in the same category
...
In black-box testing, the function is
tested on values that surround and fall on the boundaries, called boundary values, as
well as general values from the equivalence categories
...
White-box testing relies on the internal structure and implementation of a function or
algorithm
...
Suppose that you want to ensure whether an if statement works
properly
...
Loops and other
structures can be tested similarly
...
Usually, there are various ways to
design a particular algorithm
...
Let us consider the following problem
...
They have hired
extra delivery people to deliver the packages on time
...
Suppose that 50 packages are to be delivered to 50 different houses
...
(See Figure 1-1, in which each dot represents a house and the distance between
houses is 1 mile
...
Gift shop and each dot representing a house
To deliver 50 packages to their destinations, one of the drivers picks up all 50 packages,
drives one mile to the first house and delivers the first package
...
Figure 1-2 illustrates this delivery scheme
...
Package delivering scheme
It now follows that using this scheme, the distance driven by the driver to deliver the
packages is:
1 þ 1 þ 1 þ ::: þ 1 ¼ 50 miles
Therefore, the total distance traveled by the driver to deliver the packages and then
getting back to the shop is:
50 þ 50 ¼ 100 miles
Another driver has a similar route to deliver another set of 50 packages
...
Next,
the driver picks up the second package, drives 2 miles, delivers the second package, and
then returns to the shop
...
Figure 1-3 illustrates this delivery scheme
...
Another package delivery scheme
The driver delivers only one package at a time
...
Using this scheme, the total
distance traveled by this driver to deliver the packages and then getting back to the store is:
2 Á ð1 þ 2 þ 3 þ
...
If the packages are delivered using the
first scheme, the following equation gives the total distance traveled:
1 þ 1 þ
...
þ nÞ ¼ 2 Á ðnðn þ 1Þ=2Þ ¼ n2 þ n
ð1-2Þ
10 |
Chapter 1: Software Engineering Principles and C++ Classes
In Equation (1-1), we say that the distance traveled is a function of n
...
In this equation, for large values of n, we will find that the term consisting
of n2 will become the dominant term and the term containing n will be negligible
...
Table 1-1 evaluates Equations
(1-1) and (1-2) for certain values of n
...
)
TABLE 1-1
Various values of n, 2n, n2, and n2 + n
n
2n
n2
n2 + n
1
2
1
2
10
20
100
110
100
200
10,000
10,100
1000
2000
1,000,000
1,001,000
10,000
20,000
100,000,000
100,010,000
While analyzing a particular algorithm, we usually count the number of operations
performed by the algorithm
...
This is because a particular algorithm can be
implemented on a variety of computers and the speed of the computer can affect the
execution time
...
Let us consider the following examples
...
(Assume that all variables are properly declared
...
Either Line 4
or Line 6 executes
...
In this algorithm, the number of operations executed is fixed
...
Similarly,
there are nine or eight operations after the while loop, depending on whether Line 11 or
Line 13 executes
...
Thus, Lines 5 through 8 have five operations
...
One extra operation is also executed at Line 5 to
terminate the loop
...
If the while loop executes 10 times, the total number of operations executed is:
10 Á 5 þ 1 þ 5 þ 9 or 10 Á 5 þ 1 þ 5 þ 8
that is,
10 Á 5 þ 15 or 10 Á 5 þ 14
We can generalize it to the case when the while loop executes n times
...
12 |
Chapter 1: Software Engineering Principles and C++ Classes
Usually, in an algorithm, certain operations are dominant
...
Similarly, in a search
algorithm, because the search item is compared with the items in the list, the dominant
operations would be comparison, that is, the relational operation
...
For another example, suppose
that we write a program to multiply matrices
...
Because multiplication takes more computer time to execute,
to analyze a matrix multiplication algorithm, we count the number of multiplications
...
If there are various algorithms to accomplish a particular task, the algorithm
analysis allows the programmer to choose between various options
...
Suppose that you want to determine whether an item is in a list
...
To determine whether the item is in the
list, there are various algorithms, as you will see in Chapter 9
...
Therefore, the performance of the
algorithm depends on the number of comparisons
...
Suppose that,
on a particular computer, it takes c units of computer time to execute one operation
...
Clearly, the
constant c depends on the speed of the computer and, therefore, varies from computer to
computer
...
If we know how the function f(n) grows as the size of the problem grows, we can
determine the efficiency of the algorithm
...
TABLE 1-2
Growth rates of various functions
n
log2n
n log2n
n2
2n
1
0
0
1
2
2
1
2
2
4
4
2
8
16
16
8
3
24
64
256
16
4
64
256
65,536
32
5
160
1024
4,294,967,296
Algorithm Analysis: The Big-O Notation
|
13
Table 1-2 shows how certain functions grow as the parameter n, that is, the problem size,
grows
...
From Table 1-2, it follows that if the
number of basic operations is a function of f(n) ¼ n2, the number of basic operations is
quadrupled
...
However, if the number of operations is a function of f(n) ¼
log2n, the change in the number of basic operations is insignificant
...
Table 1-3
shows the time that the computer takes to execute f(n) basic operations
...
01ms
0
...
033ms
0
...
02ms
0
...
086ms
0
...
03ms
0
...
147ms
0
...
04ms
0
...
213ms
1
...
3min
50
0
...
006ms
0
...
5ms
13 days
100
0
...
007ms
0
...
00ms
0
...
966ms
1ms
10,000
10ms
0
...
10ms
0
...
67ms
10s
1,000,000
1 ms
0
...
93ms
16
...
01s
0
...
23s
1
...
10s
0
...
66s
115
...
1
14 |
Chapter 1: Software Engineering Principles and C++ Classes
Figure 1-4 shows the growth rate of functions in Table 1-3
...
That is, we develop a notation that is useful in describing the
behavior of the algorithm, which gives us the most useful information about the algorithm
...
Let f be a function of n
...
Consider the functions g(n) ¼ n2 and f(n) ¼ n2 + 4n + 20
...
Consider Table 1-4
...
For large values of n, we can predict the
behavior of f(n) by looking at the behavior of g(n)
...
Let f and g be real-valued functions
...
Definition: We say that f(n) is Big-O of g(n), written f(n) ¼ O(g(n)), if there exists
positive constants c and n0 such that f(n) cg(n) for all n ! n0
...
Note that f is a constant
function
...
Let c ¼ a, n0 ¼ a, and g(n) ¼ 1
...
cg(n) for all n ! n0
...
EXAMPLE 1-4
Let f(n) ¼ 2n + 5, n ! 0
...
Let c ¼ 3, n0 ¼ 5, and g(n) ¼ n
...
cg(n) for all n ! 5
...
Note that 3n + 2
that
f(n) ¼ n2+ 3n + 2
n 2+ n 2
Let c ¼ 2 and n0 ¼ 4
...
This implies
2n2¼ 2g(n) for all n ! 4
...
It now follows that f(n) ¼ O(g(n)) ¼ O(n2)
...
Here we state the theorem without proof
...
Then f(n) ¼
O(nm)
...
EXAMPLE 1-6
In the following, f(n) is a nonnegative real-valued function
...
f (n) ¼ n2 + 5n + 1
f (n) ¼ 4n6 + 3n3 + 1
f (n) ¼ 10n7 + 23
f (n) ¼ 6n15
f (n)
f (n)
f (n)
f (n)
f (n)
¼
¼
¼
¼
¼
O (n)
O (n2)
O (n6)
O (n7)
O (n15)
EXAMPLE 1-7
Suppose that f(n) ¼ 2log2n + a, where a is a real number
...
EXAMPLE 1-8
Consider the following code, where m and n are int variables and their values are
nonnegative:
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
cout << i * j << endl;
//Line 1
//Line 2
//Line 3
This code contains nested for loops
...
For each iteration of the outer loop, the inner loop, at Line 2, executes n times
...
It follows that the
total number of iterations of the nested for loop is mn
...
Therefore, this algorithm is O(mn)
...
Classes
|
17
Table 1-5 shows some common Big-O functions that appear in the algorithm analysis
...
TABLE 1-5
Some Big-O functions that appear in algorithm analysis
Function g (n)
Growth rate of f (n)
g (n) ¼ 1
The growth rate is constant and so does not depend on n, the size of the
problem
...
Because a logarithm function grows
slowly, the growth rate of the function f is also slow
...
The growth rate of f is directly proportional to the
size of the problem
...
g (n) ¼ n2
The growth rate of such functions increases rapidly with the size of the
problem
...
g (n) ¼ 2n
The growth rate is exponential
...
It can be shown that
O (1)
O (log2n)
O (n)
O (nlog2n)
O (n2)
O (2n)
...
If you are familiar with how classes are implemented in C++, you can skip this section
...
In
C++, the mechanism that allows you to combine data and the operations on that data in a
single unit is called a class
...
The
components of a class are called the members of the class
...
That is, a
member of a class can be either a variable (to store data) or a function
...
Furthermore, in the definition of the class, you cannot initialize
a variable when you declare it
...
• If a member of a class is a function, it can (directly) access any member of
the class—data members and function members
...
The only obvious
condition is that you must declare an identifier before you can use it
...
It
announces the declaration of a class
...
The
semicolon is part of the syntax
...
The members of a class are classified into three categories: private, public, and
protected, called member access specifiers
...
Following are some facts about private and public members of a class:
•
•
•
•
By default, all members of a class are private
...
A public member is accessible outside the class
...
In C++, private, protected, and public are reserved words
...
Furthermore, suppose that the time is represented as a set of three integers: one
to represent the hours, one to represent the minutes, and one to represent the seconds
...
2
...
4
...
6
...
Set the time
...
Print the time
...
Increment the time by one minute
...
Compare two times for equality
...
Some members of the class clockType will be private; others will be public
...
The general rule is that any member that needs to be accessed
outside the class is declared public; any member that should not be accessed directly by
the user should be declared private
...
Therefore, the members that set the time and print the time should be
declared public
...
On the other hand, to control the direct manipulation of the data
members hr, min, and sec, we will declare these data members private
...
The following statements define the class clockType:
class clockType
{
public:
void setTime(int hours, int minutes, int seconds);
//Function to set the time
//The time is set according to the parameters
//Postcondition: hr = hours; min = minutes; sec = seconds
//
The function checks whether the values of hours,
//
minutes, and seconds are valid
...
void getTime(int& hours, int& minutes, int& seconds) const;
//Function to return the time
//Postcondition: hours = hr; minutes = min; seconds = sec
void printTime() const;
//Function to print the time
//Postcondition: Time is printed in the form hh:mm:ss
...
//
If the before-increment time is 23:59:59, the time
//
is reset to 00:00:00
...
//
If the before-increment time is 23:59:53, the time
//
is reset to 00:00:53
...
//
If the before-increment time is 23:45:53, the time
//
is reset to 00:45:53
...
It has three data members: hr, min, and sec
...
The seven function members—setTime, getTime, printTime,
incrementSeconds, incrementMinutes, incrementHours, and
equalTime—can directly access the data members (hr, min, and sec)
...
In the function equalTime, the parameter otherClock is a constant
reference parameter
...
You could
have declared otherClock as a value parameter, but that would require
otherClock to copy the value of the actual parameter, which could result
in poor performance
...
)
The word const at the end of the member functions getTime,
printTime, and equalTime specifies that these functions cannot modify
the data members of a variable of type clockType
...
The only thing
you need to remember is that, by default, all members of a class are private
...
If you decide to declare
the private members after the public members (as is done in the case of clockType),
you must use the private label to begin the declaration of the private members
...
However, a function member can also be private
...
Similarly, a data member of a class can also be public
...
You will learn how to write them shortly
...
The
given values are passed as parameters to the function setTime
...
The function incrementSeconds increments
the time by one second, the function incrementMinutes increments the time by one
minute, the function incrementHours increments the time by one hour, and the function
equalTime compares the two times for equality
...
Therefore, when
an object is instantiated, there is no guarantee that the data members of the object will be
initialized
...
There are two types of constructors: with parameters and without parameters
...
Constructors have the following properties:
•
•
•
•
•
•
The name of a constructor is the same as the name of the class
...
That is, it is
neither a value-returning function nor a void function
...
However, all constructors of
a class have the same name
...
That is, either they have a different
number of formal parameters or, if the number of formal parameters is
the same, the data type of the formal parameters, in the order you list,
must differ in at least one position
...
Because they have no types, they cannot be called like other functions
...
1
22 |
Chapter 1: Software Engineering Principles and C++ Classes
Let us extend the definition of the class clockType by including two constructors:
class clockType
{
public:
//Place the function prototypes of the functions setTime,
//getTime, printTime, incrementSeconds, incrementMinutes,
//incrementHours, and equalTime as described earlier, here
...
//Postconditions: hr = hours; min = minutes; sec = seconds
//
The constructor checks whether the values of hours,
//
minutes, and seconds are valid
...
clockType();
//Default constructor with parameters
//The time is set to 00:00:00
...
For example, Figure 1-5 shows the UML class
diagram of the class clockType
...
The middle box contains the data members and
their data types
...
A + (plus) sign in front of a member indicates that this member is
a public member; a – (minus) sign indicates that this is a private member
...
Variable (Object) Declaration
Once a class is defined, you can declare variables of that type
...
To help you become familiar with this
terminology, from now on we will use the term class object, or simply object, for a class variable
...
Therefore, when you declare a class object, either the default constructor
executes or the constructor with parameters executes
...
In this case, the default constructor
executes and the instance variables of myClock are initialized to 0
...
In
fact, if you accidentally include the empty parentheses, the compiler generates a syntax error
message
...
);
where each of argument1, argument2, and so on is either a variable or an expression
...
• If the type of the arguments does not match the formal parameters of any
constructor (in the order given), C++ uses type conversion and looks for
the best match
...
Any ambiguity will result
in a compile-time error
...
Here, we are passing
three values of type int, which matches the type of the formal parameters of the
constructor with a parameter
...
Consider the following statements that declare two objects of type clockType:
clockType myClock(8, 12, 30);
clockType yourClock(12, 35, 45);
Each object has 10 members: seven member functions and three instance variables
...
In actuality, memory is allocated only for the instance variables of each class object
...
Accessing Class Members
Once an object of a class is declared, it can access the members of the class
...
memberName
In C++, the dot,
...
The class members that a class object can access depend on where the object is declared
...
(We
will elaborate on this when we write the definition of the member
function equalTime of the class clockType in the section ‘‘Implementation of Member Functions,’’ later in this chapter
...
Example 1-10 illustrates how to access the members of a class
...
setTime(5, 2, 30);
myClock
...
equalTime(yourClock))
...
...
In the first statement, myClock
...
The values 5, 2, and 30 are passed as parameters to the function setTime, and
the function uses these values to set the values of the three instance variables hr, min, and
sec of myClock to 5, 2, and 30, respectively
...
In the third statement, the member function equalTime executes and compares the three
instance variables of myClock to the corresponding instance variables of yourClock
...
So it needs one more object, which in
this case is yourClock, to compare
...
The objects myClock and yourClock can access only public members of the class
...
hr = 10;
myClock
...
min;
//illegal
//illegal
Implementation of Member Functions
When we defined the class clockType, we included only the function prototype for
the member functions
...
One way to implement these functions is to provide the function definition
rather than the function prototype in the class itself
...
Another reason for providing function
prototypes instead of function definitions relates to information hiding; that is, we want
to hide the details of the operations on the data
...
That
is, we will write the definitions of the functions setTime, getTime, printTime,
incrementSeconds, equalTime, and so on
...
To reference these identifiers, we use the scope resolution operator, :: (double colon)
...
For example, the definition
of the function setTime is as follows:
void clockType::setTime(int hours, int minutes, int seconds)
{
if (0 <= hours && hours < 24)
hr = hours;
else
hr = 0;
if (0 <= minutes && minutes < 60)
min = minutes;
else
min = 0;
}
if (0 <= seconds && seconds < 60)
sec = seconds;
else
sec = 0;
Note that the definition of the function setTime checks for the valid values of hours,
minutes, and seconds
...
Suppose that myClock is an object of type clockType (as declared previously)
...
Consider the following statement:
myClock
...
setTime(3, 48, 52);, setTime is accessed by the object
myClock
...
Thus, the values 3,
48, and 52, which are passed as parameters in the preceding statement, are assigned to the
three instance variables of myClock by the function setTime (see the body of the
function setTime)
...
myClock
3
min
48
sec
FIGURE 1-6
hr
52
Object myClock after the statement myClock
...
The definitions of these functions are simple and easy to follow
...
The function equalTime has the following definition:
bool clockType::equalTime(const clockType& otherClock) const
{
return (hr == otherClock
...
min
&& sec == otherClock
...
Suppose that myClock and yourClock are objects of type clockType, as declared previously
...
myClock
sec
yourClock
14
min
FIGURE 1-7
hr
hr
14
8
min
25
25
sec
54
Objects myClock and yourClock
Consider the following statement:
if (myClock
...
...
In the expression
myClock
...
Because otherClock is a
reference parameter, the address of the actual parameter yourClock is passed to the
formal parameter otherClock, as shown in Figure 1-8
...
In other words, when the body of the function equalTime executes, the
value of otherClock
...
min is 25, and the value of
otherClock
...
The function equalTime is a member of myClock
...
Therefore, the member hr
of myClock is compared with otherClock
...
min, and the member sec of myClock is compared with
otherClock
...
Once again, from the definition of the function equalTime, it is clear why this function
has only one parameter
...
Notice that within the
definition of this function, the object otherClock accesses the instance variables hr, min,
and sec
...
So is there any violation? The
answer is no
...
Moreover, otherClock is an object of type
clockType
...
The same is true for any member function of a class
...
This definition of the class clockType includes two constructors: one with three
parameters and one without any parameters
...
clockType::clockType()
{
hr = 0;
min = 0;
sec = 0;
}
//default constructor
clockType::clockType(int hours, int minutes, int seconds)
{
if (0 <= hours && hours < 24)
hr = hours;
else
hr = 0;
if (0 <= minutes && minutes < 60)
min = minutes;
else
min = 0;
1
30 |
}
Chapter 1: Software Engineering Principles and C++ Classes
if (0 <= seconds && seconds < 60)
sec = seconds;
else
sec = 0;
From the definitions of these constructors, it follows that the default constructor sets the
three instance variables—hr, min, and sec—to 0
...
Moreover, we can write the definition of the constructor with parameters by calling
the function setTime, as follows:
clockType::clockType(int hours, int minutes, int seconds)
{
setTime(hours, minutes, seconds);
}
Once a class is properly defined and implemented, it can be used in a program
...
When you declare objects of the class clockType, every object has its own copy of the
instance variables hr, min, and sec
...
Reference Parameters and Class Objects (Variables)
Recall that when a variable is passed by value, the formal parameter copies the value
of the actual parameter
...
As a parameter, a class object can be passed
by value
...
The corresponding formal parameter
then receives a copy of the data of the variable
...
This operation might require, in addition to a large amount of storage
space, a considerable amount of computer time to copy the value of the actual parameter
into the formal parameter
...
Therefore, an efficient way to pass a variable as a
parameter is by reference
...
Sometimes, however, you do not
want the function to be able to change the values of the instance variables
...
As an example, consider
the following function definition:
Classes
|
31
void testTime(const clockType& otherClock)
{
clockType dClock;
...
...
The parameter
otherClock is declared using the keyword const
...
For
example, after the following statement executes, the value of myClock will not be altered:
testTime(myClock);
Generally, if you want to declare a class object as a value parameter, you declare it as a
reference parameter using the keyword const, as described previously
...
That is, you can use an assignment
statement to change the value of the formal parameter (which, of course, would have
no effect on the actual parameter)
...
Therefore, within
the definition of the function testTime, you cannot alter the value of otherClock
...
setTime(5, 34, 56); //illegal
otherClock = dClock;
//illegal
BUILT-IN OPERATIONS ON CLASSES
The two built-in operations that are defined for class objects are member access (
...
You have seen how to access an individual member of a class by using the
name of the class object, then a dot, and then the member name
...
Assignment Operator and Classes
Suppose that myClock and yourClock are variables of type clockType as defined
previously
...
That is, the value of yourClock
...
hr; the value of yourClock
...
min; and the value
of yourClock
...
sec
...
Therefore, an assignment statement performs a memberwise copy
...
Also, you can declare an array of class objects
...
A member of a class is local to the class
...
Functions and Classes
The following rules describe the relationship between functions and classes:
•
Class objects can be passed as parameters to functions and returned as
function values
...
• If a class object is passed by value, the contents of the instance variables of
the actual parameter are copied into the corresponding instance variables
of the formal parameter
...
In such a case, the rules for declaring
formal parameters are the same as those for declaring default formal parameters in a
function
...
Using the rules for defining
default parameters, in the definition of the class clockType, you can replace both
constructors using the following statement
...
)
clockType clockType(int = 0, int = 0, int = 0); //Line 1
In the implementation file, the definition of this constructor is the same as the definition
of the constructor with parameters
...
The data member hr of clock2 is
initialized to 5, and the data members min and sec of clock2 are initialized to 0
...
The data
member hr of clock4 is initialized to 7, the data member min of clock4 is initialized
to 34, and the data member sec of clock4 is initialized to 18
...
Destructors
Like constructors, destructors are also functions
...
That is, it is neither a value-returning function nor a void function
...
The
name of a destructor is the tilde character ($), followed by the name of the class
...
Structs
Structs are a special type of classes
...
In C++, you define structs by
using the reserved word struct
...
A struct is
defined just like a class
...
Most people are not concerned with the complexity of how the engine works
...
Our daily life has other similar examples
...
Separating the design details (that is, how the car’s engine works) from its use is called
abstraction
...
Thus, abstraction is the process of separating the logical properties from
the implementation details
...
We have an abstract view of what the
engine does, but are not interested in the engine’s actual implementation
...
Earlier sections of this chapter defined a data type
clockType
...
2
...
4
...
6
...
Set the time
...
Print the time
...
Increment the time by one minute
...
Compare two times to see whether they are equal
...
Data abstraction is defined as a process of separating the logical properties of the data from
its implementation
...
Abstract data type (ADT): A data type that separates the logical properties from the
implementation details
...
Following these conventions, we can define the
clockType ADT as follows:
dataTypeName
clockType
domain
Each clockType value is a time of day in the form of hours,
minutes, and seconds
...
Return the time
...
Increment the time by
Increment the time by
Increment the time by
Compare the two times
one second
...
one hour
...
To implement an ADT, you must represent the data and write algorithms to perform the
operations
...
Furthermore, our
definition of a class consisted only of the specifications of the operations; functions to
Data Abstraction, Classes, and Abstract Data Types |
35
implement the operations were written separately
...
In fact, in C++, classes were specifically designed
to handle ADTs
...
Because all values in a list are of the
same type, a convenient way to represent and process a list is to use an array
...
operations
Check to see whether the list is empty
...
Search the list for a given item
...
Insert an item in the list
...
Print the list
...
To be specific, suppose that the list is a set
of elements of the type int
...
//Precondition: The list must exist
...
bool isFull();
//Function to determine whether the list is full
...
//Postcondition: Returns true if the list is full,
//
false otherwise
...
//Postcondition: If searchItem is in the list, returns its
//
index, that is, its position in the list;
//
otherwise, it returns -1
...
//Precondition: The list must exist and must not be full
...
1
36 |
Chapter 1: Software Engineering Principles and C++ Classes
void remove(int removeItem);
//Function to delete removeItem from the list
...
void printList();
//Function to output the elements of the list
...
//Postcondition: The elements of the list are
//
printed on the standard output device
...
EXAMPLE 1-12
The most common attributes of a person are the person’s first name and last name
...
The
following statements define a class with these properties
...
S
...
//************************************************************
#include
using namespace std;
class personType
{
public:
void print() const;
//Function to output the first name and last name
//in the form firstName lastName
...
//Postcondition: firstName = first; lastName = last
Data Abstraction, Classes, and Abstract Data Types |
string getFirstName() const;
//Function to return the first name
...
string getLastName() const;
//Function to return the last name
...
personType();
//Default constructor
//Sets firstName and lastName to null strings
...
//Sets firstName and lastName according to the parameters
...
personType
–firstName: string
–lastName: string
+print(): void
+setName(string, string): void
+getFirstName() const: string
+getLastName() const: string
+personType()
+personType(string, string)
FIGURE 1-9
UML class diagram of the class personType
We now give the definitions of the member functions of the class personType
...
The machine dispenses apple juice, orange
juice, mango lassi, and fruit punch in recyclable containers
...
The program should do the following:
1
...
2
...
3
...
4
...
5
...
Input
The item selection and the cost of the item
...
Programming Example: Fruit Juice Machine |
39
PROBLEM
ANALYSIS AND
ALGORITHM
DESIGN
A juice machine has two main components: a built-in cash register and several
dispensers to hold and release the products
...
The cash register has some cash on
hand, it accepts the amount from the customer, and if the amount deposited is more
than the cost of the item, then—if possible—the cash register returns the change
...
The cash register should also be able to show the juice machine’s owner
the amount of money in the register at any given time
...
//****************************************************************
// Author: D
...
Malik
//
// class cashRegister
// This class specifies the members to implement a cash register
...
//Postcondition: The value of cashOnHand is returned
...
//Postcondition: cashOnHand = cashOnHand + amountIn;
cashRegister();
//Default constructor
//Sets the cash in the register to 500 cents
...
cashRegister(int cashIn);
//Constructor with a parameter
...
//Postcondition: cashOnHand = cashIn;
private:
int cashOnHand; //variable to store the cash in the register
};
1
40 |
Chapter 1: Software Engineering Principles and C++ Classes
Figure 1-10 shows the UML class diagram of the class cashRegister
...
The definitions of these functions are simple and easy to
follow
...
It
returns the value of the instance variable cashOnHand
...
The dispenser should show
the number of items in the dispenser and the cost of the item
...
Let us call this class dispenserType
...
S
...
//************************************************************
class dispenserType
{
public:
int getNoOfItems() const;
//Function to show the number of items in the machine
...
int getCost() const;
//Function to show the cost of the item
...
void makeSale();
//Function to reduce the number of items by 1
...
//Postcondition: numberOfItems = 50; cost = 50;
dispenserType(int setNoOfItems, int setCost);
//Constructor with parameters
//Sets the cost and number of items in the dispenser
//to the values specified by the user
...
dispenserType
–numberOfItems: int
–cost: int
+getNoOfItems() const: int
+getCost() const: int
+makeSale(): void
+dispenserType()
+dispenserType(int, int)
FIGURE 1-11
UML class diagram of the class dispenserType
Because the juice machine sells four types of items, we shall declare four objects of
type dispenserType
...
Following the definitions of the class dispenserType, the definitions of the
member functions and constructors are as follows:
int dispenserType::getNoOfItems() const
{
return numberOfItems;
}
int dispenserType::getCost() const
{
return cost;
}
void dispenserType::makeSale()
{
numberOfItems--;
}
dispenserType::dispenserType()
{
numberOfItems = 50;
cost = 50;
}
Programming Example: Fruit Juice Machine |
43
dispenserType::dispenserType(int setNoOfItems, int setCost)
{
if (setNoOfItems >= 0)
numberOfItems = setNoOfItems;
else
numberOfItems = 50;
}
MAIN
PROGRAM
if (setCost >= 0)
cost = setCost;
else
cost = 50;
When the program executes, it must do the following:
1
...
2
...
3
...
Furthermore, these instructions must be displayed after processing each selection
(except exiting the program), so that the user need not remember what to do if he
or she wants to buy two or more items
...
If the user has opted to buy a product
and if that product is available, the juice machine should show the cost of the product
and ask the user to deposit the money
...
This discussion translates into the following algorithm:
1
...
2
...
3
...
We divide this program into three functions—showSelection, sellProduct, and main
...
The definition of this function is:
void showSelection()
{
cout << "*** Welcome to Shelly's Fruit Juice Shop ***" << endl;
cout << "To select an item, enter " << endl;
cout << "1 for apple juice" << endl;
cout << "2 for orange juice" << endl;
cout << "3 for mango lassi" << endl;
cout << "4 for fruit punch" << endl;
cout << "9 to exit" << endl;
}//end showSelection
1
44 |
sellProduct
Chapter 1: Software Engineering Principles and C++ Classes
This function attempts to sell the product selected by the customer
...
The first thing that this function
does is check whether the dispenser holding the product is empty
...
If the dispenser
is not empty, it tells the user to deposit the necessary amount to buy the product
...
If the user fails to deposit
enough money, in two tries, to buy the product, the function simply returns
the money
...
) If the amount deposited by the user is sufficient, it accepts the money and sells
the product
...
(We also assume that this program does not return the extra money deposited by
the customer
...
)
From this discussion, it is clear that the function sellProduct must have access to
the dispenser holding the product (to decrement the number of items in the dispenser
by 1 and to show the cost of the item) as well as the cash register (to update the cash)
...
Furthermore, both parameters must be
referenced
...
If the dispenser is not empty
a
...
b
...
c
...
Show and prompt the customer to enter the additional amount
...
Calculate the total amount entered by the customer
...
If the amount entered by the customer is at least the cost of the
product,
i
...
Update the amount in the cash register
...
iii
...
e
...
Programming Example: Fruit Juice Machine |
45
2
...
The definition of the function sellProduct is:
void sellProduct(dispenserType& product, cashRegister& pCounter)
{
int amount; //variable to hold the amount entered
int amount2; //variable to hold the extra amount needed
if (product
...
getCost()
<< " cents" << endl;
cin >> amount;
if (amount < product
...
getCost()- amount << " cents" << endl;
cin >> amount2;
amount = amount + amount2;
}
if (amount >= product
...
acceptAmount(amount);
product
...
"
<< endl;
}
else
cout << "The amount is not enough
...
" << endl;
cout << "*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
<< endl << endl;
}
else
cout << "Sorry, this item is sold out
...
Create the cash register—that is, declare a variable of type
cashRegister
...
Create four dispensers—that is, declare four objects of type
dispenserType and initialize these objects
...
The
number of items in the dispenser is 75, and the cost of an item is 45 cents
...
4
...
6
...
Show the selection; call the function showSelection
...
While not done (a selection of 9 exits the program),
a
...
b
...
c
...
The definition of the function main is as follows:
int main()
{
cashRegister counter;
dispenserType appleJuice(100, 50);
dispenserType orangeJuice(100, 65);
dispenserType mangoLassi(75, 45);
dispenserType fruitPunch(100, 85);
int choice;
//variable to hold the selection
showSelection();
cin >> choice;
while (choice != 9)
{
switch (choice)
{
case 1:
sellProduct(appleJuice, counter);
break;
case 2:
sellProduct(orangeJuice, counter);
break;
case 3:
sellProduct(mangoLassi, counter);
break;
case 4:
sellProduct(fruitPunch, counter);
break;
default:
cout << "Invalid selection
...
S
...
// *************************************************************
#include
#include "cashRegister
...
h"
using namespace std;
void showSelection();
void sellProduct(dispenserType& product, cashRegister& pCounter);
//Place the definitions of the functions main, showSelection, and
//sellProduct here
...
*** Welcome to Shelly's Fruit Juice Shop ***
To select an item, enter
1 for apple juice
2 for orange juice
3 for mango lassi
4 for fruit punch
9 to exit
1
Please deposit 50 cents
50
Collect your item at the bottom and enjoy
...
Identifying Classes, Objects, and Operations
The hardest part of OOD is to identify the classes and objects
...
We begin with a description of the problem and then identify all of the nouns and verbs
...
For example, suppose that we want to write a program that calculates and prints the
volume and surface area of a cylinder
...
In this statement, the nouns are bold and the verbs are italic
...
The nouns—dimensions, surface area, and volume—
are characteristics of a cylinder and, thus, can hardly be considered classes
...
For
example, from the list of verbs for the cylinder problem description—write, input, calculate,
and print—the possible operations for a cylinder object are input, calculate, and print
...
The center of the base,
radius of the base, and height of the cylinder are the characteristics of the dimensions
...
Quick Review |
49
The verb calculate applies to determining the volume and the surface area
...
Similarly, the
verb print applies to the display of the volume and the surface area on an output device
...
There are several other OOD techniques in the literature
...
QUICK REVIEW
1
...
3
...
5
...
7
...
9
...
11
...
13
...
15
...
17
...
A program goes through many phases from the time it is first conceived
until the time it is retired, called the life cycle of the program
...
The new program is created in the software development stage
...
A program is retired if no new version of the program will be released
...
During the design phase, algorithms are designed to solve the problem
...
Two well-known design techniques are structured-design and objectoriented design
...
Each
subproblem is solved, and the solutions of all the subproblems are then
combined to solve the problem
...
An object consists of data and operations on those data
...
In the implementation phase, you write and compile programming code to
implement the classes and functions that were discovered in the design phase
...
A postcondition is a statement specifying what is true after the function call
is completed
...
19
...
21
...
23
...
25
...
27
...
29
...
31
...
33
...
35
...
37
...
39
...
Debugging refers to finding and fixing the errors, if they exist
...
A test case consists of a set of inputs, user actions, or other initial conditions,
and the expected output
...
While analyzing a particular algorithm, we usually count the number of
operations performed by the algorithm
...
The term asymptotic refers to the study of the
function f as n becomes larger and larger without bound
...
Components of a class are called the members of the class
...
In C++, class is a reserved word
...
The private members of a class are not accessible outside the class
...
By default, all members of a class are private
...
The private members are declared using the member access specifier
private
...
If any member of a class is a function, you usually use the function
prototype to declare it
...
In the definition of the class, you cannot initialize a variable when you
declare it
...
The middle box contains the data
members and their data types
...
A + (plus) sign in front of a member indicates that this member is a
public member; a – (minus) sign indicates that this is a private
member
...
Exercises
40
...
42
...
44
...
46
...
48
...
50
...
52
...
54
...
56
...
No memory is allocated; memory is
allocated for the class variables when you declare them
...
A class member is accessed using the class variable name, followed by the
dot operator (
...
The only built-in operations on classes are the assignment and member
selection
...
As parameters to functions, classes can be passed either by value or by
reference
...
The name of a constructor is the same as the name of the class
...
A constructor without parameters is called the default constructor
...
Destructors automatically execute when a class object goes out of scope
...
The name of a destructor is the tilde ($), followed by the class name (no
spaces in between)
...
As a result, they cannot be called like
other functions
...
An easy way to identify classes, objects, and operations is to describe the
problem in English and then identify all of the nouns and verbs
...
EXERCISES
1
...
a
...
c
...
The three fundamental stages of software are development, use, and
discard
...
|
51
1
52 |
Chapter 1: Software Engineering Principles and C++ Classes
The instance variables of a class must be of the same type
...
The function members of a class must be public
...
A class can have more than one constructor
...
A class can have more than one destructor
...
Both constructors and destructors can have parameters
...
2
...
4
...
What should be the pre- and postconditions for this function?
Each of the following expressions represents the number of operations for
certain algorithms
...
5n + 2n + 8
2
c
...
5(6n + 4)
e
...
4n log2n + 3n + 8
Consider the following function:
a
...
void funcExercise6(int x, int y)
{
int z;
}
7
...
Consider the following function:
int funcExercise7(int list[], int size)
{
int sum = 0;
for (int index = 0; index < size; index++)
sum = sum + list[index];
}
return sum;
Exercises
a
...
b
...
What is the order of the function funcExercise7?
Consider the following function prototype:
c
...
int funcExercise8(int x);
9
...
The function funcExercise8 returns the value as follows: If 0 <¼ x <¼ 50,
it returns 2x; if –50 <¼ x < 0, it returns x2; otherwise it returns –999
...
What is the order of your function?
Characterize the following algorithm in terms of Big-O notation
...
(Assume that all
variables are properly declared
...
Characterize the following algorithm in terms of Big-O notation
...
(Assume that all variables are properly declared
...
Characterize the following algorithm in terms of Big-O notation
...
Characterize the following algorithm in terms of Big-O notation
...
Find the syntax errors in the definitions of the following classes:
a
...
c
...
class BB
{
int one ;
int two;
public:
bool equal();
print();
BB(int, int);
}
class CC
{
public;
void set(int, int);
void print();
CC();
CC(int, int);
bool CC(int, int);
private:
int u;
int v;
};
Consider the following declarations:
class xClass
{
public:
void func();
void print() const;
xClass ();
xClass (int, double);
private:
int u;
double w;
};
xClass x;
a
...
c
...
e
...
How many members does class xClass have?
How many private members does class xClass have?
How many constructors does class xClass have?
Write the definition of the member function func so that u is set to 10
and w is set to 15
...
Write the definition of the member function print that prints the
contents of u and w
...
Exercises
Write a C++ statement that prints the values of the data members of
the object x
...
Write a C++ statement that declares an object t of the type xClass,
and initializes the data members of t to 20 and 35
...
Consider the definition of the following class:
g
...
class CC
{
public:
CC();
CC(int);
CC(int, int);
CC(double, int);
...
...
//Line
//Line
//Line
//Line
Give the line number containing the constructor that is executed in
each of the following declarations:
i
...
CC two(5, 6);
iii
...
5, 8);
Write the definition of the constructor in Line 1 so that the private
data members are initialized to 0
...
Write the definition of the constructor in Line 2 so that the private
data member u is initialized according to the value of the parameter,
and the private data member v is initialized to 0
...
Write the definition of the constructors in Lines 3 and 4 so that the
private data members are initialized according to the values of the
parameters
...
17
...
printTime();
cout << endl;
clock2
...
setTime(6, 59, 39);
clock1
...
incrementMinutes();
clock1
...
setTime(0, 13, 0);
if (clock1
...
" << endl;
else
cout << "The two times are different
...
Write the definition of a class that has the following properties:
a
...
c
...
The name of the class is secretType
...
The class secretType has the following member functions:
print—Outputs the data stored in the instance variables with the
appropriate titles
setName—Function to set the name
setAge—Function to set the age
setWeight—Function to set the weight
setHeight—Function to set the height
getName—Value-returning function to return the name
getAge—Value-returning function to return the age
getWeight—Value-returning function to return the weight
getHeight—Value-returning function to return the height
Default constructor—Sets name to the empty string and age, weight,
and height to 0
Constructor with parameter—Sets the values of the instance variables
to the values specified by the user
d
...
Assume the definition of the class personType as given in this chapter
...
b
...
Write a C++ statement that declares student to be a personType
object, and initialize its first name to "Buddy" and last name to "Arora"
...
Write C++ statements that change the first name of student to
"Susan" and the last name to "Miller"
...
Write a program that converts a number entered in Roman numerals to
decimal form
...
An
object of romanType should do the following:
a
...
c
...
Convert and store the number into decimal form
...
(Write two separate functions—one to print the number as a
Roman numeral and the other to print the number as a decimal number
...
Remember, a larger numeral preceding a smaller numeral means addition,
so LX is 60
...
Any place in a decimal number, such as the 1s place, the
10s place, and so on, requires from zero to four Roman numerals
...
Test your program using the following Roman numerals: MCXIV,
CCCLIX, and MDCLXVI
...
The class dayType should store the day, such as
Sunday for Sunday
...
b
...
c
...
d
...
e
...
f
...
For example, if the current day is Monday and we add 4 days, the day to
be returned is Friday
...
g
...
Write the definitions of the functions to implement the operations for the
class dayType as defined in Programming Exercise 2
...
a
...
1000
500
100
50
10
5
1
|
57
1
58 |
4
...
The member functions that we included merely print the name and set the
name of a person
...
b
...
d
...
5
...
Set the first name only
...
Store and set the middle name
...
Check whether a given last name is the same as the last name of this person
...
Also, write a program to test various operations on this class
...
Rewrite the definition of the function sellProduct so that it keeps prompting
the user to enter more money as long as the user has not entered enough money
to buy the product
...
The equation of a line in standard form is ax + by ¼ c, where a and b both
cannot be zero, and a, b, and c are real numbers
...
If a ¼ 0, then it is a horizontal line, and if b ¼ 0, then it is a
vertical line
...
Two lines are parallel if
they have the same slope or both are vertical lines
...
Design the class lineType to store a line
...
Your class must contain the following operations:
If a line is nonvertical, then determine its slope
...
Determine if two lines are equal
...
)
c
...
d
...
e
...
Add appropriate constructors to initialize variables of lineType
...
(Tic-Tac-Toe) Write a program that allows two players to play the tic-tactoe game
...
Include a 3 by 3 two-dimensional array, as a private
instance variable, to create the board
...
Some of the operations on a ticTacToe object are printing the
current board, getting a move, checking if a move is valid, and determining
the winner after each move
...
a
...
Chapter 1: Software Engineering Principles and C++ Classes
2
CHAPTER
O BJECT -O RIENTED D ESIGN
(OOD) AND C++
I N T H I S C H A P T E R , YO U W I L L :
...
Learn about derived and base classes
...
Examine how the constructors of base and derived classes work
...
Explore three types of inheritance: public, protected, and private
...
Become familiar with the three basic principles
of object-oriented design
...
Become aware of the restrictions on operator overloading
...
Learn about friend functions
...
Discover how to overload various operators
...
Explore how to construct function templates and class templates
60 |
Chapter 2: Object-Oriented Design (OOD) and C++
Chapter 1 introduced classes, abstract data types (ADT), and ways to implement ADT in
C++
...
An object,
therefore, becomes a self-contained entity
...
In addition to implementing ADT, classes have other features
...
This important feature encourages code reuse
...
The main features associated with a part-time
employee are the name, pay rate, and number of hours worked
...
Every part-time employee
is a person
...
Of course, we do not want to make the necessary changes directly to the class
personType—that is, edit the class personType, and add and/or delete members
...
For
example, because the class personType already has data members to store the first
name and last name, we will not include any such members in the class
partTimeEmployee
...
(We will design such a class in Example 2-2
...
The class clockType has three data members to store hours,
minutes, and seconds
...
In this case, we would likely extend the
definition of the class clockType and create a class, extClockType, to accommodate
this new information
...
In C++, the mechanism that
allows us to accomplish this task is the principle of inheritance
...
’’
Inheritance lets us create new classes from existing classes
...
The derived class inherits the properties of the base classes
...
Each derived class, in turn, becomes a base class for a future derived class
...
In a single inheritance,
Inheritance |
61
the derived class is derived from a single base class; in a multiple inheritance, the derived
class is derived from more than one base class
...
Inheritance can be viewed as a treelike, or hierarchical, structure wherein a base class is
shown with its derived classes
...
shape
circle
rectangle
square
FIGURE 2-1
Inheritance hierarchy
In this diagram, shape is the base class
...
Every circle and
every rectangle is a shape
...
The general syntax of a derived class is:
class className: memberAccessSpecifier baseClassName
{
member list
};
where memberAccessSpecifier is public, protected, or private
...
(We
discuss protected inheritance later in this chapter
...
The following statements specify that
the class circle is derived from shape, and it is a public inheritance:
class circle: public shape
{
...
...
...
};
This is a private inheritance
...
So any object of type circle cannot directly
access these members
...
...
};
That is, if we do not use either the memberAccessSpecifier public or private, the
public members of a base class are inherited as private members
...
1
...
In other words, when you write the definitions of the member functions
of the derived class, you cannot directly access the private members
of the base class
...
The public members of a base class can be inherited either as public
members or as private members by the derived class
...
3
...
4
...
That is, in the derived class, you can have a member function with
the same name, number, and types of parameters as a function in the
base class
...
5
...
Similarly, the member functions of the base class (unless
redefined) are also member functions of the derived class
...
)
The next sections describe two important issues related to inheritance
...
While discussing
Inheritance |
63
this issue, we also address how to access the private (data) members of the base class in the
derived class
...
The constructor of a derived class cannot directly access the private member variables of the base
class
...
Redefining (Overriding) Member Functions of the Base Class
Suppose that a class derivedClass is derived from the class baseClass
...
It then
follows that the member variables of the class derivedClass are its own member
variables, together with the member variables of baseClass
...
Now derivedClass contains member variables in addition to the member variables
inherited from baseClass
...
You can give any name to this function
...
This is called redefining (or overriding) the member function of the
base class
...
To redefine a public member function of a base class in the derived class, the
corresponding function in the derived class must have the same name, number, and types
of parameters
...
If the corresponding
functions in the base class and the derived class have the same name but different sets
of parameters, this is function overloading in the derived class, which is also allowed
...
S
...
//***************************************************************
class rectangleType
{
public:
void setDimension(double l, double w);
//Function to set the length and width of the rectangle
...
//Postcondition: The value of length is returned
...
//Postcondition: The value of width is returned
...
//Postcondition: The area of the rectangle is calculated
//
and returned
...
//Postcondition: The perimeter of the rectangle is
//
calculated and returned
...
rectangleType();
//default constructor
//Postcondition: length = 0; width = 0;
rectangleType(double l, double w);
//constructor with parameters
//Postcondition: length = l; width = w;
private:
double length;
double width;
};
Figure 2-2 shows the UML class diagram of the class rectangleType
...
S
...
//***************************************************************
class boxType: public rectangleType
{
public:
void setDimension(double l, double w, double h);
//Function to set the length, width, and height of the box
...
//Postcondition: The value of height is returned
...
//Postcondition: The surface area of the box is
//
calculated and returned
...
//Postcondition: The volume of the box is calculated and
//
returned
...
boxType();
//Default constructor
//Postcondition: length = 0; width = 0; height = 0;
boxType(double l, double w, double h);
//Constructor with parameters
//Postcondition: length = l; width = w; height = h;
private:
double height;
};
Inheritance |
67
Figure 2-3 shows the UML class diagram of the class boxType and the inheritance
hierarchy
...
Therefore, all public members of the class rectangleType are public members of the
class boxType
...
In general, while writing the definitions of the member functions of a derived class to
specify a call to a public member function of the base class, we do the following:
•
If the derived class overrides a public member function of the base class,
then to specify a call to that public member function of the base class, you
use the name of the base class, followed by the scope resolution operator,
::, followed by the function name with the appropriate parameter list
...
(See the following note for member functions of the base class that are
overloaded in the derived class
...
For example, the
class boxType overloads the member function setDimension of the class
rectangleType
...
)
68 |
Chapter 2: Object-Oriented Design (OOD) and C++
Next, let us write the definition of the member function print of the class boxType
...
The
member function print of the class boxType prints the values of these member
variables
...
Therefore, when writing the definition of the function print
of the class boxType, we cannot access length and width directly
...
Therefore, when writing the definition of the member function print of the class boxType, we first
call the member function print of the class rectangleType to print
the values of length and width
...
To call the member function print of rectangleType in the definition of the member
function print of boxType, we must use the following statement:
rectangleType::print();
This statement ensures that we call the member function print of the base class
rectangleType, not of the class boxType
...
The definition of the function setDimension is as follows:
void boxType::setDimension(double l, double w, double h)
{
rectangleType::setDimension(l, w);
}
if (h >= 0)
height = h;
else
height = 0;
Notice that in the preceding definition of the function setDimension, a call to the
member function setDimension of the class rectangleType is preceded by the
name of the class and the scope resolution operator, even though the class boxType
overloads—not overrides—the function setDimension
...
To determine the surface area of a box, we need to access the length and width of the
box, which are declared as private members of the class rectangleType
...
Because the class boxType does not
contain any member functions that have the names getLength or getWidth, we call
these member functions of the class rectangleType without using the name of the
base class
...
To determine the volume of a box, you multiply the length, width, and height of the
box, or multiply the area of the base of the box by its height
...
To do this, you can use the
member function area of the class rectangleType to determine the area of the base
...
Constructors of Derived and Base Classes
A derived class can have its own private member variables, and so a derived class can
explicitly include its own constructors
...
When we declare a derived class object, this object inherits the
members of the base class, but the derived class object cannot directly access the private
(data) members of the base class
...
That is, the member functions of a derived class cannot directly access the private
members of the base class
...
Thus, when a
derived class object is declared, it must also automatically execute one of the constructors
of the base class
...
This is, in fact, what happens
...
In the preceding section, we defined the class rectangleType and derived the class
boxType from it
...
Let us now discuss how to write the definitions of the constructors of the class boxType
...
The class
boxType has three member variables: length, width, and height
...
First, let us write the definition of the default constructor of the class boxType
...
Because the class rectangleType contains the default constructor, when writing
the definition of the default constructor of the class boxType, we do not specify any
constructor of the base class
...
0;
}
Next, we discuss how to write the definitions of constructors with parameters
...
Consider the following definition of the constructor with parameters of the class
boxType:
boxType::boxType(double l, double w, double h)
: rectangleType(l, w)
{
if (h >= 0)
height = h;
else
height = 0;
}
In this definition, we specify the constructor of rectangleType with two parameters
...
Inheritance |
71
Consider the following statements:
rectangleType myRectangle(5
...
0);
boxType myBox(6
...
0, 4
...
Thus, the
object myRectangle has two member variables: length and width
...
Thus, the object myBox has three member
variables: length, width, and height
...
myBox
myRectangle
length
width
length
3
...
0
width
5
...
0
height
FIGURE 2-4
4
...
print();
cout << endl;
myBox
...
In the statement in Line 5, the function print associated with the class
boxType is executed
...
Thus, the
output of the statement in Line 3 is:
Length = 5
...
0
The output of the statement in Line 5 is:
Length = 6
...
0; Height = 4
...
Therefore, a derived class can also have a
constructor with default parameters
...
(To save space, these definitions have no
documentation
...
Inheritance |
73
Suppose that a base class, baseClass, has private member variables and
constructors
...
Therefore, the member
variables of derivedClass are the ones inherited from baseClass
...
To guarantee
the initialization of the inherited member variables of an object of type
derivedClass, even though derivedClass has no member variables, it must
have the appropriate constructors
...
Therefore,
when you write the definition of the constructor (with parameters) of derivedClass,
the heading of the definition of the constructor contains a call to an appropriate
constructor (with parameters) of baseClass, and the body of the constructor is
empty—that is, it contains only the opening and closing braces
...
There
are both full-time employees and part-time employees
...
Suppose that you want to
define a class to keep track of a part-time employee’s information such as name, pay
rate, and hours worked
...
Because every employee is a person, and Example 1-12 (Chapter 1)
defined the class personType to store the first name and the last name together with
the necessary operations on name, we can define a class partTimeEmployee based on
the class personType
...
//***************************************************************
// Author: D
...
Malik
//
// class partTimeEmployee
// This class is derived from the class personType and it
// specifies the members to implement the properties of a
// part-time employee
...
//Postcondition: Outputs: firstName lastName wages are $$$$
...
//Postcondition: Pay is calculated and returned
...
//Postcondition: firstName = first; lastName = last;
//
payRate = rate; hoursWorked = hours
partTimeEmployee(string first = "", string last = "",
double rate = 0, double hours = 0);
//Constructor with parameters
//Sets the first name, last name, payRate, and hoursWorked
//according to the parameters
...
//Postcondition: firstName = first; lastName = last;
//
payRate = rate; hoursWorked = hours
private:
double payRate;
//variable to store the pay rate
double hoursWorked; //variable to store the hours worked
};
Figure 2-5 shows the UML class diagram of the class partTimeEmployee and the
inheritance hierarchy
...
To define new classes, you create new header files
...
Thus, to create new classes based on the
previously defined classes, the header files of the new classes contain commands that tell
the computer where to look for the definitions of the base classes
...
h
...
h—must contain the preprocessor directive:
#include "personType
...
To be specific, the header file
partTimeEmployee
...
h"
//***************************************************************
// Author: D
...
Malik
//
// class partTimeEmployee
// This class is derived from the class personType and it
// specifies the members to implement the properties of a
// part-time employee
...
//Postcondition: Outputs: firstName lastName wages are $$$$
...
//Postcondition: Pay is calculated and returned
...
//Postcondition: firstName = first; lastName = last;
//
payRate = rate; hoursWorked = hours
partTimeEmployee(string first = "", string last = "",
double rate = 0, double hours = 0);
//Constructor with parameters
//Sets the first name, last name, payRate, and hoursWorked
//according to the parameters
...
//Postcondition: firstName = first; lastName = last;
//
payRate = rate; hoursWorked = hours
private:
double payRate;
//variable to store the pay rate
double hoursWorked; //variable to store the hours worked
};
The definitions of the member functions can be placed in a separate file (whose extension
is
...
Recall that to include a system-provided header file, such as iostream, in a user
program, you enclose the header file between angular brackets; to include a user-defined
header file in a program, you enclose the header file between double quotation marks
...
To include
a header file in a program, you use the preprocessor command include
...
Consider the
following header file:
//Header file test
...
h includes the file test
...
To be specific, suppose that the header file testA
...
h
#include "test
...
...
Now consider the following program code:
//Program headerTest
...
h"
#include "testA
...
...
When the program headerTest
...
The preprocessor includes first the header file test
...
h
...
h is included, because it contains the preprocessor
directive #include "test
...
h is included twice in the program
...
h results in compile-time errors, such as the
identifier ONE already being declared
...
h has already defined the variables ONE and TWO
...
Let us first rewrite the header file test
...
//Header file test
...
#ifndef H_test means ‘‘if not defined H_test’’
b
...
#endif means ‘‘end if’’
Here H_test is a preprocessor identifier
...
If the header file test
...
In fact, all header files are written using similar preprocessor commands
...
Only member functions of that class can access the private members
...
However, it is sometimes necessary for a derived class to access a private member of a
base class
...
Recall that the members of a class are classified into three categories: public,
private, and protected
...
Thus, the accessibility of a protected
member of a class is in between public and private
...
To summarize, if a derived class needs to access a member of a base class, that member of
the base class should be declared under the member access specifier protected
...
Then B cannot directly access the private
members of A
...
What about the
public and protected members of A? This section gives the rules that generally apply
when accessing the members of a base class
...
...
};
In this statement, memberAccessSpecifier is either public, protected, or private
...
If memberAccessSpecifier is public—that is, the inheritance is
public—then
The public members of A are public members of B
...
b
...
They
can be directly accessed by the member functions (and friend
functions) of B
...
The private members of A are hidden to B
...
a
...
If memberAccessSpecifier is protected—that is, the inheritance is
protected—then
The public members of A are protected members of B
...
b
...
They
can be accessed by the member functions (and friend functions) of B
...
The private members of A are hidden to B
...
3
...
The public members of A are private members of B
...
b
...
They can
be accessed by the member functions (and friend functions) of B
...
The private members of A are hidden to B
...
a
...
Composition
Composition is another way to relate two classes
...
Composition is a ‘‘has-a’’ relationship; for
example, ‘‘every person has a date of birth
...
The class personType
stores a person’s first name and last name
...
Because every person has a personal ID and a date of birth, we can
define a new class, called personalInfoType, in which one of the members is an object
of type personType
...
First, we define another class, dateType, to store only a person’s date of birth, and then
construct the class personalInfoType from the classes personType and dateType
...
2
80 |
Chapter 2: Object-Oriented Design (OOD) and C++
To define the class dateType, we need three data members to store the month, day
number, and year
...
The following statements define the class dateType:
//************************************************************
// Author: D
...
Malik
//
// class dateType
// This class specifies the members to implement a date
...
//The member variables dMonth, dDay, and dYear are set
//according to the parameters
...
//Postcondition: The value of dDay is returned
...
//Postcondition: The value of dMonth is returned
...
//Postcondition: The value of dYear is returned
...
dateType(int month = 1, int day = 1, int year = 1900);
//Constructor to set the date
//The member variables dMonth, dDay, and dYear are set
//according to the parameters
...
If
//
no values are specified, the default values are used to
//
initialize the member variables
...
dateType
–dMonth: int
–dDay: int
–dYear: int
+setDate(int, int, int): void
+getDay() const: int
+getMonth() const: int
+getYear() const: int
+printDate() const: void
+dateType(int = 1, int = 1, int = 1900)
FIGURE 2-6
UML class diagram of the class dateType
The definitions of the member functions of the class dateType are as follows:
void dateType::setDate(int month, int day, int year)
{
dMonth = month;
dDay = day;
dYear = year;
}
The definition of the function setDate, before storing the date into the data members,
does not check whether the date is valid
...
In Programming Exercise 2 at the end of this chapter, you
are asked to rewrite the definition of the function setDate so that the date is validated
before storing it in the data members
...
In Programming
Exercise 2 at the end of this chapter, when you rewrite the definition of the function
setDate to validate the date, and the constructor uses the function setDate, the date set
by the constructor will also be validated
...
S
...
//************************************************************
class personalInfoType
{
public:
void setPersonalInfo(string first, string last, int month,
int day, int year, int ID);
//Function to set the personal information
...
//Postcondition: firstName = first; lastName = last;
//
dMonth = month; dDay = day; dYear = year;
//
personID = ID;
void printPersonalInfo () const;
//Function to print the personal information
...
//Postcondition: firstName = first; lastName = last;
//
dMonth = month; dDay = day; dYear = year;
//
personID = ID;
//
If no values are specified, the default values are
//
used to initialize the member variables
...
personalInfoType
–name: personType
–bDay: dateType
–personID: int
setPersonalInfo(string, string, int, int,
int, int): void
printPersonalInfo() const: void
personalInfoType(string = "", string = "",
int = 1, int = 1,
int = 1900, int = 0)
FIGURE 2-7
personalInfoType
personType
dateType
UML class diagram of the class personalInfoType and composition (aggregation)
Before we give the definition of the member functions of the class personalInfoType,
let us discuss how the constructors of the objects bDay and name are invoked
...
Suppose that we have the following statement:
personalInfoType student;
When the object student enters its scope, the objects bDay and name, which are
members of student, also enter their scopes; as a result, one of their constructors is
executed
...
Recall that constructors do not have a type and
so cannot be called like other functions
...
The following statements illustrate how to pass arguments to the constructors
of the member objects:
personalInfoType::personalInfoType(string first, string last, int month,
int day, int year, int ID)
: name(first, last), bDay(month, day, year)
{
...
...
Thus, in our case, the object name
is initialized first, then bDay, and, finally, student
...
setName(first,last);
bDay
...
print();
cout << "'s date of birth is ";
bDay
...
In the
case of composition, use the member object name to invoke its own constructor
...
The ability to combine data and operations is called
encapsulation
...
Chapter 1
defined the abstract data type (ADT) and described how classes in C++ implement
ADTs
...
Inheritance, the second principle
of OOD, encourages code reuse
...
First we discuss polymorphism via operator overloading, and then via templates
...
We will simplify function overloading through the use of templates, called function
templates
...
But first let us see why you
would want to overload operators
...
It also showed how you can
use the class clockType to represent the time of day in a program
...
Consider the following statements:
clockType myClock(8,23,34);
clockType yourClock(4,5,30);
The first statement declares myClock to be an object of type clockType and initializes
the data members hr, min, and sec of myClock to 8, 23, and 34, respectively
...
Now consider the following statements:
myClock
...
incrementSeconds();
if (myClock
...
...
The first statement prints the value of myClock in the form hr:min:sec
...
The third statement checks
whether the value of myClock is the same as the value of yourClock
...
However, if we can use the insertion operator << to
output the value of myClock, the increment operator ++ to increment the value of
myClock by one second, and relational operators for comparison, we can enhance the
flexibility of C++ considerably and can improve code readability
...
...
Recall that the only built-in operations on classes are the assignment operator and the
member selection operator
...
However, C++ allows the programmer to extend the definitions of most of the
operators so that operators such as relational operators, arithmetic operators, insertion
2
86 |
Chapter 2: Object-Oriented Design (OOD) and C++
operators for data output, and extraction operators for data input can be applied to classes
...
In addition to operator
overloading, this chapter discusses function overloading
...
If both operands of / are integers, the result
is an integer; otherwise, the result is a floating-point number
...
The
operator << is used as both a stream insertion operator and a left shift operator
...
These
are examples of operator overloading
...
The results of + and – are different
for integer arithmetic, floating-point arithmetic, and pointer arithmetic
...
It does not allow the user to create new operators
...
To overload an operator, you must write functions (that is, the header and body)
...
For example, the name of the function to overload the
operator >= is
operator>=
Operator function: The function that overloads an operator
...
The syntax of the heading for an operator function is as follows:
returnType operator operatorSymbol(arguments)
In C++, operator is a reserved word
...
To overload an operator for a class, you do the following:
1
...
2
...
Certain rules must be followed when you include an operator function in a class
definition
...
Operator Overloading |
87
Overloading an Operator: Some Restrictions
When overloading an operator, keep the following in mind:
•
•
You cannot change the precedence of an operator
...
(For example, the associativity of
the arithmetic operator + is from left to right and it cannot be changed
...
• You cannot change the number of arguments that an operator takes
...
Only existing operators can be overloaded
...
...
• Operators can be overloaded either for objects of the user-defined type,
or for a combination of objects of the user-defined type and objects of
the built-in type
...
Sometimes it is necessary for a function member to refer to the object as a
whole, rather than the object’s individual data members
...
In C++, this is a reserved word
...
When an object invokes a member function,
the member function references the pointer this of the object
...
Further suppose that the
definition of funcOne looks like the following:
test test::funcOne()
{
...
...
funcOne();
copies the value of the object x into the object y; that is, the data members of x are copied
into the corresponding data members of y
...
2
88 |
Chapter 2: Object-Oriented Design (OOD) and C++
The following example illustrates how the pointer this works
...
Here we extend the definition of the class personType to individually set a
person’s first name and last name, and then return the entire object
...
S
...
//************************************************************
class personType
{
public:
void print() const;
//Function to output the first name and last name in
//the form firstName lastName
void setName(string first, string last);
//Function to set firstName and lastName according to the
//parameters
...
//Postcondition: firstName = first
//
After setting the first name, a reference to the
//
object, that is, the address of the object, is
//
returned
...
//Postcondition: lastName = last
//
After setting the last name, a reference to the object,
//
that is, the address of the object, is returned
...
//Postcondition: The value of firstName is returned
...
//Postcondition: The value of lastName is returned
...
//Postcondition: firstName = first; lastName = last
Operator Overloading |
89
private:
string firstName; //variable to store the first name
string lastName; //variable to store the last name
};
Notice that in this definition of the class personType, we replace the default constructor and the constructor with parameters by one constructor with default parameters
...
The definitions of the functions
setFirstName and setLastName are as follows:
personType& personType::setLastName(string last)
{
lastName = last;
}
return *this;
personType& personType::setFirstName(string first)
{
firstName = first;
}
return *this;
The following program shows how to use the class personType
...
h
...
S
...
h"
//Line 1
//Line 2
//Line 3
using namespace std;
//Line 4
int main()
{
personType student1("Lisa", "Smith");
personType student2;
personType student3;
//Line
//Line
//Line
//Line
//Line
cout << "Line 10 -- Student 1: ";
student1
...
setFirstName("Shelly")
...
print();
cout << endl;
//Line 14
//Line 15
//Line 16
2
90 |
Chapter 2: Object-Oriented Design (OOD) and C++
student3
...
print();
cout << endl;
//Line 18
//Line 19
//Line 20
student3
...
print();
cout << endl;
}
//Line 17
//Line 22
//Line 23
//Line 24
return 0;
//Line 25
//Line 26
Sample Run:
Line
Line
Line
Line
10
14
18
22
-----
Student
Student
Student
Student
1:
2:
3:
3:
Lisa Smith
Shelly Malik
Cindy
Cindy Tomek
The statements in Lines 7, 8, and 9 declare and initialize the objects student1,
student2, and student3, respectively
...
The statement in Line 11 outputs the value of student1
(see Line 10 in the sample run, which contains the output of Lines 10, 11, and 12)
...
In the statement
student2
...
setLastName("Malik");
first the expression
student2
...
This
expression sets the first name to "Shelly" and returns a reference to the object, which
is student2
...
setLastName("Malik")
which sets the last name of student2 to "Malik"
...
The statement in Line 17 sets the first name of the object student3
to "Cindy", and ignores the value returned
...
Notice the output in Line 18
...
The last name of
student3 is still empty, which was set by the statement in Line 9 when student3 was
declared
...
Operator Overloading |
91
Friend Functions of Classes
A friend function of a class is a nonmember function of the class, but has access to all
the members (public or non-public) of the class
...
The word friend appears only in the function prototype in the class definition, not in
the definition of the friend function
...
...
};
In the definition of the class classIllusFriend, two is declared as a friend of the
class classIllusFriend
...
When you write the definition of the function two, any object of
type classIllusFriend—which is either a local variable of two or a formal parameter of
two—can access its private members within the definition of the function two
...
) Moreover, because a friend function is not a member of a
class, its declaration can be placed within the private, protected, or public part of the
class
...
DEFINITION OF A friend FUNCTION
When writing the definition of a friend function, the name of the class and the scope
resolution operator do not precede the name of the friend function in the function
heading
...
Thus, the definition of the function two in the previous class
classIllusFriend is as follows:
void friendFunc(/*parameters*/)
{
...
...
The next section illustrates the difference between a member function and a nonmember
function (friend function), when we overload some of the operators for a specific class
...
2
92 |
Chapter 2: Object-Oriented Design (OOD) and C++
EXAMPLE 2-4
Consider the following class:
class classIllusFriend
{
friend void friendFunc(classIllusFriend cIFObject);
public:
void print();
void setx(int a);
private:
int x;
};
In the definition of the class classIllusFriend, friendFunc is declared as a
friend function
...
x = 45;
//Line 4
localTwoObject
...
x
endl;
cIFObject
...
print();
}
//Line 7
//Line 8
cout << "Line 9: In friendFunc accessing "
<< "private member variable " << "x = "
<< cIFObject
...
In the statement in Line 4, the
object localTwoObject accesses its private member variable x and sets its value to 45
...
Similarly, in the statement in Line 7, the formal parameter
cIFObject accesses its private member variable x and sets its value to 88
...
The statement in Line 6 outputs the value of
the private member variable x of localTwoObject by directly accessing x
...
The
function friendFunc also prints the value of x by using the function print (see the
statements in Lines 6 and 9)
...
setx(32);
cout << "Line 15: aObject
...
print();
cout << endl;
//Line 15
//Line 16
//Line 17
cout << "*~*~*~* Testing friendFunc *~*~*~*"
<< endl << endl;
//Line 18
friendFunc(aObject);
}
//Line 14
//Line 19
return 0;
//Line 20
//Line 21
Sample Run:
Line 15: aObject
...
The statement in Line 19 calls the
function friendFunc (a friend function of the class classIllusFriend) and passes
the object aObject as an actual parameter
...
2
94 |
Chapter 2: Object-Oriented Design (OOD) and C++
Operator Functions as Member Functions and Nonmember
Functions
Earlier in this chapter we stated that certain rules must be followed when you include an
operator function in the definition of a class
...
Most operator functions can be either member functions or nonmember functions—that
is, friend functions of a class
...
The function that overloads any of the operators (), [], ->, or = for a
class must be declared as a member of the class
...
Suppose that an operator op is overloaded for a class—say, opOverClass
...
)
If the leftmost operand of op is an object of a different type (that is,
not of type opOverClass), the function that overloads the operator
op for opOverClass must be a nonmember—that is, a friend of the
class opOverClass
...
If the operator function that overloads the operator op for the
class opOverClass is a member of the class opOverClass,
then when applying op on objects of type opOverClass, the leftmost operand of op must be of type opOverClass
...
You must follow these rules when including an operator function in a class definition
...
Except for certain operators noted previously, operators can be overloaded either as
member functions or as nonmember functions
...
To facilitate our discussion of operator overloading, we will use the class
rectangleType, defined earlier in this chapter
...
C++ consists of both binary and unary operators
...
The next few sections discuss how to overload various binary and
unary operators
...
This operator can be overloaded as
either a member function of the class or as a friend function
...
OVERLOADING THE BINARY OPERATORS AS MEMBER FUNCTIONS
Suppose that # is overloaded as a member function of the class rectangleType
...
operator#(yourRectangle)
This expression clearly shows that the function operator# has only one parameter,
which is yourRectangle
...
Thus, the first parameter of
operator# is the object that is invoking the function operator#, and the second
parameter is passed as a parameter to this function
...
Function Prototype (to be included in the definition of the class):
returnType operator#(const className&) const;
where # stands for the binary operator, arithmetic or relational, to be overloaded;
returnType is the type of value returned by the function; and className is the name
of the class for which the operator is being overloaded
...
EXAMPLE 2-5
Let us overload +, *, ==, and != for the class rectangleType
...
class rectangleType
{
public:
void setDimension(double l, double w);
double getLength() const;
double getWidth() const;
double area() const;
double perimeter() const;
void print() const;
rectangleType operator+(const rectangleType&) const;
//Overload the operator +
rectangleType operator*(const rectangleType&) const;
//Overload the operator *
bool operator==(const rectangleType&) const;
//Overload the operator ==
bool operator!=(const rectangleType&) const;
//Overload the operator !=
rectangleType();
rectangleType(double l, double w);
private:
double length;
double width;
};
The definition of the function operator+ is as follows:
rectangleType rectangleType::operator+
(const rectangleType& rectangle) const
{
rectangleType tempRect;
tempRect
...
length;
tempRect
...
width;
}
return tempRect;
Operator Overloading |
97
Notice that operator+ adds the corresponding lengths and widths of the two rectangles
...
length = length * rectangle
...
width = width * rectangle
...
Two rectangles are equal if their lengths and widths are equal
...
length &&
width == rectangle
...
Therefore, the definition of the function to overload the operator != is as follows:
bool rectangleType::operator!=
(const rectangleType& rectangle) const
{
return (length != rectangle
...
width);
}
OVERLOADING THE BINARY OPERATORS (ARITHMETIC OR RELATIONAL) AS NONMEMBER
FUNCTIONS
Suppose that # represents the binary operator (arithmetic or relational) that is to be
overloaded as a nonmember function of the class rectangleType
...
This expression also clearly
shows that the function operator# is neither a member of the object myRectangle nor
a member of the object yourRectangle
...
To include the operator function operator# as a nonmember function of the class in the
definition of the class, the reserved word friend must appear before the function
heading
...
GENERAL SYNTAX TO OVERLOAD THE BINARY (ARITHMETIC OR RELATIONAL) OPERATORS AS
NONMEMBER FUNCTIONS
This section describes the general form of the functions that overload binary operators as
nonmember functions of a class
...
Function Definition:
returnType operator#(const className& firstObject,
const className& secondObject)
{
//algorithm to perform the operation
}
return value;
Overloading the Stream Insertion (<<) and Extraction (>>)
Operators
The operator function that overloads the insertion operator, <<, or the extraction operator,
>>, for a class must be a nonmember function of that class for the following reason
...
Because the leftmost operand of << is not an object of
type rectangleType, the operator function that overloads the insertion operator for
rectangleType must be a nonmember function of the class rectangleType
...
Operator Overloading |
99
OVERLOADING THE STREAM INSERTION OPERATOR (<<)
The general syntax to overload the stream insertion operator, <<, for a class is described next
...
//osObject <<
...
return osObject;
In this function definition:
•
•
Both parameters are reference parameters
...
• The second parameter is a const reference to a particular class
...
OVERLOADING THE STREAM EXTRACTION OPERATOR (>>)
The general syntax to overload the stream extraction operator, >>, for a class is described next
...
//isObject >>
...
return isObject;
We note the following in this function definition
...
The first parameter—that is, isObject—is a reference to an istream object
...
The data
read will be stored in the object
...
Example 2-6 shows how the stream insertion and extraction operators are overloaded for
the class rectangleType
...
EXAMPLE 2-6
The definition of the class rectangleType and the definitions of the operator functions are as follows:
#include
using namespace std;
class rectangleType
{
//Overload the stream insertion and extraction operators
friend ostream& operator<< (ostream&, const rectangleType &);
friend istream& operator>> (istream&, rectangleType &);
public:
void setDimension(double l, double w);
double getLength() const;
double getWidth() const;
double area() const;
double perimeter() const;
void print() const;
rectangleType operator+(const rectangleType&) const;
//Overload the operator +
rectangleType operator*(const rectangleType&) const;
//Overload the operator *
bool operator==(const rectangleType&) const;
//Overload the operator ==
bool operator!=(const rectangleType&) const;
//Overload the operator !=
rectangleType();
rectangleType(double l, double w);
private:
double length;
double width;
};
//The definitions of the functions operator+, operator*, operator==,
//operator!=, and the constructor are the same as in Example 2-5
...
length
<< "; Width = " << rectangle
...
length >> rectangle
...
(We assume that the definition of the class
rectangleType is in the header file rectangleType
...
)
//****************************************************************
// Author: D
...
Malik
//
// This program shows how to use the modified class rectangleType
...
h"
//Line 2
using namespace std;
//Line 3
int main()
{
rectangleType myRectangle(23, 45);
rectangleType yourRectangle;
//Line
//Line
//Line
//Line
4
5
6
7
cout << "Line 8: myRectangle: " << myRectangle
<< endl;
//Line 8
cout << "Line 9: Enter the length and width "
<< "of a rectangle: ";
cin >> yourRectangle;
cout << endl;
//Line 9
//Line 10
//Line 11
cout << "Line 12: yourRectangle: "
<< yourRectangle << endl;
//Line 12
cout <<
<<
cout <<
<<
}
"Line 13: myRectangle + yourRectangle: "
myRectangle + yourRectangle << endl;
"Line 14: myRectangle * yourRectangle: "
myRectangle * yourRectangle << endl;
return 0;
//Line 13
//Line 14
//Line 15
//Line 16
102 |
Chapter 2: Object-Oriented Design (OOD) and C++
Sample Run: In this sample run, the user input is shaded
...
The statement in Line 8 outputs the value of
myRectangle using cout and the insertion operator
...
The statement in Line 12
outputs the value of yourRectangle using cout and the insertion operator
...
Similarly, the cout statement in Line 14 multiplies the lengths and
widths of myRectangle and yourRectangle and outputs the result
...
OVERLOADING UNARY OPERATORS
The process of overloading unary operators is similar to the process of overloading binary
operators
...
Therefore,
to overload a unary operator for a class we do the following
...
If the operator function is a nonmember—that is, a friend function of
the class—it has one parameter
...
Certain
operators must be overloaded as member functions of the class, and some must be
overloaded as nonmember (friend) functions
...
If you overload + as a member function, the operator + has direct access to the data
members of one of the objects, and you need to pass only one object as a parameter
...
Therefore, overloading + as a nonmember could require additional memory
and computer time to make a local copy of the data
...
Programming Example: Complex Numbers
PROGRAMMING EXAMPLE:
|
103
Complex Numbers
A number of the form a + ib, where i2 ¼ -1, and a and b are real numbers, is called a
complex number
...
Complex
numbers can also be represented as ordered pairs (a, b)
...
In this
example, we will construct a data type, complexType, that can be used to process
complex numbers
...
We will also overload the operators + and * to
perform addition and multiplication of complex numbers
...
#ifndef H_complexNumber
#define H_complexNumber
//*****************************************************************
// Author: D
...
Malik
// class complexType
...
//*****************************************************************
#include
using namespace std;
class complexType
{
//Overload the stream insertion and extraction operators
friend ostream& operator<<(ostream&, const complexType&);
friend istream& operator>>(istream&, complexType&);
public:
void setComplex(const double& real, const double& imag);
//Function to set the complex numbers according to
//the parameters
...
//Postcondition: real = realPart; imag = imaginaryPart;
2
104 |
Chapter 2: Object-Oriented Design (OOD) and C++
complexType(double real = 0, double imag = 0);
//Constructor
//Initializes the complex number according to the parameters
...
The definitions of most of these functions are straightforward
...
To output a complex number in the form:
(a, b)
where a is the real part and b is the imaginary part, clearly the algorithm is as follows:
a
...
c
...
e
...
Output the real part
...
Output the imaginary part
...
Therefore, the definition of the function operator<< is as follows:
Programming Example: Complex Numbers
|
105
ostream& operator<<(ostream& osObject, const complexType& complex)
{
osObject << "(";
//Step a
osObject << complex
...
imaginaryPart; //Step d
osObject << ")";
//Step e
}
return osObject;
//return the ostream object
Next, we discuss the definition of the function to overload the stream extraction
operator, >>
...
Clearly, the algorithm to read this complex number is as follows:
a
...
c
...
e
...
Read and store the real part
...
Read and store the imaginary part
...
Following these steps, the definition of the function operator>> is as follows:
istream& operator>>(istream& isObject, complexType& complex)
{
char ch;
isObject
isObject
isObject
isObject
isObject
}
>>
>>
>>
>>
>>
ch;
complex
...
imaginaryPart;
ch;
return isObject;
//Step
//Step
//Step
//Step
//Step
a
b
c
d
e
//return the istream object
The definitions of the other functions are as follows:
bool complexType::operator==
(const complexType& otherComplex) const
{
return (realPart == otherComplex
...
imaginaryPart);
}
2
106 |
Chapter 2: Object-Oriented Design (OOD) and C++
//Constructor
complexType::complexType(double real, double imag)
{
realPart = real;
imaginaryPart = imag;
}
//Function to set the complex number after the object
//has been declared
...
realPart = realPart + otherComplex
...
imaginaryPart = imaginaryPart
+ otherComplex
...
realPart = (realPart * otherComplex
...
imaginaryPart);
temp
...
imaginaryPart)
+ (imaginaryPart * otherComplex
...
S
...
//**********************************************************
#include
#include "complexType
...
Line 9: Num1 = (23, 34)
Line 10: Num2 = (0, 0)
Line 11: Enter the complex number in the form (a, b): (3, 4)
Line
Line
Line
Line
14:
16:
17:
18:
New value of num2 = (3, 4)
Num3 = (26, 38)
(23, 34) + (3, 4) = (26, 38)
(23, 34) * (3, 4) = (-67, 194)
2
108 |
Chapter 2: Object-Oriented Design (OOD) and C++
Function Overloading
The previous section discussed operator overloading
...
The types of arguments used with an operator determine the action to take
...
Recall that a class can have more than one constructor, but all constructors of a class have the
same name, which is the name of the class
...
Overloading a function refers to the creation of several functions with the same name
...
The types of parameters determine which function to execute
...
Both items
can be integers, floating-point numbers, characters, or strings
...
These functions all
perform similar operations
...
Thus, you can write the previous function prototypes simply as
int larger(int x, int y);
char larger(char first, char second);
double larger(double u, double v);
string larger(string first, string second);
If the call is larger(5,3), for example, the first function executes
...
For function overloading to work, we must give the definition of each function
...
Templates
Templates are very powerful features of C++
...
The syntax we use for templates is as follows:
template
declaration;
Templates |
109
where Type is the type of data, and declaration is either a function declaration or a
class declaration
...
The word class in the heading
refers to any user-defined type or built-in type
...
Just as variables are parameters to functions, types (that is, data types) are parameters to
templates
...
To implement the function larger,
we need to write four function definitions for the data type: one for int, one for char, one
for double, and one for string
...
C++
simplifies the process of overloading functions by providing function templates
...
It is used to specify the
type of parameters to the function and the return type of the function, and to declare
variables within the function
...
In the function
heading, the type of the formal parameters x and y is Type, which will be specified by the
type of the actual parameters when the function is called
...
Because 5 and 6 are of type int, the data type
int is substituted for Type and the compiler generates the appropriate code
...
2
110 |
Chapter 2: Object-Oriented Design (OOD) and C++
The following example illustrates the use of function templates
...
//****************************************************************
// Author: D
...
Malik
//
// This program illustrates how to write and use a template in a
// program
...
6 and 3
...
6, 3
...
6 and 3
...
6
13: Larger of Hello and Happy = Hello
Class Templates
Like function templates, class templates are used to write a single code segment for a set of
related classes
...
If the list element type changes from int to, say, char, double, or
string, we need to write separate classes for each element type
...
Using class templates, we can create a generic class listType, and the compiler can
generate the appropriate source code for a specific implementation
...
For example, if the template parameter type is int, we can
generate a list to process integers; if the parameter type is string, we can generate a list
to process strings
...
To derive a specific list from this list and to add or rewrite
the operations, we declare the array containing the list elements and the length of the list
as protected
...
Suppose that you want to create a list to process integer
data
...
Similarly, the statement
listType
//Line 2
declares stringList to be a list of 100 components, with each component being of
type string
...
A template instantiation can
be created with either a built-in or user-defined type
...
Thus, when
giving the definitions of function members of a class template, we must follow the
definition of the function template
...
...
}
In the heading of the member function’s definition, elemType specifies the data type of
the list elements
...
The
object code was generated from the implementation file (independently of any client
code) and linked with the client code
...
Passing parameters to a function has an effect at run time, whereas passing a parameter to
a class template has an effect at compile time
...
Quick Review |
113
This problem has several possible solutions
...
Another alternative is to put the class definition and the definitions of the functions in
separate files (as usual), but include a directive to the implementation file at the end of the
header file (that is, the specification file)
...
For illustrative purposes, we will put the class definition and the function definitions in the same header file
...
2
...
4
...
6
...
8
...
10
...
12
...
14
...
16
...
Inheritance is an ‘‘is a’’ relationship
...
In single inheritance, the derived class is derived from only one existing
class, called the base class
...
The private members of a base class are private to the base class
...
The public members of a base class can be inherited either as public,
protected, or private by the derived class
...
A call to a base class’s constructor is specified in the heading of the
definition of the derived class’s constructor
...
Review the inheritance rules given in this chapter
...
In composition, a call to the constructor of the member objects is specified
in the heading of the definition of the class’s constructor
...
An operator that has different meanings with different data types is said to
be overloaded
...
Similarly, >> is used as a stream extraction operator and as a right shift
operator
...
2
114 |
17
...
Chapter 2: Object-Oriented Design (OOD) and C++
The function that overloads an operator is called an operator function
...
20
...
22
...
24
...
26
...
28
...
30
...
32
...
34
...
36
...
38
...
In C++, operator is a reserved word
...
Except for the assignment operator and the member selection operator, to
use an operator on class objects, that operator must be overloaded
...
When an operator is overloaded, its precedence cannot be changed, its
associativity cannot be changed, default arguments cannot be used, the
number of arguments that the operator takes cannot be changed, and
the meaning of how an operator works with built-in data types remains
the same
...
Only existing operators can be
overloaded
...
The operators that cannot be overloaded are
...
The pointer this refers to the object as a whole
...
A friend function is a nonmember of a class
...
In C++, friend is a reserved word
...
The binary operator function as a member of a class has only one parameter; as a nonmember of a class, it has two parameters
...
In C++, a function name can be overloaded
...
In C++, template is a reserved word
...
Using templates, you can write a single code segment for a set of related
classes—called the class template
...
|
115
A syntax of a template is
template
declaration;
41
...
43
...
The word class in the heading refers to any user-defined data type
or built-in data type
...
In a class template, the parameter elemType specifies how a generic class
template is to be customized to form a specific class
...
The heading of the function definition of func is
template
funcType cType
44
...
Suppose cType is a class template, which can take int as a parameter
...
EXERCISES
1
...
a
...
c
...
e
...
g
...
i
...
The constructor of a derived class specifies a call to the constructor of
the base class using the name of the class
...
The constructor of
x specifies a call to the constructor of y by using the object name of
type y
...
In C++, all operators can be overloaded for user-defined data types
...
The function that overloads an operator is called the operator function
...
The precedence of an operator cannot be changed, but its associativity
can be changed
...
k
...
l
...
m
...
n
...
Draw a class hierarchy in which several classes are derived from a single base class
...
Give examples of data
and function members that can be added to the class employeeType
...
Consider the following class definition:
j
...
3
...
5
...
class bClass public aClass
{
public:
void print();
void set(int, int, int);
private:
int z;
};
b
...
117
Consider the following statements:
class yClass
{
public:
void one();
void two(int, int);
yClass();
private:
int a;
int b;
};
class xClass: public yClass
{
public:
void one();
xClass();
private:
int z;
};
yClass y;
xClass x;
a
...
The private members of yClass are public members of xClass
...
If a statement is
invalid, explain why
...
void yClass::one()
{
cout << a + b << endl;
}
ii
...
a = 15;
x
...
void xClass::one()
{
a = 10;
b = 15;
z = 30;
cout << a + b + z << endl;
}
iv
...
|
cout << y
...
b << " " << x
...
a
...
Write the definition of
private data members
Write the definition of
private data members
the default constructor of yClass so that the
of yClass are initialized to 0
...
2
118 |
Chapter 2: Object-Oriented Design (OOD) and C++
Write the definition of the member function two of yClass so that the
private data member a is initialized to the value of the first parameter
of two, and the private data member b is initialized to the value of the
second parameter of two
...
8
...
...
int main()
{
classA aObject;
}
9
...
setX(4);
return 0;
//Line
//Line
//Line
//Line
1
2
3
4
//Line 5
//Line 6
//Line 7
Consider the following code:
class one
{
public:
void print() const;
//Outputs the values of x and y
protected:
void setData(int u, int v);
//Postcondition: x = u; y = v;
private:
int x;
int y;
};
class two: public one
{
public:
void setData(int a, int b, int c);
//Postcondition: x = a; y = b; z = c;
void print() const;
//Outputs the values of x, y, and z
private:
int z;
};
a
...
Write the definition of the function setData of the class two
...
Exercises
10
...
print();
derivedObject
...
What is the output of the following program?
#include
using namespace std;
class baseClass
{
public:
void print()const;
int getX();
baseClass(int a = 0);
protected:
int x;
};
class derivedClass: public baseClass
{
public:
void print()const;
int getResult();
derivedClass(int a = 0, int b = 0);
private:
int y;
};
int main()
{
baseClass baseObject(7);
derivedClass derivedObject(3,8);
baseObject
...
print();
Exercises
|
121
cout << "**** " << baseObject
...
getResult() << endl;
}
return 0;
void baseClass::print() const
{
cout << "In base: x = " << x << endl;
}
baseClass::baseClass(int a)
{
x = a;
}
int baseClass::getX()
{
return x;
}
void derivedClass::print() const
{
cout << "In derived: x = " << x << ", y = " << y
<< ", x + y = " << x + y << endl;
}
int derivedClass::getResult()
{
return x + y;
}
derivedClass::derivedClass(int a, int b)
:baseClass(a)
{
y = b;
}
12
...
14
...
What is a friend function?
Suppose that the operator << is to be overloaded for a user-defined class
mystery
...
How many parameters does the function operator+ have?
Consider the following declaration:
class strange
{
...
...
b
...
c
...
d
...
Assume the declaration of Exercise 15
...
16
...
b
...
c
...
Find the error(s) in the following code:
a
...
class mystery
{
...
};
bool mystery::<=(mystery rightObj)
{
...
//Line 1
//Line 2
//Line 3
Find the error(s) in the following code:
class mystery
{
...
};
19
...
friend operator+ (mystery);
//Overload the binary operator +
...
//Line 1
//Line 1
//Line 2
How many parameters are required to overload the preincrement operator
for a class as a member function?
Exercises
21
...
23
...
How many parameters are required to overload the preincrement operator
for a class as a friend function?
How many parameters are required to overload the postincrement operator
for a class as a member function?
How many parameters are required to overload the postincrement operator
for a class as a friend function?
Find the error(s) in the following code:
template
class strange
{
...
//Line 1
//Line 2
//Line 3
//Line 4
Consider the following declaration:
template
class strange
{
...
b
...
c
...
Write the definition of the function
operator== for the class strange, which is overloaded as a member
function
...
26
...
cout << surprise(5, 7) << endl;
b
...
Chapter 2: Object-Oriented Design (OOD) and C++
Consider the definition of the following function template:
Template
Type funcExp(Type list[], int size)
{
Type x = list[0];
Type y = list[size - 1];
for (int j = 1; j < (size - 1)/2; j++)
{
if (x < list[j])
x = list[j];
if (y > list[size - 1 -j])
y = list[size - 1 -j];
}
}
return x + y;
Further suppose that you have the following declarations:
int list[10] = {5,3,2,10,4,19,45,13,61,11};
string strList[] = {"One", "Hello", "Four", "Three", "How", "Six"};
What is the output of the following statements?
a
...
28
...
PROGRAMMING EXERCISES
1
...
In Chapter 1, the class clockType was designed to implement the time
of day in a program
...
Derive the class
extClockType from the class clockType by adding a data member
to store the time zone
...
Also, write the definitions of the
member functions and the constructors
...
In this chapter, the class dateType was designed to implement the date
in a program, but the member function setDate and the constructor do
not check whether the date is valid before storing the date in the data
members
...
Add a function member,
isLeapYear, to check whether a year is a leap year
...
Programming Exercises
3
...
5
...
A point in the x-y plane is represented by its x-coordinate and y-coordinate
...
You should then perform operations on the point, such as showing
the point, setting the coordinates of the point, printing the coordinates of the
point, returning the x-coordinate, and returning the y-coordinate
...
Every circle has a center and a radius
...
Given the center, we can determine its
position in the x-y plane
...
Design a class, circleType, that can store the radius and center of the
circle
...
You
should be able to perform the usual operations on a circle, such as setting
the radius, printing the radius, calculating and printing the area and circumference, and carrying out the usual operations on the center
...
Design a
class, cylinderType, that can capture the properties of a cylinder and
perform the usual operations on a cylinder
...
Some of the
operations that can be performed on a cylinder are as follows: Calculate
and print the volume, calculate and print the surface area, set the height, set
the radius of the base, and set the center of the base
...
Redefine
the class dateType so that it can perform the following operations on a
date in addition to the operations already defined:
a
...
c
...
e
...
g
...
i
...
Set the day
...
Return the month
...
Return the year
...
Return the number of days in the month
...
Return the number of days passed in the year
...
Note that the
number of days returned also includes the current day
...
For example, if the
date is 3-18-2011, the number of days remaining in the year is 288
...
Calculate the new date by adding a fixed number of days to the date
...
Write the definitions of the functions to implement the operations defined
for the class dateType in Programming Exercise 6
...
Some applications might require the date to be printed in
another form, such as March 24, 2003
...
j
...
8
...
Add a function member to output the month in the
string format followed by the year—for example, in the form March 2003
...
9
...
To print
a monthly calendar, you must know the first day of the month and the number of
days in that month
...
Clearly, the
month and the year can be stored in an object of the form extDateType by
setting the day component of the date to 1, and the month and year as specified
by the user
...
Design the class calendarType so that the program can print a calendar
for any month starting January 1, 1500
...
To calculate the first day of a month, you can add
the appropriate days to Monday of January 1, 1500
...
b
...
d
...
f
...
Determine the first day of the month for which the calendar will be
printed
...
Set the month
...
Return the month
...
Print the calendar for the particular month
...
Programming Exercises
10
...
b
...
Write a test program to print the calendar for either a particular month
or a particular year
...
12
...
This chapter discussed how to
overload various operators
...
Also write a test program to test various
operations of the class clockType
...
Extend the definition of the class complexType so that it performs
the subtraction and division operations
...
If (a, b) and (c, d) are complex numbers,
(a, b) À (c, d) ¼ (a À c, b À d),
If (c, d) is nonzero,
(a, b) / (c, d) ¼ ((ac + bd) / (c2 + d2), (Àad + bc) / (c2 + d2))
b
...
13
...
b
...
Write the definitions of the functions to overload the operators – and /
as defined in part a
...
Format your answer with two decimal places
...
Write the definitions of the member functions of the class
complexType as designed in part a
...
Format your answer with
two decimal places
...
15
...
17
...
Chapter 2: Object-Oriented Design (OOD) and C++
Let a + ib be a complex number
...
Extend the definition of the class
complexType of the Programming Example, Complex Numbers by overloading the operators $ and ! as member functions so that $ returns the
conjugate of a complex number and ! returns the absolute value
...
Redo Programming Exercise 13 so that the operators $ and ! are overloaded as nonmember functions
...
So an integer larger than this
cannot be stored and processed as an integer
...
One way to store and manipulate large integers is to store each individual digit
of the number in an array
...
Overload the
operators + and – to add and subtract, respectively, the values of two objects
of this class
...
) Overload the assignment operator to copy the
value of a large integer into another large integer
...
Your program
must contain appropriate constructors to initialize objects of the class
largeIntegers
...
Add instance variables to store the number of
digits and the sign of the number
...
If b2 À 4ac ¼ 0,
the equation has a single (repeated) root
...
If b2 À 4ac < 0, the equation has two complex roots
...
Overload the operators + and – to add and subtract, respectively, the corresponding coefficients
of two quadratic equations
...
Add appropriate constructors to initialize objects
...
Also, include function members to
determine and output the type and the roots of the equation
...
Programming Exercise 6 in Chapter 1 describes how to design the class
lineType to implement a line
...
b
...
d
...
f
...
h
...
Overloads the stream insertion operator, <<, for easy output
...
(The line
ax + by ¼ c is input as (a, b, c)
...
Overloads the unary operator + as a member function, so that it returns
true if a line is vertical; false otherwise
...
Overloads the operator == as a member function, so that it returns true
if two lines are equal; false otherwise
...
Overloads the operator && as a member function, so that it returns
true if two lines are perpendicular; false otherwise
...
Rational fractions are of the form a / b, where a and b are integers and b 6¼ 0
...
Suppose a / b
and c / d are fractions
...
Fractions are compared as follows: a / b op c / d if ad op bc, where op is any of
the relational operations
...
Design a class—say, fractionType—that performs the arithmetic and
relational operations on fractions
...
Also, overload the stream insertion and stream extraction operators for easy input and output
...
b
...
Among other things, test the following: Suppose x, y, and z are objects
of type fractionType
...
The statement
cout << x + y << endl;
|
129
2
130 |
Chapter 2: Object-Oriented Design (OOD) and C++
should output the value of x + y in fraction form
...
a
...
Your answer
need not be in the lowest terms
...
In that
exercise, we also implemented a function, romanToDecimal, to convert a Roman numeral into its equivalent decimal number
...
Use the class string to manipulate
the strings
...
The stream insertion
operator outputs the Roman numeral in the Roman format
...
Write the definition of the member
function decimalToRoman
...
c
...
For simplicity, we assume that only the letter I can appear in front of
another letter and that it appears only in front of the letters V and X
...
Also, 40 will be represented
as XXXX, 190 will be represented as CLXXXX, and so on
...
In the class extRomanType, overload the arithmetic
operators +, -, *, and / so that arithmetic operations can be performed
on Roman numerals
...
To add (subtract, multiply, or divide) Roman numerals, add (subtract,
multiply, or divide, respectively) their decimal representations and then
convert the result to the Roman numeral format
...
Similarly, for division, the
numerator must be larger than the denominator
...
Write the definitions of the functions to overload the operators
described in part b
...
3
CHAPTER
P OINTERS AND
A RRAY -B ASED L ISTS
I N T H I S C H A P T E R , YO U W I L L :
...
Explore how to declare and manipulate pointer variables
...
Discover dynamic variables
...
Learn about pointer arithmetic
...
Become aware of the shallow and deep copies of data
...
Explore how dynamic arrays are used to process lists
...
Become aware of abstract classes
132 |
Chapter 3: Pointers and Array-Based Lists
The data types in C++ are classified into three categories: simple, structured, and pointers
...
This chapter discusses
the third data type: the pointer data type
...
Later, you use
these concepts when you study dynamic arrays and linked lists
...
The Pointer Data Type and Pointer Variables
The values belonging to pointer data types are the memory addresses of your computer
...
Because the
domain, (that is, the values of a pointer data type), consists of addresses (memory
locations), a pointer variable is a variable whose content is an address, that is, a memory
location
...
Declaring Pointer Variables
The value of a pointer variable is an address
...
The data is typically stored in this memory space
...
In C++, you declare a pointer variable by using the asterisk symbol ( *) between the
data type and the variable name
...
The content of p (when properly
assigned) points to a memory location of type int, and the content of ch points to a
memory location of type char
...
Before discussing how pointers work, let us make the following observations
...
Now consider the following statement:
int* p, q;
In this statement, only p is a pointer variable, not q
...
To avoid
confusion, we prefer to attach the character * to the variable name
...
Now that you know how to declare pointers, next we discuss how to make a pointer
point to a memory space and how to manipulate the data stored in these memory
locations
...
For example, if p is a pointer of type int, p can
store the address of any memory space of type int
...
The
next two sections describe these operators
...
For example, given the statements
int x;
int *p;
the statement
p = &x;
assigns the address of x to p
...
Dereferencing Operator (*)
The previous chapters used the asterisk character, *, as the binary multiplication operator
...
When *, commonly referred to as the dereferencing operator or indirection operator, is used as a unary operator, * refers to the
3
134 |
Chapter 3: Pointers and Array-Based Lists
object to which the operand of the * (that is, the pointer) points
...
Also, the statement
*p = 55;
stores 55 in the memory location to which p points—that is, 55 is stored in x
...
EXAMPLE 3-1
Let us consider the following statements:
int *p;
int num;
In these statements, p is a pointer variable of type int and num is a variable of type int
...
(See Figure 3-1
...
...
num = 78;
2
...
*p = 24;
...
After
statement
Values of the variables
1
...
78
1200
p
...
78
24
1200
p
1800
num
The statement num = 78; stores 78 into
num
...
1800
...
1800
num
1200
p
2
Explanation
3
...
The statement p = # stores the
address of num, which is 1800, into p
...
Because
the value of p is 1800, statement 3 stores
24 into memory location 1800
...
Let us summarize the preceding discussion
...
A declaration such as int *p; allocates memory for p only, not for *p
...
2
...
3
...
4
...
5
...
6
...
Note
that the value of *p is 78 after the statement p = # executes; the
value of *p is 24 after the statement *p = 24; executes
...
EXAMPLE 3-2
//***********************************************************
// Author: D
...
Malik
//
// This program illustrates how a pointer variable works
...
Let us look at some of these
statements
...
The statement in
Line 9 outputs the value of &num1, the address of num1, and the value of p
...
When you execute this program on your
computer, you are likely to get different values of &num1 and p
...
Because p points to the memory location of num1,
*p outputs the value of this memory location, that is, of num1
...
Because p points to the memory location num1, the value
of num1 is also changed
...
The statement in Line 13 stores the address of num2 into p
...
So, any change that *p makes immediately changes the value
The Pointer Data Type and Pointer Variables |
137
of num2
...
This
statement in Line 16 multiplies the value of *p, which is the value of num2, by 2 and
stores the new value into *p
...
The
statement in Line 17 outputs the value of num2 and *p
...
The second
statement allocates memory of type string and stores the address of the allocated memory
in str
...
Now suppose that you want to use the string function length to find the length of
the string "Sunny Day"
...
length() returns the length of the string
...
The expression (*str)
...
In C++, the dot operator,
...
Let us elaborate on this a little more
...
length(), the operator * evaluates first, so the expression *str
evaluates first
...
Therefore, in the expression (*str)
...
Now consider the expression *str
...
Let us see how this expression gets evaluated
...
has a higher precedence than *, the
expression str
...
The expression str
...
As you can see, in the expression (*str)
...
However, typos are unavoidable
...
The operator -> consists of two consecutive
symbols: a hyphen and the ‘‘greater than’’ symbol
...
length()
is equivalent to the expression
str->length()
3
138 |
Chapter 3: Pointers and Array-Based Lists
Accessing class (struct) components via pointers using the operator -> thus eliminates
the use both of the parentheses and of the dereferencing operator
...
Initializing Pointer Variables
Because C++ does not automatically initialize variables, pointer variables must be
initialized if you do not want them to point to anything
...
Thus, the statement p = 0; stores the
null pointer in p; that is, p points to nothing
...
The following two statements are equivalent:
p = NULL;
p = 0;
The number 0 is the only number that can be directly assigned to a pointer variable
...
However, you learned how to use pointers to manipulate
data only into memory spaces that were created using other variables
...
So what is the benefit to using
pointers? You can access these memory spaces by working with the variables that were
used to create them
...
In
particular, you learn how to allocate and deallocate memory during program execution
using pointers
...
With
the help of pointers, C++ creates dynamic variables
...
When a program
requires a new variable, the operator new is used
...
In C++, new and delete are reserved words
...
The syntax to use the operator new is as follows:
new dataType;
new dataType[intExp];
//to allocate a single variable
//to allocate an array of variables
where intExp is any expression evaluating to a positive integer
...
Moreover, the allocated
memory is uninitialized
...
However, no new memory is allocated
...
The allocated memory is accessed via
pointer dereferencing—namely, *p
...
Because a dynamic variable is unnamed, it cannot be accessed directly
...
The following statements illustrate this concept:
int *p;
//p is a pointer of type int
p = new int;
*p = 28;
//allocates memory of type int and stores the address
//of the allocated memory in p
//stores 28 in the allocated memory
The operator new allocates memory space of a specific type and returns the (starting)
address of the allocated memory space
...
Operator delete
Suppose you have the following declaration:
int *p;
This statement declares p to be a pointer variable of type int
...
The statement in Line 1 allocates memory space
of type int and stores the address of the allocated memory space into p
...
Then, the value of p after the execution of
this statement is 1500
...
)
1500
p 1500
FIGURE 3-2
p after the execution of p = new int;
In Figure 3-2, the number 1500 on top of the box indicates the address of the memory
space
...
In other words, after execution of the statement in Line 2, the value stored into
memory space at location 1500 is 54
...
)
p 1500
FIGURE 3-3
1500
54
p and *p after the execution of *p = 54;
Next, the statement in Line 3 executes, which allocates a memory space of type int and
stores the address of the allocated memory space into p
...
It follows that the value of p is now 1800
...
)
1500
54
1800
p 1800
FIGURE 3-4
p after the execution of p = new int;
The statement in Line 4 stores 73 into the memory space to which p points, which is
1800
...
(See Figure 3-5
...
The previous
memory space at location 1500 is now inaccessible
...
In other words, it cannot be reallocated
...
That is, there is an unused memory space that cannot be allocated
...
There will be a good amount of memory leak
...
The question at hand is how to avoid memory leak
...
The C++ operator
delete is used to destroy dynamic variables, so that its memory space can be allocated
again when needed
...
Suppose p is a pointer variable, as declared previously
...
Depending
on a particular system, after these statements execute, these pointer variables might still contain
the addresses of the deallocated memory spaces
...
Therefore, if you later access the memory spaces via these pointers without properly
initializing them, depending on a particular system, either the program will access a wrong
memory space, which might result in corrupting data, or the program will terminate with an error
message
...
The program in the following example illustrates how to allocate dynamic memory and
how to manipulate data into that dynamic memory
...
S
...
//***********************************************************
#include
//Line 1
using namespace std;
//Line 2
int main()
{
int *p;
int *q;
//Line
//Line
//Line
//Line
p = new int;
*p = 34;
cout << "Line 9: p = " << p
<< ", *p = " << *p << endl;
q = p;
cout << "Line 11: q = " << q
<< ", *q = " << *q << endl;
*q = 45;
cout << "Line
<< ", *p
cout << "Line
<< ", *q
p = new int;
*p = 18;
cout << "Line
<< ", *p
cout << "Line
<< ", *q
delete q;
q = NULL;
q = new int;
*q = 62;
cout << "Line
<< ", *p
cout << "Line
<< ", *q
}
return 0;
3
4
5
6
//Line 7
//Line 8
//Line 9
//Line 10
//Line 11
//Line 12
13:
= "
14:
= "
p = "
<< *p
q = "
<< *q
<<
<<
<<
<<
p
endl;
q
endl;
//Line 13
//Line 14
//Line 15
//Line 16
17:
= "
18:
= "
p = "
<< *p
q = "
<< *q
<<
<<
<<
<<
p
endl;
q
endl;
//Line 17
//Line 18
//Line
//Line
//Line
//Line
23:
= "
24:
= "
p = "
<< *p
q = "
<< *q
<<
<<
<<
<<
p
endl;
q
endl;
19
20
21
22
//Line 23
//Line 24
//Line 25
//Line 26
The Pointer Data Type and Pointer Variables |
143
Sample Run:
Line
Line
Line
Line
Line
Line
Line
Line
9: p = 00355620, *p = 34
11: q = 00355620, *q = 34
13: p = 00355620, *p = 45
14: q = 00355620, *q = 45
17: p = 003556C8, *p = 18
18: q = 00355620, *q = 45
23: p = 003556C8, *p = 18
24: q = 00355620, *q = 62
3
The statements in Lines 5 and 6 declare p and q to be pointer variables of type int
...
(See Figure 3-6
...
The statement in Line 8 stores
34 into the memory location to which p points
...
)
p
FIGURE 3-7
34
Pointer p and the value of the memory location to which p points
The statement in Line 9 outputs the value of p and *p
...
When you execute this program, you
are likely to get different values of p and q
...
(See Figure 3-8
...
So any changes made into that memory location by q immediately change the
value of *p
...
The statement in
Line 12 stores 45 into the memory location to which q points
...
)
p
45
q
FIGURE 3-9 Pointers p and q and the memory space to which they point after the execution
of the statement in Line 12
The statements in Lines 13 and 14 output the values of p, *p, q, and *q
...
(See Figure 3-10
...
(See Figure 3-11
...
The statement in Line 19 deallocates the memory space to which q points and the
statement in Line 20 sets the value of q to NULL
...
(See Figure 3-12
...
The statement in Line 22 stores 62 in the memory space to
which q points
...
)
p
18
q
62
Pointers p and q and the memory space to which they point after the execution
of the statement in Line 22
FIGURE 3-13
The statements in Lines 23 and 24 output the values of p, *p, q, and *q
...
Operations on Pointer Variables
The operations that are allowed on pointer variables are the assignment and relational
operations and some limited arithmetic operations
...
Two pointer variables of the
same type can be compared for equality, and so on
...
The value of one pointer variable can be subtracted
from another pointer variable
...
After this statement executes, both p and q point to the same
memory location
...
3
146 |
Chapter 3: Pointers and Array-Based Lists
The expression
p == q
evaluates to true if p and q have the same value—that is, if they point to the same
memory location
...
The arithmetic operations that are allowed differ from the arithmetic operations on
numbers
...
The statement
p++; or p = p + 1;
increments the value of p by 4 bytes because p is a pointer of type int
...
The increment operator increments the value of a pointer variable by the size of the
memory to which it is pointing
...
Moreover, the statement
p = p + 2;
increments the value of p by 8 bytes
...
Similarly, when an integer is subtracted from a pointer variable, the value of the pointer variable
is decremented by the integer times the size of the memory to which the pointer is pointing
...
Using pointer arithmetic, the program can
accidentally access the memory locations of other variables and change their content
without warning
...
If a
pointer variable tries to access either the memory spaces of other variables or an illegal
memory space, some systems might terminate the program with an appropriate error
message
...
The Pointer Data Type and Pointer Variables |
147
Dynamic Arrays
The arrays used earlier are called static arrays because their size was fixed at compile time
...
One way to handle this limitation is to declare an array that is large enough to
process a variety of data sets
...
On the other hand, it would be helpful if, during
program execution, you could prompt the user to enter the size of the array and then create an
array of the appropriate size
...
In this section, you learn how to create arrays during program execution and how to
process such arrays
...
To create
a dynamic array, we use the second form of the new operator
...
The statement
p = new int[10];
allocates 10 contiguous memory locations, each of type int, and stores the address of the
first memory location into p
...
Thus, the statement
*p = 25;
stores 25 into the first memory location, and the statements
p++;
*p = 35;
//p points to the next array component
store 35 into the second memory location
...
Of course, after performing a few
increment operations, it is possible to lose track of the first array component
...
For example, the statements
p[0] = 25;
p[1] = 35;
store 25 and 35 into the first and second array components, respectively
...
In general, p[i] refers to the (i + 1)th array component
...
The following for loop initializes each array component to 0:
for (int j = 0; j < 10; j++)
p[j] = 0;
3
148 |
Chapter 3: Pointers and Array-Based Lists
When the array notation is used to process the array to which p points, p stays fixed at
the first memory location
...
EXAMPLE 3-4
The following program segment illustrates how to obtain a user’s response to get the array size
and create a dynamic array during program execution
...
The statement in Line 3 prompts the
user to enter the size of the array, and the statement in Line 4 inputs the array size into the
variable arraySize
...
From this point on,
you can treat intList just like any other array
...
Array Name: A Constant Pointer
The statement
int list[5];
declares list to be an array of five components
...
Suppose the address of the first array component is 1000
...
list
1000
1000
list[0]
FIGURE 3-14
list and array list
1004
1008
list[1] list[2]
1012
list[3]
1016
list[4]
The Pointer Data Type and Pointer Variables |
149
Because the value of list, which is 1000, is a memory address, list is a pointer variable
...
That is, the value of list is constant
...
In fact, any attempt to use the increment or decrement operations
on list results in a compile-time error
...
However,
the data in the array list can be manipulated as usual
...
Similarly, the statement
list[2] = 78; stores 78 into the third component of list
...
)
list 1000
25
1000
list[0]
FIGURE 3-15
78
1004
1008
list[1] list[2]
1012
list[3]
1016
list[4]
Array list after the execution of the statements list[0] = 25; and list[2] = 78;
If p is a pointer variable of type int, then the statement
p = list;
copies the value of list, which is 1000, the base address of the array, into p
...
An array name is a constant pointer
...
To declare a pointer as a value parameter in a function heading, you use the
same mechanism as you use to declare a variable
...
Therefore, to declare a formal parameter as a reference parameter, you must
use &
...
The obvious question is:
In what order should & and * appear between the data type name and the identifier to
declare a pointer as a reference parameter? In C++, to make a pointer a reference
parameter in a function heading, * appears before the & between the data type name
and the identifier
...
...
}
3
150 |
Chapter 3: Pointers and Array-Based Lists
In this example, both p and q are pointers
...
Pointers and Function Return Values
In C++, the return type of a function can be a pointer
...
)
{
...
...
Dynamic Two-Dimensional Arrays
The beginning of this section discussed how to create dynamic one-dimensional arrays
...
In this section, we discuss how to create
dynamic two-dimensional arrays
...
There are various ways you can create dynamic two-dimensional arrays
...
Consider the statement:
int *board[4];
This statement declares board to be an array of four pointers wherein each pointer is of
type int
...
Suppose that each row of board
has six columns
...
for (int row = 0; row < 4; row++)
board[row] = new int[6];
Note that the expression new int[6] creates an array of six components of type int and
returns the base address of the array
...
It follows that after the execution of the previous for loop,
board is a two-dimensional array of 4 rows and 6 columns
...
In other words, the number of
columns of board can be specified during execution
...
So in reality, board is not a true dynamic two-dimensional array
...
In other words, board and
*board are pointers
...
The Pointer Data Type and Pointer Variables |
151
Suppose that you want board to be an array of 10 rows and 15 columns
...
The following statement accomplishes this:
board = new int* [10];
Next we create the columns of board
...
For
example, see the next example
...
The program in Example 3-5 further
explains how to create two-dimensional arrays
...
S
...
//*************************************************************
#include
#include
//Line 1
//Line 2
using namespace std;
//Line 3
void fill(int **p, int rowSize, int columnSize);
void print(int **p, int rowSize, int columnSize);
//Line 4
//Line 5
int main()
{
int **board;
//Line 6
//Line 7
//Line 8
int rows;
int columns;
//Line 9
//Line 10
cout << "Line 11: Enter the number of rows "
<<"and columns: ";
cin >> rows >> columns;
cout << endl;
//Line 11
//Line 12
//Line 13
//Create the rows of board
board = new int* [rows];
//Line 14
//Create the columns of board
for (int row = 0; row < rows; row++)
board[row] = new int[columns];
//Line 15
//Line 16
152 |
Chapter 3: Pointers and Array-Based Lists
//Insert elements into board
fill(board, rows, columns);
cout << "Line 18: Board:" << endl;
//Line 18
//Output the elements of board
print(board, rows, columns);
}
//Line 17
//Line 19
return 0;
//Line 20
//Line 21
void fill(int **p, int rowSize, int columnSize)
{
for (int row = 0; row < rowSize; row++)
{
cout << "Enter " << columnSize << " number(s) for row "
<< "number " << row << ": ";
for (int col = 0; col < columnSize; col++)
cin >> p[row][col];
cout << endl;
}
}
void print(int **p, int rowSize, int columnSize)
{
for (int row = 0; row < rowSize; row++)
{
for (int col = 0; col < columnSize; col++)
cout << setw(5) << p[row][col];
cout << endl;
}
}
Sample Run: In this sample run, the user input is shaded
...
The function fill
prompts the user to enter the elements of a two-dimensional array of type int
...
The Pointer Data Type and Pointer Variables |
153
For the most part, the preceding output is self-explanatory
...
The statement in Line 8 declares board to be a pointer to a pointer
of type int
...
The statement in Line 11 prompts the user to input the number of rows and number of
columns
...
The statement in Line 14 creates the
rows of board and the for loop in Lines 15 and 16 creates the columns of board
...
Shallow Vs
...
This
event might result in unsuspected or erroneous results
...
To facilitate the discussion, we will use diagrams to show pointers and
their related memory
...
first
FIGURE 3-16
10
36
89
29
47
64
28
92
37
73
Pointer first and its array
Next, consider the following statement:
second = first;
//Line A
This statement copies the value of first into second
...
first
10
36
89
29
47
64
28
92
37
73
second
FIGURE 3-17
first and second after the statement second = first; executes
3
154 |
Chapter 3: Pointers and Array-Based Lists
The statement first[4] = 10; not only changes the value of first[4], it also changes
the value of second[4] because they point to the same array
...
This action
results in Figure 3-18
...
Therefore, if the program later tries to access the memory to which first
pointed, either the program will access the wrong memory or it will terminate in an
error
...
More formally, in a shallow copy, two
or more pointers of the same type point to the same memory; that is, they point to the
same data
...
The second statement copies the array to which first
points into the array to which second points
...
)
first
36
89
29
47
64
28
92
37
73
second
FIGURE 3-19
10
10
36
89
29
47
64
28
92
37
73
first and second both pointing to their own data
Classes and Pointers: Some Peculiarities
|
155
Both first and second now point to their own data
...
This case is an example of a deep copy
...
From the preceding discussion, it follows that you must know when to use a shallow
copy and when to use a deep copy
...
To facilitate the discussion, we use the following class:
class pointerDataClass
{
public:
...
...
(See Figure 3-20
...
Suppose that during program
execution the pointer p creates a dynamic array
...
However, p created a dynamic array,
and dynamic memory must be deallocated using the operator delete
...
How do we ensure that when p is destroyed, the dynamic memory created by p
is also destroyed? Suppose that objectOne is as shown in Figure 3-21
...
Object objectOne and its data
Recall that if a class has a destructor, the destructor automatically executes whenever a
class object goes out of scope (see Chapter 1)
...
For example, the definition of the destructor for the
class pointerDataClass is as follows:
pointerDataClass::~pointerDataClass()
{
delete [] p;
}
Of course, you must include the destructor as a member of the class in its definition
...
Moreover, the remainder of this section assumes that the definition of the destructor is as
given previously—that is, the destructor deallocates the memory space pointed to by p
...
...
private:
int x;
int lenP;
int *p;
};
For the destructor to work properly, the pointer p must have a valid value
...
For this reason, you should exercise extra caution while working
with pointers
...
Suppose that objectOne and objectTwo are as shown in Figure 3-22
...
Objects objectOne and objectTwo
Recall that one of the built-in operations on classes is the assignment operator
...
That is, the value of
objectOne
...
x, and the value of objectOne
...
p
...
That is, both objectTwo
...
p
would point to the same memory space, as shown in Figure 3-23
...
Objects objectOne and objectTwo after the statement objectTwo =
objectOne; executes
FIGURE 3-23
Now, if objectTwo
...
p would
become invalid
...
It suggests that there must be a way to avoid
this pitfall
...
This process is called overloading the assignment operator
...
Once the
assignment operator is properly overloaded, both objectOne and objectTwo have their
own data, as shown in Figure 3-24
...
5
36 24 15
...
General Syntax to Overload the Assignment Operator = for a Class
Function Prototype (to be included in the definition of the class):
const className& operator=(const className&);
Function Definition:
const className& className::operator=(const className& rightObject)
{
//local declaration, if any
if (this != &rightObject) //avoids self-assignment
{
//algorithm to copy rightObject into this object
}
//returns the object assigned
return *this;
}
In the definition of the function operator=:
•
•
•
There is only one formal parameter
...
The return type of the function is a reference to a particular class
...
We must prevent such statements because they waste computer time
...
Let us see how
...
operator=(x);
Because the function operator= is invoked by the object x, the pointer this in the
body of the function operator= refers to the object x
...
Therefore, in the expression
this != &rightObject
this means the address of x, and &rightObject also means the address of x
...
Notice that the return type of the function to overload the assignment operator is a
reference
...
In the section ‘‘Array-Based Lists,’’ later in this chapter, we explicitly illustrate how to
overload the assignment operator
...
For example, consider the following statement:
pointerDataClass objectThree(objectOne);
The object objectThree is being declared and is also being initialized by using the value of
objectOne
...
This initialization is called the default
memberwise initialization
...
Just as in the case of the assignment
operator, because the class pointerDataClass has pointer member variables, this default
initialization would lead to a shallow copying of the data, as shown in Figure 3-25
...
)
objectOne
x 8
lenP 50
objectThree
p
5
FIGURE 3-25
x 8
lenP 50
p
36 24 15
...
The solution to both these problems
is the same
...
Remember that the class pointerDataClass has the destructor, which deallocates
the memory space pointed to by p
...
objectOne
x 8
lenP 50
p
5
FIGURE 3-26
36 24 15
...
Now
consider the following statement:
destroyList(objectOne);
In this statement, objectOne is passed as a parameter to the function destroyList
...
Just
as in the previous case, paramObject
...
p would point to the same
memory space, as shown in Figure 3-27
...
FIGURE 3-27 Pointer member variables of objects objectOne and paramObject pointing
to the same array
Because objectOne is passed by value, the member variables of paramObject should
have their own copy of the data
...
p should have its own
memory space
...
• If, as a parameter, an object is passed by value and the default memberwise copying of data is allowed, it leads to a shallow copying of the data
...
This is usually done by putting a statement that
includes the copy constructor in the definition of the class, and then writing the definition
of the copy constructor
...
Therefore, for the class pointerDataClass, we can overcome this shallow copying
problem by including the copy constructor in the class pointerDataClass
...
p and objectThree
...
Similarly, objectOne
...
p will have their own copies of the data, as
shown in Figure 3-28
...
5
36 24 15
...
p
...
3
162 |
Chapter 3: Pointers and Array-Based Lists
The general syntax to include the copy constructor in the definition of a class is as
follows:
className(const className& otherObject);
Notice that the formal parameter of the copy constructor is a constant reference
parameter
...
For classes with pointer member variables, three things are normally done:
1
...
2
...
3
...
Inheritance, Pointers, and Virtual Functions
Recall that, as a parameter, a class object can be passed either by value or by reference
...
However, in the case of classes, C++ allows the user to pass an object of a derived class to a
formal parameter of the base class type
...
To be specific, let us consider the following classes:
class baseClass
{
public:
void print();
baseClass(int u = 0);
private:
int x;
};
class derivedClass: public baseClass
{
public:
void print();
derivedClass(int u = 0, int v = 0);
private:
int a;
};
The class baseClass has three members
...
Both classes have a member function
...
print();
}
The function callPrint has a formal reference parameter p of type baseClass
...
Moreover, the body of the function callPrint calls the
member function print
...
print();
two
...
Let us look at the statements
in Lines 8 and 9
...
The statement in Line
9 also calls the function callPrint, but passes the object two as the parameter; it generates
the sixth line of the output
...
(Because in Line 9 object two is passed as a parameter to the function
callPrint, one would expect that the output generated by the statement in Line 9 should
be similar to the output in the second and third lines of the output
...
This is due to the fact that the binding of the member function
print, in the body of the function callPrint, occurred at compile time
...
print();, the compiler associates the function print of the class baseClass
...
(Compile-time binding is also known as static binding
...
Thus, when
the body of the function callPrint executes, logically the print function of object two
should execute, which is not the case
...
The binding of virtual
functions occurs at program execution time, not at compile time
...
More formally, in run-time binding, the compiler does not
generate the code to call a specific function
...
Run-time binding is also known as dynamic binding
...
Let us redefine
the previous classes using this feature:
class baseClass
{
public:
virtual void print();
baseClass(int u = 0);
private:
int x;
};
//virtual function
Inheritance, Pointers, and Virtual Functions |
165
class derivedClass: public baseClass
{
public:
void print();
derivedClass(int u = 0, int v = 0);
private:
int a;
};
3
Note that we need to declare a virtual function only in the base class
...
If we execute the
previous program with these modifications, the output is as follows
...
The previous discussion also applies when a formal parameter is a pointer to a class, and a
pointer of the derived class is passed as an actual parameter
...
(We assume that the definition of the class baseClass is in
the header file baseClass
...
h
...
S
...
//******************************************************
#include
//Line 1
#include "derivedClass
...
However, if p is a value parameter, then this mechanism of passing a derived class object as
an actual parameter to p does not work, even if p uses a virtual function
...
Therefore, if a formal parameter is of a class type, the member
variables of the actual object are copied into the corresponding member variables of the
formal parameter
...
Consider the following function definition:
void callPrint(baseClass p)
{
p
...
The member variable x is inherited
from the base class
...
However, because p is an
object of type baseClass, it has only one member variable
...
Also, the
statement:
p
...
The output of the following program further illustrates this concept
...
h,
and the definition of the class derivedClass is in the header file derivedClass
...
)
//*******************************************************
// Author: D
...
Malik
//
// This program illustrates how virtual functions and a
// pointer variable of base class as a formal parameter
// work
...
h"
//Line 2
using namespace std;
//Line 3
void callPrint(baseClass p);
//Line 4
int main()
{
baseClass one(5);
derivedClass two(3, 15);
//Line
//Line
//Line
//Line
5
6
7
8
one
...
print();
cout << "*** Calling the function "
<< "callPrint ***" << endl;
//Line 11
callPrint(one);
callPrint(two);
}
//Line 9
//Line 10
//Line 12
//Line 13
return 0;
//Line 14
//Line 15
3
168 |
Chapter 3: Pointers and Array-Based Lists
void callPrint(baseClass p)
{
p
...
In Line 13, because the formal parameter p is a value parameter, the member
variables of two are copied into the corresponding member variables of p
...
Consequently, only the member variable x of two is copied into the member variable x of p
...
print(); in the function callPrint executes the function
print of the class baseClass, not the class derivedClass
...
An object of the base class type cannot be passed to a formal parameter of the derived
class type
...
The destructor is automatically executed when the class
object goes out of scope
...
If a derived class object is passed to a
formal parameter of the base class type, the destructor of the base class executes regardless
of whether the derived class object is passed by reference or by value
...
To correct this problem, the destructor of the base class must be virtual
...
When a derived class object is passed to a formal parameter of the base class type, then
when the object goes out of scope, the destructor of the derived class executes
...
Therefore, when the derived class object is destroyed, the base class part (that is, the
members inherited from the base class) of the derived class object is also destroyed
...
Abstract Classes and Pure Virtual Functions |
169
Abstract Classes and Pure Virtual Functions
The preceding section discussed virtual functions
...
Chapter 2 described the second principal of OOD—inheritance
...
The derived classes, in
addition to inheriting the existing members of the base class, can add their own members
and also redefine or override public and protected member functions of the base class
...
There are many scenarios when a class is desired to be served as a base class for a
number of derived classes, however, the base class may contain certain functions that may
not have meaningful definitions in the base class
...
As noted in that chapter, from the
class shape you can derive other classes such as rectangle, circle, ellipse, and
so on
...
Among others, we can include
these in the class shape
...
};
virtual void move(double x, double y);
//Function to move the shape at the position (x, y)
...
...
Because the definitions of the functions draw and move are specific to a particular shape,
each derived class can provide an appropriate definition of these functions
...
The way the definition of the class shape is written when you write the definition of
the functions of the class shape, you must also write the definitions of the functions
draw and move
...
Therefore,
these function bodies have no code
...
This solution would work, but it has another drawback
...
Because there is no shape to work with, we would like to prevent the user from
creating objects of the class shape
...
3
170 |
Chapter 3: Pointers and Array-Based Lists
Because we do not want to include the definitions of the functions draw and move of the
class shape, we must convert these functions to pure virtual functions
...
Once you make these functions pure
virtual functions in the class shape, you no longer need to provide the definitions of
these functions for the class shape
...
Thus, the abstract definition of the class shape is similar to the following:
class shape
{
public:
virtual void draw() = 0;
//Function to draw the shape
...
};
virtual void move(double x, double y) = 0;
//Function to move the shape at the position (x, y)
...
...
...
Now suppose that we derive the class rectangle from the class shape
...
Note that in addition to the pure virtual functions, an abstract class can contain instance
variables, constructors, and functions that are not pure virtual
...
Array-Based Lists
Everyone is familiar with the term list
...
One thing common to all lists is that all
the elements of a list are of the same type
...
The length of a list is the number of elements in the list
...
2
...
4
...
6
...
8
...
10
...
Create the list
...
Determine whether the list is empty
...
Find the size of the list
...
Determine whether an item is the same as a given list element
...
Remove an item from the list at the specified location
...
Retrieve an item from the list from the specified location
...
Before discussing how to implement these operations, we must first decide how to store the
list in the computer’s memory
...
Initially, the size of the
array holding the list elements is usually larger than the number of elements in the list so that,
at a later stage, the list can grow
...
C++ allows the programmer to
create dynamic arrays
...
The
size of the array can be specified when a list object is declared
...
Then
length and maxSize are nonnegative integers and, therefore, we can declare them to be
of type int
...
If we
have a list of names, the array elements are of type string
...
As
you can see, there are various types of lists
...
To insert an item
at the end of a list of any type would require you to add the element after the current last
element and then increment the length by one
...
We do not want to spend time and efforts to
3
172 |
Chapter 3: Pointers and Array-Based Lists
develop separate code for each type of list we encounter
...
In other words,
while designing the algorithms, we do not want to be concerned whether we are
processing a list of numbers, a list of names, or a list of students’ data
...
To develop
generic algorithms to implement list operations, we make use of class templates
...
The following class, arrayListType, defines the list as an ADT:
//***********************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement the basic
// properties of array-based lists
...
bool isFull() const;
//Function to determine whether the list is full
...
int listSize() const;
//Function to determine the number of elements in the list
//Postcondition: Returns the value of length
...
//Postcondition: Returns the value of maxSize
...
bool isItemAtEqual(int location, const elemType& item) const;
//Function to determine whether the item is the same
//as the item in the list at the position specified by
//Postcondition: Returns true if list[location]
//
is the same as the item; otherwise,
//
returns false
...
The item to be inserted
//is passed as a parameter to the function
...
If the list is full or location is
//
out of range, an appropriate message is displayed
...
//The parameter insertItem specifies the item to be inserted
...
void removeAt(int location);
//Function to remove the item from the list at the
//position specified by location
//Postcondition: The list element at list[location] is removed
//
and length is decremented by 1
...
void retrieveAt(int location, elemType& retItem) const;
//Function to retrieve the element from the list at the
//position specified by location
...
void replaceAt(int location, const elemType& repItem);
//Function to replace the elements in the list at the
//position specified by location
...
//Postcondition: list[location] = repItem
//
If location is out of range, an appropriate message is
//
displayed
...
//After this operation, the size of the list is zero
...
//Postcondition: If the item is found, returns the location
//
in the array where the item is found; otherwise,
//
returns -1
...
However, first the
//list is searched to see whether the item to be inserted
//is already in the list
...
void remove(const elemType& removeItem);
//Function to remove an item from the list
...
//Postcondition: If removeItem is found in the list,
//
it is removed from the list and length is
//
decremented by one
...
The default array size is 100
...
protected:
elemType *list;
int length;
int maxSize;
};
//array to hold the list elements
//to store the length of the list
//to store the maximum size of the list
Figure 3-29 shows the UML class diagram of the class arrayListType
...
This is because we want to derive classes from this class to implement special lists such as
an ordered list
...
The list is empty if length is zero; it is full if length is equal to maxSize
...
Similarly, because the size of the array holding the list elements is stored in the data
member maxSize, maxSize specifies the maximum size of the list
...
It
follows that each of these functions is of O(1)
...
We assume that the output
is sent to the standard output device
...
The number of times
the for loop executes depends on the number of elements of the list
...
In general, suppose that the number of
elements in the list is n
...
The definition of the function isItemAtEqual is straightforward
...
It is easy to see that this function is of O(1)
...
The item to be
inserted and the insert location in the array are passed as parameters to this function
...
That is, we need to move certain elements right one array slot
...
(Note that
this figure does not show the data members length and maxSize
...
Array list
The number of elements currently in the list is 6, so length is 6
...
If the item is to be inserted at, say location 6, we
can easily accomplish this by copying the item into list[6]
...
Thus, we
must first copy list[5] into list[6], list[4] into list[5], and list[3] into
list[4], in this order
...
Of course, special cases such as trying to insert in a full list must be handled separately
...
The definition of the function insertAt is as follows:
template
void arrayListType
(int location, const elemType& insertItem)
{
if (location < 0 || location >= maxSize)
cerr << "The position of the item to be inserted "
<< "is out of range" << endl;
Array-Based Lists
|
177
else
if (length >= maxSize) //list is full
cerr << "Cannot insert in a full list" << endl;
else
{
for (int i = length; i > location; i--)
list[i] = list[i - 1];
//move the elements down
list[location] = insertItem;
length++;
}
} //end insertAt
//insert the item at the
//specified position
//increment the length
The function insertAt uses a for loop to shift the elements of the list
...
If the
item is to be inserted at the first position, all the elements of the list are shifted
...
The function insertEnd can be implemented by using the function insertAt
...
Therefore, we
give its definition directly
...
Therefore, this function is of O(1)
...
The function
removeAt removes an item from a specific location in the list
...
After removing the item
from the list, the length of the list is reduced by 1
...
Suppose that the data member list of an arrayListType object
is as shown in Figure 3-31
...
)
3
178 |
Chapter 3: Pointers and Array-Based Lists
list
FIGURE 3-31
[0] [1] [2] [3] [4] [5] [6] [7] [8]
35 24 45 17 26 78
...
Thus, after removing an
element, the length of the list is 5
...
Clearly, we must move list[4] into list[3] and list[5] into list[4],
in this order
...
The definition of the function retrieveAt is straightforward
...
Similarly, the definition of the function replaceAt is straightforward
...
" << endl;
else
retItem = list[location];
} //end retrieveAt
template
void arrayListType
(int location, const elemType& repItem)
Array-Based Lists
|
179
{
if (location < 0 || location >= length)
cerr << "The location of the item to be replaced is "
<< "out of range
...
Because
the data member length indicates the number of elements in the list, the elements are
removed by simply setting length to 0
...
The constructor
creates an array of the size specified by the user, and initializes the length of the list to 0
and the maxSize to the size of the array specified by the user
...
The default array size is 100
...
The definition of
the constructor and the destructor are as follows:
template
arrayListType
{
if (size < 0)
{
cerr << "The array size must be positive
...
" << endl;
maxSize = 100;
}
else
maxSize = size;
length = 0;
}
list = new elemType[maxSize];
assert(list != NULL);
template
arrayListType
{
delete [] list;
}
3
180 |
Chapter 3: Pointers and Array-Based Lists
As before, it is easy to see that each of the functions retrieveAt, replaceAt,
clearList, as well as the constructor and destructor is of O(1)
...
It copies the data members of the actual object into the
corresponding data members of the formal parameter and the object being created
...
maxSize;
length = otherList
...
list[j];
} //end copy constructor
Overloading the Assignment Operator
Next, because we are overloading the assignment operator for the class arrayListType,
we give the definition of the function template to overload the assignment operator
...
maxSize;
length = otherList
...
list[i];
return *this;
Array-Based Lists
|
181
Similar to the function print, it is easy to see that both the copy constructor and the
function to overload the assignment operator are of O(n)
...
3
Consider the list of seven elements shown in Figure 3-32
...
List of seven elements
Suppose that you want to determine whether 27 is in the list
...
Because list[0] 6¼ 27, you then compare 27 with list[1] (that is, with 12, the second
item in the list)
...
Because list[2] = 27, the search stops
...
Let us now search for 10
...
This time the search item, which is 10, is compared with every item
in the list
...
This is an unsuccessful search
...
’’ (In this case, you usually also tell the
location in the list where the search item was found
...
’’
Suppose that the name of the array containing the list elements is list
...
Recall that the function insert
inserts a new item at the end of the list if this item does not exist in the list and
the list is not full
...
Chapter 9 explicitly shows that the function seqSearch is of O(n)
...
Because duplicates are not allowed,
this function first searches the list to determine whether the item to be inserted is already
in the list
...
If the item to be
inserted is not in the list, the new item is inserted at the end of the list and the length of
the list is increased by 1
...
The definition of this function is as follows:
template
void arrayListType
{
int loc;
if (length == 0)
//list is empty
list[length++] = insertItem;
//insert the item and
//increment the length
else if (length == maxSize)
cerr << "Cannot insert in a full list
...
No duplicates are allowed
...
Because the function seqSearch is of O(n), it follows
that the function insert is of O(n)
...
The item to be deleted is passed as a
parameter to this function
...
If the item to be
deleted is found in the list, the item is removed from the list and the length of the list is
decremented by 1
...
We can now use the index
returned by the function seqSearch, and use the function removeAt to remove the item
from the list
...
" << endl;
else
{
loc = seqSearch(removeItem);
if (loc != -1)
removeAt(loc);
else
cout << "The item to be deleted is not in the list
...
Because each of these functions is of O(n) and because they are called in
sequence, it follows that the function remove is of O(n)
...
TABLE 3-1
Time complexity of list operations
Function
Time-complexity
isEmpty
O (1)
isFull
O (1)
listSize
O (1)
maxListSize
O (1)
3
184 |
Chapter 3: Pointers and Array-Based Lists
TABLE 3-1
Time complexity of list operations (continued)
Function
Time-complexity
O (n)
isItemAtEqual
O (1)
insertAt
O (n)
insertEnd
O (1)
removeAt
O (n)
retrieveAt
O (1)
replaceAt
O (n)
clearList
O (1)
constructor
O (1)
destructor
O (1)
copy constructor
O (n)
overloading the assignment
operator
O (n)
seqSearch
O (n)
insert
O (n)
remove
O (n)
The following program tests the various operations on array-based lists
...
S
...
//****************************************************************
#include
//Line 1
#include
#include "arrayListType
...
insertAt(counter, number);
}
//Line
//Line
//Line
//Line
//Line
11
12
13
14
15
cout << endl;
cout << "List 19: The list you entered is: ";
intList
...
remove(number);
cout << "Line 23: After removing " << number
<< ", the list is:" << endl;
intList
...
insertAt(counter, str);
}
//Line
//Line
//Line
//Line
//Line
//Line 23
//Line 24
//Line 25
28
29
30
31
32
cout << endl;
//Line 33
cout << "Line 34: The list you entered is: " << endl; //Line 34
stringList
...
remove(str);
cout << "Line 40: After removing " << str
<< ", the list is:" << endl;
//Line 37
//Line 38
//Line 39
//Line 40
3
186 |
Chapter 3: Pointers and Array-Based Lists
stringList
...
List 10: Enter 5 integers: 23 78 56 12 79
List 19: The list you entered is: 23 78 56 12 79
Line 20: Enter the item to be deleted: 56
Line 23: After removing 56, the list is:
23 78 12 79
Line 27: Enter 5 strings: hello sunny warm winter summer
Line 34: The list you entered is:
hello sunny warm winter summer
Line 37: Enter the string to be deleted: hello
Line 40: After removing hello, the list is:
sunny warm winter summer
The preceding program works as follows
...
The data member list of intList is an array of
100 components and the component type is int
...
The data member list of
stringList is an array of 100 components (the default size) and the component type
is string
...
The
statement in Line 13 gets the next number from the input stream
...
The statement in Line 18 uses the member function print of intList to output the
elements of intList
...
The statement in Line 22 uses the member function remove of
intList to remove the number from intList
...
These statements process a list of strings
...
þ anÀ1 xnÀ1 þ an xn ;
where ai are real (or complex) numbers and n is a nonnegative integer
...
If p(x) is a nonzero constant polynomial,
the degree of p(x) is defined to be 0
...
If p(x) is not constant and an 6¼ 0, n is
called the degree of p(x); that is, the degree of a nonconstant polynomial is defined
to be the exponent of the highest power of x
...
)
The basic operations performed on polynomials are add, subtract, multiply, divide,
and evaluate a polynomial at any given point
...
Moreover,
pð2Þ ¼ 1 þ 2 Á 2 þ 3 Á 22 ¼ 17
pðxÞ þ qðxÞ ¼ 5 þ 3x þ 3x2
pðxÞ À qðxÞ ¼ À3 þ x þ 3x2
pðxÞÃ qðxÞ ¼ 4 þ 9x þ 14x2 þ 3x3
The purpose of this programming example is to design and implement the class
polynomialType to perform the various polynomial operations in a program
...
2
...
4
...
Add polynomials
...
Multiply polynomials
...
You
will be asked in Programming Exercise 8 to generalize it so that the coefficients can
also be complex numbers
...
Let list be an array of size n + 1
...
See Figure 3-33
...
[n-1]
ai
[n]
an–1
...
Suppose that p(x) = 1 + 8x – 3x2 + 5x4 + 7x8
...
[0] [1] [2] [3] [4] [5] [6] [7] [8]
p(x)
FIGURE 3-34
1
–3
8
0
5
0
0
0
7
Polynomial p (x) of degree 8 and its coefficients
Similarly, if q(x) = –5x2 + 16x5, the array storing the coefficient of q(x) is given in
Figure 3-35
...
Suppose that
pðxÞ ¼ a0 þ a1 x þ
...
þ bmÀ1 xmÀ1 þ am xm :
Let t = max(n, m)
...
þ ctÀ1 xtÀ1 þ ct xt ;
where for i = 0, 1, 2,
...
It follows that the
degree of the polynomials is max(n, m)
...
þ dnþm xnþm ;
The coefficient dk, for k ¼ 0, 1, 2,
...
þ ak à b0 ;
where if either ai or bi does not exist, it is assumed to be zero
...
dnþm ¼ an bm
In Chapter 2, you learned how to overload various operators
...
Moreover, we also overload the function call operator, (), to evaluate a
polynomial at a given value
...
Because the coefficients of a polynomial are stored in a dynamic array, we use the
class arrayListType to store and manipulate the coefficients of a polynomial
...
The following class defines polynomials as an ADT:
//***********************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement the basic
// polynomial operations
...
If p(x) is a polynomial of degree 3, we can create an object, say p, of type polynomialType
and set the size of the array list to 4
...
Next we discuss the definitions of the functions
...
polynomialType::polynomialType(int size)
: arrayListType
{
length = size;
}
for (int i = 0; i < size; i++)
list[i] = 0;
The definition of the function to overload the operator () is quite straightforward
and is given next
...
0;
for (int i = 0; i < length; i++)
{
if (list[i] != 0
...
If n ¼ m, the operator + adds the corresponding coefficients of p(x) and q(x)
...
The
remaining coefficients of p(x) are copied into the polynomial containing the sum of p(x)
and q(x)
...
The remaining coefficients of q(x) are copied into the polynomial
containing the sum
...
The definitions of these two operator functions are as follows:
polynomialType polynomialType::operator+
(const polynomialType& right)
{
int size = max(length, right
...
length); i++)
temp
...
list[i];
if (size == length)
for (int i = min(length, right
...
list[i] = list[i];
else
for (int i = min(length, right
...
length;
i++)
temp
...
list[i];
}
return temp;
polynomialType polynomialType::operator(const polynomialType& right)
{
int size = max(length, right
...
length); i++)
temp
...
list[i];
if (size == length)
for (int i = min(length, right
...
list[i] = list[i];
else
for (int i = min(length, right
...
length;
i++)
temp
...
list[i];
}
return temp;
The definition of the function to overload the operator * to multiply two polynomials is left as an exercise for you
...
The definitions of the remaining functions of the class polynomialType
are as follows:
int polynomialType::min(int x, int y) const
{
if (x <= y)
return x;
else
return y;
}
int polynomialType::max(int x, int y) const
{
if (x >= y)
return x;
else
return y;
}
ostream& operator<<(ostream& os, const polynomialType& p)
{
int indexFirstNonzeroCoeff = 0;
for (int i = 0; i < p
...
list[i] != 0
...
length)
{
if (indexFirstNonzeroCoeff == 0)
os << p
...
list[indexFirstNonzeroCoeff] << "x^"
<< indexFirstNonzeroCoeff << " ";
for (int i = indexFirstNonzeroCoeff + 1; i < p
...
list[i] != 0
...
list[i] >= 0
...
list[i] << "x^" << i << " ";
else
os << "- " << -p
...
length - 1 << endl;
for (int i = 0; i < p
...
list[i];
}
}
MAIN
PROGRAM
return is;
//****************************************************************
// Author: D
...
Malik
//
// This program illustrates how to use the class polynomialType
...
h"
//Line 2
using namespace std;
//Line 3
int main()
{
polynomialType p(8);
polynomialType q(4);
polynomialType t;
//Line
//Line
//Line
//Line
//Line
4
5
6
7
8
3
194 |
Chapter 3: Pointers and Array-Based Lists
cin >> p;
cout << endl << "Line 10: p(x): " << p << endl;
//Line 9
//Line 10
cout << "Line 11: p(5): " << p(5) << endl << endl; //Line 11
cin >> q;
cout << endl << "Line 13: q(x): " << q << endl
<< endl;
//Line 12
//Line 13
t = p + q;
//Line 14
cout << "Line 15: p(x) + q(x): " << t << endl;
//Line 15
cout << "Line 16: p(x) - q(x): " << p - q << endl; //Line 16
}
return 0;
//Line 17
//Line 18
Sample Run: In this sample run, the user input is shaded
...
2
...
Pointer variables contain the addresses of other variables as their values
...
A pointer variable is declared using an asterisk, *, between the data type and
the variable
...
5
...
The address of operator returns the address of its operand
...
7
...
When used as a unary operator, * is called the dereferencing operator
...
For example, if p is a pointer variable
of type int, the statement
*p = 25;
8
...
10
...
12
...
14
...
16
...
18
...
20
...
You can use the member access operator arrow, ->, to access the component of an object pointed to by a pointer
...
The only integer value that can be directly assigned to a pointer variable is 0
...
Pointer arithmetic is different from ordinary arithmetic
...
Similarly, when an integer is subtracted from a pointer, the value subtracted
from the value of the pointer variable is the integer times the size of the
object to which the pointer is pointing
...
(It makes
sense to compare pointers of the same type
...
A variable created during program execution is called a dynamic variable
...
The operator delete is used to deallocate the memory occupied by a
dynamic variable
...
The operator new has two forms: one to create a single dynamic variable,
and another to create an array of dynamic variables
...
3
196 |
21
...
23
...
Chapter 3: Pointers and Array-Based Lists
The operator delete has two forms: one to deallocate the memory
occupied by a single dynamic variable, and another to deallocate the
memory occupied by an array of dynamic variables
...
The array name is a constant pointer
...
To create a dynamic array, the form of the new operator that creates an
array of dynamic variables is used
...
26
...
creates an array of 10 components of type int
...
We call p a dynamic array
...
For
example, suppose p is a dynamic array of 10 components
...
In particular, p[i] refers to the (i + 1)th component of the array
...
If p is a dynamic array, then the statement
delete [] p;
28
...
30
...
32
...
34
...
36
...
In a shallow copy, two or more pointers of the same type point to the same
memory space; that is, they point to the same data
...
If a class has a destructor, the destructor automatically executes whenever a
class object goes out of scope
...
A copy constructor executes when an object is declared and initialized by
using the value of another object, and when an object is passed by value as a
parameter
...
The binding of virtual functions occurs at execution time, not at compile
time, and is called dynamic or run-time binding
...
A class is called an abstract class if it contains one or more pure virtual
functions
...
38
...
40
...
In addition to the pure virtual functions, an abstract class can contain instance
variables, constructors, and functions that are not pure virtual
...
A list is a collection of elements of the same type
...
EXERCISES
1
...
a
...
c
...
e
...
In C++, pointer variables are declared using the reserved word pointer
...
The statement delete p; deallocates the dynamic variable to which p
points
...
is valid in C++
...
h
...
The address of operator returns the address and value of its operand
...
|
197
3
198 |
2
...
If a statement is invalid,
explain why
...
b
...
p = x;
d
...
q = &x;
f
...
p = q;
*p = q;
What is the output of the following C++ code?
int x;
int y;
int *p = &x;
int *q = &y;
*p = 35;
*q = 98;
*p = *q;
cout << x << " " << y << endl;
cout << *p << " " << *q << endl;
4
...
Given the declaration
int num = 6;
int *p = #
which of the following statement(s) increment the value of num?
a
...
(*p)++;
c
...
(*num)++;
Exercises
6
...
What is wrong with the following code?
int *p;
int *q;
//Line 1
//Line 2
p = new int;
*p = 43;
//Line 3
//Line 4
q = p;
*q = 52;
//Line 5
//Line 6
delete q;
//Line 7
cout << *p << " " << *q << endl;
9
...
|
//Line 8
What is the output of the following code?
int x;
int *p;
int *q;
p = new int[10] ;
q = p;
*p = 4;
for(int j = 0; j < 10; j++)
{
x = *p;
p++;
*p = x + j;
}
200 |
Chapter 3: Pointers and Array-Based Lists
for (int k = 0; k < 10; k++)
{
cout << *q << " ";
q++;
}
cout << endl;
10
...
12
...
What is wrong with the following code?
int *p;
int *q;
p = new int [5];
*p = 2;
//Line 3
//Line 4
for (int i = 1; i < 5; i++)
p[i] = p[i-1] + i;
//Line 5
//Line 6
q = p;
//Line 7
delete [] p;
//Line 8
for (int j = 0; j < 5; j++)
cout << q[j] << " ";
//Line 9
//Line 10
cout << endl;
13
...
What is the output of the following code?
int **p;
p = new int* [5];
for (int i = 0; i < 5; i++)
p[i] = new int[3];
for (int i = 1; i < 5; i++)
for (int j = 0; j < 3; j++)
p[i][j] = 2 * i + j;
for (int i = 1; i < 5; i++)
{
for (int j = 0; j < 3; j++)
cout << p[i][j] << " ";
cout << endl;
}
15
...
17
...
What is the purpose of the copy constructor?
Name three situations when a copy constructor executes
...
Suppose that you have the following definition of a class
...
private:
int listLength;
int *list;
double salary;
string name;
}
a
...
Write the function prototype to overload the assignment operator for
the class dummyClass
...
3
202 |
Chapter 3: Pointers and Array-Based Lists
Write the function prototype to include the copy constructor for the
class dummyClass
...
Write the definition of the copy constructor for the class dummyClass
...
19
...
return 0;
What is the output of the function main of Exercise 19, if the definition of
classA is replaced by the following definition?
class classA
{
public:
virtual void print() const;
virtual void doubleNum();
classA(int a = 0);
private:
int x;
};
21
...
Chapter 3: Pointers and Array-Based Lists
Consider the following definition of the class student
...
Rewrite the definition of the class student so that the functions print
and calculateGPA are pure virtual functions
...
arrayListType
c
...
arrayListType
b
...
Also show the
inheritance hierarchy
...
2
...
However, if the element to be
removed is at the beginning of the list and the list is fairly large, it could take a
lot of computer time
...
Rewrite the
definition of the function removeAt using this technique
...
Add the function removeAll to the class
arrayListType that would remove all occurrences of a given element
...
Programming Exercises
3
...
5
...
7
...
9
...
Also, write the definition of the function min and a
program to test this function
...
Also, write the definition of the function max and a
program to test this function
...
Redo Programming Example Polynomial Operations so
that these operators are overloaded as nonmember functions
...
Write the definition of the function to overload the operator * (as a member
function) for the class polynomialType to multiply two polynomials
...
Let p(x) = a0 + a1x +
...
The
derivative of p(x), written p'(x), is defined to be p' (x) = a1 + 2a2x2 +
...
If p(x) is constant, then p' (x) ¼ 0
...
The class polynomialType as given in the Programming Example Polynomial Operations processes polynomials with coefficients that are real
numbers
...
Your class must overload
the operators +, -, * to perform addition, subtraction, and multiplication;
and the operator () to evaluate a polynomial at a given complex number
...
Using classes, design an online address book to keep track of the names,
addresses, phone numbers, and dates of birth of family members, close friends,
and certain business associates
...
a
...
c
...
Use the appropriate functions to print and store the address
...
Define a class extPersonType using the class personType (as defined
in Example 1-12, Chapter 1), the class dateType (as designed in Programming Exercise 2 of Chapter 2), and the class addressType
...
Also, add a data member to store the phone number
...
Use constructors to automatically initialize the data members
...
An object of type addressBookType
should be able to process a maximum of 500 entries
...
ii
...
iii
...
iv
...
v
...
vi
...
(Safe Arrays) In C++, there is no check to determine whether the array
index is out of bounds
...
Also, recall that in C++ the array index
starts at 0
...
10
...
Every object of type safeArray should be an array of
type int
...
For example,
safeArray list(2,13);
safeArray yourList(-5,9);
11
...
In this example, list is an array of 11 components, the component type is
int, and the components are list[2], list[3],
...
Also,
yourList is an array of 15 components, the component type is int, and the
components are yourList[-5], yourlist[-4],
...
,
yourList[8]
...
Redesign the class
safeArray using class templates so that the class can be used in any
application that requires arrays to process data
...
A matrix is a set of
numbers arranged in rows and columns
...
If A is a matrix of 5
rows and 6 columns, we say that matrix A is of the size 5 Â 6 and
sometimes denote it as A5Â6
...
Two matrices can be added
and subtracted if they have the same size
...
The sum
and difference of A and B is given by
A þ B ¼ ½aij þ bij ;
A À B ¼ ½aij À bij
The multiplication of A and B (A * B) is defined only if the number
of columns of A are the same as the number of rows of B
...
þ ain bnk
13
...
Overload the operators +, -, and * to perform the addition, subtraction, and multiplication operations, respectively, and overload the operator
<< to output a matrix
...
The class largeIntegers in Programming Exercise 16, in Chapter 2, is
designed to process large integers of at most 100 digits
...
Also overload the multiplication operator to multiply large
integers
...
Learn about the Standard Template Library (STL)
...
Explore how vector and deque containers are used
to manipulate data in a program
...
With the help of class templates, we
developed (and used) a generic code to process lists
...
In Chapters 5,
7, and 8, we will study the three most important data structures: linked lists, stacks,
and queues
...
In addition, using the second principle, inheritance, of objectoriented programming (OOP), we will develop a generic code to process ordered lists
...
Along the way, you will see that a template is a powerful
tool that promotes code reuse
...
Among other things, the STL
provides class templates to process lists (contiguous or linked), stacks, and queues
...
Chapter 13 describes the features of the STL not
described in this chapter
...
Components of the STL
The main objective of a program is to manipulate data and generate results
...
For example, if all the data items are of the same type and we have some idea of the
number of data items, we could use an array to store the data
...
Using a loop and the array index,
we can step through the elements of the array
...
On the other hand, if we do not want to worry about the size of the data, we can use a
linked list, as is described in Chapter 5, to process it
...
Similarly, if the
data needs to be processed in a First In First Out (FIFO) manner, we can use a queue
(Chapter 8)
...
More formally,
the STL has three main components:
•
•
•
Containers
Iterators
Algorithms
Components of the STL
|
211
Containers and iterators are class templates
...
Algorithms are used to manipulate data
...
Algorithms are discussed in Chapter 13
...
The STL containers are classified
into three categories:
•
•
•
Sequence containers (also called sequential containers)
Associative containers
Container adapters
Associative containers are described in Chapter 13, and container adapters are described
in Chapters 7 and 8
...
The three predefined
sequence containers are as follows:
•
•
•
vector
deque
list
Before discussing container types in general, let us first briefly describe the sequence
container vector
...
Also, with the help of vector containers,
we can describe several properties that are common to all containers
...
Of course, there are operations that are
specific to a container
...
This chapter discusses vector and deque containers
...
Sequence Container: vector
A vector container stores and manages its objects in a dynamic array
...
Item
insertion in the middle or beginning of an array is time consuming, especially if the array
is large
...
The name of the class that implements the vector container is vector
...
) The name of the header file containing the class vector
is vector
...
For example, the statement
vector
declares intList to be a vector and the component type to be int
...
DECLARING VECTOR OBJECTS
The class vector contains several constructors, including the default constructor
...
Table 4-1 describes how a vector container of a specific type can be declared and
initialized
...
(The default
constructor is invoked
...
vecList and otherVecList
are of the same type
...
vecList is initialized using the default
constructor
...
vecList is initialized using n copies of
the element elem
...
vecList is
initialized to the elements in the range
[begin, end), that is, all elements in
the range begin
...
Components of the STL
|
213
EXAMPLE 4-1
a
...
vector
b
...
The elements of intList
are initialized to 0
...
The following statement declares intList to be a vector container
of size 5 and the element type is int
...
int intArray[5] = {2,4,6,8,10};
vector
The container intList is initialized using the elements of the array
intArray
...
Now that we know how to declare a vector sequence container, let us now discuss how
to manipulate data stored in a vector container
...
TABLE 4-2
Operations to access the elements of a vector container
Expression
Effect
vecList
...
vecList[index]
Returns the element at the position specified by index
...
front()
Returns the first element
...
)
vecList
...
(Does not check whether the
container is empty
...
(Recall that in C++, arrays start at location 0
...
)
EXAMPLE 4-2
Consider the following statement, which declares intList to be a vector container of
size 5 and the element type is int
...
The class vector provides various operations to process the elements of a vector
container
...
Item insertion and
deletion in vecList can be accomplished using the operations given in Table 4-3
...
Table 4-3 also shows how these operations are used
...
clear()
Deletes all elements from the
container
...
erase(position)
Deletes the element at the position
specified by position
...
erase(beg, end)
Deletes all elements starting at beg
until end-1
...
insert(position, elem)
A copy of elem is inserted at the
position specified by position
...
vecList
...
vecList
...
Components of the STL
TABLE 4-3
|
215
Various operations on a vector container (continued)
Expression
Effect
vecList
...
vecList
...
vecList
...
resize(num, elem)
Changes the number of elements to
num
...
Changes the number of elements to
num
...
In Table 4-3, the argument position in STL terminology is called an iterator
...
In general, iterators are used to step through the
elements of a container
...
In the next
section, we describe how to declare an iterator in a vector container and how to
manipulate the data stored in a container
...
The function push_back is quite useful
...
The container intList of size 5 was declared in
Example 4-2
...
However, this is not the case
...
You cannot use the array subscripting operator, as in
Example 4-2, to add elements past the position 4 unless you increase the size of the
container
...
In this case, you can use the function push_back, as shown in Examples
4-3 and 4-5, to add elements into a vector container
...
vector
To add elements into intList, we can use the function push_back as follows:
intList
...
push_back(55);
After these statements execute, the size of intList is 2 and intList = {34, 55}
...
However, at times, the push_back function is
more convenient because it does not need to know the size of the container; it simply
adds elements at the end
...
(Recall that an iterator is just like a pointer
...
Because
the element is to be inserted at a specific position, this would require shifting the elements of
the container (unless the element is added at the end)
...
To make element(s) insertion convenient, the class vector
provides the function insert to insert the elements at a specific position in a vector
container
...
Similarly, the function erase, to remove an element, also
requires the use of an iterator
...
The class vector contains a typedef iterator, which is declared as a public
member
...
For example, the statement
vector
declares intVecIter to be an iterator into a vector container of type int
...
Suppose that the iterator intVecIter points to an element of a vector container whose
elements are of type int
...
The expression
*intVecIter
returns the element at the current iterator position
...
Recall that when used as a unary operator, * is called the dereferencing operator
...
Suppose that we have the following statements:
vector
vector
//Line 1
//Line 2
The statement in Line 1 declares intList to be a vector container and the element type
to be int
...
Containers and the Functions begin and end
Every container contains the member functions begin and end
...
These functions have no parameters
...
begin();
the iterator intVecIter points to the first element into the container intList
...
begin(); intVecIter != intList
...
Now
consider the following statements:
intVecIter = vecList
...
insert(intVecIter, 22};
//Line 4
//Line 5
//Line 6
4
218 |
Chapter 4: Standard Template Library (STL) I
The statement in Line 4 initializes the iterator intVecIter to the first element of
vecList
...
The statement in Line 6 inserts 22 at the position specified by intVecIter
...
Notice
that the size of the container also increases
...
Table 4-4 describes some of these operations
...
)
TABLE 4-4
Functions to determine the size of a vector container
Expression
Effect
vecCont
...
vecCont
...
vecCont
...
vecCont
...
Example 4-5 illustrates how to use a vector container in a program and how to process
the elements into a vector container
...
S
...
//***********************************************************
#include
#include
//Line 1
//Line 2
using namespace std;
//Line 3
Components of the STL
int main()
{
vector
219
//Line 4
//Line 5
//Line 6
intList;
intList
...
push_back(75);
intList
...
push_back(35);
//Line
//Line
//Line
//Line
7
8
9
10
cout << "Line 11: List Elements: ";
for (int i = 0; i < 4; i++)
cout << intList[i] << " ";
cout << endl;
//Line
//Line
//Line
//Line
11
12
13
14
for (int i = 0; i < 4; i++)
intList[i] *= 2;
//Line 15
//Line 16
cout << "Line 17: List Elements: ";
for (int i = 0; i < 4; i++)
cout << intList[i] << " ";
cout << endl;
//Line
//Line
//Line
//Line
vector
//Line 21
listIt;
17
18
19
20
cout << "Line 22: List Elements: ";
//Line 22
for (listIt = intList
...
end(); ++listIt) //Line 23
cout << *listIt << " ";
//Line 24
cout << endl;
//Line 25
listIt = intList
...
insert(listIt, 88);
//Line
//Line
//Line
//Line
26
27
28
29
cout << "Line 30: List Elements: ";
//Line 30
for (listIt = intList
...
end(); ++listIt) //Line 31
cout << *listIt << " ";
//Line 32
cout << endl;
//Line 33
}
|
return 0;
//Line 34
//Line 35
Sample Run:
Line
Line
Line
Line
11:
17:
22:
30:
List
List
List
List
Elements:
Elements:
Elements:
Elements:
13
26
26
26
75 28 35
150 56 70
150 56 70
150 88 56 70
4
220 |
Chapter 4: Standard Template Library (STL) I
The statement in Line 6 declares a vector container (or vector for short), intList, of
type int
...
The statements in Lines 12 and 13
use the for loop and the array subscripting operator [] to output the elements of
intList
...
The statements in Lines 15 and 16 use a for loop to
double the value of each element of intList; the statements in Lines 18 and 19 output
the elements of intList
...
The statement in Line 21 declares listIt to be a vector iterator that processes any vector
container whose elements are of type int
...
After the statement in Line 26 executes,
listIt points to the first element of intList
...
The statement in Line 29 inserts 88 into intList at the position
specified by the iterator listIt
...
The statements in Lines 31 and 32 output the
modified intList
...
We now look at operations that are
common to all containers
...
Recall that a class encapsulates data, and operations on that data, into a single unit
...
Also, recall that the operations to
manipulate the data are implemented with the help of functions and are called member
functions of the class
...
Suppose that ct, ct1, and ct2 are containers of the same type
...
Components of the STL
TABLE 4-5
|
221
Member functions common to all containers
Member function
Effect
Default constructor
Initializes the object to an empty state
...
We
describe these constructors when we discuss a
specific container
...
Destructor
Executes when the object goes out of scope
...
empty()
Returns true if container ct is empty and
false otherwise
...
size()
Returns the number of elements currently in
container ct
...
max_size()
Returns the maximum number of elements that
can be inserted into container ct
...
swap(ct2)
Swaps the elements of containers ct1 and ct2
...
begin()
Returns an iterator to the first element into
container ct
...
end()
Returns an iterator to the last element into
container ct
...
rbegin()
Reverse begin
...
This function is used
to process the elements of ct in reverse
...
rend()
Reverse end
...
ct
...
Note
that here position is an iterator
...
erase(begin, end)
Deletes all elements between begin
...
4
222 |
Chapter 4: Standard Template Library (STL) I
TABLE 4-5
Member functions common to all containers (continued)
Member function
Effect
ct
...
After a
call to this function, container ct is empty
...
After this
operation, the elements in both containers are
the same
...
ct1 != ct2
Returns true if containers ct1 and ct2 are
not equal and false otherwise
...
ct1 <= ct2
Returns true if container ct1 is less than or
equal to container ct2 and false otherwise
...
ct1 >= ct2
Returns true if container ct1 is greater than
or equal to container ct2 and false
otherwise
...
Member Functions Common to Sequence Containers
The previous section described the member functions that are common to all containers
...
(Suppose that seqCont is a sequence container
...
insert(position, elem)
A copy of elem is inserted at the
position specified by position
...
seqCont
...
seqCont
...
seqCont
...
seqCont
...
seqCont
...
seqCont
...
seqCont
...
seqCont
...
If size() grows, the
new elements are created by their
default constructor
...
resize(num, elem)
Changes the number of elements to
num
...
The copy Algorithm
Example 4-5 used a for loop to output the elements of a vector container
...
The function copy is provided as a part of the generic algorithms of the STL
and can be used with any container type as well as arrays
...
4
224 |
Chapter 4: Standard Template Library (STL) I
Like the function copy, the STL contains many functions as part of generic algorithms,
which are described in Chapter 13
...
In general, it
allows us to copy the elements from one place to another
...
The prototype of the function template copy is as follows:
template
outputItr copy(inputIterator first1, inputIterator last,
outputIterator first2);
The parameter first1 specifies the position from which to begin copying the elements;
the parameter last specifies the end position
...
Therefore, the parameters first1 and last specify the source, and
the parameter first2 specifies the destination
...
last-1 are copied
...
Thus, to use the function copy, the program must include the statement
#include
The function copy works as follows
...
The statement in Line 2 creates an empty container of nine components of type vector
and the element type int
...
Therefore, intArray points to the first component of the array,
intArray + 1 points to the second component of the array, and so on
...
begin());
//Line 3
This statement copies the elements starting at the location intArray, which is the first
component of the array intArray, until intArray + 9 - 1 (that is, intArray + 8),
which is the last element of the array intArray, into the container vecList
...
begin()
...
Also, first2 is intArray;
that is, first2 points to the location of the first element of the array intArray
...
After the statement in Line 5
executes,
intArray[] = {6, 8, 3, 40, 36, 98, 29, 75, 75}
//Line 6
Notice that the elements of the array intArray are shifted to the left by one position
...
Consider the statement
copy(vecList
...
rend(),
vecList
...
Therefore,
vecList
...
Similarly, the function rend (reverse end) returns a pointer to the first element
of a container
...
After the statement in Line 7 executes, the container vecList
is as follows:
vecList = {5, 6, 5, 6, 8, 3, 40, 36, 98}
Example 4-6 shows the effect of the preceding statements using a C++ program
...
ostream Iterator and Function copy
One way to output the contents of a container is to use a for loop and the function
begin to initialize the for loop control variable, and to use the function end to set the
limit
...
In this case, an iterator of type ostream specifies the destination (ostream iterators are
discussed in detail later in this chapter)
...
The following statement illustrates how to create an ostream iterator of type int:
ostream_iterator
4
226 |
Chapter 4: Standard Template Library (STL) I
This statement creates screen to be an ostream iterator with the element type int
...
Thus, the iterator
screen is initialized using the object cout, and when this iterator outputs the elements
they are separated by a space
...
Similarly, the statement
copy(vecList
...
end(), screen);
outputs the elements of the container vecList on the screen
...
Also, until we discuss ostream iterators in detail, we will use
statements similar to the statement in Line A to create an ostream iterator
...
For
example, the statement (shown previously)
copy(vecList
...
end(), screen);
is equivalent to the statement
copy(vecList
...
end(), ostream_iterator
Finally, the statement
copy(vecList
...
end(),
ostream_iterator
outputs the elements of vecList with a comma and space between them
...
EXAMPLE 4-6
//***********************************************************
// Author: D
...
Malik
//
// This program illustrates how to use the function copy and
// an ostream iterator in a program
...
begin());
//Line 14
cout << "Line 15: vecList: ";
copy(vecList
...
end(), screen);
cout << endl;
//Line 15
//Line 16
//Line 17
copy(intArray + 1, intArray + 9, intArray);
cout << "Line 19: After shifting the elements one "
<< "position to the left, intArray: " << endl;
copy(intArray, intArray + 9, screen);
cout << endl;
//Line 18
//Line 19
//Line 20
//Line 21
copy(vecList
...
rend(),
vecList
...
begin(), vecList
...
The term deque stands for
double-ended queue
...
Thus, a deque can expand in either
4
228 |
Chapter 4: Standard Template Library (STL) I
direction
...
Inserting elements at the beginning
or at the end is fast; inserting elements in the middle, however, is time consuming
because the elements in the queue need to be shifted
...
The definition of the
class deque, and the functions to implement the various operations on a deque object,
are also contained in the header file deque
...
Thus, a deque object can be initialized
in various ways when it is declared, as described in Table 4-7
...
(The default constructor is
invoked
...
deque
deq(size);
Creates a deque container, deq, of size
size
...
deque
deq(n, elem);
Creates a deque container, deq, of size n
...
deque
deq(begin, end);
Creates a deque container, deq
...
end-1
...
The name of the function implementing the operations is shown in bold
...
Suppose that deq is a deque
container
...
assign(n,elem)
Assigns n copies of elem
...
assign(beg,end)
Assigns all the elements in the range beg
...
deq
...
deq
...
deq
...
deq[index]
Returns the element at the position specified by
index
...
front()
Returns the first element
...
)
deq
...
(Does not check whether
the container is empty
...
EXAMPLE 4-7
//***********************************************************
// Author: D
...
Malik
//
// This program illustrates how to use a deque container in a
// program
...
push_back(13);
intDeq
...
push_back(28);
intDeq
...
begin(), intDeq
...
push_front(0);
intDeq
...
begin(), intDeq
...
pop_front();
intDeq
...
begin(), intDeq
...
pop_back();
intDeq
...
begin(), intDeq
...
begin();
//Line 33
++deqIt;
//deqIt points to the second element
//Line 34
intDeq
...
begin(), intDeq
...
The statement in Line 9 declares screen to be
an ostream iterator initialized to the standard output device
...
The statement in Line 15 outputs the elements of intDeq
...
The statement in Line 17 inserts 0 at the beginning of intDeq
...
The statement in Line 20 outputs the
modified intDeq
...
The statements in Lines 27 and 28 use the operation pop_back to remove
the last two elements of intDeq; the statement in Line 30 outputs the modified
intDeq
...
After the statement in Line 33
executes, deqIt points to the first element of intDeq
...
The statement in Line 35 inserts 444
into intDeq at the position specified by deqIt
...
Iterators
Examples 4-5 through 4-7 further clarify that iterators are quite important to efficiently
process the elements of a container
...
Iterators work just like pointers
...
Thus, with the help of iterators, we can successively
access each element of a container
...
Suppose that cntItr is an iterator into a container
...
The statement
*cntItr;
returns the value of the element of the container pointed to by cntItr
...
In the next few sections, we describe
these iterators
...
These iterators are provided for reading data from an input stream
...
Table 4-9 describes the operations on
inputIterator
...
inputIterator->member
Gives access to the member of the element
...
inputIterator++
Moves forward, returns the old position (postincrement)
...
inputIt1 != inputIt2
Returns true if the two iterators are not the same
and false otherwise
...
Output Iterators
Output iterators, with write access, step forward element-by-element
...
Suppose outputIterator is an output iterator
...
Iterators |
TABLE 4-10
233
Operations on an output iterator
Expression
Effect
*outputIterator = value;
Writes the value at the position specified
by the outputIterator
...
outputIterator++
Moves forward, returns the old position
(postincrement)
...
Output iterators cannot be used to iterate over a range twice
...
Forward Iterators
Forward iterators combine all of the functionality of input iterators and almost all of the
functionality of output iterators
...
Table
4-11 describes the operations on forwardIterator
...
forwardIterator->member
Gives access to the member of the element
...
forwardIterator++
Moves forward, returns the old position
(postincrement)
...
forwardIt1 != forwardIt2
Returns true if the two iterators are not
the same and false otherwise
...
4
234 |
Chapter 4: Standard Template Library (STL) I
A forward iterator can refer to the same element in the same collection and process the
same element more than once
...
Suppose biDirectionalIterator is a bidirectional iterator
...
To step backward, the decrement operations are also defined for
biDirectionalIterator
...
TABLE 4-12
Additional operations on a bidirectional iterator
Expression
Effect
--biDirectionalIterator
Moves backward, returns the new position
(predecrement)
...
Bidirectional iterators can be used only with containers of type vector, deque, list,
set, multiset, map, and multimap
...
These iterators can be used with containers of type vector, deque, string,
and arrays
...
Table 4-13 describes the additional
operations that are defined for random access iterators
...
)
Iterators |
TABLE 4-13
235
Additional operations on a random access iterator
Expression
Effect
rAccessIterator[n]
Accesses the nth element
...
rAccessIterator -= n
Moves rAccessIterator backward
n elements if n >= 0 and forward
if n < 0
...
n + rAccessIterator
Returns the iterator of the next nth element
...
rAccessIt1 - rAccessIt2
Returns the distance between the iterators
rAccessIt1 and rAccessIt2
...
rAccessIt1 <= rAccessIt2
Returns true if rAccessIt1 is before or
equal to rAccessIt2 and false
otherwise
...
rAccessIt1 >= rAccessIt2
Returns true if rAccessIt1 is after
or equal to rAccessIt2 and false
otherwise
...
Input iterators
Output iterators
Forward iterators
Bidirectional iterators
Random access iterators
FIGURE 4-1
Iterator hierarchy
Now that you know the different types of iterators, next we describe how to declare an
iterator to a container
...
Thus, an iterator into a container is declared using the typedef iterator
...
Moreover,
the iterator intVecIter can be used on any vector
container, such as vector
...
typedef const_iterator Because an iterator works like a pointer, with the help
of an iterator into a container and the dereferencing operator, *, we can modify the
elements of the container
...
To handle this situation, every container contains another typedef const_iterator
...
The iterator intConstVecIt is used to process the elements of those vector
containers that are declared as constant vector containers of type vector
...
Iterators |
237
typedef reverse_iterator Every container also contains the typedef
reverse_iterator
...
typedef const_reverse_iterator An iterator of this type is a read-only iterator
and is used to iterate through the elements of a container in reverse
...
In addition to the previous four typedefs, several other typedefs are common to all
containers
...
TABLE 4-14
Various typedefs common to all containers
typedef
Effect
difference_type
The type of result from subtracting two iterators
referring to the same container
...
reference
A reference to the type of elements stored in the
container
...
A constant reference is read-only
...
This
type is also used to index through sequence containers,
except list containers
...
Stream Iterators
Another useful set of iterators is stream iterators—istream iterators and ostream
iterators
...
istream_iterator The istream iterator is used to input data into a program from
an input stream
...
The general syntax to use an istream iterator is as follows:
istream_iterator
4
238 |
Chapter 4: Standard Template Library (STL) I
where Type is either a built-in type or a user-defined class type, for which an input
iterator is defined
...
ostream_iterator The ostream iterators are used to output data from a program into
an output stream
...
We review them
here for the sake of completeness
...
The
general syntax to use an ostream iterator is as follows:
ostream_iterator
or
ostream_iterator
where Type is either a built-in type or a user-defined class type, for which an output
iterator is defined
...
In the second form used for declaring an ostream
iterator, by using the second argument (deLimit) of the initializing constructor, we can
specify a character separating the output
...
The registrar’s office
wants to prepare the grade reports as soon as the students’ grades are recorded
...
If a student has paid the tuition, the grades are shown on the grade report together
with the grade point average (GPA)
...
For these students, the grade report contains a message indicating that
the grades have been held for nonpayment of the tuition
...
The registrar’s office and the business office want your help in writing a program that
can analyze the students’ data and print the appropriate grade reports
...
studentName studentID isTuitionPaid
courseName courseNumber creditHours
courseName courseNumber creditHours
...
The students’ data is given
thereafter
...
The first line indicates that the tuition rate is $345 per credit hour
...
The course number for the mathematics class she is taking is
MTH345, the course has 4 credit hours, her midsemester grade is A, and so on
...
54
This output shows that the courses must be ordered according to the course number
...
Input
A file containing the data in the form given previously
...
txt
...
Let us assume
that the name of the output file is stDataOut
...
240 |
Chapter 4: Standard Template Library (STL) I
PROBLEM
ANALYSIS
AND
ALGORITHM
DESIGN
We must first identify the main components of the program
...
Thus, the two main components are the
student and the course
...
Although the grade a student receives is not really a characteristic of
a course, to simplify the program this component also includes the student’s grade
...
Some of the basic operations that need to be performed on an object of the course
type are as follows:
1
...
3
...
5
...
Print the course information
...
Show the course number
...
The following class defines the course as an ADT:
class courseType
{
public:
void setCourseInfo(string cName, string cNo,
char grade, int credits);
//Function to set the course information
//The course information is set according to the
//incoming parameters
...
int getCredits();
//Function to return the credit hours
//The value of the private data member courseCredits
//is returned
...
Programming Example: Grade Report |
bool
bool
bool
bool
bool
bool
241
operator==(const courseType&) const;
operator!=(const courseType&) const;
operator<=(const courseType&) const;
operator<(const courseType&) const;
operator>=(const courseType&) const;
operator>(const courseType&) const;
courseType(string cName = "", string cNo = "",
char grade = '*', int credits = 0);
//Constructor
//The object is initialized according to the parameters
...
courseType
–courseName: string
–courseNo: string
–courseGrade: char
–courseCredits: int
+setCourseInfo(string, string, char, int): void
+print(ostream&, bool): void
+getCredits(): int
+getCourseNumber(string&): void
+getGrade(): char
+operator==(const courseType&) const: bool
+operator!=(const courseType&) const: bool
+operator<=(const courseType&) const: bool
+operator<(const courseType&) const: bool
+operator>=(const courseType&) const: bool
+operator>(const courseType&) const: bool
+courseType(string = "", string = "", char = '*', int = 0)
FIGURE 4-2
UML class diagram of the class courseType
Next, we discuss the definition of the functions to implement the operations of the
class courseType
...
The function setCourseInfo sets the values of the private data members according to the values of the parameters
...
If the bool parameter isGrade
is true, the grade is printed on the screen; otherwise, three stars are shown in place
of the grade
...
Thus, we need to set the left manipulator
...
The
following steps describe this function:
1
...
3
...
5
...
Set the left manipulator
...
Print the course name
...
Print the credit hours
...
The definition of the function print is as follows:
void courseType::print(ostream& outp, bool isGrade)
{
outp << left;
//Step
outp << setw(8) << courseNo << "
";
//Step
outp << setw(15) << courseName;
//Step
outp
...
If no values are specified when a
courseType object is declared, the constructor uses the default to initialize the object
...
Otherwise, the values specified in the object declaration are used to initialize the
object
...
int courseType::getCredits()
{
return courseCredits;
}
char courseType::getGrade()
{
return courseGrade;
}
void courseType::getCourseNumber(string& cNo)
{
cNo = courseNo;
}
bool courseType::operator==(const courseType& right) const
{
return (courseNo == right
...
courseNo);
}
bool courseType::operator<=(const courseType& right) const
{
return (courseNo <= right
...
courseNo);
}
bool courseType::operator>=(const courseType& right) const
{
return (courseNo >= right
...
courseNo);
}
4
244 |
Chapter 4: Standard Template Library (STL) I
Next we discuss the component student
...
Because every student has to pay tuition, we also include a member to indicate
whether the student has paid the tuition
...
We have already designed a
class personType to process a person’s first name and last name
...
Thus, we see that we can
derive the class studentType to keep track of a student’s information from the
class personType, and one member of this class is of type courseType
...
The basic operations to be performed on an object of type studentType are as follows:
1
...
3
...
5
...
Set the student information
...
Calculate the number of credit hours taken
...
Calculate the billing amount
...
The following class defines studentType as an ADT
...
void print(ostream& out, double tuitionRate);
//Function to print the student's grade report
//The output is stored in a file specified by the
//parameter out
...
Programming Example: Grade Report |
245
int getHoursEnrolled();
//Function to return the credit hours a student
//is enrolled in
...
double getGpa();
//Function to return the grade point average
...
double billingAmount(double tuitionRate);
//Function to return the tuition fees
//Postcondition: The tuition fees due is calculated
//
and returned
...
studentType
–sId: int
–numberOfCourses: int
–isTuitionPaid: bool
–coursesEnrolled: vector
+setInfo(string, string, int, bool,
vector
+print(ostream& out, double tuitionRate): void
+getHoursEnrolled(): int
+getGpa(): double
+billingAmount(double): double
+studentType()
FIGURE 4-3
personType
studentType
UML class diagram of the class studentType and the inheritance hierarchy
Next, we discuss the definitions of the functions to implement the operations of the
class studentType
...
The class studentType is derived from the class
personType, and the variables to store the first name and last name are private data
4
246 |
Chapter 4: Standard Template Library (STL) I
members of that class
...
To sort the array coursesEnrolled we use the algorithm sort provided by
the STL
...
When we declare the vector coursesEnrolled, we did not specify its size
...
Therefore, coursesEnrolled
...
end() specifies the position
of the last element
...
The following statement sorts the
vector coursesEnrolled
...
begin(), coursesEnrolled
...
size();
coursesEnrolled = courses;
}
sort(coursesEnrolled
...
end());
The default constructor initializes the private data members to the default values
...
studentType::studentType()
{
numberOfCourses = 0;
sId = 0;
isTuitionPaid = false;
}
Programming Example: Grade Report |
247
The function print prints the grade report
...
Otherwise, three stars are printed in place of each grade,
the GPA is not shown, a message indicates that the grades are being held for nonpayment
of the tuition, and the amount due is shown
...
2
...
4
...
6
...
Output the student’s name
...
Output the number of courses in which enrolled
...
Print the total credit hours
...
Also,
set the precision to two decimal places
...
if isTuitionPaid is true
Output the GPA
else
Output the billing amount and a message about withholding the
grades
...
unsetf(ios::left);
for (int i = 0; i < numberOfCourses; i++)
coursesEnrolled[i]
...
***" << endl;
outp << "Amount Due: $" << billingAmount(tuitionRate)
<< endl;
}
}
outp << "-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
<< "-*-*-*-*-" << endl << endl;
The function getHoursEnrolled calculates and returns the total credit hours that a
student is taking
...
The total credit hours are calculated by adding the credit hours of
each course in which the student is enrolled
...
Therefore, we use the
member function getCredits of the class courseType to retrieve the credit
hours
...
getCredits();
return totalCredits;
If a student has not paid the tuition, the function billingAmount calculates and
returns the amount due, based on the number of credit hours enrolled
...
This function calculates a student’s GPA
...
The definition of this
function is as follows:
double studentType::getGpa()
{
double sum = 0
...
getGrade())
{
case 'A':
sum += coursesEnrolled[i]
...
getCredits() * 3;
break;
case 'C':
sum += coursesEnrolled[i]
...
getCredits() * 1;
break;
case 'F':
break;
}
}
MAIN
PROGRAM
default:
cout << "Invalid Course Grade" << endl;
}
if (getHoursEnrolled() != 0)
return sum / getHoursEnrolled();
else
return 0;
Now that we have designed the classes courseType and studentType, we will use
these classes to complete the program
...
In fact, all that is left
for the main program is to declare the objects to hold the students’ data, load the data
into these objects, and then print the grade reports
...
Essentially, the main algorithm for the program is as follows:
1
...
3
...
Declare the variables
...
If the input file does not exist, exit the program
...
4
250 |
Chapter 4: Standard Template Library (STL) I
5
...
6
...
7
...
Variables
To store students’ data, we use the vector container, studentList, whose
elements are of type studentType
...
Because the
data will be read from a file, and because the output is sent to a file, we need two stream
variables to access the input and output files
...
The next two sections describe these functions
...
In pseudocode, the definition of this
function is as follows:
For each student in the university,
1
...
2
...
Get the number of courses the student is taking
...
For each course
a
...
b
...
c
...
5
...
6
...
We need to declare several local variables to read and store the data
...
clear();
for (int i = 0; i < noOfCourses; i++)
//Step 4
{
infile >> cName >> cNo >> credits >> grade; //Step 4
...
setCourseInfo(cName, cNo,
grade, credits);
//Step 4
...
push_back(cTemp);
//Step 4
...
setInfo(fName, lName, ID, isTuitionPaid,
courses);
studentList
...
For each student, it calls the function print of
the class studentType to print the grade report
...
size(); count++)
studentList[count]
...
S
...
//****************************************************************
#include
#include
#include
#include
#include
#include
#include "studentType
...
open("stData
...
"
<< "Program terminates
...
open("stDataOut
...
54
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*Student Name: Bill Wilton
Student ID: 798324
Number of courses enrolled: 5
Course No
BIO234
CHM256
ENG378
MTH346
PHL534
Course Name
Biology
Chemistry
English
Mathematics
Philosophy
Credits Grade
4
***
4
***
3
***
3
***
3
***
Total number of credit hours: 17
*** Grades are being held for not paying the tuition
...
00
-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
4
254 |
Chapter 4: Standard Template Library (STL) I
Student Name: Dandy Goat
Student ID: 746333
Number of courses enrolled: 6
Course No
BUS128
CHM348
CSC201
ENG328
HIS101
MTH137
Course Name
Business
Chemistry
ComputerSci
English
History
Mathematics
Credits Grade
3
C
4
B
3
B
3
B
3
A
3
A
Total number of credit hours: 19
Midsemester GPA: 3
...
2
...
4
...
The three main components of the STL are containers, iterators, and
algorithms
...
Iterators are used to step through the elements of a container
...
6
...
8
...
10
...
12
...
14
...
16
...
18
...
20
...
22
...
24
...
The main categories of containers are sequence containers, associative
containers, and container adapters
...
A vector container stores and manages its objects in a dynamic array
...
The name of the class that implements the vector container is vector
...
Item deletion in a vector container is accomplished by using the operations
pop_back, erase, and clear
...
Member functions common to all containers are the default constructor,
constructors with parameters, the copy constructor, the destructor, empty,
size, max_size, swap, begin, end, rbegin, rend, insert, erase,
clear, and the relational operator functions
...
The member function end returns an iterator to the last element into the
container
...
The copy algorithm is used to copy the elements in a given range to
another place
...
When we create an iterator of the type ostream, we also specify the type
of element that the iterator will output
...
A deque can expand in either direction
...
In addition to the operations that are common to all containers, the other
operations that can be used to manipulate the elements of a deque are
assign, push_front, pop_front, at, array subscripting operator [],
front, and back
...
26
...
28
...
30
...
32
...
Input iterators are used to input data from an input stream
...
A forward iterator can refer to the same element in the same collection and
process the same element more than once
...
Bidirectional iterators can be used with containers of type list, set,
multiset, map, and multimap
...
Random access iterators can be used with containers of type vector,
deque, string, and arrays
...
2
...
4
...
Write a statement that declares and stores the elements of the following
array into a vector object:
char vowels[5] = {'a', 'e', 'i', 'o', 'u'};
5
...
Write a statement to declare screen to be an ostream_iterator initialized
to the standard output device that outputs the elements of an int vector object
...
Moreover, suppose that
screen is an ostream_iterator initialized to the standard output device
to output the elements of an int vector object
...
begin(), vecList
...
What is the output of the following program segment?
vector
for (int j = 0; j < 5; j++)
vecList[j] = 2 * j;
for (int j = 0; j < 5; j++)
cout << vecList[j] << " ";
cout << endl;
Exercises
8
...
)
int list[5] = {2,4,6,8,10};
vector
copy(list, list + 5, vecList
...
begin(), vecList
...
What is the output of the following program segment? (Assume that
screen is an ostream_iterator initialized to the standard output device
to output elements of type int
...
push_back(3);
vecList
...
push_back(7);
vecIt = vecList
...
erase(vecIt);
vecList
...
begin(), vecList
...
What is the output of the following program segment? (Assume that
screen is an ostream_iterator initialized to the standard output device
to output elements of type int
...
begin());
vecList
...
begin(), vecList
...
What is the output of the following program segment? (Assume that
screen is an ostream_iterator initialized to the standard output device
to output elements of type double
...
00;
sales[1] = 75
...
00;
4
258 |
Chapter 4: Standard Template Library (STL) I
sales
...
00;
sales[4] = 95
...
begin(), sales
...
What is the output of the following program segment? (Assume that
screen is an ostream_iterator initialized to the standard output device
that outputs elements of type int
...
push_back(15);
intVector
...
push_back(10);
intVector
...
begin();
vecIt++;
intVector
...
pop_back();
copy(intVector
...
end(), screen);
13
...
begin() + 2, vecList
...
begin());
14
...
rbegin() + 3, vecList
...
rbegin());
15
...
push_back(5);
intDeq
...
push_front(45);
intDeq
...
push_front(0);
intDeq
...
push_front(34);
deqIt = intDeq
...
insert(deqIt,76);
intDeq
...
begin();
++deqIt;
++deqIt;
intDeq
...
push_front(2 * intDeq
...
push_back(3 * intDeq
...
begin(), intDeq
...
Write a program that allows the user to enter the last names of five candidates in
a local election and the votes received by each candidate
...
Your program should
also output the winner of the election
...
91
Miller
4000
20
...
09
Robinson
2500
12
...
33
Total
19300
The Winner of the Election is Duffy
...
Write a program that allows the user to input the students’ names followed
by their test scores and outputs the following:
Class average
b
...
Highest test score and the names of all students having the highest score
Write a program that uses a vector object to store a set of real numbers
...
When declaring the vector object, do not specify its size
...
Write the definition of the function template reverseVector to reverse the
elements of a vector object
...
3
...
template
void reverseVector(vector
//Reverses the elements of the vector list
...
//
After a call to this function, list = {5, 2, 8, 4}
...
Chapter 4: Standard Template Library (STL) I
Also, write a program to test the function reverseVector
...
Use the function push_back to
insert elements in the vector object
...
template
int seqSearch(const vector
//If item is found in the list, returns the
//position of the item in the list; otherwise, returns -1
...
Also, write a program to test the function seqSearch
...
Write a program to find the mean and standard deviation of numbers
...
, xn is x ¼ (x1 + x2 +
...
The standard deviation of these numbers is as follows:
sffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffiffi
ðx1 À xÞ2 þ ðx2 À xÞ2 þ Á Á Á þ ðxi À xÞ2 þ Á Á Á þ ðxn À xÞ2
s¼
n
7
...
a
...
Design the class bookType that
defines the book as an ADT
...
c
...
To keep track of the number of authors,
add another data member
...
For example, the typical operations that can be performed on the title are to show the title, set the title, and check whether a title
is the same as the actual title of the book
...
Add similar
operations for the publisher, ISBN, book price, and authors
...
Write the definitions of the member functions of the class bookType
...
Declare a vector container of
type bookType
...
Programming Exercises
8
...
|
261
In the first part of this exercise, you will design a class memberType
...
ii
...
Similarly, update, modify, and show the
number of books bought and the amount spent
...
Add the appropriate constructors and a destructor (if one is needed)
...
Write the definitions of the member functions of memberType
...
Using the classes designed in Programming Exercise 7 and part (8a),
write a program to simulate a bookstore
...
Each member has to
pay a $10 yearly membership fee and receives a 5% discount on each
book bought
...
For every eleventh book that a
member buys, the bookstore takes the average of the total amount of
the last 10 books bought, applies this amount as a discount, and then
resets the total amount spent to 0
...
Redo Programming Exercise 9 of Chapter 3 so that the address book is
stored in a vector object
...
The company invests only in the stock market
...
We assume that the company invests in, say, 10 different stocks
...
The input data is stored in a file in the following format:
i
...
10
...
50 115
...
50 111
...
50 6723823
CBA 67
...
50 78
...
50 65
...
...
4
262 |
Chapter 4: Standard Template Library (STL) I
The first line indicates that the stock symbol is MSMT, today’s opening price
was 112
...
75, today’s high price was 116
...
75, yesterday’s closing price was 113
...
The listing sorted by stock symbols must be in the following form:
********* First Investor's Heaven **********
*********
Financial Report
**********
Stock
Today
Previous Percent
Symbol Open
Close High
Low Close Gain Volume
------ -------- ------ ----- ---- -----ABC
123
...
95 132
...
00 120
...
67% 10000
AOLK
80
...
00 82
...
00 83
...
64% 5000
CSCO 100
...
00 105
...
00 101
...
99% 25000
IBD
68
...
00 72
...
00 75
...
33% 15000
MSET 120
...
00 145
...
00 115
...
74% 30920
Closing Assets: $9628300
...
In the first step (part a),
design and implement a stock object
...
a
...
Call the class
that captures the various characteristics of a stock object stockType
...
Moreover, we need to output the opening price, high
price, low price, previous price, and the percent gain/loss for the day
...
Therefore, the stock
object should store all this information
...
ii
...
iv
...
a
...
a
...
a
...
Set the stock information
...
Show the different prices
...
Show the number of shares
...
Overload the
relational operators to compare two stock objects by their symbols
...
Because data is stored in a file, overload the stream extraction operator,
>>, for easy input
...
Further suppose that
myStock is a stock object
...
|
263
reads data from the input file and stores it in the object myStock
...
)
Now that you have designed and implemented the class stockType
to implement a stock object in a program, it is time to create a list of
stock objects
...
To store the list of stocks, you need to declare a
vector
...
Because the company also requires you to produce the list ordered by
the percent gain/loss, you need to sort the stock list by this component
...
To do so, add a data member, a vector, to hold the indices of the stock list
ordered by the component percent gain/loss
...
When printing the list ordered by the component percent gain/loss, use
the array indexByGain to print the list
...
In skeleton form, the definition of the class stockListType is as follows:
class stockListType
{
public:
void insert(const stockType& item));
//Function to insert a stock in the list
...
private:
vector
vector
};
c
...
4
This page intentionally left blank
5
CHAPTER
L INKED L ISTS
I N T H I S C H A P T E R , YO U W I L L :
...
Become aware of the basic properties of linked lists
...
Discover how to build and manipulate a linked list
...
Discover how to use the STL container list
...
Become aware of circular linked lists
266 |
Chapter 5: Linked Lists
You have already seen how data is organized and processed sequentially using an array, called a
sequential list
...
You also found that if data is not sorted, searching for an item in the list
can be very time consuming, especially with large lists
...
However, in this case, insertion and deletion
become time consuming, especially with large lists because these operations require data
movement
...
Thus, there are limitations when you organize data in an array
...
Chapter 3 showed how
memory (variables) can be dynamically allocated and deallocated using pointers
...
Recall that when data is
stored in an array, memory for the components of the array is contiguous—that is, the blocks
are allocated one after the other
...
Linked Lists
A linked list is a collection of components, called nodes
...
Thus, every node in a linked list has two components:
one to store the relevant information (that is, data) and one to store the address, called the
link, of the next node in the list
...
Figure 5-1 is a pictorial representation of a node
...
The list in Figure 5-2 is an example of a linked list
...
The down arrow in the last node indicates that this link field is NULL
...
1200
head
FIGURE 5-3
1200
45
1575
1575
65
Linked list and values of the links
The value of the head is 1200, the data part of the first node is 45, and the link component
of the first node contains 1575, the address of the second node
...
For simplicity and for the ease of understanding and clarity, Figures 5-3 through 5-5 use
decimal integers as the values of memory addresses
...
Because each node of a linked list has two components, we need to declare each node as a
class or struct
...
However, the link component of each node is a
pointer
...
For the previous linked
list, the definition of the node is as follows
...
)
struct nodeType
{
int info;
nodeType *link;
};
The variable declaration is as follows:
nodeType *head;
Linked Lists: Some Properties
To better understand the concept of a linked list and a node, some important properties
of linked lists are described next
...
FIGURE 5-4
2000
17 2800
2800
92 1500
1500
63 3600
3600
45
0
info
head
2000
info
info
info
link
link
Linked list with four nodes
link
link
5
268 |
Chapter 5: Linked Lists
This linked list has four nodes
...
Each node has two components: info, to store the info, and link, to store the address of
the next node
...
Suppose that the first node is at location 2000, the second node is at location 2800, the
third node is at location 1500, and the fourth node is at location 3600
...
TABLE 5-1
Values of head and some of the nodes of the linked list in Figure 5-4
Value
head
2000
head->info
17
head->link
2800
head->link->info
92
Explanation
Because head is 2000 and the info of the
node at location 2000 is 17
Because head->link is 2800 and the
info of the node at location 2800 is 92
Suppose that current is a pointer of the same type as the pointer head
...
Now consider the following statement:
current = current->link;
This statement copies the value of current->link, which is 2800, into current
...
(When working with linked lists, we typically use these types of statements to advance a
pointer to the next node in the list
...
head
2000
2000
17 2800
info link
2800
92 1500
info link
1500
63 3600
info link
3600
45
info
current 2800
FIGURE 5-5
List after the statement current = current->link; executes
0
link
Linked Lists
|
269
Table 5-2 shows the values of current, head, and some other nodes in Figure 5-5
...
TRAVERSING A LINKED LIST
The basic operations of a linked list are as follows: Search the list to determine whether a
particular item is in the list, insert an item in the list, and delete an item from the list
...
That is, given a pointer to the first node
of the list, we must step through the nodes of the list
...
We cannot use the pointer head to traverse the list because if we use the
head to traverse the list, we would lose the nodes of the list
...
The pointer head contains the address of the first
node, the first node contains the address of the second node, the second node contains the
address of the third node, and so on
...
If we keep advancing head to the next
node, we will lose all the nodes of the list (unless we save a pointer to each node before
advancing head, which is impractical because it would require additional computer time
and memory space to maintain the list)
...
It now follows that we must
traverse the list using another pointer of the same type
...
The following code traverses the list:
5
270 |
Chapter 5: Linked Lists
current = head;
while (current != NULL)
{
//Process current
current = current->link;
}
For example, suppose that head points to a linked list of numbers
...
Consider the following definition of a node
...
The next section, which discusses linked lists as an abstract data type (ADT)
using templates, uses the generic definition of a node
...
head
45
65
p
FIGURE 5-6
Linked list before item insertion
34
76
Linked Lists
|
271
Suppose that p points to the node with info 65, and a new node with info 50 is to be
created and inserted after p
...
TABLE 5-3
Inserting a node in a linked list
Statement
Effect
head
45
65
34
65
34
5
76
76
p
newNode = new nodeType;
newNode
head
45
p
newNode->info = 50;
newNode
head
45
50
65
76
p
newNode->link = p->link;
newNode
head
p->link = newNode;
34
45
50
34
65
76
p
newNode
50
Note that the sequence of statements to insert the node, that is,
newNode->link = p->link;
p->link = newNode;
is very important because to insert newNode in the list we use only one pointer, p, to
adjust the links of the nodes of the linked list
...
head
45
34
65
76
p
newNode
50
FIGURE 5-7 List after the execution of the statement p->link = newNode; followed by the
execution of the statement newNode->link = p->link;
From Figure 5-7, it is clear that newNode points back to itself and the remainder of the
list is lost
...
Suppose q points to
the node with info 34
...
)
head
45
65
p
newNode
FIGURE 5-8
34
76
q
50
List with pointers p and q
The following statements insert newNode between p and q:
newNode->link = q;
p->link = newNode;
The order in which these statements execute does not matter
...
TABLE 5-4
Inserting a node in a linked list using two pointers
Statement
Effect
head
45
34
65
p
p->link = newNode;
newNode
head
45
50
65
34
p
newNode->link = q;
76
q
newNode
50
76
q
DELETION
Consider the linked list shown in Figure 5-9
...
The following
statement removes the node from the list:
p->link = p->link->link;
Figure 5-10 shows the resulting list after the preceding statement executes
...
However, the memory is still occupied by this node and this memory is inaccessible; that
5
274 |
Chapter 5: Linked Lists
is, this node is dangling
...
The
following statements delete the node from the list and deallocate the memory occupied
by this node:
q = p->link;
p->link = q->link;
delete q;
Table 5-5 shows the effect of these statements
...
First, we consider a linked list in general
...
Such a list can be built in two ways: forward and backward
...
In the backward manner, a new
node is always inserted at the beginning of the list
...
BUILDING A LINKED LIST FORWARD
Suppose that the nodes are in the usual info-link form and info is of type int
...
Consider the following variable declaration:
nodeType *first, *last, *newNode;
int num;
Linked Lists
|
275
Suppose that first points to the first node in the list
...
Thus, we must have the statements
first = NULL;
last = NULL;
to initialize first and last to NULL
...
Initially, both first and last are NULL
...
first
last
FIGURE 5-11
Empty list
After statement 1 executes, num is 2
...
Statement 3 stores 2 in the info field of newNode, and statement 4
stores NULL in the link field of newNode
...
)
newNode
FIGURE 5-12
newNode with info 2
2
5
276 |
Chapter 5: Linked Lists
Because first is NULL, we execute statements 5a and 5b
...
first
last
2
newNode
FIGURE 5-13
List after inserting newNode in it
We now repeat statements 1 through 6b
...
Statement 2 creates a node and stores the address of this node in newNode
...
(See Figure 5-14
...
Figure 5-15 shows the
resulting list
...
Figure 5-16 shows the resulting list
...
We can, in fact, write a C++ function to build a linked list
...
The following function,
buildListForward, builds a linked list (in a forward manner) and returns the pointer
of the built list:
nodeType* buildListForward()
{
nodeType *first, *newNode, *last;
int num;
cout << "Enter a list of integers ending with -999
...
For the previously given
data—2, 15, 8, 24, and 34—the linked list is as shown in Figure 5-17
...
Also, after inserting the new node at the
beginning, the new node becomes the first node in the list
...
We see, then, that we need
only two pointers to build the linked list: one to point to the list and one to create the new
node
...
The
following C++ function builds the linked list backward and returns the pointer of the built list:
nodeType* buildListBackward()
{
nodeType *first, *newNode;
int num;
cout << "Enter a list of integers ending with -999
...
Because a linked list is a very important data structure, rather than
discuss specific lists such as a list of integers or a list of strings, this section discusses linked lists
as an abstract data type (ADT)
...
The programming
example at the end of this chapter also uses this generic definition of linked lists
...
2
...
4
...
Initialize the list
...
Print the list
...
Destroy the list
...
7
...
9
...
11
...
Retrieve the info contained in the last node
...
Insert an item in the list
...
Make a copy of the linked list
...
The algorithms to
implement the operations search, insert, and remove slightly differ for sorted and unsorted lists
...
Using the principal of inheritance, we derive two classes—
unorderedLinkedList and orderedLinkedList—from the class linkedListType
...
On the other hand, objects of the class
orderedLinkedList would arrange elements according to some comparison criteria, usually
less than or equal to
...
Moreover, after inserting an
element into or removing an element from an ordered list, the resulting list will be ordered
...
Furthermore, you can build such a list in either a forward manner or a backward manner
...
To accommodate both
operations, we will write two functions: insertFirst to insert the new item at the
beginning of the list and insertLast to insert the new item at the end of the list
...
Structure of Linked List Nodes
Recall that each node of a linked list must store the data as well as the address for the next
node in the list (except the last node of the list)
...
To simplify operations such as insert and delete, we define the class to implement
the node of a linked list as a struct
...
Programming
Exercise 8, at the end of this chapter, asks you to redefine the class to implement the nodes
of a linked list so that the instance variables of the class nodeType are private
...
The pointer first
points to the first node in the list, and last points to the last node in the list
...
Therefore, the class linkedListType
has three instance variables, as follows:
protected:
int count; //variable to store the number of elements in the list
nodeType
nodeType
Linked List Iterators
One of the basic operations performed on a list is to process each node of the list
...
Moreover, a specific application
requires each node to be processed in a very specific way
...
So what is an iterator? An iterator is an object
that produces each element of a container, such as a linked list, one element at a time
...
The increment operator advances the iterator to the next
node in the list while the dereferencing operator returns the info of the current node
...
So we need to define a class, which we will call
linkedListIterator, to create iterators to objects of the class linkedListType
...
//***********************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement an iterator
// to a linked list
...
//Postcondition: current = ptr;
Type operator*();
//Function to overload the dereferencing operator *
...
linkedListIterator
//Overload the preincrement operator
...
Linked List as an ADT
|
281
bool operator==(const linkedListIterator
//Overload the equality operator
...
bool operator!=(const linkedListIterator
//Overload the not equal to operator
...
private:
nodeType
//node in the linked list
};
Figure 5-18 shows the UML class diagram of the class linkedListIterator
...
current);
}
template
bool linkedListIterator
(const linkedListIterator
{
return (current != right
...
Now that we have defined the classes to implement the node of a linked list and an
iterator to a linked list, next, we describe the class linkedListType to implement the
basic porperties of a linked list
...
S
...
This is an abstract class
...
//***********************************************************
template
class linkedListType
{
public:
const linkedListType
(const linkedListType
//Overload the assignment operator
...
//Postcondition: first = NULL, last = NULL, count = 0;
Linked List as an ADT
|
283
bool isEmptyList() const;
//Function to determine whether the list is empty
...
void print() const;
//Function to output the data contained in each node
...
//Postcondition: The value of count is returned
...
//Postcondition: first = NULL, last = NULL, count = 0;
Type front() const;
//Function to return the first element of the list
...
//Postcondition: If the list is empty, the program terminates;
//
otherwise, the first element of the list is returned
...
//Precondition: The list must exist and must not be empty
...
virtual bool search(const Type& searchItem) const = 0;
//Function to determine whether searchItem is in the list
...
virtual void insertFirst(const Type& newItem) = 0;
//Function to insert newItem at the beginning of the list
...
virtual void insertLast(const Type& newItem) = 0;
//Function to insert newItem at the end of the list
...
virtual void deleteNode(const Type& deleteItem) = 0;
//Function to delete deleteItem from the list
...
first points to the first node,
//
last points to the last node of the updated list, and
//
count is decremented by 1
...
//Postcondition: Returns an iterator such that current is set
//
to first
...
//Postcondition: Returns an iterator such that current is set
//
to NULL
...
//Postcondition: first = NULL, last = NULL, count = 0;
linkedListType(const linkedListType
//copy constructor
~linkedListType();
//destructor
//Deletes all the nodes from the list
...
protected:
int count; //variable to store the number of list elements
//
nodeType
nodeType
private:
void copyList(const linkedListType
//Function to make a copy of otherList
...
};
Linked List as an ADT
|
285
Figure 5-19 shows the UML class diagram of the class linkedListType
...
The instance variables first and last, as defined earlier, of the class linkedListType
are protected, not private, because as noted previously, we will derive the classes
unorderedLinkedList and orderedLinkedList from the class linkedListType
...
The definition of the class linkedListType includes a member function to overload
the assignment operator
...
For the same reason, the
definition of the class also includes a copy constructor
...
This is because this function is used
only to implement the copy constructor and overload the assignment operator
...
The list is empty if first is NULL
...
It simply initializes
the list to an empty state
...
template
linkedListType
{
first = NULL;
last = NULL;
count = 0;
}
From the definitions of the functions isEmptyList and the default constructor, it
follows that each of these functions is of O(1)
...
We traverse
the list starting from the first node and deallocate the memory by calling the operator
delete
...
Once the entire list is
destroyed, we must set the pointers first and last to NULL and count to 0
...
From this, it follows that the
function destroyList is of O(n)
...
Note that the default
constructor or the copy constructor has already initialized the list when the list object was
declared
...
This task can be accomplished by using the destroyList
operation, which also resets the pointers first and last to NULL and sets count to 0
...
Therefore, the function initializeList is of O(n)
...
To print the data
contained in each node, we must traverse the list starting at the first node
...
(If we use first to traverse the list, the entire list will be lost
...
Length of a List
The length of a linked list (that is, how many nodes are in the list) is stored in the variable
count
...
template
int linkedListType
{
return count;
}
5
288 |
Chapter 5: Linked Lists
Retrieve the Data of the First Node
The function front returns the info contained in the first node, and its definition is
straightforward
...
Therefore,
before calling this function check, you have to check to see whether the list is nonempty
...
Its definition is as follows:
template
Type linkedListType
{
assert(last != NULL);
return last->info; //return the info of the last node
}//end back
Notice that if the list is empty, the assert statement terminates the program
...
From the definitions of the functions length, front, and back, it follows easily that
each of these functions are of O(1)
...
Their definitions are as follows:
template
linkedListIterator
{
linkedListIterator
}
return temp;
template
linkedListIterator
{
linkedListIterator
}
return temp;
Linked List as an ADT
|
289
From the definitions of the functions length, front, back, begin, and end, it follows
easily that each of these functions are of O(1)
...
Therefore, we traverse
the list to be copied starting at the first node
...
Create a node and call it newNode
...
Copy the info of the node (in the original list) into newNode
...
Insert newNode at the end of the list being created
...
first == NULL) //otherList is empty
{
first = NULL;
last = NULL;
count = 0;
}
else
{
current = otherList
...
count;
//copy the first node
first = new nodeType
first->info = current->info; //copy the info
first->link = NULL; //set the link field of the node to NULL
last = first;
//make last point to the first node
current = current->link; //make current point to the next
// node
//copy the remaining list
while (current != NULL)
{
newNode = new nodeType
newNode->info = current->info; //copy the info
newNode->link = NULL; //set the link of newNode to NULL
5
290 |
Chapter 5: Linked Lists
last->link = newNode; //attach newNode after last
last = newNode; //make last point to the actual last
//node
current = current->link; //make current point to the
//next node
}//end while
}//end else
}//end copyList
The function copyList contains a while loop
...
If the list contains n items, the while
loop executes n times
...
Destructor
The destructor deallocates the memory occupied by the nodes of a list when the class
object goes out of scope
...
We
must traverse the list, starting at the first node, and delete each node in the list
...
Therefore, the definition of the
destructor is as follows:
template
linkedListType
{
destroyList();
}
Copy Constructor
Because the class linkedListType contains pointer data members, the definition of
this class contains the copy constructor
...
The copy constructor also executes when an object is declared and initialized using
another object
...
This can be done by
calling the function copyList
...
The definition of the copy constructor is as follows:
template
linkedListType
(const linkedListType
{
first = NULL;
copyList(otherList);
}//end copy constructor
Linked List as an ADT
|
291
Overloading the Assignment Operator
The definition of the function to overload the assignment operator for the class
linkedListType is similar to the definition of the copy constructor
...
//overload the assignment operator
template
const linkedListType
(const linkedListType
{
if (this != &otherList) //avoid self-copy
{
copyList(otherList);
}//end else
}
return *this;
The destructor uses the function destroyList, which is of O(n)
...
Therefore, each of these functions are of O(n)
...
The following class defines an unordered linked list as an ADT:
//***********************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement the basic
// properties of an unordered linked list
...
//***********************************************************
template
class unorderedLinkedList: public linkedListType
{
public:
bool search(const Type& searchItem) const;
//Function to determine whether searchItem is in the list
...
void insertFirst(const Type& newItem);
//Function to insert newItem at the beginning of the list
...
//
void insertLast(const Type& newItem);
//Function to insert newItem at the end of the list
...
void deleteNode(const Type& deleteItem);
//Function to delete deleteItem from the list
...
first points to the first
node, last points to the last node of the updated
list, and count is decremented by 1
...
unorderedLinkedList
linkedListType
+search(const Type&) const: bool
+insertFirst(const Type&): void
+insertLast(const Type&): void
+deleteNode(const Type&): void
5
unorderedLinkedList
FIGURE 5-20 UML class diagram of the class unorderedLinkedList and the
inheritance hierarchy
Next we give the definitions of the member functions of the class unorderedLinkedList
...
If the item is found, it
returns true; otherwise, it returns false
...
This function has the following steps:
1
...
If the info of
the current node is the same as the search item, stop the search; otherwise,
make the next node the current node
...
Repeat Step 1 until either the item is found or no more data is left in the
list to compare with the search item
...
Suppose the list has n items
...
On the other hand, if the search item is the first
item, the while loop executes 1 time
...
From these observations, we can show that the function
search is of O(n)
...
Insert the First Node
The function insertFirst inserts the new item at the beginning of the list—that is, before
the node pointed to by first
...
2
...
4
...
Create a new node
...
Store the new item in the new node
...
Increment count by 1
...
Here, we insert the new node after last
...
DELETE A NODE
Next, we discuss the implementation of the member function deleteNode, which
deletes a node from the list with a given info
...
The node is nonempty and the node to be deleted is the first node
...
• The node to be deleted is not in the list
...
If list is
not empty, we search the list for the node with the given info and, if such a node is
found, we delete this node
...
In
pseudocode, the algorithm is as follows:
if list is empty
Output(cannot delete from an empty list);
else
{
if the first node is the node with the given info
adjust the head pointer, that is, first, and deallocate
the memory;
else
{
search the list for the node with the given info
if such a node is found, delete it and adjust the
values of last (if necessary) and count
...
If the list is empty, output an error message as shown in the
pseudocode
...
This case has
two scenarios: list has only one node, and list has more than one node
...
Therefore, after
deletion, both first and last are set to NULL and count is set to 0
...
Then after deleting this node, the second node becomes the first node
...
Case 3: The node to be deleted is not the first node, but is somewhere in the list
...
Let us illustrate the first cases
...
Consider the list shown in Figure 5-21
...
After deleting this node, the resulting list
is as shown in Figure 5-22
...
The link field of the previous node—that is,
17—changes
...
)
list
first
last
count 4
FIGURE 5-22
list after deleting 37
28
17
24
54
Unordered Linked Lists
|
297
Case 3b: The node to be deleted is the last node
...
It contains the address of the node just
before the node to be deleted
...
After deleting 54, last contains the
address of the node with info 24
...
Case 4: The node to be deleted is not in the list
...
We
simply output an error message, indicating that the item to be deleted is not in the list
...
Because a linked list is not a random access data structure, we must sequentially search
the list
...
We
sequentially search the list starting at the second node
...
Thus, we need a pointer to the previous node
...
If the node to be deleted is the last
node, we must adjust the pointer last
...
cout << "Cannot delete from an empty list
...
" << endl;
}//end else
}//end else
}//end deleteNode
From the definition of the function deleteNode, it can be shown that this function is of O(n)
...
TABLE 5-7
Time-complexity of the operations of the class unorderedLinkedList
Function
Time-complexity
search
O (n)
insertFirst
O (1)
insertLast
O (1)
deleteNode
O (n)
Header File of the Unordered Linked List
For the sake of completeness, we show how to create the header file that defines the
class unorderedListType and the operations on such lists
...
h
...
S
...
This class is
// derived from the class linkedListType
...
h"
using namespace std;
template
class unorderedLinkedList: public linkedListType
{
public:
bool search(const Type& searchItem) const;
//Function to determine whether searchItem is in the list
...
void insertFirst(const Type& newItem);
//Function to insert newItem at the beginning of the list
...
void insertLast(const Type& newItem);
//Function to insert newItem at the end of the list
...
};
void deleteNode(const Type& deleteItem);
//Function to delete deleteItem from the list
...
first points to the first
//
node, last points to the last node of the updated list,
//
and count is decremented by 1
...
...
...
5
300 |
Chapter 5: Linked Lists
Ordered Linked Lists
The preceding section described the operations on an unordered linked list
...
As noted earlier, we derive the class orderedLinkedList
from the class linkedListType and provide the definitions of the abstract functions
insertFirst, insertLast, search, and deleteNode to take advantage of the fact that
the elements of an ordered linked list are arranged using some ordering criteria
...
Because the elements of an ordered linked list are in order, we include the function
insert to insert an element in an ordered list at the proper place
...
S
...
This class is
// derived from the class linkedListType
...
//Postcondition: Returns true if searchItem is in the list,
//
otherwise the value false is returned
...
//Postcondition: first points to the new list, newItem
//
is inserted at the proper place in the list, and
//
count is incremented by 1
...
//Postcondition: first points to the new list, newItem is
//
inserted at the beginning of the list, last points to the
//
last node in the list, and count is incremented by 1
...
//Postcondition: first points to the new list, newItem is
//
inserted at the end of the list, last points to the
//
last node in the list, and count is incremented by 1
...
//Postcondition: If found, the node containing deleteItem is
Ordered Linked Lists
};
//
//
//
//
|
301
deleted from the list; first points to the first node
of the new list, and count is decremented by 1
...
Figure 5-23 shows a UML class diagram of the class orderedLinkedList and the
inheritance hierarchy
...
Search the List
First, we discuss the search operation
...
Here, because the list is
sorted, we can improve the search algorithm somewhat
...
We stop the search as soon as we find a node in the list with info
greater than or equal to the search item, or we have searched the entire list
...
Compare the search item with the current node in the list
...
2
...
Note that the loop does not explicitly check whether the search item is equal to an item
in the list
...
template
bool orderedLinkedList
search(const Type& searchItem) const
302 |
Chapter 5: Linked Lists
{
bool found = false;
nodeType
current = first;
//start the search at the first node
while (current != NULL && !found)
if (current->info >= searchItem)
found = true;
else
current = current->link;
if (found)
found = (current->info == searchItem); //test for equality
return found;
}//end search
As in the case of the search function of the class unorderedLinkedList, the search
function of the class orderedLinkedList is also of O(n)
...
To find the place for the new item in the
list, as before, we search the list
...
The pointer current points to the node whose info is being compared with
the item to be inserted, and trailCurrent points to the node just before current
...
The following cases arise:
Case 1: The list is initially empty
...
Case 2: The new item is smaller than the smallest item in the list
...
In this case, we need to adjust the list’s head pointer—
that is, first
...
Case 3: The item is to be inserted somewhere in the list
...
In this case, the new
item is inserted at the end of the list
...
Also, count is incremented by 1
...
In this
case, the new item is inserted between trailCurrent and current
...
The following statements can accomplish both cases 3a and 3b
...
trailCurrent->link = newNode;
newNode->link = current;
Ordered Linked Lists
|
303
Let us next illustrate Case 3
...
As indicated previously, this case has two scenarios
...
Consider the list shown in Figure 5-24
...
After inserting 65, the resulting list is as
shown in Figure 5-25
...
Again
consider the list shown in Figure 5-24
...
Clearly, 25 goes between 17 and 27, which would require the
link of the node with info 17 to be changed
...
list
first
17
25
27
38
last
count 5
FIGURE 5-26
list after inserting 25 in the list in Figure 5-24
54
304 |
Chapter 5: Linked Lists
From case 3, it follows that we must first traverse the list to find the place where the new
item is to be inserted
...
The pointer current is used to traverse the list and
compare the info of the node in the list with the item to be inserted
...
For example, in case 3b, when
the search stops, trailCurrent points to node 17 and current points to node 27
...
In case 3a, after searching the list to find the place
for 65, trailCurrent points to node 54 and current is NULL
...
It can be
shown that the function insert is of O(n)
...
In Programming Exercise 7 at the end of this chapter
you are asked to revise the definition of the function insert so that before inserting the
item it checks whether the item to be inserted is already in the list
...
In other
words, duplicates are not allowed
...
However,
because the resulting list must be sorted, the new item must be inserted at the proper
place
...
We, therefore, use the function insert to insert the new item at its proper place
...
However, you
must provide its definition as these functions are declared as abstract in the parent class
...
It follows that these functions are of O(n)
...
The function to implement this operation is the same
as the delete operation on general linked lists
...
As in the case of insertNode, we search the list with two pointers, current and
trailCurrent
...
We have an error
...
Case 2: The item to be deleted is contained in the first node of the list
...
Case 3: The item to be deleted is somewhere in the list
...
Case 4: The list is not empty, but the item to be deleted is not in the list
...
The definition of the function
deleteNode is as follows:
template
void orderedLinkedList
{
nodeType
nodeType
bool found;
if (first == NULL) //Case 1
cout << "Cannot delete from an empty list
...
"
<< endl;
else
if (current->info == deleteItem) //the item to be
//deleted is in the list
Ordered Linked Lists
|
307
{
if (first == current)
{
first = first->link;
//Case 2
if (first == NULL)
last = NULL;
delete current;
}
else
//Case 3
{
trailCurrent->link = current->link;
if (current == last)
last = trailCurrent;
delete current;
}
count--;
}
else
//Case 4
cout << "The item to be deleted is not in the "
<< "list
...
Table 5-8 gives the time-complexity of the operations of the class orderedLinkedList
...
(We assume that the defini-
5
308 |
Chapter 5: Linked Lists
tion of the class linkedListType and the definitions of the functions to implement
the operations are in the header file linkedList
...
)
#ifndef H_orderedListType
#define H_orderedListType
//***********************************************************
// Author: D
...
Malik
//
// This class specifies the members to implement the basic
// properties of an ordered linked list
...
//***********************************************************
#include "linkedList
...
//Postcondition: Returns true if searchItem is in the list,
//
otherwise the value false is returned
...
//Postcondition: first points to the new list, newItem is
//
inserted at the proper place in the list, and count
//
is incremented by 1
...
//Postcondition: first points to the new list, newItem is
//
inserted at the beginning of the list, last points to the
//
last node in the list, and count is incremented by 1
...
//Postcondition: first points to the new list, newItem is
//
inserted at the end of the list, last points to the
//
last node in the list, and count is incremented by 1
...
//Postcondition: If found, the node containing deleteItem is
//
deleted from the list; first points to the first node of
//
the new list, and count is decremented by 1
...
Ordered Linked Lists
|
309
//Place the definitions of the functions search, insert,
//insertfirst, insertLast, and deleteNode here
...
...
#endif
The following program tests various operations on an ordered linked list:
//**********************************************************
// Author: D
...
Malik
//
// This program tests the various operations on an ordered
// linked list
...
h"
//Line 1
//Line 2
using namespace std;
//Line 3
int main()
{
orderedLinkedList
int num;
//Line 4
//Line 5
//Line 6
cout << "Line 7: Enter numbers ending "
<< "with -999
...
insert(num);
cin >> num;
}
//Line
//Line
//Line
//Line
//Line
cout << endl;
//Line 14
cout << "Line 15: list1: ";
list1
...
print();
cout << endl;
//Line 19
//Line 20
//Line 21
cout << "Line 22: Enter the number to be "
<< "deleted: ";
cin >> num;
cout << endl;
//Line 22
//Line 23
//Line 24
5
310 |
Chapter 5: Linked Lists
list2
...
print();
cout << endl;
}
//Line 25
//Line 26
//Line 27
//Line 28
return 0;
//Line 29
//Line 30
Sample Run: In this sample run, the user input is shaded:
Line 7: Enter numbers ending with -999
...
The details are left as an exercise for you
...
Programming Exercise 7 at
the end of this chapter asks you to revise the definition of the function insert so that
before inserting the item it checks whether it is already in the list
...
In
other words, duplicates are not allowed
...
In other words, every node contains the address of the next node (except the last
node), and every node contains the address of the previous node (except the first node)
...
)
first
last
FIGURE 5-27
Doubly linked list
Doubly Linked Lists
|
311
A doubly linked list can be traversed in either direction
...
As before, the typical operations on a doubly linked list are as follows: Initialize the list,
destroy the list, determine whether the list is empty, search the list for a given item, insert
an item, delete an item, and so on
...
S
...
//***********************************************************
//Definition of the node
template
struct nodeType
{
Type info;
nodeType
nodeType
};
template
class doublyLinkedList
{
public:
const doublyLinkedList
(const doublyLinkedList
//Overload the assignment operator
...
//Postcondition: first = NULL; last = NULL; count = 0;
bool isEmptyList() const;
//Function to determine whether the list is empty
...
void destroy();
//Function to delete all the nodes from the list
...
void reversePrint() const;
//Function to output the info contained in each node
//in reverse order
...
//Postcondition: The value of count is returned
...
//Precondition: The list must exist and must not be empty
...
Type back() const;
//Function to return the last element of the list
...
//Postcondition: If the list is empty, the program terminates;
//
otherwise, the last element of the list is returned
...
//Postcondition: Returns true if searchItem is found in the
//
list, otherwise returns false
...
//Precondition: If the list is nonempty, it must be in order
...
void deleteNode(const Type& deleteItem);
//Function to delete deleteItem from the list
...
doublyLinkedList();
//default constructor
//Initializes the list to an empty state
...
protected:
int count;
nodeType
nodeType
Doubly Linked Lists
|
313
private:
void copyList(const doublyLinkedList
//Function to make a copy of otherList
...
};
We leave the UML class diagram of the class doublyLinkedList as an exercise for
you, see Exercise 11 at the end of this chapter
...
Here, because every node has two pointers, back and next, some of the
operations require the adjustment of two pointers in each node
...
Let us call this pointer current
...
We give the definition of each function here, with four exceptions
...
(See Programming Exercise 10 at the end
of this chapter
...
Default Constructor
The default constructor initializes the doubly linked list to an empty state
...
template
doublyLinkedList
{
first= NULL;
last = NULL;
count = 0;
}
isEmptyList
This operation returns true if the list is empty; otherwise, it returns false
...
template
bool doublyLinkedList
{
return (first == NULL);
}
Destroy the List
This operation deletes all the nodes in the list, leaving the list in an empty state
...
Furthermore, count is set to 0
...
This task can be done by
using the operation destroy
...
Therefore, this function returns the value of this variable
...
We traverse the list
starting from the first node
...
We traverse the
list in reverse order starting from the last node
...
The search algorithm is exactly the same as the search algorithm for an ordered linked list
...
If the list is empty, both functions terminate the program
...
As before, we find the
place where the new item is supposed to be inserted, create the node, store the new
item, and adjust the link fields of the new node and other particular nodes in the list
...
Cases 3 and 4 are
similar
...
Next, we show case 4
...
first
8
15
last
count 4
FIGURE 5-28
Doubly linked list before inserting 20
24
40
Doubly Linked Lists
|
317
Suppose that 20 is to be inserted in the list
...
first
8
15
24
40
20
last
count 5
FIGURE 5-29
Doubly linked list after inserting 20
From Figure 5-29, it follows that the next pointer of node 15, the back pointer of node 24,
and both the next and back pointers of node 20 need to be adjusted
...
As before, we
first search the list to see whether the item to be deleted is in the list
...
Similar to the insert operation, this operation (if the
item to be deleted is in the list) requires the adjustment of two pointers in certain nodes
...
Case 2: The item to be deleted is in the first node of the list, which would require us to
change the value of the pointer first
...
Case 4: The item to be deleted is not in the list
...
Let us demonstrate case 3
...
first
5
17
44
52
last
count 4
FIGURE 5-30
Doubly linked list before deleting 17
Suppose that the item to be deleted is 17
...
(See Figure 5-31
...
(See Figure 5-32
...
" << endl;
else if (first->info == deleteItem) //node to be deleted is
//the first node
{
current = first;
first = first->next;
if (first != NULL)
first->back = NULL;
else
last = NULL;
count--;
delete current;
}
else
{
found = false;
current = first;
while (current != NULL && !found) //search the list
if (current->info >= deleteItem)
found = true;
else
current = current->next;
if (current == NULL)
cout << "The item to be deleted is not in "
<< "the list
...
" endl;
}//end else
}//end deleteNode
STL Sequence Container: list |
321
STL Sequence Container: list
Chapter 4 listed three types of sequence containers—vector, deque, and list
...
This section describes
the STL sequence container list
...
Thus, every element in a list points to its immediate predecessor and to its immediate
successor (except the first and last elements)
...
Therefore, to access, for example, the fifth element
in the list, we must first traverse the first four elements
...
The
definition of the class list, and the definitions of the functions to implement the
various operations on a list, are contained in the header file list
...
Thus, a
list object can be initialized in several ways when it is declared, as described in
Table 5-9
...
(The default constructor is
invoked
...
listCont and
otherList are of the same type
...
listCont is initialized
using the default constructor
...
listCont is initialized using
n copies of the element elem
...
listCont is initialized to the elements in
the range [beg, end), that is, all the
elements in the range beg
...
Both beg and end are iterators
...
In addition to these
common operations, Table 5-10 describes the operations that are specific to a list
container
...
(Suppose that listCont is a container of type list
...
assign(n, elem)
Assigns n copies of elem
...
assign(beg, end)
Assigns all the elements in the range
beg
...
Both beg and end are
iterators
...
push_front(elem)
Inserts elem at the beginning of listCont
...
pop_front()
Removes the first element from listCont
...
front()
Returns the first element
...
)
listCont
...
(Does not check
whether the container is empty
...
remove(elem)
Removes all the elements that are equal
to elem
...
remove_if(oper)
Removes all the elements for which oper
is true
...
unique()
If the consecutive elements in listCont
have the same value, removes the
duplicates
...
unique(oper)
If the consecutive elements in listCont
have the same value, removes the
duplicates, for which oper is true
...
splice(pos, listCont2)
All the elements of listCont2 are
moved to listCont1 before the position
specified by the iterator pos
...
STL Sequence Container: list |
TABLE 5-10
323
Operations specific to a list container (continued)
Expression
Description
listCont1
...
listCont1
...
end-1
of listCont2 are moved to listCont1
before the position specified by the iterator
pos
...
listCont
...
The sort criterion is <
...
sort(oper)
The elements of listCont are sorted
...
listCont1
...
This
operation moves all the elements of
listCont2 into listCont1
...
Moreover, after this operation,
listCont2 is empty
...
merge(listCont2, oper)
Suppose that the elements of listCont1
and listCont2 are sorted according to the
sort criteria oper
...
After this operation, the
elements in listCont1 are sorted
according to the sort criteria oper
...
reverse()
The elements of listCont are reversed
...
EXAMPLE 5-1
//****************************************************************
// Author: D
...
Malik
//
// This program illustrates how to use a list container in a
// program
...
push_back(23);
intList1
...
push_back(58);
intList1
...
push_back(15);
intList1
...
push_back(58);
//Line
//Line
//Line
//Line
//Line
//Line
//Line
cout << "Line 17: intList1: ";
copy(intList1
...
end(), screen);
cout << endl;
//Line 17
//Line 18
//Line 19
intList2 = intList1;
//Line 20
cout << "Line 21: intList2: ";
copy(intList2
...
end(), screen);
cout << endl;
//Line 21
//Line 22
//Line 23
intList1
...
begin(), intList1
...
sort();
//Line 28
cout << "Line 29: After sorting, intList2: ";
copy(intList2
...
end(), screen);
cout << endl;
}
//Line 9
//Line 29
//Line 30
//Line 31
return 0;
//Line 32
//Line 33
Sample Run:
Line 17: intList1: 23 58 58 36 15 98 58
Line 21: intList2: 23 58 58 36 15 98 58
Line 25: After removing the consecutive duplicates,
intList1: 23 58 36 15 98 58
Line 29: After sorting, intList2: 15 23 36 58 58 58 98
10
11
12
13
14
15
16
Linked Lists with Header and Trailer Nodes
|
325
For the most part, the output of the preceding program is straightforward
...
The statement in Line 20 copies the elements of intList1 into
intList2
...
The
statement in Line 24 removes any consecutive occurrences of the same elements
...
The operation unique removes
two occurrences of 58
...
The statement in Line 28 sorts intList2
...
These cases needed to be handled separately
...
One way to
simplify these algorithms is to never insert an item before the first or last item and to never
delete the first node
...
Suppose the nodes of a list are in order; that is, they are arranged with respect to a given key
...
In this case, we can set up a node, called the header, at the beginning of the list containing
a value smaller than the smallest value in the data set
...
These two nodes, header and trailer, serve merely to simplify the insertion and deletion
algorithms and are not part of the actual list
...
For example, suppose the data are ordered according to the last name
...
The smallest last name is larger than
the string "A" and the largest last name is smaller than the string "zzzzzzzz"
...
Figure 5-33 shows an empty and a nonempty linked list with header and trailer nodes
...
We leave it as an exercise for you to design a class to implement a linked list with header
and trailer nodes
...
)
Circular Linked Lists
A linked list in which the last node points to the first node is called a circular linked list
...
first
(a) Empty circular list
first
(b) Circular linked list with one node
first
(c) Circular linked list with more than one node
FIGURE 5-34
Circular linked lists
In a circular linked list with more than one node, as in Figure 5-34(c), it is convenient to
make the pointer first point to the last node of the list
...
For example, first points to the last node
and first->link points to the first node
...
We leave it as exercise for you to design a class to implement a sorted circular linked list
...
)
Programming Example: Video Store
PROGRAMMING EXAMPLE:
|
327
Video Store
During holidays or on weekends, a family or an individual typically rents a movie either
from a local store or online
...
2
...
4
...
6
...
8
...
Return, or check in, a video
...
Show the details of a particular video
...
Check whether a particular video is in the store
...
Print a list of all the videos rented by each customer
...
This example further illustrates the objectoriented design methodology and, in particular, inheritance and overloading
...
We will describe these two components in detail
...
In part 1, we design, implement, and test
the video component
...
That is, after
completing parts 1 and 2, we can perform all the operations listed previously
...
The common
things associated with a video are as follows:
•
•
•
•
•
•
Name of the movie
Names of the stars
Name of the producer
Name of the director
Name of the production company
Number of copies in the store
5
328 |
Chapter 5: Linked Lists
From this list, we see that some of the operations to be performed on the video object
are as follows:
1
...
3
...
5
...
Set the video information—that is, the title, stars, production
company, and so on
...
Check the number of copies in the store
...
In other words, if the number of
copies is greater than zero, decrement the number of copies by one
...
To check in a video, first we
must check whether the store owns such a video and, if it does,
increment the number of copies by one
...
The deletion of a video from the video list requires that the video list be searched
for the video to be deleted
...
For simplicity, we assume that two
videos are the same if they have the same title
...
S
...
//************************************************************
#include
#include
using namespace std;
class videoType
{
friend ostream& operator<< (ostream&, const videoType&);
public:
void setVideoInfo(string title, string star1,
string star2, string producer,
string director, string productionCo,
int setInStock);
//Function to set the details of a video
...
Programming Example: Video Store
|
329
//Postcondition: videoTitle = title; movieStar1 = star1;
//
movieStar2 = star2; movieProducer = producer;
//
movieDirector = director;
//
movieProductionCo = productionCo;
//
copiesInStock = setInStock;
int getNoOfCopiesInStock() const;
//Function to check the number of copies in stock
...
void checkOut();
//Function to rent a video
...
void checkIn();
//Function to check in a video
...
void printTitle() const;
//Function to print the title of a movie
...
//Postcondition: The title of the movie, stars, director,
//
and so on are displayed on the screen
...
//Postcondition: Returns the value true if the title is the
//
same as the title of the video; false otherwise
...
//Postcondition: copiesInStock = copiesInStock + num;
void setCopiesInStock(int num);
//Function to set the number of copies in stock
...
//Postcondition: The title of the video is returned
...
If no values are specified, the
//default values are assigned
...
bool operator==(const videoType&) const;
bool operator!=(const videoType&) const;
private:
string
string
string
string
};
videoTitle; //variable to store the name of the movie
movieStar1; //variable to store the name of the star
movieStar2; //variable to store the name of the star
movieProducer; //variable to store the name of the
//producer
string movieDirector; //variable to store the name of the
//director
string movieProductionCo; //variable to store the name
//of the production company
int copiesInStock; //variable to store the number of
//copies in stock
We leave the UML diagram of the class videoType as an exercise for you, see
Exercise 15 at the end of this chapter
...
Next, we write the definitions of each function in the class videoType
...
videoTitle);
}
bool videoType::operator!=(const videoType& other) const
{
return (videoTitle != other
...
videoTitle << endl;
osObject << "Stars: " << video
...
movieStar2 << endl;
osObject << "Producer: " << video
...
movieDirector << endl;
osObject << "Production Company: "
<< video
...
copiesInStock
<< endl;
osObject << "_____________________________________" << endl;
}
Video List
return osObject;
This program requires us to maintain a list of all the videos in the store, and we should
be able to add a new video to our list
...
Therefore, we will use a linked list to create a list of videos
...
We also defined the basic operations such as insertion and deletion
Programming Example: Video Store
|
333
of a video in the list
...
These operations are not available in the class unorderedLinkedList
...
The definition of the class videoListType is as follows:
//*****************************************************************
// Author: D
...
Malik
//
// class videoListType
// This class specifies the members to implement a list of videos
...
h"
#include "videoType
...
//Postcondition: Returns true if the title is found, and
//
false otherwise
...
//Postcondition: Returns true if at least one copy of the
//
video specified by title is in the store, and false
//
otherwise
...
//Postcondition: copiesInStock is decremented by one
...
//Postcondition: copiesInStock is incremented by one
...
//Postcondition: Returns true if the video’s title is the
//
same as title, and false otherwise
...
The
//parameter title specifies the name of the video for
//which the number of copies is to be updated
...
//The parameter title specifies the name of the video
//for which the number of copies is to be reset, and the
//parameter num specifies the number of copies
...
private:
void searchVideoList(string title, bool& found,
nodeType
//This function searches the video list for a particular
//video, specified by the parameter title
...
The parameter
//
current points to the node containing the video
...
Furthermore, unorderedLinkedList is a class template and
we have passed the class videoType as a parameter to this class
...
Because we are now dealing with a very specific data
type, the class videoListType is no longer needed to be a template
...
Through the member functions
of the class videoType, certain members—such as videoTitle and copiesInStock
of an object of type videoType—can now be accessed
...
The primary operations on the video list are to check in a video and to check out a
video
...
Other operations
such as seeing whether a particular video is in the store, updating the number of
copies of a video, and so on also require the video list to be searched
...
If the video is found, it sets a parameter found to true and returns
a pointer to the video so that check-in, check-out, and other operations on the
video object can be performed
...
First, we describe the search procedure
...
checkTitle(title)) //the item is found
found = true;
else
current = current->link; //advance current to
//the next node
}//end searchVideoList
If the search is successful, the parameter found is set to true and the parameter
current points to the node containing the video info
...
The definitions of the other functions of the class videoListType follow:
bool videoListType::isVideoAvailable(string title) const
{
bool found;
nodeType
searchVideoList(title, found, location);
if (found)
found = (location->info
...
checkIn();
5
336 |
Chapter 5: Linked Lists
else
}
cout << "The store does not carry " << title
<< endl;
void videoListType::videoCheckOut(string title)
{
bool found = false;
nodeType
searchVideoList(title, found, location); //search the list
}
if (found)
location->info
...
updateInStock(num);
else
cout << "The store does not carry " << title
<< endl;
void videoListType::videoSetCopiesInStock(string title, int num)
{
bool found = false;
nodeType
searchVideoList(title, found, location);
if (found)
location->info
...
printTitle();
current = current->link;
}
PART 2:
CUSTOMER
COMPONENT
Customer
Object
The customer object stores information about a customer, such as the first name,
last name, account number, and a list of videos rented by the customer
...
We have already designed the class personType in Example
1-12 (Chapter 1) and described the necessary operations on the name of a person
...
First, however, we must redefine the class
personType to take advantage of the new features of object-oriented design that you have
learned, such as operator overloading, and then derive the class customerType
...
2
...
4
...
Print the name, the account number, and the list of rented videos
...
Rent a video; that is, add the rented video to the list
...
Show the account number
...
(See Programming Exercise 14 at the end of this chapter
...
We assume that the
necessary data for the videos are stored in a file
...
The data in the input file is in the following
form:
video title (that is, the name of the movie)
movie star1
movie star2
movie producer
movie director
movie production co
...
...
We will write a function, createVideoList, to read the data from the input file and
create the list of videos
...
The algorithm of the function main is as follows:
1
...
If the input file does not exist, exit the program
...
Create the list of videos (createVideoList)
...
Show the menu (displayMenu)
...
While not done
Perform various operations
...
Let us describe Steps 2 and 3, which are
accomplished by writing two separate functions: createVideoList and displayMenu
...
Because the data will be read from a file and the input file was opened in the function
main, we pass the input file pointer to this function
...
Both parameters are
reference parameters
...
The general algorithm is as follows:
1
...
2
...
3
...
Programming Example: Video Store
displayMenu
|
339
This function informs the user what to do
...
S
...
//****************************************************************
#include
#include
#include
#include
#include
"videoType
...
h"
using namespace std;
void createVideoList(ifstream& infile,
videoListType& videoList);
void displayMenu();
int main()
{
videoListType videoList;
int choice;
char ch;
string title;
ifstream infile;
//open the input file
infile
...
txt");
if (!infile)
5
340 |
Chapter 5: Linked Lists
{
}
cout << "The input file does not exist
...
close();
//show the menu
displayMenu();
cout << "Enter your choice: ";
cin >> choice;
//get the request
cin
...
videoSearch(title))
cout << "The store carries " << title
<< endl;
else
cout << "The store does not carry "
<< title << endl;
break;
case 2:
cout << "Enter the title: ";
getline(cin, title);
cout << endl;
if (videoList
...
isVideoAvailable(title))
{
videoList
...
" << endl;
}
Programming Example: Video Store
|
341
else
cout << "The store does not carry "
<< title << endl;
break;
case 3:
cout << "Enter the title: ";
getline(cin, title);
cout << endl;
if (videoList
...
videoCheckIn(title);
cout << "Thanks for returning "
<< title << endl;
}
else
cout << "The store does not carry "
<< title << endl;
break;
case 4:
cout << "Enter the title: ";
getline(cin, title);
cout << endl;
if (videoList
...
isVideoAvailable(title))
cout << title << " is currently in "
<< "stock
...
" << endl;
}
else
cout << "The store does not carry "
<< title << endl;
break;
case 5:
videoList
...
print();
break;
default:
cout << "Invalid selection
...
get(ch);
cout << endl;
}//end while
}
return 0;
void createVideoList(ifstream& infile,
videoListType& videoList)
{
string title;
string star1;
string star2;
string producer;
string director;
string productionCo;
char ch;
int inStock;
videoType newVideo;
getline(infile, title);
while (infile)
{
getline(infile, star1);
getline(infile, star2);
getline(infile, producer);
getline(infile, director);
getline(infile, productionCo);
infile >> inStock;
infile
...
setVideoInfo(title, star1, star2, producer,
director, productionCo, inStock);
videoList
...
" << endl;
cout << "2: To check out a video
...
" << endl;
Quick Review |
343
cout << "4: To check whether a particular video is "
<< "in stock
...
"
<< endl;
cout << "6: To print a list of all the videos
...
2
...
4
...
6
...
8
...
10
...
12
...
14
...
16
...
The pointer to a linked list—that is, the pointer to the first node in the
list—is stored in a separate location, called the head or first
...
The length of a linked list is the number of nodes in the list
...
A (single) linked list is traversed in only one direction
...
The first (or head) pointer of a linked list is always fixed, pointing to the
first node in the list
...
In a doubly linked list, every node has two links: one points to the next
node, and one points to the previous node
...
In a doubly linked list, item insertion and deletion requires the adjustment
of two pointers in a node
...
In addition to the operations that are common to sequence containers (see
Chapter 4), the other operations that can be used to manipulate the
elements in a list container are assign, push_front, pop_front,
front, back, remove, remove_if, unique, splice, sort, merge, and
reverse
...
The header and trailer nodes are not part of the actual list
...
5
344 |
17
...
Chapter 5: Linked Lists
A linked list with header and trailer nodes is empty if the only nodes in the
list are the header and the trailer
...
EXERCISES
1
...
a
...
c
...
e
...
In a linked list, memory allocated for the nodes is sequential
...
In a linked list, nodes are always inserted either at the beginning or the
end because a linked link is not a random access data structure
...
Consider the linked list shown in Figure 5-35
...
Use this list to answer Exercises 2 through 7
...
(Assume that list, p, s, A, and B are
pointers of type nodeType
...
87
25
44
B
Linked list for Exercises 2–7
What is the output of each of the following C++ statements?
a
...
cout << A->info;
c
...
3
...
list->info >= 18
b
...
A->link->info == 16
d
...
list->info == 18
Exercises
4
...
list->link = A->link;
c
...
*list = B;
e
...
B = A->link->info;
g
...
list = B->link->link;
i
...
b
...
c
...
d
...
e
...
f
...
g
...
Also, deallocate the memory occupied
by this node
...
6
...
345
Mark each of the following statements as valid or invalid
...
a
...
|
If the following C++ code is valid, show the output
...
a
...
p = A;
p = p->link;
s = p;
p->link = NULL;
s = s->link;
cout << p->info << " " << s->info << endl;
5
346 |
8
...
Assume the node is in
the usual info-link form with the info of type int
...
)
a
...
9
...
(The class unorderedLinkedList
is as defined in this chapter
...
insertFirst(15);
list
...
insertFirst(30);
list
...
insertLast(45);
list
...
insertLast(25);
list
...
insertFirst(18);
list
...
deleteNode(12);
list
...
What is the output of this program segment?
Suppose the input is:
18 30 4 32 45 36 78 19 48 75 -999
What is the output of the following C++ code? (The class unorderedLinkedList
is as defined in this chapter
...
insertFirst(num);
else
list
...
print();
cout << endl;
copyList = list;
copyList
...
deleteNode(35);
cout << "Copy List = ";
copyList
...
12
...
Suppose that intList is a list container and
intList = {3, 23, 23, 43, 56, 11, 11, 23, 25}
13
...
unique();
Suppose that intList1 and intList2 are list containers and
intList1 = {3, 58, 78, 85, 6, 15, 93, 98, 25}
intList2 = {5, 24, 16, 11, 60, 9}
Show intList1 after the following statement executes:
intList1
...
begin(), intList2);
5
348 |
14
...
push_back(5);
intList
...
push_front(45);
intList
...
push_back(35);
intList
...
push_back(50);
intList
...
begin(), intList
...
begin();
intList
...
insert(listIt,38);
intList
...
erase(listIt);
intList
...
back());
intList
...
front());
copy(intList
...
end(), screen);
cout << endl;
15
...
Draw the UML diagram of the class videoType of the Programming
Example Video Store
...
PROGRAMMING EXERCISES
1
...
Using linked lists, redo the program to
handle as many entries as required
...
b
...
When the program terminates, write the data in the address book to a disk
...
|
349
Extend the class linkedListType by adding the following operations:
Find and delete the node with the smallest info in the list
...
)
b
...
(Traverse
the list only once
...
Also write a program to test these
functions
...
3
...
If no such element exists, terminate the program
...
Write a function that deletes the k element of the linked list
...
Provide the definitions of these functions in the class linkedListType
...
(Use either the class
unorderedLinkedList or the class orderedLinkedList to test
your function
...
4
...
Add the operation divideMid to the class linkedListType as follows:
void divideMid(linkedListType
//This operation divides the given list into two sublists
//of (almost) equal sizes
...
//
sublist
...
last
//
points to the last node of the second sublist
...
The statement:
myList
...
divides myList into two sublists: myList points to the list with the
elements 34 65 27, and subList points to the sublist with the elements
89 12
...
Also write a program to test your function
...
Chapter 5: Linked Lists
(Splitting a linked list, at a given node, into two sublists)
a
...
//Postcondition: first and last point to the first and last
//
nodes of the first sublist
...
first and secondList
...
Consider the following statements:
unorderedLinkedList
unorderedLinkedList
Suppose myList points to the list with the elements:
34 65 18 39 27 89 12
(in this order)
...
divideAt(otherList, 18);
b
...
a
...
Write the definition of the function template to implement the operation divideAt
...
Add the following operation to the class orderedLinkedList:
void mergeLists(orderedLinkedList
orderedLinkedList
//This function creates a new list by merging the
//elements of list1 and list2
...
The statement:
newList
...
8
...
Also, after the preceding statement
executes, list1 and list2 are empty
...
Write the definition of the function template mergeLists to implement
the operation mergeLists
...
The function insert of the class orderedLinkedList does not check if
the item to be inserted is already in the list; that is, it does not check for
duplicates
...
If the item to be inserted is already in the list, the function outputs an
appropriate error message
...
In this chapter, the class to implement the nodes of a linked list is defined as a
struct
...
template
class nodeType
{
public:
const nodeType
//Overload the assignment operator
...
//Postcondition: info = elem;
Type getInfo() const;
//Function to return the info of the node
...
void setLink(nodeType
//Function to set the link of the node
...
//Postcondition: The value of link is returned
...
//Postcondition: info = elem; link = ptr
|
351
5
352 |
Chapter 5: Linked Lists
nodeType(const nodeType
//Copy constructor
~nodeType();
//Destructor
private:
Type info;
nodeType
};
9
...
11
...
Write the definitions of the member functions of the class nodeType
...
Programming Exercise 8 asks you to redefine the class to implement the nodes
of a linked list so that the instance variables are private
...
Rewrite the definitions of these classes so that these
classes use the member functions of the class nodeType to access the info
and link fields of a node
...
Write the definitions of the function copyList, the copy constructor, and the
function to overload the assignment operator for the class doublyLinkedList
...
(Linked List with Header and Trailer Nodes) This chapter defined and
identified various operations on a linked list with header and trailer nodes
...
b
...
(You may assume that the elements of the linked list with header and
trailer nodes are in ascending order
...
Write a program to test various operations of the class defined in (a)
...
a
...
Write the definitions of the class circularLinkedList and its
member functions
...
)
b
...
(Programming Example Video Store)
a
...
a
...
Complete the design and implementation of the class customerType
defined in the Programming Example Video Store
...
Programming Exercises
15
...
17
...
In other words, write a program that
uses the classes designed in the Programming Example Video Store and in
Programming Exercise 14 to make a video store operational
...
Extend the class linkedListType by adding the following function:
void rotate();
//Function to remove the first node of a linked list and put it
//at the end of the linked list
...
Write a program that prompts the user to input a string and then outputs
the string in the pig Latin form
...
b
...
d
...
For example, the pig Latin form of the string "eye" is "eye-way"
...
Then rotate the string one character at a time; that is, move the
first character of the string to the end of the string until the first
character of the string becomes a vowel
...
For example, the pig Latin form of the string "There" is
"ere-Thay"
...
In cases like this, the letter y
can be considered a vowel
...
Therefore, the pig Latin form of "by" is
"y-bay"
...
The pig Latin form of the
string "1234" is "1234-way"
...
Your program must store the characters of a string into a linked list and
use the function rotate, as described in Programming Exercise 17, to
rotate the string
...
Learn about recursive definitions
...
Learn about recursive algorithm
...
Explore how to use recursive functions to implement recursive algorithms
356 |
Chapter 6: Recursion
In previous chapters, to devise solutions to problems we used the most common
technique, called iteration
...
This chapter introduces another problemsolving technique, called recursion, and provides several examples demonstrating how
recursion works
...
Recursion is a very powerful way to solve certain problems for which the
solution would otherwise be very complicated
...
In an algebra course, you probably learned how to find the factorial of a nonnegative
integer
...
Similarly, 4! ¼ 4 Â 3 Â 2 Â 1 = 24
...
Note
that 5! ¼ 5 Â 4 Â 3 Â 2 Â 1 = 5 Â ( 4 Â 3 Â 2 Â 1 ) = 5Â 4!
...
To find (n À 1)!, we apply the definition
again
...
Thus, for an integer n greater than 0, n! is obtained by first finding (n À 1)! and then
multiplying (n À 1)! by n
...
Here n ¼ 3
...
Because n > 0, we use Equation 6-2 to obtain:
2! ¼ 2 Â 1!
Now to find 1!, we again use Equation 6-2 because n ¼ 1 > 0
...
Substituting 0! into 1! gives 1! ¼ 1
...
Recursive Definitions
|
357
The solution in Equation 6-1 is direct—that is, the right side of the equation contains no
factorial notation
...
The definition of the factorial given in Equations 6-1 and 6-2 is called a recursive
definition
...
Recursive definition: A definition in which something is defined in terms of a smaller
version of itself
...
Every recursive definition must have one (or more) base cases
...
The general case must eventually be reduced to a base case
...
The base case stops the recursion
...
Here, we talk about recursive
algorithms and recursive functions
...
The
recursive algorithm must have one or more base cases, and the general solution must
eventually be reduced to a base case
...
That is, the body of the
recursive function contains a statement that causes the same function to execute again
before completing the current call
...
Next, let us write the recursive function that implements the factorial function
...
Let us note the following from the previous example, involving the factorial function:
•
Logically, you can think of a recursive function as having an unlimited
number of copies of itself
...
• After completing a particular recursive call, control goes back to the
calling environment, which is the previous call
...
The execution in the previous call begins from the point immediately following the recursive call
...
A function that calls another
function and eventually results in the original function call is said to be indirectly
Problem Solving Using Recursion |
359
recursive
...
Indirect recursion can be several layers deep
...
Function A is then
indirectly recursive
...
The base cases
must be identified and appropriate solutions to them must be provided
...
You must, therefore, exercise extra care when
designing indirect recursive functions
...
A recursive function in which the last statement executed is the recursive call is called a
tail recursive function
...
Infinite Recursion
Figure 6-1 shows that the sequence of recursive calls eventually reached a call that made
no further recursive calls
...
On the other hand, if every recursive call results in another recursive call, the
recursive function (algorithm) is said to have infinite recursion
...
Every call to a recursive function requires the system to
allocate memory for the local variables and formal parameters
...
Therefore, because computer memory is finite, if you execute an infinite recursive
function on a computer, the function executes until the system runs out of memory and
results in an abnormal termination of the program
...
You must make
sure that every recursive call eventually reduces to a base case
...
To design a recursive function, you must do the following:
1
...
2
...
For example, for a list, the limiting condition
is the number of elements in the list
...
Identify the base cases and provide a direct solution to each base case
...
Identify the general cases and provide a solution to each general case in terms of
smaller versions of itself
...
6
360 |
Chapter 6: Recursion
Largest Element in an Array
In this example, we use a recursive algorithm to find the largest element in an array
...
list
FIGURE 6-2
[0] [1] [2] [3] [4] [5] [6]
5
8
2
10
9
4
list with six elements
The largest element in the list in Figure 6-2 is 10
...
Also, suppose that
list[a]
...
, list[b]
...
list[5] represents the array elements list[0], list[1],
list[2], list[3], list[4], and list[5]
...
list[5] represents
the array elements list[1], list[2], list[3], list[4], and list[5]
...
If list is of length 1, then list has only one element, which is the largest element
...
To find the largest element in
list[a]
...
list[b] and
then compare this largest element with list[a]
...
list[b] is given by:
maximum(list[a], largest(list[a + 1]
...
This
list has six elements, given by list[0]
...
Now the largest element in list is
given by:
maximum(list[0], largest(list[1]
...
list[5]
...
list[5], we use
the same formula again because the length of this list is greater than 1
...
list[5] is then:
maximum(list[1], largest(list[2]
...
We see that every time we use the previous formula to find the largest element
in a sublist, the length of the sublist in the next call is reduced by one
...
From this point onward, we backtrack through the recursive
calls
...
list[b]
1
...
list[b]
and call it max
2
...
list[b] is list[a]
otherwise
the largest element in list[a]
...
list
FIGURE 6-3
[0] [1] [2] [3]
5
10 12
8
list with four elements
Let us trace the execution of the following statement:
cout << largest(list, 0, 3) << endl;
6
362 |
Chapter 6: Recursion
Here upperIndex = 3 and the list has four elements
...
largest(list,0,3)
return 12
lowerIndex 0
upperIndex 3
max
because lowerIndex != upperIndex
max = largest(list,1,3)
largest(list,1,3)
max 12
because list[0] < max
return max
return 12
lowerIndex 1
upperIndex 3
max
because lowerIndex != upperIndex
max = largest(list,2,3)
largest(list,2,3)
lowerIndex 2
upperIndex 3
max
because lowerIndex != upperIndex
max = largest(list,3,3)
max 12
because list[1] < max
return max
return 12
max 8
because list[2] > max
return list[2]
largest(list,3,3)
lowerIndex 3
upperIndex 3
max
return 8
because lowerIndex = upperIndex
return list[3]
FIGURE 6-4
Execution of largest(list, 0, 3)
The value returned by the expression largest(list, 0, 3) is 12, which is the largest
element in list
...
S
...
//************************************************************
#include
using namespace std;
Problem Solving Using Recursion |
363
int largest(const int list[], int lowerIndex, int upperIndex);
int main()
{
int intArray[10] = {23, 43, 35, 38, 67, 12, 76, 10, 34, 8};
cout << "The largest element in intArray: "
<< largest(intArray, 0, 9);
cout << endl;
}
return 0;
int largest(const int list[], int lowerIndex, int upperIndex)
{
int max;
if (lowerIndex == upperIndex) //size of the sublist is one
return list[lowerIndex];
else
{
max = largest(list, lowerIndex + 1, upperIndex);
}
}
if (list[lowerIndex] >= max)
return list[lowerIndex];
else
return max;
Sample Run:
The largest element in intArray: 76
Print a Linked List in Reverse Order
The nodes of an ordered linked list (as constructed in Chapter 5) are in ascending order
...
We now discuss the function reversePrint
...
Consider the linked list shown in Figure 6-5
...
Let us see how we can effectively use recursion to print the list in reverse order
...
We cannot print the info of the first node until we
have printed the remainder of the list (that is, the tail of the first node)
...
Every time we consider the tail of a node, we reduce the size of the list
by 1
...
Base Case: List is empty: no action
General Case: List is nonempty
1
...
Print the element
Let us write this algorithm
...
)
if (current != NULL)
{
reversePrint(current->link);
cout << current->info << endl;
}
//print the tail
//print the node
Here, we do not see the base case; it is hidden
...
Also, inside the if statement the recursive call is on the
tail of the list
...
Also, note that statements (for example, printing
the info of the node) appear after the recursive call; thus, when the transfer comes back
to the calling function, we must execute the remaining statements
...
(By the ‘‘last statement,’’ we do
not mean the physical last statement, but rather the logical last statement
...
template
void linkedListType
(nodeType
{
if (current != NULL)
{
reversePrint(current->link);
//print the tail
cout << current->info << " ";
//print the node
}
}
Consider the statement
reversePrint(first);
where first is a pointer of type nodeType
...
Because the formal parameter is a value parameter, the value of the actual
parameter is passed to the formal parameter
...
reversePrint(first)
current->5
because(current != NULL)
reversePrint(current->link)
execute the statement
cout << current->info;
Print 5
Now control goes back
to the caller
reversePrint(current->link)
current->10
because(current != NULL)
reversePrint(current->link)
execute the statement
cout << current->info;
Print 10
Now control goes back
to the caller
reversePrint(current->link)
current->15
because(current != NULL)
reversePrint(current->link)
execute the statement
cout << current->info;
Print 15
Now control goes back
to the caller
reversePrint(current->link)
current is NULL
because current is NULL
the if statement fails
control goes back to the
caller
FIGURE 6-6
Execution of the statement reversePrint(first);
THE FUNCTION printListReverse
Now that we have written the function reversePrint, we can write the definition of the
function printListReverse, which can be used to print an ordered linked list contained in
an object of the type linkedListType
...
We include the
6
366 |
Chapter 6: Recursion
function reversePrint as a private member because it is used only to implement the
function printListReverse
...
:
Given the first two numbers of the sequence (say a1 and a2), the nth number an, n>¼ 3,
of this sequence is given by:
an ¼ anÀ1 þ anÀ2
Thus:
a3 ¼ a2 + a1 ¼ 1 + 1 ¼ 2, a4 ¼ a3 + a2 ¼ 2 + 1 ¼ 3, and so on
...
In the previous sequence, a2 ¼ 1 and a1 ¼ 1
...
The number determined this way is called the nth Fibonacci
number
...
Then:
a3 ¼ a2 þ a1 ¼ 6 þ 3 ¼ 9;
a4 ¼ a3 þ a2 ¼ 9 þ 6 ¼ 15:
In this example, we write a recursive function, rFibNum, to determine the desired
Fibonacci number
...
The function rFibNum returns the nth Fibonacci number in the sequence
...
The fourth Fibonacci number in a sequence is the sum of the second and third Fibonacci
numbers
...
The following recursive algorithm calculates the nth Fibonacci
number, where a denotes the first Fibonacci number, b the second Fibonacci number,
and n the nth Fibonacci number:
8
if n ¼ 1
>a
>
<
b
if n ¼ 2 ðEquation 6-3Þ
rFibNumða; b; nÞ ¼
> rFibNumða; b; n À 1Þþ
>
:
rFibNumða; b; n À 2Þ
if n > 2:
Problem Solving Using Recursion |
367
The following recursive function implements this algorithm:
int rFibNum(int a, int b, int n)
{
if (n == 1)
return a;
else if (n == 2)
return b;
else
return rFibNum(a, b, n - 1) + rFibNum(a, b, n - 2);
}
Let us trace the execution of the following statement:
cout << rFibNum(2, 3, 4) << endl;
In this statement, the first number is 2, the second number is 3, and we want to
determine the 4th Fibonacci number of the sequence
...
The value returned is 8, which is the 4th Fibonacci
number of the sequence whose first number is 2 and whose second number is 3
...
In
the recursive version, some values are calculated more than once
...
So a recursive
method might be easier to write, but might not execute as efficiently
...
The following C++ program uses the function rFibNum:
//*************************************************************
// Author: D
...
Malik
//
// Given the first two numbers of a Fibonacci sequence, this
// program uses a recursive function to determine a specific
// number(s) of a Fibonacci sequence
...
Enter the first Fibonacci number: 2
Enter the second Fibonacci number: 5
Enter the position of the desired Fibonacci number: 6
The Fibonacci number at position 6 is: 31
Tower of Hanoi
In the nineteenth century, a game called the Tower of Hanoi became popular in Europe
...
At the creation of
the universe, priests in the temple of Brahma were supposedly given three diamond
needles, with one needle containing 64 golden disks
...
The priests’ task is to move all 64 disks from the first needle to the
third needle
...
Only one disk can be moved at a time
...
The removed disk must be placed on one of the needles
...
A larger disk cannot be placed on top of a smaller disk
...
Our objective is to write a program that prints the sequence of moves needed to transfer
the disks from the first needle to the third needle
...
1
2
3
FIGURE 6-8
Tower of Hanoi problem with three disks
6
370 |
Chapter 6: Recursion
As before, we think in terms of recursion
...
In this case, the disk can be moved directly from
needle 1 to needle 3
...
In this case, first we move the first disk from needle 1 to needle 2, and
then we move the second disk from needle 1 to needle 3
...
Next, we consider the case when the first needle
contains three disks, and then generalize this to the case of 64 disks (in fact, to an
arbitrary number of disks)
...
To move disk number 3 to needle 3, the top
two disks must first be moved to needle 2
...
To move the top two disks from needle 2 to needle 3, we use the
same strategy as before
...
Figure 6-9
shows a solution to the Tower of Hanoi problem with three disks
...
To begin, the first needle
contains all 64 disks
...
So first we move the top 63 disks from needle
1 to needle 2, and then we move disk number 64 from needle 1 to needle 3
...
To move disk number 63 from needle 2 to needle 3, we first
move the top 62 disks from needle 2 to needle 1, and then we move disk number 63
from needle 2 to needle 3
...
This discussion translates into the following recursive algorithm given in pseudocode
...
1
...
2
...
3
...
This recursive algorithm translates into the following C++ function:
void moveDisks(int count, int needle1, int needle3, int needle2)
{
if (count > 0)
{
moveDisks(count - 1, needle1, needle2, needle3);
cout << "Move disk " << count << " from " << needle1
<< " to " << needle3 << "
...
If needle 1 contains 3 disks, then the number of moves required to move all 3 disks from
needle 1 to needle 3 is 23 À1 ¼ 7
...
Because 210 ¼ 1024 % 1000 ¼ 103, we have
264 ¼ 24 Â 260 % 24 Â 1018 ¼ 1:6 Â 1019
The number of seconds in one year is approximately 3
...
Suppose the priests move
one disk per second and they do not rest
...
It is estimated that our universe is about 15 billion years old (1
...
Also,
6
372 |
Chapter 6: Recursion
5 Â 1011 ¼ 50 Â 1010 % 33 Â (1
...
This calculation shows that our universe
would last about 33 times as long as it already has
...
Then the number
of moves that the computer can generate in one year is:
ð3:2 Â 107 Þ Â 109 ¼ 3:2 Â 1016
So the computer time required to generate 264 moves is:
264 % 1:6 Â 1019 ¼ 1:6 Â 1016 Â 103 ¼ ð3:2 Â 1016 Þ Â 500
Thus, it would take about 500 years for the computer to generate 264 moves at the rate of
1 billion moves per second
...
First we define some terms
...
We call the remainder of x after division by 2 the rightmost bit of x
...
(Recall that in C++, % is the mod operator; it produces the remainder
of the integer division
...
Suppose we want to find the binary representation of 35
...
The
quotient is 17 and the remainder—that is, the rightmost bit of 35—is 1
...
The quotient is 8 and the remainder—that is, the rightmost bit of 17—is 1
...
The quotient is 4 and the remainder—that is, the rightmost bit of
8—is 0
...
The rightmost bit of 35 cannot be printed until we have printed the rightmost bit of 17
...
Thus, the binary representation of 35 is the binary representation of 17 (that is, the quotient
of 35 after division by 2), followed by the rightmost bit of 35
...
This discussion translates into the following recursive algorithm, where binary(num)
denotes the binary representation of num:
1
...
2
...
Problem Solving Using Recursion |
373
The following recursive function implements this algorithm:
void decToBin(int num, int base)
{
if (num > 0)
{
decToBin(num / base, base);
cout << num % base;
}
}
Figure 6-10 traces the execution of the following statement:
decToBin(13, 2);
where num is 13 and base is 2
...
The first output
is produced by call 4, which prints 1; the second output is produced by call 3, which
prints 1; the third output is produced by call 2, which prints 0; and the fourth output is
produced by call 1, which prints 1
...
S
...
//******************************************************
#include
using namespace std;
void decToBin(int num, int base);
int main()
{
int decimalNum;
int base;
base = 2;
cout << "Enter number in decimal: ";
cin >> decimalNum;
cout << endl;
cout << "Decimal " << decimalNum << " = ";
decToBin(decimalNum, base);
cout << " binary" << endl;
}
return 0;
void decToBin(int num, int base)
{
if (num > 0)
{
decToBin(num / base, base);
cout << num % base;
}
}
Recursion or Iteration?
|
375
Sample Run: In this sample run, the user input is shaded
...
The
programs in the previous chapters used a loop to repeat a set of statements to perform
certain calculations
...
Formally, iterative control structures use a
looping structure, such as while, for, or do
...
This chapter began by designing a recursive method to find the factorial of a nonnegative
integer
...
Given our familiarity with iterative techniques, the iterative
solution will seem simpler than the recursive solution
...
In this chapter, we also used recursion to determine the largest element in a list,
determining a Fibonacci number
...
Similarly, an algorithm that uses an
iterative control structure can be designed to find the Fibonacci number
...
In addition to the nature of the solution, efficiency is the
other key factor in determining the better approach
...
When the function terminates, that memory space is deallocated
...
That is, every recursive call required the system to
allocate memory space for its formal parameters and local variables, and then deallocate the
memory space when the function exited
...
Therefore, a
recursive function executes more slowly than its iterative counterpart
...
Clearly, a recursive function is less efficient than a
corresponding iterative function in terms of execution time and memory usage
...
Chances are that
you have never been concerned with either execution time or memory usage as you have
been writing C++ programs
...
Chances are that you have considered carefully what you can do to minimize
the time required to produce C++ programs
...
As a
professional programmer, your time typically is far more expensive than the cost of the
6
376 |
Chapter 6: Recursion
computer you use to produce programs
...
Today’s computers are fast and have abundant memory
...
Given the ever-increasing speed and memory capacity of today’s computers, the
choice between iteration and recursion depends more and more on how the programmer
envisions the solution to the problem, and less on execution time and memory usage
...
Fortunately, any program that can be written recursively also
can be written iteratively
...
On the other hand, if the
recursive solution is more obvious and easier to construct, such as the solution to the
Towers of Hanoi problem, choose the recursive solution
...
You’ll quickly gain an increased appreciation for
recursion
...
Recursion and Backtracking: 8-Queens Puzzle
This section describes a problem-solving and algorithm design technique called backtracking
...
For any two queens to
be nonattacking, they cannot be in the same row, same column, or same diagonals
...
Q
Q
Q
Q
Q
Q
Q
Q
FIGURE 6-11
A solution to the 8-queens puzzle
Recursion and Backtracking: 8-Queens Puzzle
|
377
In 1850, the 8-queens puzzle was considered by the great C
...
Gauss, who was unable to
obtain a complete solution
...
H
...
In 1960, R
...
Walker gave an algorithmic account of backtracking
...
Golomb and L
...
Backtracking
The backtracking algorithm attempts to find solutions to a problem by constructing
partial solutions and making sure that any partial solution does not violate the problem
requirements
...
However, if it is determined that the partial solution would not lead to a solution, that is, the
partial solution would end in a dead end, then the algorithm backs up by removing the
most recently added part and trying other possibilities
...
, xn), where xi is an integer such that
1 xi n
...
Therefore, for the 8-queens puzzle the solution is an 8-tuple (x1, x2, x3, x4, x5, x6,
x7, x8), where xi is the column where to place the ith queen in the ith row
...
That is, the first
queen is placed in the first row and fourth column, the second queen is placed in the second
row and sixth column, and so on
...
Let us again consider the 8-tuple (x1, x2, x3, x4, x5, x6, x7, x8), where xi is an integer such
that 1 xi 8
...
However, because no two queens can be placed in the same row, no two
elements of the 8-tuple (x1, x2, x3, x4, x5, x6, x7, x8) are the same
...
The solution that we develop can, in fact, be applied to any number of queens
...
That is,
you are given a 4 by 4 square board (see Figure 6-12) and you are to place 4 queens on
the board so that no two queens attack each other
...
(A cross in a box means no other queen can be placed in that box
...
Clearly,
the first square in the second row where the second queen can be placed is the third
column
...
Next, we try to place the third queen in the third row
...
At this point we
backtrack to the previous board configuration and place the second queen in the fourth
column; see Figure 6-13(c)
...
This
time, we successfully place the third queen in the second column of the third row; see
Figure 6-13(d)
...
We backtrack to the third row and try placing the queen in any other column
...
We, therefore, backtrack to
the first row and place the first queen in the next column
...
This time we
obtain the solution, as shown by Figure 6-14
...
(Recall that in C++, an array
index starts at 0
...
We then place the second queen in the third
column of the second row and so generate the tuple (0,2)
...
With the partial solution (0,3), we then try to place the
third queen in the third row and generate the tuple (0,3,1)
...
From the partial solution (0,3,1), the backtracking algorithm, in fact, backs up to
placing the first queen and so removes all the elements of the tuple
...
In this case, the sequence of partial solution generated is, (1), (1,3),
(1,3,0), and (1,3,0,2), which represents a solution to the 4-queens puzzle
...
1
x 1= 1
x1 = 0
(0)
x2 = 1
2
x2 = 2
3
9
x2 = 3
4
x2 = 0
5 (0,3)
x3 = 1
(0,3,1) 6
10
(1)
x2 = 3
x2 = 2
11
12 (1,3)
x3 = 0
8
13 (1,3,0)
x4 = 2
7
FIGURE 6-15
14 (1,3,0,2)
solution
4-queens tree
8-Queens Puzzle
Let us now consider the 8-queens puzzle
...
Determining whether two
queens are in the same row or column is easy because we can check their row and
column position
...
6
380 |
Chapter 6: Recursion
Consider the 8 by 8 square board shown in Figure 6-16
...
(Recall that, in C++, the array index
starts at 0
...
The
positions of the squares on this diagonal are (0,4), (1,5), (2,6), and (3,7)
...
For example, 0 – 4 ¼ 1 – 5 ¼ 2 –
6 ¼ 3 – 7 ¼ – 4
...
Now consider the diagonal from upper right to lower left as indicated by the arrow
...
Here rowPosition + columnPosition ¼ 6
...
We can use these results to determine whether two queens are on the same diagonal or
not
...
These queens are on the same diagonal if either i +
j ¼ k + l or i - j ¼ k – l
...
From this it follows that the two queens are on the
same diagonal if |j – l| ¼ |i – k|, where |j – l| is the absolute value of j – l and so on
...
For example, queensInRow[0] = 3 means the first queen is placed in
column 3 (which is the fourth column) of row 0 (which is the first row)
...
Next we try to place the kth
queen in a column of the kth row
...
The first k-1 queens are in the first k-1 rows and we are trying to place the kth queen in
the kth row
...
It thus follows that the kth
queen can be placed in column i of row k, provided no other queen is in column i and no
queens are on the diagonals on which square (k, i ) lies
...
If it finds a queen at any of such positions, the for
loop returns the value false
...
The following class defines the n-queens puzzle as an ADT:
//***************************************************************
// D
...
Malik
//
// This class specifies the functions to solve the n-queens puzzle
...
//Postcondition: returns true if a queen can be placed in
//
row k and column i; otherwise it returns false
void queensConfiguration(int k);
//Function to determine all solutions to the n-queens
//puzzle using backtracking
...
//Postcondition: All n-tuples representing solutions of
//
n-queens puzzle are generated and printed
...
int solutionsCount();
//Function to return the total number of solutions
...
private:
int noOfSolutions;
int noOfQueens;
int *queensInRow;
};
The definitions of the member functions of the class nQueensPuzzle are given next:
nQueensPuzzle::nQueensPuzzle(int queens)
{
noOfQueens = queens;
queensInRow = new int[noOfQueens];
noOfSolutions = 0;
}
bool nQueensPuzzle::canPlaceQueen(int k, int i)
{
for (int j = 0; j < k; j++)
if ((queensInRow[j] == i)
|| (abs(queensInRow[j] - i) == abs(j-k)))
return false;
return true;
}
Using recursion, the function queensConfiguration implements the backtracking
technique to determine all solutions to the n-queens puzzle
...
Its definition is straightforward and is given next
...
6
Recursion, Backtracking, and Sudoku
In the previous section, we used recursion and backtracking to solve the 8-queens
problem
...
This problem involves filling numbers 1 to 9 in
a partially filled, 9 Â 9 grid with constraints described in this section
...
6
3
2
4
6
4
8
1
3
2
5
9
8
7
1 4 3
8
5
6
3 2
2
7 5 8
6 1
6 5 3
1
2
4
6
6
4
7
5
8
1
3
(a)
FIGURE 6-17
2
5
9
8
7
1 4 3
8
5
6
3 2
2
7 5 8
6 1
(b)
1
6
7
5
6
1
9
2
3
5
8
4
7
5
7
2
8
9
4
6
1
3
3
4
8
6
1
7
5
2
9
1
3
4
5
7
6
2
9
8
2
5
6
1
8
9
3
7
4
8
9
7
4
2
3
1
5
6
7
6
5
3
4
2
9
8
1
9
8
3
7
5
1
4
6
2
4
2
1
9
6
8
7
3
5
(c)
Sudoku problem and its solution
The sudoku grid is a 9 Â 9 grid consisting of nine rows, nine columns, and nine 3 Â 3
smaller grids
...
The first 3 Â 3 smaller grid is in rows 1 to 3 and columns
1 to 3, the second 3 Â 3 smaller grid is in rows 1 to 3 and columns 4 to 6, and so on
...
For example, the
solution to the sudoku problem in Figure 6-17(a) is shown in Figure 6-17(c)
...
Starting at the first row, we find the first empty grid slot
...
Next we find the first
number, between 1 and 9, that can be placed in this slot
...
) For example, in Figure 6-17(a), the first
number that can be placed in the second row and second column is 5; see Figure 6-17(b)
...
If a number cannot be placed in a slot, then we must backtrack to
the previous slot, where the number was placed, place a different number, and continue
...
The following class implements the sudoku problem as an ADT:
//***************************************************************
// D
...
Malik
//
// This class specifies the functions to solve a sudoku problem
...
//Postcondition: grid is initialized to the numbers
//
specified by the user
...
bool solveSudoku();
//Funtion to solve the sudoku problem
...
Recursion, Backtracking, and Sudoku |
385
bool findEmptyGridSlot(int &row, int &col);
//Function to determine if the grid slot specified by
//row and col is empty
...
bool numAlreadyInRow(int row, int num);
//Function to determine if num is in grid[row][]
//Postcondition: Returns true if num is in grid[row][],
//
otherwise it returns false
...
bool numAlreadyInBox(int smallGridRow, int smallGridCol,
int num);
//Function to determine if num is in the small grid
//Postcondition: Returns true if num is in small grid,
//
otherwise it returns false
...
For example, the partially filled
sudoku grid in Figure 6-17(a) is entered and stored as:
6
0
0
0
0
0
8
0
0
0
0
2
0
0
4
0
1
3
3
0
0
6
0
0
0
0
0
0
0
4
0
0
6
2
0
0
2
5
0
1
8
0
0
7
0
0
0
7
4
0
3
0
5
6
0
0
0
3
0
2
0
8
1
9
8
0
0
5
0
0
0
0
0
0
1
0
6
0
7
0
5
Next we write the definition of the function solveSudoku
...
The general algorithm is as follows:
1
...
If the grid has no empty slots, return true and print the solution
...
Suppose the variables row and col specify the position of the empty grid
position
...
}
If all the digits have been tried and nothing worked, return false
...
QUICK REVIEW
1
...
3
...
5
...
A recursive definition defines a problem in terms of smaller versions of itself
...
A recursive algorithm solves a problem by reducing it to smaller versions of itself
...
Exercises
6
...
8
...
10
...
12
...
|
387
The solution to the problem in a base case is obtained directly
...
Recursive algorithms are implemented using recursive functions
...
The general solution breaks the problem into smaller versions of itself
...
The base case stops the recursion
...
15
...
17
...
• Every call to a recursive function—that is, every recursive call—has its
own code and its own set of parameters and local variables
...
The current (recursive)
call must execute completely before control goes back to the previous
call
...
A function is called directly recursive if it calls itself
...
A recursive function in which the last statement executed is the recursive
call is called a tail recursive function
...
b
...
d
...
Determine the limiting conditions
...
Identify the base cases and provide a direct solution to each base case
...
EXERCISES
1
...
a
...
c
...
e
...
Every recursive function must have one or more base cases
...
In the general case, the solution to the problem is obtained directly
...
6
388 |
2
...
4
...
6
...
Chapter 6: Recursion
What is a base case?
What is a recursive case?
What is direct recursion?
What is indirect recursion?
What is tail recursion?
Consider the following recursive function:
int mystery(int number)
{
if (number == 0)
return number;
else
return(number + mystery(number - 1));
}
//Line
//Line
//Line
//Line
//Line
//Line
//Line
Identify the base case
...
Identify the general case
...
What valid values can be passed as parameters to the function mystery?
d
...
e
...
f
...
Consider the following recursive function:
a
...
void funcRec(int u, char v)
{
if (u == 0)
cout << v;
else if (u == 1)
cout << static_cast
(static_cast
else
funcRec(u - 1, v);
}
Answer the following questions:
a
...
c
...
Identify the general case
...
1
2
3
4
5
6
7
Consider the following recursive function:
void exercise(int x)
{
if (x > 0 && x < 10)
{
cout << x << " ";
exercise(x + 1);
}
}
//Line
//Line
//Line
//Line
//Line
1
2
3
4
5
//Line
//Line
//Line
//Line
6
7
8
9
Exercises
|
389
What is the output of the following statements?
a
...
exercise(10);
d
...
exercise(0);
b
...
b
...
cout << test(5, 10) << endl;
cout << test(3, 9) << endl;
Consider the following function:
int func(int x)
{
if (x == 0)
return 2;
else if (x == 1)
return 3;
else
return (func(x - 1) + func(x - 2));
}
What is the output of the following statements?
a
...
cout << func(2) << endl;
d
...
cout << func(1) << endl;
c
...
cout << func(0) << endl;
cout << func(5) << endl;
Suppose that intArray is an array of integers, and length specifies the
number of elements in intArray
...
That is, low and high are two indices in intArray
...
Write a recursive algorithm to multiply two positive integers m and n using
repeated addition
...
6
390 |
14
...
The general problem can be stated as
follows: Find the number of ways r different things can be chosen from a
set of n items, where r and n are nonnegative integers and r n
...
Then C(n, r) is given by the following formula:
Cðn; rÞ ¼
n!
r!ðn À rÞ!
where the exclamation point denotes the factorial function
...
It is also known that C(n, r) ¼ C(n – 1, r – 1) + C(n – 1, r)
...
b
...
Identify the base
case(s) and the general case(s)
...
PROGRAMMING EXERCISES
1
...
If the nonnegative integer is 4,
the pattern generated is as follows:
****
***
**
*
*
**
***
****
2
...
For
example, specifying 4 as the number of lines generates the preceding pattern
...
|
391
Also, write a program that prompts the user to enter the number of lines in
the pattern and uses the recursive function to generate the pattern
...
Write a recursive a function to generate the following pattern of stars:
*
* *
* * *
* * * *
* * *
* *
*
4
...
6
...
8
...
Also, write a program that prompts the user to enter the number of lines in
the pattern and uses the recursive function to generate the pattern
...
Write a recursive function, vowels, that returns the number of vowels in a
string
...
Write a recursive function that finds and returns the sum of the elements of
an int array
...
A palindrome is a string that reads the same both forward and backward
...
Write a program that uses a
recursive function to check whether a string is a palindrome
...
Do not use any global variables;
use the appropriate parameters
...
Do
not use any global variables; use the appropriate parameters
...
Also, write a
program to test your function
...
You can use the following
recursive definition to calculate xy
...
y¼0
y¼1
y > 1:
6
392 |
10
...
11
...
13
...
Write a recursive function, gcd, that takes as parameters two integers and
returns the greatest common divisor of the numbers
...
Write a recursive function to implement the recursive algorithm of Exercise 12
(reversing the elements of an array between two indices)
...
Write a recursive function to implement the recursive algorithm of Exercise 13
(multiplying two positive integers using repeated addition)
...
Write a recursive function to implement the recursive algorithm of Exercise 14
(determining the number of ways to select a set of things from a given set of
things)
...
In the section ‘‘Converting a Number from Decimal to Binary,’’ in this
chapter, you learned how to convert a decimal number into the equivalent
binary number
...
In fact, in C++, you can
instruct the computer to store a number in octal or hexadecimal
...
The
digits in the hexadecimal number system are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B,
C, D, E, and F
...
The algorithm to convert a positive decimal number into an equivalent
number in octal (or hexadecimal) is the same as discussed for binary
numbers
...
Suppose ab represents the number a to the base b
...
Then:
75310 ¼ 13618
75310 ¼ 2F116
The method of converting a decimal number to base 2, or 8, or 16 can be extended
to any arbitrary base
...
You then divide the
decimal number n by b as in the algorithm for converting decimal to binary
...
Write a program that uses a recursive function to convert a number in decimal
to a given base b, where b is between 2 and 36
...
Test your program on the following data:
15
...
9098 and base 20
692 and base 2
753 and base 16
(Recursive Sequential Search) The sequential search algorithm given in
Chapter 3 is nonrecursive
...
The function sqrt from the header file cmath can be used to find the
square root of a nonnegative real number
...
Start with a ¼ x;
a
...
If |a2- x| epsilon, then a is the square root of x within the tolerance;
otherwise:
Replace a with (a2 + x) / (2a) and repeat Step a
where | a2- x| denotes the absolute value of a2- x
...
18
...
Write a recursive function to implement this algorithm to find the square
root of a nonnegative real number
...
Write a program to find solutions to the n-queens puzzle for various values
of n
...
Write the definitions of the remaining functions of the class sudoku
...
(Knight’s Tour) This chapter described the backtracking algorithm and
how to use recursion to implement it
...
Given an initial board position, determine a sequence of moves by a
knight that visits every square of the chessboard exactly once
...
Write a recursive backtracking
program that takes as input an initial board position and determines a sequence
of moves by a knight that visits each square of the board exactly once
...
Learn about stacks
...
Learn how to implement a stack as an array
...
Discover stack applications
...
It has numerous
applications in computer science
...
To be specific, suppose that you
have the functions A, B, C, and D in your program
...
When function
D terminates, control goes back to function C; when function C terminates, control goes
back to function B; and when function B terminates, control goes back to function A
...
What if you want to write a nonrecursive algorithm to print a linked list
backward?
This section discusses the data structure called the stack, which the computer uses to
implement function calls
...
Stacks
have numerous other applications in computer science
...
A stack is a list of homogenous elements in which the addition and deletion of elements
occurs only at one end, called the top of the stack
...
For another
example, to get to your favorite computer science book, which is underneath your math
and history books, you must first remove the math and history books
...
Figure 7-1 shows some examples of stacks
...
The top
element of the stack is the last element added to the stack
...
For this reason, a stack is also called a Last In First Out (LIFO) data
structure
...
Now that you know what a stack is, let us see what kinds of operations can be performed
on a stack
...
Similarly, because the top item
can be retrieved and/or removed from the stack, we can perform the operation top to
retrieve the top element of the stack, and the operation pop to remove the top element
from the stack
...
Initially, all of the boxes are on the floor and
the stack is empty
...
)
A
B
7
D
C
Empty stack
E
FIGURE 7-2
Empty stack
First we push box A onto the stack
...
(a) Push Box A
A
A
(b) Push Box B
(c) Push Box C
C
B
(d) Peek at the top element
FIGURE 7-3
C
B
AB
A
D
C
AB
(e) Push Box D
A
C
B
(f) Pop stack
Stack operations
We then push box B onto the stack
...
Next, we push box C onto the stack
...
Next, we look at the top element of the stack
...
We then push box D onto
398 |
Chapter 7: Stacks
the stack
...
Next, we pop
the stack
...
An element can be removed from the stack only if there is something in the stack, and an
element can be added to the stack only if there is room
...
Because a stack
keeps changing as we add and remove elements, the stack must be empty before we first
start using it
...
Therefore, to successfully implement a stack, we
need at least these six operations, which are described next
...
)
•
•
initializeStack—Initializes the stack to an empty state
...
If the stack
is empty, it returns the value true; otherwise, it returns the value
false
...
If the stack is full, it
returns the value true; otherwise, it returns the value false
...
The input to this
operation consists of the stack and the new element
...
• top—Returns the top element of the stack
...
• pop—Removes the top element of the stack
...
The following abstract class stackADT defines these operations as an ADT:
//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operations on a stack
...
//Postcondition: Stack is empty
...
//Postcondition: Returns true if the stack is empty,
//
otherwise returns false
...
//Postcondition: Returns true if the stack is full,
//
otherwise returns false
...
//Precondition: The stack exists and is not full
...
virtual Type top() const = 0;
//Function to return the top element of the stack
...
//Postcondition: If the stack is empty, the program
//
terminates; otherwise, the top element of the stack
//
is returned
...
//Precondition: The stack exists and is not empty
...
Figure 7-4 shows the UML class diagram of the class stackADT
...
Functions such
as push and pop that are required to implement a stack are not available to C++
programmers
...
Because all the elements of a stack are of the same type, a stack can be implemented as
either an array or a linked structure
...
7
400 |
Chapter 7: Stacks
Implementation of Stacks as Arrays
Because all the elements of a stack are of the same type, you can use an array to
implement a stack
...
The top of the stack is the
index of the last element added to the stack
...
However, by definition, a stack is a data structure in which the elements are
accessed (popped or pushed) at only one end—that is, a Last In First Out data
structure
...
This feature of a stack is extremely important and must be
recognized in the beginning
...
The following class, stackType, implements the functions of the abstract class
stackADT
...
We assume that the
default stack size is 100
...
Moreover, we give a generic
definition of the stack
...
//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operation on a stack as an
// array
...
void initializeStack();
//Function to initialize the stack to an empty state
...
//Postcondition: Returns true if the stack is empty,
//
otherwise returns false
...
//Postcondition: Returns true if the stack is full,
//
otherwise returns false
...
//Precondition: The stack exists and is not full
...
Type top() const;
//Function to return the top element of the stack
...
//Postcondition: If the stack is empty, the program
//
terminates; otherwise, the top element of the stack
//
is returned
...
//Precondition: The stack exists and is not empty
...
stackType(int stackSize = 100);
//Constructor
//Create an array of the size stackSize to hold
//the stack elements
...
//Postcondition: The variable list contains the base address
//
of the array, stackTop = 0, and maxStackSize = stackSize
stackType(const stackType
//Copy constructor
~stackType();
//Destructor
//Remove all the elements from the stack
...
private:
int maxStackSize; //variable to store the maximum stack size
int stackTop;
//variable to point to the top of the stack
Type *list; //pointer to the array that holds the stack elements
};
void copyStack(const stackType
//Function to make a copy of otherStack
...
7
402 |
Chapter 7: Stacks
Figure 7-5 shows the UML class diagram of the class stackType
...
If stackTop is nonzero, the stack is nonempty
and the top element of the stack is given by stackTop – 1 because the first stack
element is at position 0
...
It contains the code that
is common to the functions to overload the assignment operator and the copy constructor
...
To copy a stack into another stack, the program can use the assignment
operator
...
Note that stackTop can range from 0 to maxStackSize
...
Suppose that maxStackSize
= 100
...
...
...
D
C
B
A
stack
elements
Example of a stack
Note that the pointer list contains the base address of the array (holding the stack
elements)—that is, the address of the first array component
...
7
Initialize Stack
Let us consider the initializeStack operation
...
(See Figure 7-7
...
...
D
C
B
A
unused
stack
Empty stack
The definition of the function initializeStack is as follows:
template
void stackType
{
stackTop = 0;
}//end initializeStack
404 |
Chapter 7: Stacks
Empty Stack
We have seen that the value of stackTop indicates whether the stack is empty
...
The definition of
the function isEmptyStack is as follows:
template
bool stackType
{
return(stackTop == 0);
}//end isEmptyStack
Full Stack
Next, we consider the operation isFullStack
...
The definition of the function isFullStack is as follows:
template
bool stackType
{
return(stackTop == maxStackSize);
} //end isFullStack
Push
Adding, or pushing, an element onto the stack is a two-step process
...
Therefore, the push operation is as follows:
1
...
2
...
Figure 7-8(a) shows the stack before pushing 'y' into the stack
...
[99]
[99]
...
...
...
stack
y
n
maxStackSize 100
stackTop
list
4
[3]
n
[2]
u
[1]
S
[0]
stack
elements maxStackSize 100
stackTop
(a) Before pushing y
FIGURE 7-8
Stack before and after the push operation
5
list
(b) After pushing y
[4]
n
stack
[3]
u
stack
[2] elements
[1]
S
[0]
n
Implementation of Stacks as Arrays
|
405
The definition of the function push is as follows:
template
void stackType
{
if (!isFullStack())
{
list[stackTop] = newItem; //add newItem at the top
stackTop++; //increment stackTop
}
else
cout << "Cannot add to a full stack
...
Error checking for an overflow can be handled in different ways
...
Or, we can check for an overflow before calling the function push, as shown
next (assuming stack is an object of type stackType)
...
isFullStack())
stack
...
Its definition is as follows:
template
Type stackType
{
assert(stackTop != 0); //if stack is empty, terminate the
//program
return list[stackTop - 1]; //return the element of the stack
//indicated by stackTop - 1
}//end top
Pop
To remove, or pop, an element from the stack, we simply decrement stackTop by 1
...
Figure 7-9(b) shows the
stack after popping 'D' from the stack
...
...
stack
...
...
" << endl;
}//end pop
If we try to remove an item from an empty stack, the resulting condition is called an
underflow
...
One way
is as shown previously
...
if (!stack
...
pop();
Copy Stack
The function copyStack makes a copy of a stack
...
We will, in fact, use this function to implement
the copy constructor and overload the assignment operator
...
maxStackSize;
stackTop = otherStack
...
list[j];
} //end copyStack
Constructor and Destructor
The functions to implement the constructor and the destructor are straightforward
...
If
the user does not specify the size of the array in which to store the stack elements, the
constructor uses the default value, which is 100, to create an array of size 100
...
The definitions of the constructor and destructor are as follows:
template
stackType
{
if (stackSize <= 0)
{
cout << "Size of the array to hold the stack must "
<< "be positive
...
" << endl;
maxStackSize = 100;
}
else
maxStackSize = stackSize;
stackTop = 0;
list = new Type[maxStackSize];
}//end constructor
//set the stack size to
//the value specified by
//the parameter stackSize
//set stackTop to 0
//create the array to
//hold the stack elements
template
stackType
{
delete [] list; //deallocate the memory occupied
//by the array
}//end destructor
Copy Constructor
The copy constructor is called when a stack object is passed as a (value) parameter to a
function
...
Its definition is as follows:
7
408 |
Chapter 7: Stacks
template
stackType
{
list = NULL;
copyStack(otherStack);
}//end copy constructor
Overloading the Assignment Operator (=)
Recall that for classes with pointer member variables, the assignment operator must be
explicitly overloaded
...
For the sake of completeness, we next describe the header file
...
h
...
//Header file: myStack
...
h"
using namespace std;
//Place the definition of the class template stackType, as given
//previously in this chapter, here
...
#endif
Implementation of Stacks as Arrays
|
409
The analysis of the stack operations is similar to the operations of the class arrayListType
(Chapter 3)
...
TABLE 7-1 Time complexity of the operations of the class stackType on a stack with
n elements
Function
Time complexity
isEmptyStack
O (1)
isFullStack
O (1)
initializeStack
O (1)
constructor
O (1)
top
O (1)
push
O (1)
pop
O (1)
copyStack
O (n)
destructor
O (1)
copy constructor
O (n)
Overloading the assignment operator
O (n)
EXAMPLE 7-1
Before we give a programming example, let us first write a simple program that uses the
class stackType and tests some of the stack operations
...
The program and its output are as follows:
//*************************************************************
// Author: D
...
Malik
//
// This program tests various operations of a stack
...
h"
using namespace std;
7
410 |
Chapter 7: Stacks
void testCopyConstructor(stackType
int main()
{
stackType
stackType
stackType
stack
...
push(23);
stack
...
push(38);
copyStack = stack; //copy stack into copyStack
cout << "The elements of copyStack: ";
while (!copyStack
...
top() << " ";
copyStack
...
isEmptyStack())
cout << "The original stack is not empty
...
top() << endl;
dummyStack = stack;
//copy stack into dummyStack
cout << "The elements of dummyStack: ";
while (!dummyStack
...
top() << " ";
dummyStack
...
isEmptyStack())
cout << "otherStack is not empty
...
top() << endl;
}
Programming Example: Highest GPA
|
411
Sample Run:
The elements of copyStack: 38 45 23
otherStack is not empty
...
The top element of the original stack: 38
The elements of dummyStack: 38 45 23
It is recommended that you do a walk-through of this program
...
The program then prints the highest
GPA and the names of all the students who received that GPA
...
Moreover, we assume that there are a maximum of 100 students
in the class
...
Sample data is as follows:
3
...
6
2
...
9
3
...
9
3
...
For example, for the preceding data, the highest GPA is 3
...
We read the first GPA and the name of the student
...
Next, we read the second GPA and the name of the student
...
Three cases arise:
1
...
In this case, we
do the following:
a
...
b
...
c
...
7
412 |
Chapter 7: Stacks
2
...
In this case, we
add the name of the new student to the stack
...
The new GPA is smaller than the highest GPA so far
...
We then read the next GPA and the name of the student, and repeat Steps 1
through 3
...
From this discussion, it is clear that we need the following variables:
double GPA;
//variable to hold
double highestGPA; //variable to hold
string name;
//variable to hold
stackType
the current GPA
the highest GPA
the name of the student
to implement the stack
The previous discussion translates into the following algorithm:
1
...
3
...
Declare the variables and initialize stack
...
If the input file does not exist, exit the program
...
Also, set the precision to two decimal places
...
Read the GPA and the student name
...
highestGPA = GPA;
7
...
1
...
1
...
initializeStack(stack);
7
...
2
...
1
...
highestGPA = GPA;
}
7
...
else
if (GPA is equal to highestGPA)
push(stack, student name);
7
...
Read GPA and student name;
}
8
...
9
...
Programming Example: Highest GPA
|
413
PROGRAM LISTING
//*************************************************************
// Author: D
...
Malik
//
// This program reads a data file consisting of students' GPAs
// followed by their names
...
//*************************************************************
#include
#include
#include
#include
#include "myStack
...
open("HighestGPAData
...
Program terminates!" << endl;
return 1;
}
cout << fixed << showpoint;
cout << setprecision(2);
//Step 4
//Step 4
infile >> GPA >> name;
//Step 5
highestGPA = GPA;
//Step 6
while (infile)
{
if (GPA > highestGPA)
{
stack
...
1
//Step 7
...
1
414 |
Chapter 7: Stacks
if (!stack
...
push(name);
//Step 7
...
2
highestGPA = GPA;
//Step 7
...
3
}
else if (GPA == highestGPA)
//Step 7
...
isFullStack())
stack
...
"
<< "Program terminates!" << endl;
return 1; //exit program
}
}
infile >> GPA >> name;
//Step 7
...
isEmptyStack())
{
cout << stack
...
pop();
}
cout << endl;
}
return 0;
Sample Run:
Input File (HighestGPAData
...
4
3
...
5
3
...
8
3
...
6
3
...
8
3
...
9
3
...
9
2
...
9
3
...
90
The students holding the highest GPA are:
Vinay
Minnie
Andy
Note that the names of the students with the highest GPA are output in the reverse
order, relative to the order they appear in the input because the top element of the
stack is the last element added to the stack
...
If in a program the number of
elements to be pushed exceeds the size of the array, the program might terminate in an
error
...
We have seen that by using pointer variables we can dynamically allocate and deallocate
memory, and by using linked lists we can dynamically organize data (such as an ordered
list)
...
Recall that in the linear representation of a stack, the value of stackTop indicates the
number of elements in the stack, and the value of stackTop - 1 points to the top item in
the stack
...
Similar to the linear representation, in a linked representation stackTop is used to locate
the top element in the stack
...
In the former case,
stackTop gives the index of the array
...
The following class implements the functions of the abstract class stackADT:
//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operation on a stack as a
// linked list
...
bool isEmptyStack() const;
//Function to determine whether the stack is empty
...
bool isFullStack() const;
//Function to determine whether the stack is full
...
void initializeStack();
//Function to initialize the stack to an empty state
...
//Precondition: The stack exists and is not full
...
Type top() const;
//Function to return the top element of the stack
...
//Postcondition: If the stack is empty, the program
//
terminates; otherwise, the top element of
//
the stack is returned
...
//Precondition: The stack exists and is not empty
...
linkedStackType();
//Default constructor
//Postcondition: stackTop = NULL;
linkedStackType(const linkedStackType
//Copy constructor
~linkedStackType();
//Destructor
//Postcondition: All the elements of the stack are removed
...
//Postcondition: A copy of otherStack is created and
//
assigned to this stack
...
Logically, the stack is never full
...
Therefore, in reality, the function isFullStack does not apply to
linked implementations of stacks
...
We leave the UML class diagram of the class linkedStackType as an exercise for you
...
)
7
EXAMPLE 7-2
Suppose that stack is an object of type linkedStackType
...
stack
stack
stackTop
stackTop
(a) Empty stack
C
B
A
(b) Nonempty stack
FIGURE 7-10
Empty and nonempty linked stacks
In Figure 7-10(b), the top element of the stack is C; that is, the last element pushed onto
the stack is C
...
Default Constructor
The first operation that we consider is the default constructor
...
Thus, this function
sets stackTop to NULL
...
The
stack is empty if stackTop is NULL
...
(The stack is full
only if we run out of memory
...
The definitions of the functions to implement these operations are as
follows:
template
bool linkedStackType
{
return(stackTop == NULL);
} //end isEmptyStack
template
bool linkedStackType
{
return false;
} //end isFullStack
Recall that in the linked implementation of stacks, the function isFullStack does not
apply because logically the stack is never full
...
Initialize Stack
The operation initializeStack reinitializes the stack to an empty state
...
The definition of this function is as follows:
Linked Implementation of Stacks
|
419
template
void linkedStackType
{
nodeType
while (stackTop != NULL)
{
//while there are elements in
//the stack
temp = stackTop;
//set temp to point to the
//current node
stackTop = stackTop->link; //advance stackTop to the
//next node
delete temp;
//deallocate memory occupied by temp
}
} //end initializeStack
Next, we consider the push, top, and pop operations
...
In the case of pop, the node pointed to by stackTop will be
removed
...
The operation top
returns the info of the node to which stackTop is pointing
...
stack
stackTop
C
B
A
FIGURE 7-11
Stack before the push operation
7
420 |
Chapter 7: Stacks
Figure 7-12 shows the steps of the push operation
...
)
newNode
newNode
D
stack
stackTop
C
D
stack
stackTop
stack
C
B
stackTop
A
(a) Create newNode
and store D
D
C
B
A
FIGURE 7-12
newNode
B
A
(b) Put newNode on
the top of stack
(c) Make stackTop point
to the top element
Push operation
As shown in Figure 7-12, to push 'D' into the stack, first we create a new node and
store 'D' into it
...
Finally, we make
stackTop point to the top element of the stack
...
Return the Top Element
The operation to return the top element of the stack is quite straightforward
...
Consider the stack shown in Figure 7-13
...
temp
stack
temp
C
stackTop
C
stack
B
A
(a) Make temp point to the
top element
FIGURE 7-14
Pop operation
stackTop
B
stackTop
A
(b) Make stackTop point to
the next element
B
A
(c) Delete temp
422 |
Chapter 7: Stacks
As shown in Figure 7-14, first we make a pointer temp point to the top of the stack
...
Finally, we delete temp
...
" << endl;
}//end pop
Copy Stack
The function copyStack makes an identical copy of a stack
...
The definition of the function
copyStack is as follows:
template
void linkedStackType
(const linkedStackType
{
nodeType
if (stackTop != NULL) //if stack is nonempty, make it empty
initializeStack();
if (otherStack
...
stackTop; //set current to point
//to the stack to be copied
//copy the stackTop element of the stack
stackTop = new nodeType
stackTop->info = current->info; //copy the info
stackTop->link = NULL; //set the link field to NULL
last = stackTop;
//set last to point to the node
current = current->link; //set current to point to the
//next node
Linked Implementation of Stacks
|
423
//copy the remaining stack
while (current != NULL)
{
newNode = new nodeType
newNode->info = current->info;
newNode->link = NULL;
last->link = newNode;
last = newNode;
current = current->link;
}//end while
}//end else
} //end copyStack
Constructors and Destructors
We have already discussed the default constructor
...
(These functions
are similar to those discussed for linked lists in Chapter 5
...
TABLE 7-2 Time complexity of the operations of the class linkedStackType on a
stack with n elements
Function
Time complexity
isEmptyStack
O (1)
isFullStack
O (1)
initializeStack
O (n)
constructor
O (1)
top
O (1)
push
O (1)
pop
O (1)
copyStack
O (n)
destructor
O (n)
copy constructor
O (n)
Overloading the assignment operator
O (n)
The definition of a stack, and the functions to implement the stack operations discussed
previously, are generic
...
A client’s program can include
this header file via the include statement
...
EXAMPLE 7-3
We assume that the definition of the class linkedStackType and the functions to
implement the stack operations are included in the header file "linkedStack
...
//*************************************************************
// Author: D
...
Malik
//
// This program tests various operations of a linked stack
...
h"
using namespace std;
void testCopy(linkedStackType
int main()
{
linkedStackType
linkedStackType
linkedStackType
//Add elements into stack
stack
...
push(43);
stack
...
isEmptyStack())
{
cout << newStack
...
pop();
}
//Use the assignment operator to copy the elements
//of stack into otherStack
otherStack = stack;
cout << "Testing the copy constructor
...
isEmptyStack())
{
cout << otherStack
...
pop();
}
}
return 0;
7
426 |
Chapter 7: Stacks
//Function to test the copy constructor
void testCopy(linkedStackType
{
cout << "Stack in the function testCopy:" << endl;
}
while (!OStack
...
top() << endl;
OStack
...
Stack in the function testCopy:
27
43
34
After the copy constructor, otherStack:
27
43
34
Stack as Derived from the class unorderedLinkedList
If we compare the push function of the stack with the insertFirst function discussed
for general lists in Chapter 5, we see that the algorithms to implement these operations
are similar
...
Moreover,
the functions pop and isFullStack can be implemented as in the previous section
...
However, the class unorderedLinkedList is derived from the
the class linkedListType and provides the definitions of the abstract functions of
the class linkedListType
...
Next, we define the class linkedStackType that is derived from the class
unorderedLinkedList
...
#include
#include "unorderedLinkedList
...
For example, in the expression a + b, the operator + is between the
operands a and b
...
That is, we must
evaluate expressions from left to right, and multiplication and division have higher
precedence than addition and subtraction
...
For example, in the expression a + b * c, we
first evaluate * using the operands b and c, and then we evaluate + using the operand a
and the result of b * c
...
In the late 1950s, the Australian philosopher and early
computer scientist Charles L
...
This has the
advantage that the operators appear in the order required for computation
...
EXAMPLE 7-4
Infix expression
a+b
a+b*c
a*b+c
(a + b ) * c
(a – b) * (c + d)
(a + b) * (c – d / e) + f
Equivalent postfix expression
ab+
abc*+
ab*c+
ab+c*
ab–cd+*
ab+cde/–*f+
Shortly after Lukasiewicz’s discovery, it was realized that postfix notation had important
applications in computer science
...
Postfix expressions can be evaluated using the following algorithm:
Application of Stacks: Postfix Expressions Calculator |
429
Scan the expression from left to right
...
Consider the following postfix expression:
6 3 + 2 * =
Let us evaluate this expression using a stack and the previous algorithm
...
Expression: 6 3 + 2 * =
Push
6
into
stack
6
(a)
Push
2
into
stack
2
9
(e)
FIGURE 7-15
Push
3
into
stack
3
6
+
Pop
stack
twice
op2 = 3;
op1 = 6;
(b)
*
Pop
stack
twice
op2 = 2;
op1 = 9;
(f)
op1 + op2
= 9
Push 9
into
stack
9
(d)
(c)
op1 * op2
= 18
Push 18
into
stack
(g)
18
=
Pop
stack
and
print:
18
7
(h)
Evaluating the postfix expression: 6 3 + 2 * =
Read the first symbol, 6, which is a number
...
Read the next symbol, 3, which is a number
...
Read the next symbol, +, which is an operator
...
Perform the operation and put the result back onto the stack; see Figure 7-15(d)
...
Push the number onto the stack; see
Figure 7-15(e)
...
Because an operator
requires two operands to be evaluated, pop the stack twice; see Figure 7-15(f)
...
Scan the next symbol, =, which is the equal sign, indicating the end of the expression
...
The result of the expression is in the stack, so pop and print;
see Figure 7-15(h)
...
430 |
Chapter 7: Stacks
From this discussion, it is clear that when we read a symbol other than a number, the
following cases arise:
1
...
If the symbol is +, -, *, or /, the symbol is an operator and so we
must evaluate it
...
b
...
At this step, the stack must contain exactly one
element; otherwise, the expression has an error
...
The symbol we read is something other than +, -, *, /, or ¼
...
a
...
Consider the following expressions:
i
...
14 + 2 3 * =
iii
...
In the case of expression (iii), when we
encounter the equal sign (=), the stack will have two elements and this error cannot be
discovered until we are ready to print the value of the expression
...
If the symbol scanned is #, the
next input is a number (that is, an operand)
...
Furthermore, we assume that each expression contains only the +, -, *, and / operators
...
If the
expression has an error, the expression is discarded
...
Because an expression might
contain an error, we must clear the stack before processing the next expression
...
MAIN ALGORITHM
Pursuant to the previous discussion, the main algorithm in pseudocode is as follows:
Application of Stacks: Postfix Expressions Calculator |
431
Read the first character
while not the end of input data
{
a
...
process the expression
c
...
get the next expression
}
To simplify the complexity of the function main, we write four functions—
evaluateExpression, evaluateOpr, discardExp, and printResult
...
If the postfix expression is error free, the function printResult outputs the result
...
FUNCTION evaluateExpression
The function evaluateExpression evaluates each postfix expression
...
The general algorithm in pseudocode is as follows:
while (ch is not = '=') //process each expression
//= marks the end of an expression
{
switch (ch)
{
case '#':
read a number
output the number;
push the number onto the stack;
break;
default:
assume that ch is an operation
evaluate the operation;
} //end switch
if no error was found, then
{
read next ch;
output ch;
}
else
Discard the expression
} //end while
From this algorithm, it follows that this method has five parameters—a parameter to
access the input file, a parameter to access the output file, a parameter to access the stack, a
parameter to pass a character of the expression, and a parameter to indicate whether there
is an error in the expression
...
isFullStack())
stack
...
"
<< "Program terminates!" << endl;
exit(0); //terminate the program
}
break;
default:
evaluateOpr(outF, stack, ch, isExpOk);
}//end switch
if (isExpOk) //if no error
{
inpF >> ch;
outF << ch;
}
else
if (ch != '#')
outF << " ";
discardExp(inpF, outF, ch);
} //end while (!= '=')
} //end evaluateExpression
Note that the funtion exit terminates the program
...
Two operands are needed to evaluate
an operation and operands are saved in the stack
...
If the stack contains fewer than two numbers, the expression has an
error
...
This function also checks for any illegal operations
...
switch (ch)
{
case '+': //add the operands: op1 + op2
stack
...
push(op1 - op2);
break;
case '*': //multiply the operands: op1 * op2
stack
...
push(op1 / op2);
break;
otherwise operation is illegal
{
output an appropriate message;
set expressionOk to false
}
} //end switch
Following this pseudocode, the definition of the function evaluateOpr is as follows:
void evaluateOpr(ofstream& out, stackType
char& ch, bool& isExpOk)
{
double op1, op2;
7
434 |
Chapter 7: Stacks
if (stack
...
top();
stack
...
isEmptyStack())
{
out << " (Not enough operands)";
isExpOk = false;
}
else
{
op1 = stack
...
pop();
switch (ch)
{
case '+':
stack
...
push(op1 - op2);
break;
case '*':
stack
...
push(op1 / op2);
else
{
out << " (Division by 0)";
isExpOk = false;
}
break;
default:
out << " (Illegal operator)";
isExpOk = false;
}//end switch
} //end else
} //end else
} //end evaluateOpr
Application of Stacks: Postfix Expressions Calculator |
435
FUNCTION discardExp
This function is called whenever an error is discovered in the expression
...
The definiton
of this function is as follows:
void discardExp(ifstream& in, ofstream& out, char& ch)
{
while (ch != '=')
{
in
...
The result of the expression is in the stack
and the output is sent to a file
...
Suppose that no errors were encountered by the method
evaluateExpression
...
If either the stack is empty or it has more than
one element, there is an error in the postfix expression
...
The definition of this method is as follows:
void printResult(ofstream& outF, stackType
bool isExpOk)
{
double result;
if (isExpOk) //if no error, print the result
{
if (!stack
...
top();
stack
...
isEmptyStack())
outF << result << endl;
else
outF << " (Error: Too many operands)" << endl;
} //end if
else
outF << " (Error in the expression)" << endl;
}
else
outF << " (Error in the expression)" << endl;
outF << "_________________________________"
<< endl << endl;
} //end printResult
7
436 |
Chapter 7: Stacks
PROGRAM LISTING
//*************************************************************
// Author: D
...
Malik
//
// This program uses a stack to evaluate postfix expressions
...
h"
using namespace std;
void evaluateExpression(ifstream& inpF, ofstream& outF,
stackType
char& ch, bool& isExpOk);
void evaluateOpr(ofstream& out, stackType
char& ch, bool& isExpOk);
void discardExp(ifstream& in, ofstream& out, char& ch);
void printResult(ofstream& outF, stackType
bool isExpOk);
int main()
{
bool expressionOk;
char ch;
stackType
ifstream infile;
ofstream outfile;
infile
...
txt");
if (!infile)
{
cout << "Cannot open the input file
...
open("RpnOutput
...
initializeStack();
expressionOk = true;
outfile << ch;
Application of Stacks: Postfix Expressions Calculator |
437
evaluateExpression(infile, outfile, stack, ch,
expressionOk);
printResult(outfile, stack, expressionOk);
infile >> ch; //begin processing the next expression
} //end while
infile
...
close();
return 0;
} //end main
//Place the definitions of the function evaluateExpression,
//evaluateOpr, discardExp, and printResult as described
//previously here
...
00 #27
...
00 * = 186
...
00 #28
...
00 #2
...
00 #30
...
00 * / = 0
...
00 #3
...
00 + = (Error: Too many operands)
_________________________________
#20
...
00 #9
...
00 #23
...
00 #24
...
00 #7
...
00 - = 52
...
In this section, you will
learn how a stack can be used to design a nonrecursive algorithm to print a linked list
backward
...
first
FIGURE 7-16
5
10
15
Linked list
To print the list backward, first we need to get to the last node of the list, which we can
do by traversing the linked list starting at the first node
...
Moreover, if we do this for every node in the list, the
program might execute very slowly
...
After printing the info of a particular node, we need to move to the node immediately
behind this node
...
Thus, while initially traversing the list to move to the last node, we must save a
pointer to each node
...
After printing 15, we go back to the node with
info 10; after printing 10, we go back to the node with info 5
...
Because the number of nodes in a linked list is usually not known, we will use the linked
implementation of a stack
...
Consider the following
statements:
current = first;
//Line 1
while (current != NULL)
{
stack
...
(See Figure 7-17)
stack
first
10
5
15
stackTop
current
FIGURE 7-17
List after the statement current = first; executes
Because current is not NULL, the statements in Lines 4 and 5 execute
...
)
stack
first
5
10
15
stackTop
current
5
7
FIGURE 7-18
List and stack after the statements stack
...
In fact, statements in
Lines 4 and 5 execute until current beomes NULL
...
stack
first
5
10
15
stackTop
15
current
10
5
FIGURE 7-19
List and stack after the while statement executes
440 |
Chapter 7: Stacks
After the statement in Line 4 executes, the loop condition, in Line 2, is evaluated again
...
From Figure 7-19, it follows that a pointer to each node in the linked
list is saved in the stack
...
Let us now execute the following statements:
while (!stack
...
top();
stack
...
Therefore, the statements in Lines 9, 10, and 11 execute
...
The statement in Line 10 removes the top element of
the stack; see Figure 7-20
...
top(); and stack
...
Because stack is nonempty, the body of the while loop executes again
...
Ater printing 5, the stack becomes empty and the while
loop terminates
...
Because a stack is an
important data structure, the Standard Template Library (STL) provides a class to implement a stack in a program
...
The implementation of the class stack provided by the STL is similar to the one described in this
chapter
...
TABLE 7-3
Operations on a stack object
Operation
Effect
size
Returns the actual number of elements in
the stack
...
push(item)
Inserts a copy of item into the stack
...
This operation is implemented as a
value-returning function
...
pop
In addition to the operations size, empty, push, top, and pop, the stack container
class provides relational operators to compare two stacks
...
The program in Example 7-5 illustrates how to use the stack container class
...
S
...
//*************************************************************
#include
#include
//Line 1
//Line 2
using namespace std;
//Line 3
int main()
{
stack
//Line 4
//Line 5
//Line 6
intStack;
7
442 |
Chapter 7: Stacks
intStack
...
push(8);
intStack
...
push(3);
//Line
//Line
//Line
//Line
7
8
9
10
cout << "Line 11: The top element of intStack: "
<< intStack
...
pop();
//Line 12
cout << "Line 13: After the pop operation, the "
<< " top element of intStack: "
<< intStack
...
empty())
{
cout << intStack
...
pop();
}
//Line
//Line
//Line
//Line
//Line
cout << endl;
}
//Line 14
//Line 20
return 0;
//Line 21
//Line 22
15
16
17
18
19
Sample Run:
Line 11: The top element of intStack: 3
Line 13: After the pop operation, the top element of intStack: 20
Line 14: intStack elements: 20 8 16
The preceding output is self-explanatory
...
QUICK REVIEW
1
...
3
...
5
...
A stack is a data structure in which the items are added and deleted from one
end only
...
The basic operations on a stack are as follows: Push an item onto the stack,
pop an item from the stack, retrieve the top element of the stack, initialize the
stack, check whether the stack is empty, and check whether the stack is full
...
The middle elements of a stack should not be accessed directly
...
Exercises
7
...
9
...
In postfix notation, the operators are written after the operands
...
If an operator is found, back
operands, evaluate the operator,
The STL class stack can be used
a
...
|
right
...
to implement a stack in a program
...
Consider the following statements:
stackType
int x, y;
Show what is output by the following segment of code:
x = 4;
y = 0;
stack
...
push(x);
stack
...
top();
stack
...
push(x + y);
stack
...
push(3);
x = stack
...
pop();
cout << "x = " << x << endl;
cout << "y = " << y << endl;
while (!stack
...
top() << endl;
stack
...
Consider the following statements:
stackType
int x;
Suppose that the input is:
14 45 34 23 10 5 -999
Show what is output by the following segment of code:
7
444 |
Chapter 7: Stacks
stack
...
isFullStack())
stack
...
isEmptyStack())
{
cout << " " << stack
...
pop();
}
cout << endl;
3
...
12 25 5 1 / / * 8 7 + - =
c
...
4
...
3 5 6 * + 13 - 18 2 / + =
Convert the following infix expressions to postfix notations:
a
...
(A + B) * (C + D) - E
A - (B + C) * D + E / F
c
...
5
...
b
...
6
...
h"
using namespace std;
Exercises
|
445
template
void mystery(stackType
int main()
{
stackType
stackType
string list[] = {"Winter", "Spring", "Summer", "Fall",
"Cold", "Warm", "Hot"};
for (int i = 0; i < 7; i++)
s1
...
isEmptyStack())
{
cout << s2
...
pop();
}
cout << endl;
template
void mystery(stackType
{
while (!s
...
push(s
...
pop();
}
}
7
...
The classes stackADT, stackType, and linkedStackType
are as defined in this chapter
...
b
...
stackType
d
...
stackADT
linkedStackType
What is the output of the following program?
#include
#include
#include "myStack
...
push(list[i]);
mystery(s1, s2);
}
while (!s2
...
top() << " ";
s2
...
isEmptyStack())
{
t
...
top());
s
...
What is the output of the following program segment?
linkedStackType
myStack
...
push(20);
myStack
...
top() << endl;
myStack
...
push(2 * myStack
...
push(-10);
myStack
...
isEmptyStack())
{
cout << tempStack
...
pop();
}
cout << endl;
cout << myStack
...
11
...
13
...
Assume that this function is a
member of the class linkedListType, designed in Chapter 5
...
The
original stack remains unchanged
...
Write the definition of the function template clear that takes as a parameter a stack object of the type stack (STL class) and removes all the
elements from the stack
...
2
...
Two stacks of the same type are the same if they have the same number of
elements and their elements at the corresponding positions are the same
...
Also,
write the definition of the function template to overload this operator
...
a
...
Consider the following statements:
stackType
stackType
The statement
stack1
...
5
...
That is,
the top element of stack1 is the bottom element of stack2, and so on
...
b
...
Repeat Exercises 3a and 3b for the class linkedStackType
...
The program
outputs whether the expression contains matching grouping symbols
...
However, the expression 5 + {(13 + 7) / 8 - 2 * 9
does not contain matching grouping symbols
...
7
...
(Converting a Number from Binary to Decimal) The language of a
computer, called machine language, is a sequence of 0s and 1s
...
Also,
the collating sequence of A in the ASCII character set is 65
...
The numbering system we use is called the decimal system, or base 10
system
...
The purpose of this exercise is to write a
function to convert a number from base 2 to base 10
...
The weight of each bit in the binary number is
assigned from right to left
...
The weight
of the bit immediately to the left of the rightmost bit is 1, the weight of the
bit immediately to the left of it is 2, and so on
...
The weight of each bit is as follows:
weight 6 5 4 3 2 1 0
1001101
We use the weight of each bit to find the equivalent decimal number
...
For the binary number 1001101, the equivalent
decimal number is
1 Â 26 þ 0 Â 25 þ 0 Â 24 þ 1 Â 23 þ 1 Â 22 þ 0 Â 21 þ 1 Â 20
¼ 64 þ 0 þ 0 þ 8 þ 4 þ 0 þ 1 ¼ 77
8
...
Because we do not know in advance how many bits are in the binary
number, we must process the bits from right to left
...
Also, each bit must be extracted from the binary number and multiplied
by 2 to the power of its weight
...
Write a program that uses a stack to convert a binary number into
an equivalent decimal number and test your function for the following
values: 11000101, 10101010, 11111111, 10000000, 1111100000
...
Write a program that uses a stack to convert a
decimal number into an equivalent binary number
...
|
449
(Infix to Postfix) Write a program that converts an infix expression into an
equivalent postfix expression
...
The rules to convert infx into pfx are as follows:
a
...
Initialize pfx to an empty expression and also initialize the stack
...
b
...
b
...
b
...
b
...
If sym is an operand, append sym to pfx
...
If sym is ), pop and append all the symbols from the stack until the
most recent left parenthesis
...
If sym is an operator:
Pop and append all the operators from the stack to pfx that
are above the most recent left parenthesis and have precedence greater than or equal to sym
...
4
...
Push sym onto the stack
...
Pop
and append to pfx everything from the stack
...
4
...
c
...
You may assume that the expressions you will process are
error free
...
The class must include
the following operations:
•
•
•
getInfix—Stores the infix expression
showInfix—Outputs the infix expression
showPostfix—Outputs the postfix expression
Some other operations that you might need are the following:
•
convertToPostfix—Converts the infix expression into a postfix
expression
...
• precedence—Determines the precedence between two operators
...
Include the constructors and destructors for automatic initialization and
dynamic memory deallocation
...
11
...
Redo Programming Exercise 9 so that it uses the STL class stack to
convert the infix expressions to postfix expressions
...
Learn about queues
...
Learn how to implement a queue as an array
...
Discover queue applications
...
The notion of a
queue in computer science is the same as the notion of the queues to which you are
accustomed in everyday life
...
Similarly, because a computer can
send a print request faster than a printer can print, a queue of documents is often waiting to
be printed at a printer
...
That is, a queue is a First In First Out data structure
...
Whenever a system is modeled
on the First In First Out principle, queues are used
...
First,
however, we need to develop the tools necessary to implement a queue
...
A queue is a set of elements of the same type in which the elements are added at one end,
called the back or rear, and deleted from the other end, called the front
...
Each new customer gets in the line at the rear
...
The rear of the queue is accessed whenever a new element is added to the queue, and the
front of the queue is accessed whenever an element is deleted from the queue
...
Queue: A data structure in which the elements are added at one end, called the rear, and
deleted from the other end, called the front; a First In First Out (FIFO) data structure
...
We
call the add operation addQueue and the delete operation deleteQueue
...
We also need an operation, initializeQueue, to initialize the queue to an empty state
...
Some of the queue operations are as follows:
•
•
initializeQueue—Initializes the queue to an empty state
...
If the queue
is empty, it returns the value true; otherwise, it returns the value false
...
If the queue is
full, it returns the value true; otherwise, it returns the value false
...
Prior
to this operation, the queue must exist and must not be empty
...
Prior to this operation, the
queue must exist and must not be empty
...
Prior to this
operation, the queue must exist and must not be full
...
Prior to
this operation, the queue must exist and must not be empty
...
We will
consider both implementations
...
The following abstract class queueADT defines these operations as an ADT:
//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operations on a queue
...
//Postcondition: Returns true if the queue is empty,
//
otherwise returns false
...
//Postcondition: Returns true if the queue is full,
//
otherwise returns false
...
//Postcondition: The queue is empty
...
//Precondition: The queue exists and is not empty
...
8
454 |
Chapter 8: Queues
virtual Type back() const = 0;
//Function to return the last element of the queue
...
//Postcondition: If the queue is empty, the program
//
terminates; otherwise, the last element of the queue
//
is returned
...
//Precondition: The queue exists and is not full
...
};
virtual void deleteQueue() = 0;
//Function to remove the first element of the queue
...
//Postcondition: The queue is changed and the first element
//
is removed from the queue
...
Implementation of Queues as Arrays
Before giving the definition of the class to implement a queue as an ADT, we need to
decide how many member variables are needed to implement the queue
...
Thus, we need at least four member variables
...
How do queueFront
and queueRear indicate that the queue is empty or full? Suppose that queueFront gives
the index of the first element of the queue, and queueRear gives the index of the last
element of the queue
...
To delete an element from the queue, first we retrieve the element that queueFront is
pointing to and then advance queueFront to the next element of the queue
...
Let us see what happens when queueFront changes after a deleteQueue operation and
queueRear changes after an addQueue operation
...
Initially, the queue is empty
...
[0] [1]
[98][99]
A
...
[0] [1] [2] [3]
[98][99]
A
B
C
...
[0] [1] [2] [3]
[98][99]
A
B
C
...
Consider the following sequence of operations:
AAADADADADADADADA
...
However, the
queue has only two or three elements and the front of the array is empty
...
)
[0] [1] [2]
[97] [98][99]
...
One solution to this problem is that when the queue overflows to the rear (that is,
queueRear points to the last array position), we can check the value of the index
queueFront
...
This solution is good if the queue size is very small;
otherwise, the program might execute more slowly
...
(See Figure 8-5
...
Implementation of Queues as Arrays
|
457
Suppose that we have the queue as shown in Figure 8-6(a)
...
[0] [1]
[98][99]
Z
...
Because the array containing the queue is circular, we can use the following statement to
advance queueRear (queueFront) to the next array position:
queueRear = (queueRear + 1) % maxQueueSize;
If queueRear < maxQueueSize - 1, then queueRear + 1 <= maxQueueSize - 1, so
(queueRear + 1) % maxQueueSize = queueRear + 1
...
In this case, queueRear
will be set to 0, which is the first array position
...
Before we write the algorithms to implement the
queue operations, consider the following two cases
...
[0]
...
queueFront 99 queueRear 98
(b) After deleteQueue();
Queue before and after the delete operation
After the operation deleteQueue();, the resulting array is as shown in Figure 8-7(b)
...
[0]
[97] [98][99]
...
Z
queue elements
queueFront 99 queueRear 97
(a) Before addQueue(Queue,'Z');
FIGURE 8-8
queueFront 99 queueRear 98
(b) After addQueue(Queue,'Z');
Queue before and after the add operation
After the operation addQueue(Queue,'Z');, the resulting array is as shown in Figure 8-8(b)
...
However, the resulting array in Figure 8-7(b) represents an empty queue,
whereas the resulting array in Figure 8-8(b) represents a full queue
...
This problem has several solutions
...
In addition to the member
variables queueFront and queueRear, we need another variable, count, to implement the
queue
...
In this case, the
function initializeQueue initializes count to 0
...
Another solution is to let queueFront indicate the index of the array position preceding the
first element of the queue, rather than the index of the (actual) first element itself
...
In this solution, the slot indicated by the
index queueFront (that is, the slot preceding the first true element) is reserved
...
Finally, because the array position indicated by queueFront is to be kept empty, if the
array size is, say, 100, then 99 elements can be stored in the queue
...
)
reserved slot
[0] [1] [2]
[97] [98][99]
...
That is, we use the variable count to
indicate whether the queue is empty or full
...
Because
arrays can be allocated dynamically, we will leave it for the user to specify the size of the
array to implement the queue
...
//*************************************************************
// Author: D
...
Malik
//
// This class specifies the basic operation on a queue as an
// array
...
bool isEmptyQueue() const;
//Function to determine whether the queue is empty
...
bool isFullQueue() const;
//Function to determine whether the queue is full
...
void initializeQueue();
//Function to initialize the queue to an empty state
...
Type front() const;
//Function to return the first element of the queue
...
//Postcondition: If the queue is empty, the program
//
terminates; otherwise, the first element of the
//
queue is returned
...
//Precondition: The queue exists and is not empty
...
void addQueue(const Type& queueElement);
//Function to add queueElement to the queue
...
8
460 |
Chapter 8: Queues
//Postcondition: The queue is changed and queueElement is
//
added to the queue
...
//Precondition: The queue exists and is not empty
...
queueType(int queueSize = 100);
//Constructor
queueType(const queueType
//Copy constructor
~queueType();
//Destructor
private:
int maxQueueSize; //variable to store the maximum queue size
int count;
//variable to store the number of
//elements in the queue
int queueFront;
//variable to point to the first
//element of the queue
int queueRear;
//variable to point to the last
//element of the queue
Type *list;
//pointer to the array that holds
//the queue elements
};
We leave the UML diagram of the class queueType as an exercise for you
...
)
Next, we consider the implementation of the queue operations
...
So the functions to implement these operations are as follows:
template
bool queueType
{
return (count == 0);
} //end isEmptyQueue
template
bool queueType
{
return (count == maxQueueSize);
} //end isFullQueue
Implementation of Queues as Arrays
|
461
Initialize Queue
This operation initializes a queue to an empty state
...
Therefore, we initialize queueFront to 0, queueRear to maxQueueSize - 1,
and count to 0
...
[0] [1] [2]
[97] [98][99]
...
If the queue is nonempty, the
element of the queue indicated by the index queueFront is returned; otherwise, the
program terminates
...
If the queue is nonempty, the
element of the queue indicated by the index queueRear is returned; otherwise, the
program terminates
...
Because queueRear points to the last
element of the queue, to add a new element to the queue, we first advance queueRear to
the next array position, and then add the new element to the array position indicated by
queueRear
...
So the function addQueue is as follows:
template