[go: up one dir, main page]

0% found this document useful (0 votes)
58 views566 pages

OOPinC byJohnsonbaughMartin

Uploaded by

Peter Juul
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
58 views566 pages

OOPinC byJohnsonbaughMartin

Uploaded by

Peter Juul
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 566

RICHARD JOHNSONBAUGH

MARTIN KALIN

Object-Oriented
n
a
m
iP
rg
o
C
+

www.MathSchoolinternational.com
Object-Oriented
Programming in C + +

Richard Johnsonbaugh
Martin Kalin
D E P A U L U N IV E R S IT Y

PRENTICE HALL, Englewood Cliffs, New Jersey 07632

www.MathSchoolinternational.com
Cover photograph: Autograph score of Schubert’s Great C major Symphony,
Gesellschaft der Musikfreunde.
Used by permission

Copyright © 1995 by Prentice-Hall, Inc.


A Paramount Communications Company
Englewood Cliffs, New Jersey 07632

A ll rights reserved. No part of this book may be


reproduced in any form or by any means,
without permission in w riting from the publisher.

Printed in the United States of America

10 9 8 7 6 5 4 3 2 1

ISBN 0-05-3bDbfl2-7

Prentice-H all International (U K ) Limited, London


Prentice-H all of Australia Pty. Limited, Sydney
Prentice-H all Canada Inc., Toronto
Prentice-H all Hispanoamericana, S.A., Mexico
Prentice-H all o f India Private Limited, New D elhi
Prentice-H all o f Japan, Inc., Tokyo
Simon & Schuster Asia Pte. Ltd., Singapore
Editora Prentice-H all do brasil, Ltda., R io de Janiero

www.MathSchoolinternational.com
Trademark Notices

IBM is a registered trademark of International Business Machines Corpora­


tion.

UNIX is a registered trademark of AT& T Bell Laboratories.

Borland C + + , Turbo C, and Turbo C + + are registered trademarks of Bor­


land International, Inc.

www.MathSchoolinternational.com
,v'J.

I
{

www.MathSchoolinternational.com
Ik
Preface

This book is based on C + + courses given by the authors at DePaul Univer­


sity for the last several years. The book can be used for self-study or a course
on object-oriented programming in C + + . We assume no prior knowledge of
C + + , but we do assume knowledge of C. This book is truly about C + + , and
not simply about the C subset of C + + . Coverage of C at the level provided
in R. Johnsonbaugh and M. Kalin, Applications Programming in A N S I C,
3rd ed. (New York: Macmillan, 1995) provides sufficient background for this
book. In this book, as in our C books, we make extensive use of examples,
figures, self-study exercises, sample applications, lists of common program­
ming errors, and programming exercises. We strive for clarity throughout
the book and also illustrate a variety of good programming style possible in
C ++. Our aim is to make C + + the language of choice for those who need
the power of object-oriented programming.
This is the first introductory book on C + + that treats object-oriented
design, emphasizes sound programming practices (see, e.g., Assertions and
Program Correctness, Section 3.8), presents the C + + input/output class
hierarchy (Chapter 7) in sufficient detail so that it can be used, and features
major, useful examples (e.g., Measuring Computer Performance, Section 5.3,
and A Random Access File Class, Section 7.6).
C + + is an evolving language; hence, no final standard has yet been es­
tablished. The C + + presented in this book is based on the latest Working
Paper for Draft Proposed International Standard for Information Systems—
Programming Language C + + developed by the X3J16 standards committee
of The American National Standards Institute (ANSI). The X3J16 commit­
tee is developing a definition of C + + , which eventually will evolve into a
proposed standard. The Working Paper is available from
Standards Secretariat CBEMA
1250 Eye Street, NW
Suite 200
Washington, DC 20005

www.MathSchoolinternational.com
viii PREFACE

O verview

During the 1980s and 1990s, C became the language of choice for many
applications and systems programmers. Most major software available for
personal computers is written in C: spreadsheets, word processors, databases,
communications packages, statistical software, graphics packages, and so on.
Virtually all software written for the UNIX environment is likewise written
in C, and many mainframe systems meant to be ported from one platform
to another are also coded in C. In the early 1980s, Bjarne Stroustrup of
A T& T Bell Labs developed C + + as an extension of C that supports object-
oriented programming, a type of programming that is well suited to the large,
complex software systems now written for all platforms, from the cheapest
personal computers to the most expensive mainframes. C + + also corrects
some shortcomings in C, which C + + includes as a subset, and it supports
abstract data types. C + + ’s popularity has grown in tandem with that of
object-oriented design in software. Accordingly, we cover object-oriented
design and C + + programming.
C + + is a complex language. Fortunately, most C + + programmers can
benefit from its power without mastering each and every one of its features.
Because ours is an introductory book, we focus on the most useful aspects
of the language; but we also cover all of the language’s constructs. We
emphasize using C + + to write practical applications based on sound object-
oriented design.
The book includes the following features:
• Examples and exercises that cover a wide range of applications.
• Sample applications.
• A broad variety of programming exercises. The book contains 59 pro­
gramming exercises.
• End-of-chapter lists of common programming errors.
• Discussion of the standard C + + input/output class library (Chapter
7).
• Self-study exercises to enable readers to check their mastery of a section.
The book contains nearly 500 such exercises. Answers to the odd-
numbered exercises are in the back of the book.
• Figures to facilitate the learning process.
• Major data structures, including stacks (Section 3.2), strings (Section
4.1), binary search trees (Section 4.4), and files (Section 7.6), imple­
mented in C ++.
• A number of appendixes.
• Understandable code. We opt for clarity over subtlety throughout the
book.

www.MathSchoolinternational.com
PREFACE ix

• The latest C + + features including templates (Section 3.9), exception


handling (Section 8.1), run-time type identification (Section 8.2), and
namespaces (Section 8.3).
• An introduction to the object-oriented languages Smalltalk, Eiffel, and
Objective C (Section 8.4).

Organization of the B ook


Chapter 1 introduces key concepts associated with object-oriented design
and programming: classes, abstract data types, objects, inheritance, encap­
sulation, polymorphism, and others. The chapter contrasts object-oriented
design with top-down functional decomposition and offers examples to illus­
trate the difference between the approaches.
Chapter 2 discusses some important changes from C to C + + . It also
introduces C + + input/output so that the reader can begin using this class
library right away. Chapter 7 goes into the details of C + + input/output.
Chapters 3 and 4 are devoted to classes. Chapter 3 covers the basics so
that the reader can begin using classes at once. The chapter explains how to
declare classes; how to write constructors, destructors, and other methods;
how to overload operators; and how to write frien d functions. Chapter
3 introduces assertions as a tool to promote correctness in object-oriented
programs. Chapter 3 also introduces templates and encourages their use for
constructing generic classes. We believe that assertions and templates have
not received adequate coverage in other C + + books, particularly given their
power to promote the goals of object-oriented design. Chapter 4 expands
on Chapter 3 and introduces new material as well. Constructors are studied
closely, frien d classes are presented, and s ta tic data members and meth­
ods are introduced. Chapters 3 and 4 rely heavily on examples to explain
how classes may be used to implement abstract data types and to meet the
object-oriented goal of encapsulation.
Chapter 5 explains inheritance (including multiple inheritance) and poly­
morphism in depth. Through examples and sample applications (e.g., mea­
suring computer performance, polymorphic tree traversal), the chapter il­
lustrates basic programming techniques and also illustrates different styles
of C + + programming. The chapter carefully explains the distinction be­
tween run-time and compile-time binding so that the reader is clear about
the limits of polymorphism in C ++.
Chapter 6 is devoted to operator overloading. The chapter first reviews
earlier treatments of the topic and then offers many examples and several
sample applications to highlight the power of operator overloading. The
chapter shows how to overload the subscript, function, memory management,
preincrement, and postincrement operators among others.
Chapter 7 serves two purposes. First, the chapter examines in detail
the C + + input/output library so that the interested reader can exploit the
powerful classes contained therein. This treatment culminates in a sample

www.MathSchoolinternational.com
X PREFACE

application that builds a random access file class through inheritance from
a library file class. Second, the chapter uses the input/output library as
a major, sophisticated example of object-oriented design realized in C ++.
Chapter 7 pays close attention to manipulators, which are powerful ways
to do sophisticated input/output in C + + . We believe Chapter 7 offers an
unrivaled examination of C + + ’s input/output.
Chapter 8 covers advanced topics such as exception handling, run-time
type identification, and namespaces. These topics are recent additions to
C + + . The chapter also contrasts C + + with other object-oriented languages
such as Eiffel, Objective C, and Smalltalk. The chapter concludes with a
discussion of the future of object-oriented design and programming.
This book is devoted to C + + independent of any particular operating
system. However, the basic commands needed to run C + + programs are
given in Appendixes C and D for the UNIX g++ and Borland C + + compilers.
In addition, Appendix C contains an extended discussion of UNIX.
We rely heavily on short examples, pictures, tables, and other figures to
illustrate specific points about the syntax and semantics of C + + . From our
experience in teaching C + + and other languages, we are convinced that no
single method is appropriate for clarifying every aspect about a language.
Most of our students agree with us that learning and using C + + is fun.
We have tried to incorporate this view by using interesting examples, sample
problems, programming exercises, and short slices of code.

C hapter Structure

The chapters are organized as follows:


Contents
Overview
Section
Section Exercises
Section
Section Exercises

Common Programming Errors


Programming Exercises
In Chapters 3 through 7, several sections are devoted to sample applica­
tions. Each of these sections contains a statement of a problem, a solution to
the problem, and an implementation of a solution to the problem in C ++.
Many of these sections conclude with an extended discussion.
The sample applications include the following:
• A stack class (Section 3.2)
• A zip code class (Section 3.4)
• A complex number class (Section 3.6)

www.MathSchoolinternational.com
PREFACE xi

• A string class (Section 4.1)


• A binary search tree class (Section 4.4)
• Iterators (Section 4.5)
• Measuring computer performance (Section 5.3)
• An associative array (Section 6.4)
• Random access files (Section 7.6)
The Common Programming Errors sections highlight those aspects of the
language that are easily misunderstood. The book contains 59 programming
exercises drawn from a wide variety of applications.

Exercises

The book contains nearly 600 section review exercises, the answers to which
are short answers, code fragments, and, in a few cases, entire programs.
These exercises are suitable as homework problems or as self-tests. The
answers to the odd-numbered exercises are in the back of the book, and
the answers to the even-numbered exercises are in the Instructor’s Guide.
Our experience teaching C + + has convinced us of the importance of these
exercises.
The applications covered in the programming exercises at the ends of the
chapters include the following:
• Semaphores (Programming Exercise 3.7)
• Local area networks (Programming Exercises 4.3 and 5.2)
• Scheduling (Programming Exercise 4.7)
• Graphs (Programming Exercises 4.9 and 5.6)
• Spreadsheets (Programming Exercises 4.8 and 6.9)
• Indexed files (Programming Exercise 7.5)
Not every reader will be interested in all of these applications; however, we
think that it is important to show the variety of problems that C + + can
address.

Appendixes

Four appendixes are provided for reference. Appendix A contains the ASCII
table. Appendix B contains a list of some of the most useful C + + functions
and class methods. We describe the parameters and return values for each,
the header file to include (when specified), and what the function or method
does.
For those readers using C + + under UNIX, we have included Appendix
C. We describe the GNU C + + compiler command g++ in some detail. In ad­
dition to the g++ command, we also discuss the make utility, the file system,
commands for handling files (e.g., Is, cp), directories and several commands

www.MathSchoolinternational.com
xii PREFACE

for navigating within directories (e.g., pwd, mkdir), pipes, man (the on-line
help utility), the grep and fin d utilities, and run-time libraries.
Appendix D tells how to compile, link, and run a C + + program in
Borland C + + . Explanations are included for single-file and multiple-file
programs.

Instructor Supplements

An Instructor’s Guide and a program diskette are available at no cost to


adopters of this book. The Instructor’s Guide contains answers to the even-
numbered section review exercises.
The program diskette contains the source code, header files, and data
files for all of the book’s sample applications as well as the source code for
some of the longer examples. Some programming exercises ask for mod­
ifications of these programs. In any case, we assume that many readers
will want to experiment with the code that we provide on the diskette.
There is no charge for making copies of this diskette to distribute to stu­
dents. These files also are available on Internet through anonymous ftp. The
node is kalin.depaul.edu and the directory is /distcplus. The directory holds
standalone ASCII versions of the files as well as a compressed tar version
cpluscode.tar.Z.

Acknowledgm ents

We thank the following reviewers: Kulbir Arora, SUNY at Buffalo; Rhoda


Baggs, Florida Institute of Technology; Kenneth Basye, Clark University;
Ed Chapin, University of Maryland, Eastern Shore; Leon Charney, Boston
University; Glenn Lancaster, DePaul University; Sathis Menon, Georgia In­
stitute of Technology; George Novacky, University of Pittsburgh; and Joe
Zachary, University of Utah.
We thank our colleagues Steve Jost and Kirk Snyder for their advice on
the lATgX typesetting system.
We are indebted to the Department of Computer Science and Information
Systems at DePaul University and its chairman, Helmut Epp, for providing
time and encouragement for the development of this book.
We are grateful to the Borland Corporation for furnishing current ver­
sions of Borland C ++.
At Macmillan, we thank John Griffin and Betsy Jones, computer sci­
ence editors, Bill Winschief, senior representative/field editor, and Elisabeth
Belfer, production supervisor.

R.J.
M.K.

www.MathSchoolinternational.com
Contents

1 Introduction 1

1.1 O bject-O riented Design 2


1.2 Classes and A bstract D a ta Types 6
1.3 Inheritance 10
1.4 Polym orphism 15

2 Basic C + + 17

2.1 Some Miscellaneous Extensions in CH—K 18


2.2 Functions 23
2.3 C + + O perators 40
2.4 Introduction to C + + Input/O utput 52
Com m on Program m ing Errors 58
Program m ing Exercises 68

3 Classes 73

3.1 Creating Classes 74


3.2 Sample Application: A Stack Class 82
3.3 A First Look at Constructors and Destructors 87
3.4 Sample Application: A Zip C ode Class 103
3.5 A First Look at Class O perator Overloading 108
3.6 Sample Application: A C om plex N u m b e r Class 112
3.7 Friend Functions 116

www.MathSchoolinternational.com
xiv CONTENTS

3.8 Assertions and P ro gram Correctness 121


3.9 Generic Classes U sin g Templates 129
Com m on Program m ing Errors 133
Program m ing Exercises 137

4 M ore on Classes 141

4.1 Sam ple Application: A String Class 142


4.2 M o re on the C opy Constructor 158
4.3 Friend Classes 172
4.4 Sam ple Application: A B inary Search Tree Class 175
4.5 Sam ple Application: A n Iterator Class 181
4.6 Static D a ta M em bers and M ethods 186
Com m on Program m ing Errors 194
Program m in g Exercises 197

5 Inheritance 203

5.1 Basic Concepts and Syntax 204


5.2 Constructors U n d er Inheritance 222
5.3 Sam ple Application: M easuring Com puter
Perform ance 231
5.4 Polym orphism and V irtu al M ethods 243
5.5 Sample Application: V irtu al Tree Traversal 260
5.6 Destructors U n d er Inheritance 266
5.7 M u ltip le Inheritance 271
Com m on Program m ing Errors 279
Program m ing Exercises 285

6 Operator Overloading 289

6.1 Basic O perator Overloading 290


6.2 Sample Application: Bounds Checking 301
6.3 Sam ple Application: A n Associative A rra y 306
6.4 T y p e Conversions 311
6.5 Sam ple Application: File Subscripts 315
6.6 M em o ry M anagem ent Operators 320
Com m on Program m ing Errors 325
Program m ing Exercises 327

www.MathSchoolinternational.com
CONTENTS xv

7 The C + + Input/Output Class Hierarchy 333

7.1 Overview 334


7.2 The Class ios 340
7.3 The H igh-Level Input/O utput Classes 350
7.4 M anipulators 361
7.5 The File Input/O utput Classes 366
7.6 Sample Application: A R andom Access File
Class 373
7.7 The Character A rra y Input/O utput Classes 392
7.8 Sample Application: A H igh-Level C opy
Function 398
7.9 The Buffer Classes 401
Com m on Program m ing Errors 413
Program m ing Exercises 418

8 Advanced Topics 421

8.1 Exception H andling 422


8.2 R un -T im e T yp e Identification 427
8.3 Nam espaces 433
8.4 Other O bject-O riented Languages 435
8.5 N e w Issues 445

Appendix 449

A A S C I I Table 449
B Selected C + + Functions 453
C U N IX 481
D B orland C + + 495

Hints and Solutions to Odd-Num bered


Exercises 501

Index 539

www.MathSchoolinternational.com
I

www.MathSchoolinternational.com
L
Object-Oriented Programming in C + +

www.MathSchoolinternational.com
www.MathSchoolinternational.com
111
Chapter 1

Introduction

1.1 O bject-O riented Design


1.2 Classes and A bstract D a ta Types
1.3 Inheritance
1.4 Polym orphism

www.MathSchoolinternational.com
2 CHAPTER 1 IN TR O D U C TIO N

“Object-oriented” describes an emphasis on objects or data in a system.


Accordingly, object-oriented design is a technique that attends first and
foremost to objects or data in the design of a system, and object-oriented
program m ing is an approach that attends first and foremost to objects
or data in the programming of a system. In Section 1.1 we clarify these
concepts in more detail.
The C + + (pronounced “C plus plus” ) programming language is an ex­
tension of C that supports object-oriented programming. C + + also offers
improvements over C that are not specifically related to object-oriented pro­
gramming. Because the “+ + ” is C’s increment operator, the name “C + + ”
suggests that C + + is an incremental extension of C. Bjarne Stroustrup of
A T & T Bell Labs developed C + + in the early 1980s. The first commercial
version from Bell Labs appeared in 1985. Since then many vendors have
released C + + compilers. Because C + + is an extension of standard C, most
C programs can be compiled using a C + + compiler. (In Section 2.1, we dis­
cuss some incompatibilities between C and C + + .) In this chapter, we intro­
duce the key concepts (abstract data types, objects, classes, inheritance, and
polymorphism) and show how C + + supports object-oriented programming.
In the remainder of the book, we expand on these topics. Our emphasis
throughout is on the use of C + + to do object-oriented programming.

1.1 Object-Oriented Design


In this section, we briefly describe and illustrate object-oriented design, con­
trasting this technique with top-down design. For more on object-oriented
design, see J. Rumbaugh, et al., Object-Oriented Modeling and Design, (En­
glewood Cliffs, N.J.: Prentice Hall, 1991) or G. Booch, Object Oriented De­
sign with Applications, (Redwood City, Calif.: Benjamin/Cummings, 1991).
Among the touted benefits of object-oriented design are:
• Increased productivity.
• Enhanced reliability.
• Improved maintenance.
• Reusable code.
• Better performance of sports teams such as the Chicago Cubs.
• Shortened recessions.
The next-to-last item is a result of the Cubs’ using C + + to write some
of their software. The last item results from lower costs due to increased
productivity, improved reliability, and so on. With results such as these, it
is no wonder that the term “object-oriented” has become ubiquitous.
O bject-oriented design is characterized by
• Objects (data together with operations on the data)

www.MathSchoolinternational.com
1.1 OBJECT-ORIENTED DESIGN 3

circle line

Figure 1.1.1 Objects of the drawing system.

• Classes (abstract characterizations of objects)


• Inheritance (code sharing)
• Polymorphism (run-time binding of operations to objects)
We focus on objects in this section and leave classes, inheritance, and poly­
morphism to the remaining sections of this chapter.

Example 1.1.1. Object-oriented design emphasizes the objects or data.


Consider the object-oriented design of a system to draw figures requested
by a user on a graphics display terminal. The design of the system begins
with the objects—the figures to be drawn. After identifying the objects,
the design continues by identifying the operations to be performed on the
objects and associating these operations with the objects. In this case, the
objects become figures to be drawn (see Figure 1.1.1).
The objects exactly model the actual figures to be drawn; that is, we
have circle objects, line objects, and so on. Each object has an associated
operation called draw. The formal name for such an operation is a method.
If c is a circle object, we invoke its draw method by passing a message to
c:
c.draw{ )
Similarly, if I is a line object, we invoke its draw method by passing a message
to I:
l.drawQ
In object-oriented design the data are not static, lifeless entities. The data
are objects capable of sending messages to other objects and reacting to
messages received from other objects. A circle object, for example, might be
empowered to create an offspring circle object and then instruct its offspring
to draw itself in conformity with the parent’s instructions. Data as objects
are centers of message-passing and message-receiving activity. Computation
occurs by passing messages among objects. The messages are invocations
of methods that, in turn, are tailored to the object: a draw message sent
to, say, a circle object invokes the draw method for circles rather than the
draw method for lines. Indeed, an object’s methods are part of it. An
object’s methods are encapsulated in it. An object thus carries within it
the operations, defined as methods, that are suited to precisely that kind of
object. There is no separation of an object from the operations that may be
performed on it.

www.MathSchoolinternational.com
4 CHAPTER 1 IN TR O D U C TIO N

main

g e t-fig u re draw

/ \
p u t_p rom p t get_response :
/ w
: :
Figure 1.1.2 Top-down design.

To complete the object-oriented design of our drawing system, we would


identify suitable input/output objects with methods suitable for communi­
cating with the user and with the figure objects. For example, the user
might be given methods to select an object such as a circle, to request that
the object draw or erase itself, and so on. At the implementation stage, the
system would require appropriate data structures to represent the objects
and the appropriate routines to implement the methods. □

A prominent design paradigm that offers an alternative to object-oriented


design is top-down design. Object-oriented design emphasizes objects or
data, whereas top-down design emphasizes operations or functions. Top-
down design begins by specifying the first function to be invoked (the top
function). The top function is frequently called main, as in C or C + + . The
designer then refines main into its component functions, which may in turn
be refined into further functions, and so on (see Figure 1.1.2). After the
top-down design is complete, the code for the functions is written. Because
top-down design proceeds by refining functions, it is also known as top-
down, functional decomposition.

Example 1.1.2. Consider the top-down design of the drawing system of


Example 1.1.1. The main function would prompt the user for the figure to
draw and then invoke a function to draw the figure. Thus main would be
refined into functions such as get.figure and draw. A typical invocation of
the function draw is
draw( CIR )
where CIR is a flag to signal draw that a circle rather than some other figure
is to be drawn. The function get-figure would be refined into functions such
as putjprompt and get-response. The drawing function draw would similarly
be refined into functions that do the actual drawing. After all of the func­
tions are specified, the system would be completed by coding the functions.

The syntax of an object-oriented language emphasizes the central role of


objects, whereas the syntax of a traditional language emphasizes the oper­
ations (functions). In an object-oriented language, we pass a message to a
shape c to draw that shape:
c.drawQ

www.MathSchoolinternational.com
1.1 OBJECT- ORIENTED DESIGN 5

In a traditional language, we invoke the function draw and pass CIR as an


argument to it:
draw( CIR )
Object-oriented design can provide a cleaner separation of its compo­
nents than top-down design. In top-down design, modifying a function usu­
ally produces a ripple effect, and many other functions also then require
modification as well. For example, if we modify a function / in a system
created from a top-down design, we typically must also modify certain of fs
constituent functions, then modify their constituent functions, and so on.
Moreover, it is often difficult to identify precisely all of the functions that
need to be changed and exactly what changes need to be made to them.
If we want to modify the top-down version of the drawing system of
Example 1.1.2 by adding an additional figure, we would have to modify
almost all of the functions. We would certainly have to modify get-figure,
draw, put-prompt, get-response, and many of draw’s component functions.
We might also have to modify other less obvious functions as well. The
modular separation is clearer in the object-oriented version of the drawing
system (Example 1.1.1). If we add an additional figure, we could simply
add an object to model that figure and modify the object that handles the
input/output in fairly obvious ways. Moreover, as we will see later in this
chapter, object-oriented languages provide solid support through classes,
inheritance, and polymorphism for making these kinds of modifications.

Exercises

Exercises 1-4 refer to a system that responds to requests to provide all


films by a particular director or to provide all films featuring a particular
star.

1. What objects would you find useful in an object-oriented design of this


system?

2. What methods would you find useful in an object-oriented design of this


system?

3. How might a top-down design of this system proceed?

4. How would the object-oriented design of this system using the objects and
methods of Exercises 1 and 2 differ from the top-down design of Exercise
3?

5. Answer Exercises 1-4 for a system that lays out the pages in the chapters
of this book.

www.MathSchoolinternational.com
6 CHAPTER 1 IN TR O D U C TIO N

6. C + + , an object-oriented programming language, is an extension of C,


a traditional programming language, so C + + contains constructs that
support both object-oriented and traditional features. What advantages
are there in using a language that contains both object-oriented and tra­
ditional constructs? What disadvantages are there in using a language
that contains both object-oriented and traditional constructs?

7. How might object-oriented design improve the reliability of software?

8. Another approach to design is bottom-up design in which the most prim­


itive functions are specified first. The design continues by specifying
more complex functions in terms of those already specified until all of
the required functions have been laid out. Contrast this design method
with top-down design and object-oriented design. What advantages does
bottom-up design have over the other two paradigms? What disadvan­
tages does bottom-up design have over the other two paradigms?

1.2 Classes and Abstract Data Types


A class is an abstract characterization of a set of objects; all objects in this
set belong to a particular class. For example, in Section 1.1 we discussed
a set of objects that consisted of figures to be drawn. The figure class is
the class to which all of these objects belong. More formally, a class defines
variables (data) and methods (operations) common to a set of objects. We
can consider a class as a prototype or generator of a set of objects.
A well-designed class specifies an abstract data type. A data type is
abstract if the high-level operations appropriate to the data type are isolated
from the low-level implementation details associated with the data type.
Suppose, for example, that we design a circle class that makes a circle an
abstract data type. The class provides us with methods such as draw, move,
expand, contract, erase, and so on. We can use these methods to manipulate
circle objects in all the expected ways. The methods are all that we need to
know about the circle class. In particular, we do not need to know exactly
how a circle is represented. A circle’s underlying data structure might be an
array, a record structure, a cleverly designed bit-string, and so on. Yet these
details about a circle's internal representation can be ignored as we create
circles, enlarge them, move them, and the like. A circle as an abstract data
type lets us focus exclusively on operations (methods) appropriate to circles;
a circle as an abstract data type lets us ignore completely a circle's internal
representation.
The object-oriented environment deliberately hides implementation de­
tails about an object. This is known as inform ation hiding. What is
not hidden about an object is its public interface, which consists of the
messages that may be sent to the object. The messages represent high-level

www.MathSchoolinternational.com
CLASSES AN D A B STR A C T DATA TYPES 7

operations, such as drawing a circle. The term encapsulation is also used


to emphasize a different but related aspect of abstract data types. An ab­
stract data type combines methods (operations) and internal representation
(implementation). An object that is an instance of an abstract data type
thus encapsulates operations as well as representation. This encapsula­
tion contrasts with the traditional separation of operations (functions) and
representation (data).
A C + + class supports encapsulation. The C + + syntax for declaring a
class is adapted from the C syntax for declaring a structure. Yet a C + + class,
unlike a C structure, may combine methods (functions) and data members.

Exam ple 1.2.1. The following code declares a class string:

class strin g {
char v a l [ 80 ] ;
p u b lic:
void s to re ( char * ) ;
in t len gth ( ) ;
>;

The class s trin g consists of data (the array v a l) together with methods
store and length. Objects in the class strin g consist of the data (charac­
ters stored in the array v a l) together with operations on this data: store
to store a C string (a null-terminated array of char) in an object belonging
to the C + + class s trin g and length to compute the length of the string
represented by the s trin g object.
C + + likewise supports information hiding. The keyword public makes
the methods store and length accessible to the outside world. Yet because
v a l is not in the public section of the class string, it is p rivate by default
and therefore not accessible to the outside world. The class strin g isolates
the representation of a string, which happens to be stored in the array val,
from the methods store and length, which are publicly accessible. The
user need not be concerned with how the characters that make up the string
are represented. The user concentrates instead on the operations available
for manipulating objects in the class string. □

A class declaration such as that of Example 1.2.1 does not create any
objects and does not allocate storage. After declaring a class and writing
the code that implements the methods, we can define objects in that class
following the C syntax for defining variables. We defer the discussion of how
to implement methods to Chapter 3.

Exam ple 1.2.2. Given the class declaration of Example 1.2.1, the defini­
tion

strin g s, t ;

www.MathSchoolinternational.com
CHAPTER 1 IN TR O D U C TIO N

defines two objects s and t belonging to the class string. Storage is allo­
cated for each object; s now has an array named data of size 80 and t also
has a (distinct) array named data of size 80. □
0
C + + borrows C’s syntax for structures to reference members of a class.
Suppose that s is an object in a class with data member x and method f.
The member x is referenced by writing
s. x
and the method f is invoked by writing
s . f ( arguments )
If p tr is a pointer to s, x is referenced by writing
p tr -> x
and the method f is invoked by writing
p tr -> f ( arguments )

Exam ple 1.2.3. Given the class declaration of Example 1.2.1 and the
definitions of s and t as in Example 1.2.2, we could pass a message to object
s to store the C string "Hi Mom" in the object s by writing
s .s t o r e ( "Hi Mom" ) ;
To store the length of s in the variable len we could write
len = s .len g th O ;

Separating the operations (methods) from the implementation as in Ex­


ample 1.2.1 makes it easy to change the implementation without having to
change any of the code that accesses the objects.

Exam ple 1.2.4. Suppose that we replace the class declaration of Example
1.2.1 with
class strin g {
char data[ 80 ] ;
in t len;
p u b lic :
void s to re ( char * ) ;
in t length( ) ;
>;
We now represent a string by storing its characters without a null termina­
tor. To locate the end of the string we store the index of the last character
in the member len. We also reimplement the methods store and length to
reflect this change in representation. These changes are transparent to users

www.MathSchoolinternational.com
1.2 CLASSES AN D ABSTR A C T DATA TYPES 9

of this class. Code that accesses this class through its public members need
not be changed at all. For example, the code of Example 1.2.3 could use the
class of Example 1.2.1 or the new version of the class. □

C has a library of functions that support various operations on strings


(e.g., strlen, strca t). Nonetheless, a string is not a built-in data type in C.
The C programmer implements a string as an array of char terminated by
’ \0 ’ , a nonprinting character that is easy to forget or overlook, even though
the null terminator is essential for proper string handling. C thus extracts a
price for use of its powerful string-handling functions: the programmer must
attend to a low-level implementation detail about strings to use safely the
high-level functions in the string library. By using a C+-1- string class, the
programmer can ignore the low-level implementation details of strings and
simply master the high-level methods.
It is possible to create abstract data types in languages that are not
object-oriented. However, object-oriented languages provide constructs that
encourage abstract data types. Classes, which can be as rich and diverse as
the imagination allows, are a powerful and intuitive mechanism for creating
abstract data types because they directly support information hiding and
encapsulation. Inheritance, which we discuss in the next section, is a related
mechanism for creating new abstract data types from already existing ones.

Exercises

1. The string class of Example 1.2.1 has two methods. List some other
methods that one might expect to find in a string class.

2. In words, not in code, explain how to implement the methods store and
length in the string class of Example 1.2.1.

3. In words, not in code, explain how to implement the methods store and
length in the string class of Example 1.2.4. How would this implementa­
tion differ from the implementation of these methods in the string class
of Example 1.2.1?

4. Suggest a way different from those of Examples 1.2.1 and 1.2.4 of repre­
senting strings. Given this representation, how would the implementation
of the methods store and length differ from the implementation of these
methods in the string classes of Examples 1.2.1 and 1.2.4?

5. Write a declaration for a stack class that manipulates ints. In words,


not in code, explain how to implement the methods that push and pop
ints on and off the stack.

www.MathSchoolinternational.com
10 CH APTER 1 IN T R O D U C T IO N

6. Given the declaration of a stack class as in Exercise 5, write a line that


defines two stacks. Write a line that defines an array of 100 stacks. Write
a line that defines an array of 100 pointers to stack.

7. Given the declaration of a stack class as in Exercise 5, write a line that


pushes the value 10 onto the stack s. Write a line that pops a value from
the ninth stack in an array s_arr of 100 stacks.

1.3 Inheritance
In an object-oriented language, we can produce a new class by deriving the
new class from an already existing class. The mechanism for this derivation
is inheritance and the derived class is said to be inherited from the original
class. In CH—I- the new class is called the derived class and the original
class is called the base class. Following standard practice, we also call a
derived class a subclass of the original class, which in turn is a superclass
of the derived class. The derived class inherits all the data and methods from
the existing class, but the derived class may add additional data or methods
and, under certain circumstances, the derived class can redefine inherited
methods so that they behave differently than they do in the original class.
Deriving a class from a base class promotes code reusability and reusing
already correct code promotes code robustness.

Exam ple 1.3.1. The following declares a class pen:


class pen {
in t x;
in t y;
in t status;
p u b lic :
void set_statu s( in t ) ;
void set_lo ca tio n ( in t, in t ) ;
>;
Ifp is an object of type pen, the message
p .s e t_ lo c a tio n ( x, y ) ;
positions p at the location whose coordinates are x , y. The message
p .set_sta tu s( 1 ) ;
turns the ink in the pen on, and the message
p .set_sta tu s( 0 ) ;
turns the ink in the pen off.
Now suppose that our hardware is upgraded so that we have a colored
pen. Rather than declare a brand new class to describe the colored pen, we
can derive a colored pen class from the pen class:

www.MathSchoolinternational.com
1.3 INH ERITANCE 11

pen

x
base class
y
status
set_status
set__location

colored_pen

y from derived class

status ■ pen
set_status
set_location
color
set_color

Figure 1.3.1 Deriving one class from another.

class colored_pen : public pen {


in t color;
p u b lic:
void s e t_ c o lo r( in t ) ;
>;
The class colored_pen inherits all of the data and methods from the class
pen (see Figure 1.3.1). The declaration of class colored_pen adds the
data member color and the method set_color. Colors are coded as in­
tegers, so when the method set_co lo r is invoked and a value is passed to a
colored_pen object, the member color is set to this value.
The presence of the keyword public in the line

class colored_pen : public pen {

makes all of the base class’s public members public in the derived class.
(If the keyword public is omitted, the base class’s public members become
private in the derived class.) Thus the methods set_status, set_location ,
and set_color can be invoked on an object of type colored_pen anywhere
in the program.
We could put a public method move in the class pen that would move
the pen from its current position (given by members x , y) to a new location
supplied as an argument to move. If the ink were on, move would then draw
a line from the original location to the new location. In the derived class, we
would need to redefine move so that, if the ink were on, move would draw a
colored line from the original location to the new location. The color would
be specified by the member color in the class colored_pen. In Chapter 5,
we explain how to redefine methods in derived classes. □

www.MathSchoolinternational.com
12 CHAPTER 1 IN TR O D U C TIO N

container

set
X X list

t
sorted_set stack
/ \ queue

Figure 1.3.2 A class hierarchy.


Bi pe ds FeatherlessBeasts WarmBloodedAnimals PinkThings

t
H u ma n s 1
t
El ephants

WildElephants ZooEl ephants CircusElephants

Figure 1.3.3 A class hierarchy that uses multiple inheritance.

Using inheritance, we can build a class hierarchy. As Figure 1.3.2


illustrates, a class hierarchy is a collection of classes obtained through inher­
itance. The classes set and l i s t are derived from the base class container.
The class set serves as a base class for the derived class sorted_set, and the
class l i s t serves as base class for the derived classes stack and queue. Be­
sides any added members, the classes stack and queue have all of the mem­
bers of l i s t and container. In addition to any added members, sorted_set
has all of the members of container and set.
C + + also permits m ultiple inheritance in which a derived class has
multiple base classes.

Exam ple 1.3.2. Figure 1.3.3 shows a class hierarchy that uses multiple
inheritance. The following information is given:
• Bipeds, FeatherlessBeasts, WarmbloodedAnimals, and PinkThings
are classes. None is depicted here as derived from any other class.
• Humans is a class that is derived through multiple inheritance from
Bipeds, FeatherlessBeasts, and WarmbloodedAnimals.

• Elephants is a class that is derived from WarmbloodedAnimals. It


has three subclasses: WildElephants, ZooElephants, and CircusEl­
ephants.
• CircusElephants is also a subclass of PinkThings.

Inheritance may be used in two broad ways to create a new class from
already existing classes. Single inheritance may be used to specialize an
existing class, whereas multiple inheritance may be used to combine two
existing classes. We illustrate with two examples.

www.MathSchoolinternational.com
1.3 INH ERITANCE 13

Win

t
ScrollWin

Figure 1.3.4 Single inheritance as specialization.

Exam ple 1.3.3. The base class Win


/ / a window displayed on a video display
class Win { // base class

int x, y; // cartesian coordinates


int width, height; // dimensions

>;

// specializatio n of a Win — add s c r o ll bars


class ScrollWin : public Win { // derived class

>;
has attributes such as an x-coordinate, a y-coordinate, a width, and a
height. However, a Win does not have horizontal and vertical scroll bars.
The derived classScrollWin is a specialization of a Win in that a ScrollWin
has all the attributes or properties of a Win and, in addition, horizontal and
vertical scroll bars (see Figure 1.3.4). □

Exam ple 1.3.4. The base classes PopupMenu and ScrollWin


// a PopupMenu is a menu that pops up on
// the screen in response to some user action
// such as click ing a button
class PopupMenu : public Menu {

>;

// specialization of a Win — add s c r o ll bars


class ScrollWin : public Win { // derived class

>;
may be combined to create a ScrollPopupMenu
class ScrollPopupMenu :
public PopupMenu, public ScrollWin {

>;

www.MathSchoolinternational.com
14 CHAPTER 1 IN TR O D U C TIO N

Menu Win

t
PopupMenu ScrollWin
t
ScrollPopupMenu

Figure 1.3.5 Combining classes using multiple inheritance.

that shares the attributes of a PopupMenu and a ScrollWin. Note that, in


this example, PopupMenu and ScrollWin are both specializations through
single inheritance of base classes Menu and Win, respectively (see Figure
1.3.5). □

Classes directly support the creation of abstract data types. Inheri­


tance extends that support by promoting the derivation of new abstract data
types from already existing ones. Object-oriented languages thus provide us
with the tools for programming with abstract data types. By creating ab­
stract data types, we programmers extend the concept of function libraries
to encompass data type libraries that allow the user to focus on high-level
operations and to ignore low-level implementation details. Many graphics
packages, such as Windows and Motif, illustrate the point. In Motif, for
example, there is an extended class hierarchy in which each class represents
an abstract data type such as various flavors of windows, fonts, geometri­
cal drawings, and so on. The user knows only the public interface to such
classes, where such an interface comprises the messages that may be used
to create, manipulate, and destroy instances of an abstract data type. Im­
plementation details, typically consisting of C structures and pointers, are
hidden from the user, who is all the better off by being spared the very
details that cause programming tedium and error. In effect, a package such
as M otif is a library of abstract data types presented as an object-oriented
graphics toolkit.

Exercises

1. Declare a class book with data members t i t l e , author, and id_number,


and methods appropriate for storing information in these members.

2. Derive a class book_loan that includes the members and methods de­
clared in Exercise 1, but also adds members and methods to keep track
of whether someone has borrowed the book and, if so, who.

3. Declare a class person with data members name and address. In words,
not in code, explain how you would use this class and the class book of

www.MathSchoolinternational.com
1.4 PO LYM O RPH ISM 15

shape

c ir c le lin e

Figure 1.4.1 A class shape with subclasses c ir c le and lin e.

Exercise 1 along with multiple inheritance to derive a class that keeps


track of whether someone has borrowed a book and, if so, who.

4. Show a class hierarchy with classes that might be used to track customers
and accounts at a bank.

5. What advantages are there to using multiple inheritance? What disad­


vantages are there to using multiple inheritance?

1.4 Polymorphism
The word poly comes from a Greek word meaning many, and morphism
comes from a Greek word meaning form; thus, polym orphism means many
forms. In object-oriented programming, polymorphism refers to identically-
named methods that have different behavior depending on the type of object
that they reference. For example, the classes pen and colored_pen of Sec­
tion 1.3 might each have a method move with parameters x and y of type in t
that determines where the object moves. If the ink is on, the method move
in class pen would draw a black line from its current position to the position
with coordinates x,y, whereas the method move in class colored_pen would
draw the line in a previously set color. When the method move is invoked,
polymorphism assures that the appropriate operation occurs by determin­
ing at run time whether a pen or a colored_pen is the object in question.
Polymorphism is useful in providing a common abstract interface to users of
a class hierarchy.
The following C + + example shows why it is sometimes useful to delay
until run time the decision about which object is being referenced.

Exam ple 1.4.1. Suppose that we have a class shape with subclasses c ir c le
and lin e (see Figure 1.4.1). Suppose further that shape has a method draw
that has been redefined in both subclasses so that when c ir c le ’s draw is
invoked, a circle is drawn, and when lin e ’s draw is invoked, a line is drawn.
Suppose further that composite_f ig is an array of 100 pointers to objects
of type shape:
shape* com posite_fig[ 100 ] ;
One of the rules in C + + is that a pointer of type “pointer to base class”
can, without casting, point to any object in a derived class. Thus we assume
that each cell in composite_f ig contains the address of either a c ir c le

www.MathSchoolinternational.com
16 CHAPTER 1 IN TR O D U C TIO N

object or a lin e object. If we then step through the array and first draw the
figure to which composite_f i g [ 0 ] points, then draw the figure to which
composite_f ig [ 1 ] points, and so on, we draw the composite figure. Using
polymorphism, the following loop carries out this task
fo r ( i = 0; i < 100; i++ )
com posite_fig[ i ] -> drawO;
When this loop executes, the system determines what type of object is at ad­
dress com posite_fig[ i ]. If the object is of type c ir c le , c ir c le ’s draw
is invoked; if the object is of type lin e, lin e ’s draw is invoked. Note that
we can reload the array composite_f ig with addresses of different objects,
even objects from other classes derived from shape, and draw the new com­
posite figure using exactly the same loop. □

Object-oriented programming derives its power and elegance from the


interaction of classes, objects, inheritance, and polymorphism. Classes fur­
nish an abstract description of objects, which tie data and methods together
with appropriate encapsulation; inheritance provides a way to derive new
classes from old; and polymorphism binds methods to objects at run time.

Exercises

1. Suppose that the base class book has derived classes manual and text.
Define an array book_array of 50 pointers to book. Suppose that book
has a method p rin t that is redefined in manual and text, and suppose
that book_array has been initialized with the addresses of 50 objects of
type manual or text. Write code that will print the books pointed to in
the array book_array.

2. Give an example different from Example 1.4.1 which illustrates the use­
fulness of polymorphism.

3. The loop in Example 1.4.1 uses polymorphism to draw a composite figure.


How might a similar effect be achieved in C?

www.MathSchoolinternational.com
Chapter 2

Basic C-f

2.1 Some Miscellaneous Extensions in C + +


2.2 Functions
2.3 C + + Operators
2.4 Introduction to C + + Input/O utput
Com m on Program m ing Errors
Program m ing Exercises

17

www.MathSchoolinternational.com
18 CH APTER 2 BASIC C + +

Because C + + contains standard (ANSI) C as a subset, most standard C


programs are also C + + programs. In this chapter, we discuss some dif­
ferences between C and C + + ; we introduce some extensions of C found in
C + + ; and we introduce the C + + input/output class library. Some exten­
sions discussed here are not directly related to object-oriented programming
but rather remedy deficiencies in C or make the language more flexible.

2.1 Some Miscellaneous Extensions in C + +


In this section, we list some small differences between C and C + + .

Com m ents

Two slashes denote the beginning of a comment, which extends to the end
of the line. The C-style comment /* comment */ also can be used.

Exam ple 2.1.1. The following code illustrates the new style of comment:
// fin d largest
fo r ( i = 1, max = a [ 0 ] ; i < end; i++ )
i f ( a [ i ] > max ) // found la rg e r, so update
max = a [ i ] ;

Casts

C + + adds an alternative notation for C casts. In the code


average = ( flo a t ) h its / ( flo a t ) at_bats;

the values of the in t variables h its and at_bats are converted to flo a t by
using the cast operator before division. In C + + , this could be written
average = f l o a t ( h its ) / f l o a t ( at_bats );

Enum erated Types

In C, we declare^ an enumerated type such as


enum m arital_status { sin gle, married } ;
and then define variables of type enum m artial_type as, for example,
/ ***** C syntax *****/
enum m arital_status personl, person2;

*We use the term “define” to refer to a statement that allocates storage for a variable or to refer
to the header and body of a function. We use the term “declare” to refer to the description of a data
type.

www.MathSchoolinternational.com
2.1 SOME MISCELLANEOUS EXTENSIONS IN C + + 19

In C + + , by contrast, m arital_status without the keyword enum is a type.


Accordingly, we can define variables as in this code slice:
m arital_status personl, person2;

Structures

In C ++, a structure is a class in which the default status of each mem­


ber is public. (This default status can be changed by, for example, using
the keyword private.) Because a structure is a class, it can have member
functions.

Example 2.1.2. The following declares a structure string:


struct strin g {
char data[ 80 ] ;
void s to re ( char* ) ;
>;
By default, the members data and store are public. In C + + , in contrast
to C, strin g without the keyword struct is a type. Accordingly, we can
define variables as in this code slice:
strin g s t r l , str2;

Anonym ous Unions

C + + allows anonymous unions, that is, unions without tags.

Exam ple 2.1.3. The union


union {
in t i ;
flo a t x;
>;
is anonymous. The members can be used as ordinary variables:
i = 10;
x = -3827.34;
Within the same scope, no other variables with the same names as an anony­
mous union’s members are allowed, and no variables whose type is that of
an anonymous union can be defined. □

const

The type qualifier const first appeared in C + + but was subsequently added
to standard C. We illustrate some of its uses.

www.MathSchoolinternational.com
CHAPTER 2 BASIC C + +

The keyword const, which stands for constant, allows the programmer
to designate storage whose contents or value cannot be changed.

Exam ple 2.1.4. The code


const int size = 30;
defines a variable size, which is initialized to 30. It is an error to attempt
to change the value of size. □

We could use the #define preprocessor directive to define a constant:


#define size 30

The advantage of a const variable over the #define directive is that the
variable becomes a bona fide part of the pfogram and, as such, can be
referenced by name by the debugger, has storage allocated for it, has a
type, and so on. On the other hand, a #define directive is handled by the
preprocessor prior to compilation; consequently, the macro name cannot be
referenced by the debugger, has no storage allocated for it, and has no type.
The type qualifier const applies to the type. For example, in the defini­
tion
char s [ ] = "Hi Mom";
const char* p tr = s;
const applies to char* so it is not legal to change what p tr points to, but
it is legal to change p tr’s value. For example,
p t r [ 1 ] = ’ o ’ ; // * * * * * ERROR: can’t change "Hi Mom"
is an error but
p tr = "Hi Dad"; // * * * * * OK: can change ptr
is legal.
To define a pointer constant (a pointer whose value cannot be changed),
we would write
char s [ ] = "Hi Mom";
char* const ptr = s;
Now it is legal to change what p tr points to, but it is an error to change the
value of ptr itself. For example,
p t r [ 1 ] = ’ o ’ ; // * * * * * OK: can change what p tr points to
is legal but
ptr = "Hi Dad"; // * * * * * ERROR: can’t change ptr
is an error.
The following example shows how to use const parameters.

Exam ple 2.1.5. The function

www.MathSchoolinternational.com
2.1 SOME M ISCELLANEOUS EXTENSIONS IN C + + 21

in t search( const in t* a, const in t len , const in t key )


{
in t i ;

fo r ( i = 0; i < len; i++ )


i f ( a [ i ] == key )
return i ; // found
return -1; // not found
}
searches the array a for the value key. The parameter declaration
const in t* a
states the search will not change any of the values in the array a. The
parameter declaration
const in t len
states that search will not change len ’s value; that is, after len is initialized
to the value of the argument passed, search will not change this value.
Similarly, the parameter declaration
const in t key
states that search will not change key’s value. □

The type qualifier const serves as a recommendation to the compiler


the variable be optimized. This type qualifier assures the compiler that a
variable or parameter will not have its value altered. The compiler then may
act on the recommendation by allocating storage in a specific partition (e.g.,
read-only memory) of the system’s hierarchical storage system.

D efining Variables

In a C function, all variable definitions must occur at the beginning of a


block. In CH—h, variable definitions may occur at the point at which they
are first used.

Exam ple 2.1.6. The code


void reverse_and_print( in t a [ ] , s ize )
{
// f i l l the array
fo r ( in t i = 0; i < s iz e ; i++ )
a[ i ] = 2 * i;

// reverse the data in the array


in t temp;
f o r ( i = 0 ; i < s ize / 2; i++ ) {.
temp = a [ i ] ;

www.MathSchoolinternational.com
22 CHAPTER 2 BASIC C + +

at i ] = a [ s ize - 1 - i ] ;
a [ size - 1 - i ] = temp;
>

// prin t the array


fo r ( i = 0; i < s ize ; i++ )
p rin tf ( "'/.d\n", a [ i ] ) ;
>
shows how one can define variables within the block that delimits the body
of the function reverse_and_print.
The variable i is defined within the first fo r loop:
fo r ( in t i = 0; i < s iz e ; i++ ) {
The scope of i extends from its definition in the first fo r loop to the end of
reverse_and_print.
The variable temp is defined just before it is needed in the second fo r
loop:
in t temp;
fo r ( i = 0; i < s ize / 2; i++ ) {

Exercises

1. Given the declaration

enum good_jobs { tin k er, t a ilo r , s o ld ie r, spy } ;

define two variables of this enumerated type.

2. Declare a strin g structure with a private member val, a char array of


size 80, and public methods store, with one parameter of type char*
that returns no value, and length, with no parameters that returns an
int.

3. Define two variables of the type declared in Exercise 2.

In Exercises 4-9, determine whether the code is correct. If there are


errors, tell what the problems are.

4. in t a [ ] = { 2, 4, 6 } ;
in t i ;
const in t* p = a;
p = &i;

www.MathSchoolinternational.com
2.2 FUNCTIONS 23

5. in t a [ ] = { 2 , 4, 6 > ;
const in t* p = a;
p [ 1 ] = 12;

6. in t a [ ] = { 2, 4, 6 >;
in t* const p = a;
pC 1 ] = 12;

7. in t a [ ] = { 2 , 4, 6 } ;
in t i ;
in t* const p = a;
p = &i;

8. char* s = "Hi Mom";


const char* p = s;
p[ 1 ] = ’ o’ ;

9. char* s = "Hi Mom";


const char* p = s;
p = "Hi Dad";

10. Write a function reverse that reverses an array of floats. The argu­
ments are a, a pointer to the first flo a t, and size, the number of elements
in the array. Declare appropriate parameters as const.

11. Write a complete program to read up to 100 flo a ts from the standard
input, compute the average of the numbers read, and then print each
number and its absolute difference from the average. Use const to define
any constants that are needed. Define variables near the point at which
they are first used. Use //-style comments.

2.2 Functions
In this section, we highlight some of the ways that C + + has extended C
functions.

Prototypes

Function prototype refers to the style of declaring functions and writing


function headers in which the data types are included in the parentheses. In
C + + , prototypes are required and every function must be declared prior to
being used. Standard C borrowed the function prototype syntax from C + + .
One difference between standard C and C + + is that, in standard C, function
prototypes are optional. In C + + , the function prototype is mandatory.

Exam ple 2.2.1. The function

www.MathSchoolinternational.com
24 CHAPTER 2 BASIC C + +

char grade( const int examl,


const int exam2,
const flo a t examl_weight )

>
is written in prototype form.
If grade is invoked in a function assign and the definition of grade
appears before the definition of assign, grade’s definition can also serve as
its declaration:
// d efin itio n of grade
char grade( const int examl,
const int exam2,
const flo a t examl_weight )

>

void assign( char* cl )

// OK since grade’ s d efin itio n also serves


// as a declaration
c = grade( 9, 7, 0.6 ) ;

>
If grade is defined after assign or in a different file, grade must be
declared in prototype form either in or before assign:
// declaration of grade
char grade( const int examl,
const int exam2,
const flo a t examl_weight ) ;

void assign( char* cl )

c = grade( 9, 7, 0.6 ) ;

>

www.MathSchoolinternational.com
2.2 FUNCTIO NS 25

// d e fin itio n of grade


char grade( const in t examl,
const in t exam2,
const flo a t examl_weight )

>

The identifiers in a function declaration (examl, exam2, examl_weight,


in grade’s declaration) are optional, but they are often included to help the
user understand the meaning of the parameters. □

The compiler can use prototypes to check for matches between argu­
ments and parameters of functions and issue appropriate warning and error
messages if it detects problems. The compiler can also use prototypes to
convert one type to another, if possible, when the types do not match. For
example, if the prototype declaration specified a flo a t parameter, but the
function was called with an in t argument, the compiler would convert the
in t argument to a flo a t.
Subject to certain restrictions to be discussed in the Overloading Func­
tions subsection, C + + , unlike C, allows distinct functions with the same
name. When a function f is invoked and there is more than one function
named f, C + + determines which f to invoke by checking for a match be­
tween the types of arguments supplied and the types of parameters specified
in the prototype declarations of the various f ’s.
In C + + , empty parentheses in a function prototype are interpreted as
specifying no parameters. For example,
in t p r in t( ) ;
specifies that the function p rin t has no parameters and returns an int. This
is quite different from the meaning in standard C, where the specification
is that no information is being supplied about p rin t’s parameters and no
checking of arguments and parameters is to be done when p rin t is called.
Thus in C + + there is really no “empty” parameter list. The declaration
in t p r in t( ) ;
is equivalent to
in t p r in t( void ) ;

The main Function

Every program must contain a function called main where execution of the
program begins. Since the system does not declare a prototype for main,
its type is implementation dependent. However, C + + guarantees that every
implementation support either

www.MathSchoolinternational.com
26 CHAPTER 2 BASIC C + +

in t mainO
{

>
or
in t main( in t argc, char* argv[ ] )

>
As in C, in the latter form, argc is the number of arguments passed to the
program, and argv[ 0 ] through argv[ axgc - 1 ] are the addresses of
the passed arguments. Other definitions of main such as
void mainO
{

>
may not be supported by a particular implementation.
The return statement
return status;
in main terminates the main function and thus the program as a whole and
returns the value status to the invoking process. The status values

EXIT_SUCCESS EXIT_FAILURE

defined in stdlib.h are used to signal normal and abnormal termination, re­
spectively. (Other status values may be available in a particular implemen­
tation.)

References

A reference, signaled by the ampersand &, provides an alternative name


for storage.

Exam ple 2.2.2. The code


in t x;
int& r e f = x;
creates one in t cell with two names, x and re f. For example, either
x = 3;
or
r e f = 3;

www.MathSchoolinternational.com
2.2 FUNCTIONS 27

stores the value 3 in the in t cell. □

References operate somewhat like pointers except that no dereferencing


is required. For example, we could obtain the effect of the code in Example
2.2.2 by replacing r e f with a pointer. In this case, to store a value in the
in t cell using the pointer, a dereference would be required.

Example 2.2.3. If we write


in t x;
in t* in t_ptr = &x;
two cells are created— one to store an in t and one to store a pointer to
int. The int cell can be accessed through its name x or through the pointer
int_ptr. Either
x = 3;
or
*in t_p tr = 3;
stores the value 3 in the in t cell. □

As we will see in the next subsections, references are particularly useful


in passing arguments to and returning values from functions.

Pass by Reference

If we designate parameters as reference parameters using the ampersand &,


we obtain pass by reference in which an argument is passed toa function
by passing its address. The default parameter passing convention in C + + ,
like C, is pass by value.

Example 2.2.4. In the program


#include <stdio.h>
#include <stdlib.h >

void swap( int&, int& );

int main()

int i = 7, j = -3;

swap( i , j ) ;
p rin tf ( " i = °/,d, j = %d\n", i , j );

return EXIT_SUCCESS;
>

www.MathSchoolinternational.com
28 CHAPTER 2 BASIC C + +

i ______ J

a t>

Figure 2.2.1 Passing arguments by reference.

void swap( int& a, intfe b )

in t t ;

t = a;
a = b;
b = t;
>
the prototype
void swap( intfe, intfe ) ;
specifies that the arguments to swap axe passed by reference. Thus when
the arguments i and j in main are passed to swap
swap( i , j ) ;
swap receives the addresses of i and j . The ampersands in swap’s header
void swap( intfe a, intfe b )
signal that no dereferencing of the parameters a and b is required in swap’s
body. The names a and b in swap’s body refer directly to the storage in
main named i and j (see Figure 2.2.1). The function swap works not with
copies of i and j but directly with i and j . The output of the program is
i = -3, j = 7

In C, we can obtain the effect of pass by reference by explicitly passing


a pointer. A C version of the program of Example 2.2.4 follows.

Exam ple 2.2.5. The program of Example 2.2.4 could be written in C in


the following way:
#include <stdio.h>
#include <stdlib.h>

void swap_C_version( in t* , in t* ) ;

www.MathSchoolinternational.com
2.2 FUNCTIONS 29

in t main()
{

in t i = 7, j = -3;

swap_C_version( &i, &j ) ;


p r in tf ( " i = '/.d, j = */.d\n", i , j ) ;

return EXIT.SUCCESS;
>

void swap_C_version( in t* a, in t* b )

in t t ;

t = *a;
* a = *b;
*b = t ;
>

R etu rn by Reference

The following example illustrates how a function can return a value by ref­
erence.

Example 2.2.6. The following function can be used by programmers who


like arrays that begin at 1. The function takes an index i into an in t array
a, with 1 as the index of the first cell,translates it into a C + + index that
begins at 0 (by subtracting 1), andreturnsby reference thevalue in the
array:
intfe new_index( const in t a [ ] , const in t i )

return a [ i - 1 ] ;
>
The return designation int& signals that the function is returning an in t
by reference. In effect, the address of a [ i - 1 ] is returned, but the user
need not use the dereference operator. The function new_index could be
invoked as
va l = new_index( a, 8 ) ;

www.MathSchoolinternational.com
30 CHAPTER 2 BASIC C + +

One advantage of using return by reference is that a function that returns


a value by reference can be used on the left side of an assignment statement.
For example, the following is a legal invocation of the function new_index
of Example 2.2.6:
new_index( a, 8 ) = -16;
In this case, the value —16 would be stored in the eighth cell of a.

Inline Functions

The keyword in lin e can be used to request that a function be expanded


“inline” , that is, that each occurrence of a call of the function be replaced
with the code that implements the function. The compiler, for various rea­
sons, may not be able to honor the request. The situation is analogous to
a macro expansion. When the preprocessor expands a macro, it replaces
each occurrence of the macro with the macro’s definition. When a macro is
expanded or when a function is expanded inline and the program is run, the
overhead of a function call is avoided so that the program may execute more
efficiently. A disadvantage of using macros or inline functions is that if the
expansions are large or there are many expansions, the size of the executable
image can become quite large.
Unlike a macro that is expanded by the preprocessor, an inline function is
expanded (i.e., translated) by the compiler. When the preprocessor expands
a macro, it simply does text substitution without regard to the semantics of
the code. On the other hand, when the compiler expands an inline function,
it takes into account the semantics. For this reason, inline functions are
generally preferable to macros.
An inline function is visible only from the point at which it is declared
to the end of the file.
Example 2.2.7 shows how the program of Example 2.2.4 would be written
with swap changed to an inline function.

Exam ple 2.2.7. Consider the program


#include <stdio.h>
#include <stdlib.h>

in lin e void swap( int& a, int& b )


{
in t t ;

t = a;
a = b;
b = t;
}

www.MathSchoolinternational.com
2.2 FUNCTIONS 31

in t mainO
{
in t i = 7, j = -3;

swap( i , j ) ;
p r in t f( 11i = */,d, j = '/.d\n", i , j ) ;

return EXIT_SUCCESS;
>
Assuming that the compiler honors the request to expand swap inline, no
function call occurs at the line
swap( i , j ) ;
Because swap is an inline function, the compiler replaces the line
swap( i , j ) ;
with the code that implements swap. □

D efault Argum ents

C + + allows the programmer to specify default values for function parame­


ters. If arguments are missing in the invocation of the function, the default
values are used.

Exam ple 2.2.8. The function


void f ( in t v a l ,
flo a t s = 12.6,
char t = ’ \n’ ,
char* msg = "Error" )
{

>
has default values for the parameters s, t, and msg but no default value for
the parameter val.
Legal invocations of f are
f( 14, 48.3, ’ \ t \ "OK" ) ;
f( 14, 48.3, ’ \t> ) ;
f( 14, 48.3 ) ;
f( 14 ) ;
In the invocation
f ( 14, 48.3, ’ \ t ’ , "OK" ) ;
arguments are supplied for all of the parameters. Therefore, the initial values
of the parameters are

www.MathSchoolinternational.com
CHAPTER 2 BASIC C + +

val = 1 4 , s = 48.3 t = ’ Y t’ , msg = "OK"


In the invocation
f ( 14, 48.3, ’ \t> ) ;
no argument is supplied for the parameter msg. Therefore, the default value
"Error" is used. The initial values of the parameters are
v a l = 14, s = 48.3 t = '\ t ’ , msg = "Error"
Similarly in the invocation
f ( 14, 48.3 ) ;
the initial values of the parameters are
v a l = 14, s = 48.3 t = ’ \n’ , msg = "Error"
In the invocation
f ( 14 ) ;
the initial values of the parameters are
val = 14, s = 12.6 t = ’ \n’ , msg = "Error"
It is an error to invoke f as
f();
because no default value is supplied for the first parameter. □

Example 2.2.8 shows that it is legal to supply default values for some
parameters but not for others. However, all the parameters without default
values must come first in the parameter list and then be followed by all the
parameters with default values.

Example 2.2.9. The header of the function


// * * * * * ERROR: I lle g a l mix of default
// and nondefault values ***
void g ( int val = 0,
flo a t s,
char t = ’ \n’ ,
char* msg = "Error" )

}
is illegal because the parameter val with a default value is followed by the
parameter s without a default value. □

www.MathSchoolinternational.com
2.2 FUNCTIONS 33

Overloading Functions

C + + permits identically named functions within the same scope if they can
be distinguished by number and type of parameters. If there are multiple
definitions of a function f , f is said to be overloaded. The compiler deter­
mines which version of an overloaded function to invoke by choosing from
among the identically named functions the one function whose parameters
best match the arguments supplied.^

Example 2.2.10. The program


#include <stdio.h>
#include <stdlib.h>

void p rin t( int ) ;


void p rin t( char* ) ;

in t mainO
{
p r in t( 7 ) ;
p r in t( "Yo. L e t ’ s rap" ) ;

return EXIT_SUCCESS;
>

void p r in t( in t i )
{
p rin tf ( "7,d\n" , i ) ;
}

void p r in t( char* s )
{
p rin tf ( M‘/,s\n", s ) ;
>
contains two distinct functions named print. When the line
p r in t( 7 ) ;
is executed, the function
void p r in t( in t i )

p r in tf ( "*/,d\n", i ) ;
}
1The precise rules for determining which function is the “best match” are complicated (see, e.g.,
Working Paper fo r Draft Proposed International Standard fo r Information Systems—Programming
Language C + + ); however, an exact match is always the best match.

www.MathSchoolinternational.com
34 CHAPTER 2 BASIC C + +

is invoked because the argument is of type int. However, when the line
p r in t ( "Yo. L et 's rap" ) ;
is executed, the function
void p r in t( char* s )

p r i n t f ( "’/,s\n", s ) ;
}
is invoked because the argument is of type char*. □

Overloaded functions are used to give a common name to similar behavior


on different data types. In Example 2.2.10, “print” is a common name for
similar behavior on different data types. From the point of view of the user,
there is a single function p rin t that prints different data types.

O verloading O perators

When there are multiple definitions of an operator such as + or -, the opera­


tor is overloaded. For example, the operator + is overloaded by the system
because + can refer to any numeric type. The compiler determines which
version of an overloaded operator to invoke by checking the types of the ar­
guments. For example, if i and j are ints, the system executes instructions
appropriate for adding ints in evaluating i + j. If x and y are doubles,
however, the system executes instructions appropriate for adding doubles
in evaluating x + y. We now sketch restrictions on user-defined overloads
of C + + operators.
Any operator except

.* : : ?: s ize o f

may be overloaded by the programmer. Some operators, such as the sub­


script operator [ ] and the function call operator ( ) must be overloaded
as methods. Other operators, such as + and -, either must be overloaded
as methods or have at least one class object as an argument. These restric­
tions underscore the point that C + + operators typically are overloaded in
the course of creating classes and their methods. Indeed, the only opera­
tors that may be overloaded without any reference to a class are the memory
management operators new, delete, and d elete [ ] . For this reason, we de­
fer further discussion of operator overloading until we have examined classes
in more detail. Chapter 6 is devoted entirely to operator overloading.

Exercises

1. Write a declaration of the function whose header is

www.MathSchoolinternational.com
2.2 FUNCTIO NS 35

void move_arm( int arm_flag, flo a t d ir , flo a t d ist )

2. Find the errors and provide corrections for the following code:

#include <stdio.h>

const int size = 10;

mainO
{
int a [ size ] ;

fo r ( int i = 0; i < size; i++ )


scanf( "’/.d", &a[ i ] ) ;

reverse( a ) ;

fo r ( i = 0; i < size; i++ )


p r in t f ( "7,d\n", a [ i ] ) ;

return EXIT_SUCCESS;
}

void reverse( const in t* a )


{
int temp;

fo r ( int i = 0; i < size / 2; i++ ) {


temp = a[ i ];
a[ i ] =a [ size - 1 - i ] ;
a [ size - 1 - i ] = temp;
>
>

Which of the definitions in Exercises 3-7 are guaranteed to be portable?

3. int mainO
{

>

4. main( int argc, char* argv[ ] , char* envp[ ] )

>

www.MathSchoolinternational.com
36 CHAPTER 2 BASIC C + +

5. mainO
{

>

6. void mainO

>

7. in t main( void )
■c

>

8. Given the declaration

struct strin g {
char s [ 80 ] ;
>;

write a function upper that is passed a strin g argument and converts


the lowercase letters in s to uppercase. Characters that are not lowercase
letters are unchanged. Pass the argument by reference.

9. What is the output?

#include <stdio.h>
#include <stdlib.h>

struct point {
in t x, y;
>;

void move( point q )

q.x— ;
q-y++;
>

in t mainO

point p;

p.x = 5;
p . y = "12;

www.MathSchoolinternational.com
2.2 FUNCTIONS 37

move( p ) ;
p rin tf ( "x = */.d, y = ‘/(d\n", p .x, p.y ) ;

return EXIT_SUCCESS;
>

10. What is the output?

#include <stdio.h>
#include <stdlib.h >

struct point {
int x, y;
>;

int move( pointft q )


{
q .x — ;
q-y++;
>

int mainO
{
point p;

p.x = 5;
p.y = -12;
move( p ) ;
p rin tf ( "x = ’/.d, y = ‘/.d\n", p .x , p.y ) ;

return EXIT_SUCCESS;
>

11. What is the output?

#include <stdio.h>
#include <stdlib.h >

struct point {
int * x , * y ;
>;

void move( point q )


{
— *q.x;

www.MathSchoolinternational.com
CHAPTER 2 BASIC C + +

+ + *q .y ;

>

int mainO
■c
point p;
int a = 5, b = -12;

p.x = &a;
p.y = &b;
move( p ) ;
p rin tf ( "x = '/,d, y = 7,d\n", *p .x, *p.y ) ;

return EXIT.SUCCESS;
>

What is the output?

#include <stdio.h>
#include <stdlib.h>

struct point {
int *x, *y;
>;

void move( pointft q )

*q.x;
++*q .y ;
>

int mainO

point p;
int a = 5, b = -12;

p.x = &a;
p.y = &b;
move( p ) ;
p r in t f ( "x = %d, y = %d\n", *p .x, *p.y ) ;

return EXIT.SUCCESS;
>

www.MathSchoolinternational.com
FUNCTIO NS 39

13. Write a version of the function of Example 2.2.6 in C. How would the
lines

val = new_index( a, 8 );
new_index( a, 8 ) = -16;

have to be rewritten?

14. Show examples of legal invocations with different numbers of arguments


of the function whose header is

getstr( char* buff, int size = 1, char term = ’\n’ )

15. Write a version of prin t (see Example 2.2.10) that receives a s trin g
(declared in Exercise 8) and prints the member s. Pass the argument by
reference.

16. What is the error?

#include <stdio.h>
#include <stdlib.h>

void print( int count, int i = 0 )


{
for ( int j = 0; j < count; j++ )
printf ( "*/.d\n" , i ) ;
>

void print( int count, char* s = "" )


{
for ( int j = 0; j < count; j++ )
printf ( "'/,s\n", s );
>

int mainO
{
print( 10 );

return EXIT_SUCCESS;
>

17. Find and correct the error:

void print( const FILE* fp, int i )


{
fprintf( fp, "%d\n", i );
>

www.MathSchoolinternational.com
40 CHAPTER 2 BASIC C + +

Description Operator
Scope resolution II
Value construction type ()
Storage allocation new
Storage release (single cell) d elete
Storage release (vector) d e le te [ ]
Member object selector .*
Member pointer selector ->*
Throw exception throw

Figure 2.3.1 New operators in C + + .

2.3CH—|- Operators
C + + has extended C by adding the operators shown in Figure 2.3.1. In
Figure 2.3.2, we list the C + + operators, including those from C, together
with their precedence and associativity rules. We discuss each of the new
operators in turn.

The Scope Resolution O perator

The scope resolution operator :: may be used in two ways:


: :x External scope
c l : :m Class scope
The first form : : x is used to refer to the extern variable x when this variable
would ordinarily be inaccessible because of a name conflict with a variable
having local or class scope. The following example illustrates.

Example 2.3.1. In the code


flo a t x; // extern x

void f ( in t n )

flo a t x; // lo c a l (auto) x

x = 1.5; // f ’ s x
::x = 2.5; // extern x

}
the extern x would ordinarily be inaccessible within the function f because
f defines an identically named variable. The scope resolution operator makes
it possible to refer to the extern x within f . □

www.MathSchoolinternational.com
2.3 C + + OPERATORS 41

Description Operator Associates from the Precedence


Scope resolution left High
Function call 0 left (Evaluated first)
Value construction type ( ) left
Array subscript [ ] left
Class indirection -> left
Class member left
Size in bytes s iz e o f right
Incr/decr ++ — right
One’s complement right
Unary not ! right
Address & right
Dereference * right
Cast ( type ) right
Unary plus + right
Unary minus - right
Storage allocation new right
Free storage (single cell) d e le t e right
Free storage (array) d e le t e [ ] right
Member object selector .* left
Member pointer selector “ >* left
Multiplication * left
Division / left
Modulus 7. left
Addition + left
Subtraction - left
Left shift « left
Right shift » left
Less than < left
Less than or equal <= left
Greater than > left
Greater than or equal >= left
Equal == left
Not equal ;= left
Bitwise and & left
Bitwise exclusive or left
Bitwise inclusive or 1 left
Logical and && left
Logical or 11 left
Conditional •? : right
Assignment = */.= += -= right
— V
II V
A
A
I II
I II

II

II
PP *
>\

Throw exception throw right (Evaluated last)


Comma i left Low

Figure 2.3.2 Precedence of C + + operators (operators between horizontal lines have


the same precedence).

www.MathSchoolinternational.com
42 CHAPTER 2 BASIC C + +

The second form c l: :m, which is used throughout the remainder of the
book, is used to refer to member m in class cl. The subsection, The Mem­
ber Selector Operators, illustrates one use of the second form of the scope
resolution operator.

T he V alue Construction O perator

The value construction operator


type ()
provides an alternative to the cast operator
Ctype )
(see subsection Casts in Section 2.1).

T he new and delete Operators

The new, delete, and d elete [ ] operators are used to allocate and free
storage dynamically, (d elete frees a single cell, whereas d e le te [ ] frees a
vector of cells.) They work much like the C library functions malloc and
fre e . Unlike malloc and free, however, new, delete, and d elete [ ] are
built-in operators rather than library functions. Finally, new and d elete are
C + + keywords.
The new operator is used to allocate storage dynamically. The basic
syntax is the keyword new followed by a type. For example,
new in t
requests storage for one int. If new is successful in allocating the storage,
the value of the expression
new in t
is a pointer to the allocated storage; otherwise, the value of the expression
is 0. If in t_p tr is a pointer to int, a typical statement to allocate storage
is
in t_ p tr = new in t;
In the analogous C allocation statement,
in t_p tr = ( in t* ) m alloc( s ize o f ( in t ) ) ;
a cast to in t* is required on malloc’s return value and malloc must be
given, as an argument, the number of bytes to allocate. These requirements
are dropped from new, which is therefore easier to use. The new operator
infers the return type and the number of bytes to allocate from the type of
storage requested. In sum, new behaves like a smart malloc.
The new operator can also be used to allocate dynamically an arbitrary
number of contiguous cells, which is a cell vector. A cell vector is the
dynamic counterpart of an array. For example, the code slice

www.MathSchoolinternational.com
2.3 C + + OPERATORS 43

in t_p tr = new i n t [ 50 ] ;
requests a vector of 50 in t cells. If new succeeds in allocating 50 contiguous
cells, the first cell’s address is stored in in t_ptr. Otherwise, new returns 0,
which is stored in int_ptr.
The d elete operator is used to free storage allocated by new. If in t_p tr
points to a single in t cell allocated by new, we can release this storage by
writing
d elete in t_p tr;
If in t_p tr points to a vector of in t cells allocated by new, we can release
this storage by writing^
d e le te [ ] in t_ p tr ;

Example 2.3.2. The following program constructs a linked list using stor­
age cells allocated at run time. After constructing the list, the program
prints the contents of the cells and then steps through the list and frees the
allocated nodes.
#include <stdio.h>
#include <stdlib.h>

struct elephant {
char name[ 10 ] ;
elephant* next;
>;

void print_elephants( elephant* p tr ) ;


elephant* g et_elep h a n ts();
void f r e e _ l i s t ( elephant* p tr ) ;

in t mainO

elephant* s ta rt;

s ta rt = get_elephants( ) ;
print_elephants( sta rt ) ;
f r e e _ l i s t ( staxt ) ;

return EXIT_SUCCESS;
>

// get_elephants dynamically a llo ca tes storage


// fo r nodes. I t builds the linked l i s t and
^Some older versions of C + + require the size between the brackets, e.g., d ele te [ 50 ] in t_p tr;

www.MathSchoolinternational.com
44 CHAPTER 2 BASIC C + +

// stores user-supplied names in the name


// member of the nodes. It returns a pointer
// to the f i r s t such node.

elephant* get_elephants()
{
elephant *current, * f i r s t ;
int response;

// allocate f i r s t node
current = f i r s t = new elephant;

// store name of f i r s t elephant


p r in t f ( "\n\n\tNAME: " ) ;
scanf( "'/,s", current -> name ) ;

// prompt user about another elephant


p r in t f ( 11\n\n\n\tAdd another? (1 == yes, 0 == no): " ) ;
scanf ( M,/,d" , ftresponse ) ;

// Add elephants to l i s t u n til user sign als h a lt,


while ( response ) -[
// try to allocate another elephant node
i f ( ( current -> next = new elephant ) == 0 ) {
p r in t f ( "Out of memory\nCan’ t add "
"more elephants\n" ) ;
return ( f i r s t ) ;
>
current = current -> next;

// store name of next elephant


p r in t f ( "\n\n\tNAME: " ) ;
scanf ( "7,s", current -> name ) ;

// prompt user about another elephant


p r in t f ( "\n\n\n\tAdd another? "
"(1 == yes, 0 == no): " ) ;
scanf ( "'/.d", ^response ) ;
>

// set lin k f i e ld in la s t node to 0


current -> next = 0;
return( f i r s t ) ;

www.MathSchoolinternational.com
2.3 C + + OPERATORS 45

// print_elephants steps through the linked


// l i s t pointed to by p tr and prin ts the name
// member in each node as w ell as the p osition
// of the node in the l i s t

void print_elephants( elephant* p tr )


{
in t count = 1;

p r in t f( "\n\n\n" ) ;
while ( p tr != 0 ) {
p r in t f ( "\nElephant number '/,d is 7,s.",
count++, p tr -> name ) ;
p tr = p tr -> next;
}
>

// f r e e _ lis t steps through the linked l i s t pointed


// to by p tr and fre e s each node in the l i s t

void f r e e _ l i s t ( elephant* p tr )
{
elephant* temp_ptr;

while ( p tr != 0 ) {
temp_ptr = p tr -> next;
d elete p tr;
p tr = temp_ptr;
>
}

The operators new, delete, and d elete [ ] should be used together


and not intermixed with C storage management functions such as malloc,
calloc, and free. We recommend not using the C storage management
functions in C + + programs because the C functions, unlike new, delete,
and d elete [ ], do not interact properly with certain important parts of
C + + such as constructors and destructors (see Section 3.3).
The M em ber Selector Operators
C + + supports pointers that can point only to members of a class. The
member selector operators .* and ->* are used to dereference such a
pointer and thereby access the class member to which it points.

www.MathSchoolinternational.com
CHAPTER 2 BASIC C + +

Example 2.3.3. The code slice

struct C {
int x;
flo a t y;
flo a t z;

int m a i n O

// define a lo c a l flo a t variable


flo a t f ;

// define a pointer to int


in t* i_p tr;

// define two C objects


C c l, c2;

// define a pointer to a flo a t


// member of C
flo a t C ::* f_ p t r ;

// make f_p tr point to C member y


// (Note that the assignment does not
// specify a C object, e .g ., cl or c2.)
f_ p tr = kC: :y;

// set c l.y to 3.14


c l.* f _ p t r = 3.14;

// set c2.y to 2.01


c 2 .*f_p tr = 2.01;

// make f_ p tr point to C member z


f_ p tr = &C::z;

// set c l.z to -999.99


c l.* f _ p t r = -999.99;

// make f_p tr point again to C member y


f_ p tr = &C::y;

// reset c l.y

www.MathSchoolinternational.com
C + + OPERATORS 47

c l.* f_ p t r = -777.77;

// *** * * ERROR: x is not a flo a t


// memberof C
f_ p tr = &C::x;

// * * * * * ERROR: f is not a member


// of C, period
f_ p tr = & f;

// ok — i_ p tr can hold the address of


// any in t and C ::x is public, hence
// v is ib le in main
i_ p tr = ftcl.x ; // c l.x is an in t

illustrates the syntax for defining and using a pointer to a class member.
The syntax

c l.* f_ p t r = 3.14;

says: Access the member ( . ) by dereferencing the pointer to the member


(*). The pointer definition

// define a pointer to a flo a t


// member of C
flo a t C ::* f_ p tr ;

does not say that f_ p tr is a pointer to a flo a t, but rather that f_ p tr is a


pointer to a flo a t member of C. An error thus occurs if we try to assign to
f_ p tr the address of local flo a t variable f , which is not a C member:

// * * * * * ERROR: f is not a member


// of C, period
f_ p tr = & f;

A different error occurs when we try to make f _ptr point to data member
x, which is an in t rather than a flo a t member of C. Once defined, f_ p tr
may point to either y or z, as both members are floats.
Finally, a pointer that is not class specific may be used to access data
members. In our example, i_ p tr of type in t* can be assigned the address
of c l.x because c l.x is an int. □

Pointers to class objects may be used in combination with pointers to


class members. Such a combination requires use of the member selector
operator ->*.

www.MathSchoolinternational.com
48 CH APTER 2 BASIC C + +

Exam ple 2.3.4. We expand Example 2.3.3 to illustrate the access to a


class member through two pointers: a pointer to a class object and a pointer
to its flo a t data members:

struct C {
in t x;
flo a t y;
flo a t z;

in t mainO

// define two C objects


C c l, c2;

// define a pointer to C
C* c_ptr;

// define a pointer to a flo a t


// member of C
flo a t C ::* f_ p tr ;

// make c_ptr point to c l


c_ptr = &cl;

// make f_ p tr point to z
f_ p tr = &C::z;

// set c l . z to 123.321
c_ptr ->* f_ p tr = 123.321;

// make c_ptr point to c2


c_ptr = &c2;

// set c2.z to 987.789


c_ptr ->* f_ p tr = 987.789;

// make f_ p tr point to y
f_ p tr = &C::y;

// set c2.y to -111.99


c_ptr ->* f_ p tr = -111.99;

>

www.MathSchoolinternational.com
C + + OPERATORS 49

Pointer c_ptr is of type C* and so can hold the address of either c l or c2.
Pointer f_ p tr is again of type “pointer to flo a t member of C” and so can
hold the address of either C: : y or C: : z. In an expression such as
// set c2.y to -111.99
c_ptr ->* f_ p tr = -111.99;
the operator ->* may be viewed as performing two tasks. First, the arrow
-> gives us access to the class object, in this case c2, by dereferencing c_ptr.
Second, the star * gives us access to a particular data member, in this case
c2. y, by dereferencing f _ptr. The syntax ->* thus says: Access the member
through the pointer to the object (->) by dereferencing the pointer to the
member (*). The white space on each side of ->* is optional. We could have
written
// set c2.y to -111.99
c_p tr-> *f_p tr = -111.99;

A pointer to a class method follows the same syntax as that illustrated


in Examples 2.3.3 and 2.3.4 for pointers to class data members.

Example 2.3.5. The following program shows how to define and use point­
ers to class methods:
struct C {
in t x;
short f ( in t ) ;
short g ( in t ) ;
>;

in t mainO
{
short s;
C c;

// pointer to method with one in t


// parameter that returns type short
short ( C: : *meth_ptr ) ( in t ) ;

// make meth_ptr point to method f


meth_ptr = &C: : f ;

// invoke method f fo r object c


s = ( c . *meth_ptr ) ( 8 ) ;

www.MathSchoolinternational.com
50 CHAPTER 2 BASIC C + +

// pointer to object c
C* ptr = &c;

// invoke method f fo r object c through ptr


s = ( ptr -> * meth_ptr ) ( 9 ) ;

// make meth_ptr point to method g


meth_ptr = &C: : g ;

// invoke method g fo r object c in two ways


s = ( c . *meth_ptr ) ( 10 ) ;
s = ( ptr -> * meth_ptr ) ( 11 ) ;

>

T he T h ro w Exception O perator

When an unexpected error, also called an exception, is detected in a pro­


gram, the programmer can use the throw operator to throw the exception.
To throw the exception is to transfer control to code, called a handler,
which attempts to deal with the error. We defer the detailed discussion to
Section 8.1.

Exercises

1. What is the output?

#include <stdio.h>
#include <stdlib.h>

short m = 18;

int m a i n O

short m = 4;
p rin tf ( "'/.d #
/,d\n", : :m, m ) ;

return EXIT_SUCCESS;
>

www.MathSchoolinternational.com
C + + OPERATORS 51

2. Write a statement to allocate one cell of type double and store its address
in the pointer dbl_ptr.

3. Write a statement to free the storage allocated in Exercise 2.

4. Write a statement to allocate 100 cells of type pointer to char and store
the address of the first cell in the char pointer str.

5. Write a statement to free the storage allocated in Exercise 4.

6. Change Example 2.3.2 so that the user can choose to supply no names.

7. Revise Example 2.3.2 so that the function get_elephants checks whether


the system is able to allocate storage for the first ELEPHANT. If the system
cannot allocate the storage, get_elephants should return 0.

Exercises 8-17 assume the declaration

struct odometer {
long m iles;
in t tenths;
void set_m iles( long ) ;
>;

and the definition

odometer o d l, od2;

8. Define a pointer to a member of type long in odometer.

9. Set the pointer of Exercise 8 to miles.

10. Use the pointer of Exercises 8 and 9 to set o d l’s miles to 83117.

11. Use the pointer of Exercises 8 and 9 to set od2’s miles to 15004.

12. Define a pointer to odometer.

13. Set the pointer of Exercise 12 to odl.

14. Use the pointers of Exercises 8, 9, 12, and 13 to set od l’s miles to 73893.

15. Define a pointer to a method in odometer that takes one argument of


type long and returns void.

16. Set the pointer of Exercise 15 to set_miles.

17. Use the pointer of Exercises 15 and 16 to invoke o d l’s set_m iles with
argument 69402.

www.MathSchoolinternational.com
52 CH APTER 2 BASIC C + +

Stream
^ P l a n 9 F r o m Out er Space
Input

P l a n 9 F r o m Out er Space

Stream
P l a n 9 P r o m Out e r Space — ^
Output

Figure 2.4.1 Stream input/output.

2.4 Introduction to C + + Input/Output

C + + provides a rich set of classes and predefined objects to support input


and output. Although the programmer still can use the C input/output
library, the new input/output classes provide an easier-to-use, extensible,
more flexible system. In this section we present sufficient basic information
so that the reader can start using certain of these features. We defer an
extended discussion of the input/output class library to Chapter 7.
In C + + , input to a program is treated as a stream of consecutive bytes
from the keyboard, a disk file, or some other source. Output from a pro­
gram is treated as a stream of consecutive bytes to a video display, a disk
file, or some other destination. Thus C + + input/output is called stream
input/output (see Figure 2.4.1).
The header file iostream.h must be included to use the C + + input/output
classes, predefined input/output objects, and manipulators with no argu­
ments. The predefined objects, cin, cout, and cerr, are available. The
object cin refers to the standard input, cout refers to the standard output,
and cerr refers to the standard error. Except for the destination of the
output, cout and cerr behave similarly. The objects cin, cout, and cerr
in C + + are the more powerful and flexible counterparts of stdin, stdout,
and stderr in C.
The right shift operator » is overloaded for input and the left shift
operator « is overloaded for output. Both operators recognize the data
type supplied, so no format string (like that required for p r in tf or scanf)
is necessary. The programmer can further overload the » and « operators
for reading and writing user-defined classes (see Section 7.3.).

Exam ple 2.4.1. If x is a flo a t, the statement

cin » x;

www.MathSchoolinternational.com
IN T R O D U C T IO N TO C + + IN P U T / O U T P U T 53

reads a flo a t value from the standard input and stores it in x. The input
is converted to flo a t because the variable x is of type flo at. If len is a
long, the statement
cout « len;
writes the value of len to the standard output. The value is converted from
long because the variable len is of type long. □

The next example shows how multiple variables may be read or written
in a single statement.

Example 2.4.2. The following program prompts the user for three values:
an int, a flo a t, and a string, and then writes the values to the standard
output.
#include <iostream.h>
#include <stdlib.h >

int m a i n O
{
int id;
flo a t av;
char name[ 20 ] ;

cout « "Enter the id , the average, and the name: ";


cin » id » av » name;
cout << "Id " « id << ’ \n’
<< "Name " « name << ’ \n’
« "Average 11 « average « ’ \n’ ;

return EXIT_SUCCESS;
>
The operators » and « associate from the left; so in the line
cin » id » av » name;
first a value is read into id, then a value is read into av, and then a value is
read into name. Similarly, in the line
cout « "Id " << id « ’ \n’
<< "Name " << name << J\n’
« "Average " « average « ’ \nJ;
first Id is written, then the value of id is written, then the newline is written,
and so on. □

The default action of the input operator » is to skip white space before
reading the next input item. The situation is similar to that of the C library

www.MathSchoolinternational.com
CH APTER 2 BASIC C + +

function scanf, except that even if the variable is of type char, the operator
» skips white space before reading the character. (It is possible to change
the default action of skipping white space— see io s : : skipws in Section 7.2.)
If the variable is of type char* (e.g., name in Example 2.4.2), after skipping
white space, all characters up to but not including the next white space
character are read and stored.
The methods get and read may also be used with cin to read data. If
c is a char
c in .g e t ( c )
reads the next character without skipping white space and stores it in c.
This version of get resembles the C function get char.
Another version of get resembles the C function fgets. If buff is an
array of char,
c in .g e t ( b u ff, max_line )
reads the next line from the standard input and stores it in buff. The “next
line” consists of
The next max_line — 1 characters,
or
All characters up to and but not including the next newline character,
or
All characters up to the end of the input,
whichever is shortest. The method get then stores the characters read
and adds a terminating null ’ \0 ’ . Notice that get never stores more than
max_line characters (including ’ \0’ ). The terminating newline is not re­
moved from the standard input nor is it stored in buff.
The variant
c in .g e t ( b u ff, max_line, end_mark )
has the same behavior as get described in the previous paragraph except
that the arbitrary character end_mark marks the end of the line rather than
the default ’ \n’ .
If get is successful in reading at least one character, it returns nonzero;
otherwise, it returns zero.

Exam ple 2.4.3. The following program produces a double-spaced version


of a text file. It repeatedly reads a line from the standard input and writes
it to the standard output with an extra newline added at the end of the line.
Lines longer than 256 characters, including the newline, are truncated.
#include <iostream.h>
#include <stdlib.h>

www.MathSchoolinternational.com
IN TR O D U C TIO N TO C + + IN P U T / O U T P U T 55

const in t lin e _ le n = 256;

int mainO

char c, bu ff [ lin e_len ] ;

while ( c in .g e t( b u ff, lin e _ le n ) ) {


// w rite lin e + 2 newlines
cout « buff « "\n\n";
// d elete rest of lin e including newline
fo r ( ; ; )
i f ( !c in .g e t( c ) I I c == ’ \n’ )
break;
>

return EXIT_SUCCESS;
}

One way to format input or output is to use manipulators (see Figure


2.4.2). After a manipulator (except for setw) is placed into the stream, all
subsequent input or output is formatted accordingly. For example, placing
the manipulator hex in the output stream causes all subsequent output of
shorts, ints, and longs to be written in hexadecimal. To use manipulators
without arguments (e.g., hex, endl), the header file iostream.h must be
included. Manipulators with arguments (e.g., s e t f i l l , setw) require the
header file iomanip.h.

Example 2.4.4. The output of the program

#include <iostream.h>
#include <stdlib.h>

in t mainO
{
const in t i = 91;

cout « "i = " « i « " (d ecim al)11 « endl;


cout « "i = " « oct « i « " (o c t a l)" « endl;
cout « "i = " « hex « i « " (hexadecimal)" « endl;
cout « "i = " « dec « i « " (decim al)" « endl;

return EXIT_SUCCESS;
>

www.MathSchoolinternational.com
56 CH APTER 2 BASIC C + +

Manipulator Example Effect


dec cout « dec Write output in decimal
hex cout « hex Write output in hexadeci­
mal
oct cout « oct Write output in octal
endl cout « endl Write newline and flush
output stream
WS cin » ws Remove white space
flush cout « flush Flush output stream
s e tfill( c ) cout « s e tfill( c ) Make c the fill character
s etp recisio n ( n ) cout « setp recision ( n ) Set floating-point preci­
sion to n
setw( n ) cout « setw( n ) Set field width to n

Figure 2.4.2 Some C + + manipulators.

is
i = 91 (decimal)
i = 133 (o c ta l)
i = 5b (hexadecimal)
i = 91 (decimal)
If dec were omitted from the last line, the output would again be in hex­
adecimal because once the status of cout is changed by using a manipulator,
the status remains in effect until changed again. □

Exam ple 2.4.5. The following program numbers lines. The line numbers
are right aligned.
#include <iostream.h>
#include <iomanip.h>
#include <stdlib.h>

const in t b u ffsize = 256;

in t mainO

in t line_no = 0;
char c, buff [ b u ffs ize ] ;

while ( c in .g e t( b u ff, b u ffs ize ) ) {


// d elete rest of lin e including newline
fo r ( ; ; )

www.MathSchoolinternational.com
IN TR O D U C TIO N TO C + + IN P U T / O U T P U T 57

if ( ! cin.g e t( c ) I I c == ’\ n ’ )
break;
cout « setw( 4 ) « ++line_no
« " « buff « endl;
>

return EXIT_SUCCESS;
>
If the input is this source code, the output is
1: #include <iostream.h>
2: #include <iomanip.h>
3: #include <stdlib.h>
4:
5: const in t b u ffs ize = 256;
6:
7: in t mainO
8: {
9: in t line_no = 0;
10: char c, b u ff[ b u ffs ize ] ;
11:
12: while ( c in .g e t( b u ff, b u ffsize ) ) {
13: // d elete rest of lin e including newline
14: fo r ( ; ; )
15: if ( !c in .g e t( c ) || c == ’ \n’ )
16: break;
17: cout « setw( 4 ) « ++line_no
18: « 11: 11 « buff « endl;
19:
20:
21: return EXIT.SUCCESS;
22: }
The default field width is zero. Therefore, to right-justify the line num­
bers, in line 16 we first set the field width to 4 by using the manipulator
setw. The line number is then printed in the first four columns; by default,
it is right justified. After printing the line number, the field width auto­
matically returns to its default value of zero. We then print a colon and a
space, the contents of b u ff, and a newline. In C+-1-, as in C, if the output is
bigger than the field width, it is printed anyway using the minimum number
of columns. □

Using the C input/output library functions (p rin tf and the like) and the
C + + class library (cout and the like) in the same program can cause prob­
lems because reads and writes from the two libraries are not automatically

www.MathSchoolinternational.com
58 CH APTER 2 BASIC C + +

synchronized. If the two libraries are mixed, the function


i o s : : syn c_w ith _std io();
should be invoked before doing any input or output. The function call does
the synchronization required for intermixing C + + input/output and C in­
put/output.

Exercises

1. Write a line that uses cin to read a short value into i, a long double
value into x, and a string into the char array str.

2. Write a line that uses cout to print the values read in Exercise 1.

3. What will happen in the program of Example 2.4.3 if we delete the lines

fo r ( ; ; )
if ( !c in .g e t( c ) I I c == ’ \n’ )
break;

4. Write a program that prints a hexadecimal dump of the standard input.


Write 25 hexadecimal values per line. Separate the values with one space.
Use s e tf i l l to write a single digit hexadecimal value h as Oh.

Com m on Program m ing Errors

1. It is an error to modify a const variable. For example, after

const double p i = 3.141592654;

it is illegal to change the value of pi:

p i = 3.0; // * * * * * ERROR * * ***

2. The definition of p

in t a [ ] = { -1, -2, -3 >;


const in t* p = a;

promises not to change what p points to, although the value of p itself
can be changed. Thus

p [ 0 ] += 2; // * * *** ERROR * * ***

www.MathSchoolinternational.com
CO M M O N PRO G R A M M IN G ERRORS 59

is an error, but

in t b [ 4 ] ;
p = b; // * * * * * OK * * * * *

is legal.

3. The definition of p

in t a [ ] = { -1, -2, -3 >;


in t* const p = a;

promises not to change the value of p, although the value of what p points
to can be changed. Thus

in t b [ ] = { 1, 2 , 3, 4 >;
p = b; // * * * * * ERROR * * ***

is an error, but

p [ 2 ]+ + ; // * * * * * OK * * * * *

is legal.

4. It is illegal to change the value of a const parameter:

void f ( const in t i )
{

i = 8; // * * * * * ERROR * * * * *

>

5. It is illegal to change the value to which a const pointer parameter


points:

void f ( const flo a t * p )

p[ 1 ] *= 2 .0 ; // * * * * * ERROR * * * * *

>

6. It is illegal to invoke an undeclared function:

#include <iostream.h>
#include <stdlib.h>

www.MathSchoolinternational.com
CHAPTER 2 BASIC C + +

int m a i n O
■C
in t i = 1, j = 3;

// * * * * * ERROR: swap not declared * * * * *


swap( i , j ) ;
cout « " i = 11 « i « endl;

return EXIT_SUCCESS;
>

void swap( intfe a, intfe b )

int t;

t = a;
a = b;
b = t;
>

This error can be corrected by defining, and implicitly declaring, swap


before main

#include <iostream.h>
#include <stdlib.h >

void swap( intfe a, intfe b )

int t ;

t = a;
a = b;
b = t;
}

int main()

>

or declaring swap before main

#include <iostream.h>
#include <stdlib.h>

www.MathSchoolinternational.com
CO M M O N PR O G R A M M IN G ERRORS 61

void swap( intfe, intfe ) ;

in t mainO
•C

>

or declaring swap in main

#include <iostream.h>
#include <stdlib.h>

in t main()
{
void swap( intfe, intfe ) ;
in t i = 1, j = 3;

7. Since it is illegal to invoke an undeclared function, each system function


must be declared. Typically, system functions are declared by including
system header files such as stdio.h and stdlib.h, which contain the re­
quired function declarations. Also, all system macros must be defined by
including the appropriate header files. Thus

in t mainO

// * * * * * ERROR: p r in tf not declared * * * * *


p r in t f ( "Here’ s lo o k in ’ at you kid.\n" ) ;

// * * * * * ERROR: EXIT_SUCCESS not defined *****


return EXIT_SUCCESS;
>

is illegal since p r in tf is not declared and EXIT_SUCCESS is not defined.


These errors can be corrected by including stdio.h, which contains a decla­
ration of p rin tf, and stdlib.h, which contains the definition of EXIT_SUC-
CESS:

#include <stdio.h>
#include <stdlib.h>

in t mainO
{
// * * * * * CORRECT *** * *
p r in t f ( "Here’ s lo o k in ’ at you kid.\n" ) ;

www.MathSchoolinternational.com
CH APTER 2 BASIC C + +

return EXIT_SUCCESS;
>

8. It is an error to interpret the function declaration

int f ( ) ;

as giving no information about f ’s parameters. In fact, the declaration


states that f has no parameters. For this reason, the following is an
error:

int f ( ) ;

int mainO

// * * * * * ERROR: c a ll does not match declaration * * ** *


i = f( 6 );

9. The only definitions of main that are guaranteed to be portable are

int mainO
{

and

int main( int argc, chair* argv[ ] )


{

Thus definitions such as

// * * * * * ERROR: nonportable d efin itio n of main


void main()
{

>

may not work in some implementations.

10. Explicit dereference is not used when an argument is passed by reference.


For this reason, the following is an error:

www.MathSchoolinternational.com
CO M M O N PR O G R A M M IN G ERRORS 63

void swap( int& a, int& b )


{
int t;

// * * * * * ERROR: a and b axe not pointers * * * * *


t = *a;
*a = *b;
*b = t;
>

A correct version is shown in Example 2.2.4.


Similarly, the following is illegal:

struct strin g {
char s [ 80 ] ;
>;

void copy( stringfe s tr, char* t )


{
// * * * * * ERROR: s tr is not a pointer * * * * *
strcpy( s tr -> s, t ) ;
>

The error can be corrected by changing the offending line to

strcpy( s t r .s , t ) ;

11. An inline function is visible only from the point at which it is declared to
the end of the file. For this reason, the following is an error:

#include <stdio.h>
#include <stdlib.h >

int m a i n O
{
int i = 9, j = 10;

// * * * * * ERROR: can’ t find swap * * * * *


swap( i , j ) ;
p r in t f ( " i = 7.d, j = '/.d\n", i , j );

return EXIT_SUCCESS;
}

www.MathSchoolinternational.com
CH APTER 2 BASIC C + +

in lin e void swap( intfe a, intfe b )


{
int t ;

t = a;
a = b;
b = t;
>

The error can be corrected by defining, and implicitly declaring, swap


before main

#include <iostream.h>
#include <stdlib.h>

in lin e void swap( intfe a, intfe b )


{
int t;

t = a;
a = b;
b = t;
}

int mainO

or declaring swap before main

#include <iostream.h>
#include <stdlib.h>

in lin e void swap( intfe, intfe ) ;

int mainO

or declaring swap in main

#include <iostream.h>
#include <stdlib.h>

www.MathSchoolinternational.com
C O M M O N PR O G R A M M IN G ERRORS 65

in t mainO
{
in lin e void swap( intfe, intfe ) ;
int i = 1, j = 3;

12. All parameters without default values must come first in the parameter
list and then be followed by all the parameters with default values. For
this reason, the following is an illegal function header:

// * * * * * ERROR: I l le g a l ordering of parameters with


// default values * * * * *
int f ( flo a t x = 1.3, int i , chcir c = ’ \n’ )

The error can be corrected by omitting the default value for x or by


adding a default value for i.

13. If a parameter does not have a default value, an argument must be sup­
plied when the function is invoked. For example, if the header of f is

int f ( flo a t x, int i , char c = ’ \n’ ) ;

legal invocations of f are

// * * * * * LEGAL * * * * *
f ( 93.6, 0, »\t» ) ;
f ( 93.6, 0 ) ;

but

// * * * * * ERROR: f must have 2 or 3 arguments * * * * *


f ( 93.6 ) ;

is illegal.

14. To free a number of contiguous cells allocated by new, use d elete [ ],


not delete. As examples,

flo a t_ p tr = new f l o a t [ 100 ] ;


delete flo a t_ p tr; // * * * * * ERROR * * * * *
d e le te [ ] flo a t_ p tr; // * * * * * CORRECT * * * * *

15. Do not use new and d elete with the C storage management functions:

flo a t_ p tr = new f l o a t [ 100 ] ;


fr e e ( flo a t_ p tr ) ; // * * * * * BAD LUCK * * * * *

16. The right shift operator » is overloaded for input:

www.MathSchoolinternational.com
CH APTER 2 BASIC C + +

cin » x; // * * * * * CORRECT * * ***


cin « x; // * * * * * ERROR * * ***

17. The left shift operator « is overloaded for output:

cout « x; // * * * * * CORRECT * * ***


cout » x; // * * * * * ERROR * * ***

18. To use the C + + classes and predefined objects for input and output, the
file iostream.h must be included:

// * * * * * ERROR: std io.h is the wrong .h f i l e * * * * *


#include <stdio.h>
#include <stdlib.h>

in t mainO

cout « "Go Cubs!" « endl;

return EXIT_SUCCESS;
}

19. The method get, when used in the form,

c in .g e t( b u ff, max_line )

does not store the terminating newline and it does not remove it from the
standard input. For this reason, the following is an infinite loop:

char buff [ 80 ] ;

// * * * * * ERROR: In fin it e loop *** * *


while ( c in .g e t( b u ff, 80 ) )
cout « buff « endl;

20. After a manipulator is placed into the stream, all subsequent input or
output is formatted accordingly except for the field width, which reverts
to zero after a string or number is printed. Do not assume that at the end
of a statement, all input/output reverts to default settings. For example,
in the following code, the input is written in decimal, then in hexadecimal
twice:

in t n = 100;

cout « n « endl;
cout « hex « n « endl;
cout « n « endl; // s t i l l hex

www.MathSchoolinternational.com
C O M M O N PR O G R A M M IN G ERRORS 67

21. Mixing the C and C + + input/output facilities may produce unexpected


results unless the function

ios::sync_with_stdio

is invoked. As examples, the code

#include <stdio.h>
#include <iostream.h>
#include <stdlib.h >

int m a i n O
{
int a = 2 , b = 5 ;

// * * * * * RISKY * * * * *
p rin tf ( "*/,d ", a ) ;
cout « b « endl;

return EXIT_SUCCESS;
>

is risky; but

#include <stdio.h>
#include <iostream.h>
#include <std lib.h >

int m a i n O

int a = 2 , b = 5 ;

// * * * * * OK * * * * *
ios::sync_with_stdio();
printf ( "'/.d ", a ) ;
cout << b << endl;

return EXIT_SUCCESS;
>

is acceptable.

www.MathSchoolinternational.com
68 CH APTER 2 BASIC C + +

Program m in g Exercises

In these programming exercises, use the C + + extensions discussed in this


chapter wherever possible. As examples, use C + + comments (//) rather
than C comments (/* */); use const rather than #def ine; and use the
C + + input/output facilities.

2.1. Write a program that reads integers from the standard input until the
end of the file and then prints the largest and smallest values.

2.2. Write a program that echoes the standard input to the standard output,
except that each tab is replaced by the appropriate number of spaces.
Assume that tabs are set every 8 columns, unless the user specifies a
different tab setting on the command line.

2.3. Write a function dbl that takes an in t argument and multiplies it by 2.


Pass the argument by reference. Example:

in t x = 6;
d b l( x ) ;
cout « x; // output is 12

Write a main function that invokes dbl to demonstrate that dbl is working
properly.

2.4. The structure

struct twodim {
in t r;
in t c;
flo a t * a;
>;

represents a two-dimensional array of flo a ts with r rows and c columns


as a one-dimensional array a of r*c floats. Write functions main, val,
get_twodim, and free_twodim.
The function get_twodim is passed an argument of type twodim by ref­
erence and in t values row and col by value. The function get_twodim
sets r to row, c to col, and dynamically allocates r*c contiguous flo a t
cells and stores the address of the first cell in a.
The function val receives arguments i and j by value and x by reference.
It returns the value x . a [ i * r + j ] by reference.
The function free_twodim receives an argument of type twodim by ref­
erence and deletes the storage to which a points.

www.MathSchoolinternational.com
PR O G R A M M IN G EXERCISES 69

The function main invokes get_twodim to create a two-dimensional array


of size 3 x 4 . For all i and j, main then stores the value 2. 5 * i* j in cell
i , j by repeatedly invoking the function val. It then uses cout to print
the values just stored by repeatedly invoking val. Finally, main invokes
free_twodim to free the storage to which a points.

2.5. Write a function p rin t_ s tr that works as follows. If p rin t_ s tr is in­


voked with one argument s, a null terminated array of char, it prints
s or the first 10 characters in s, whichever is shortest. If p rin t_ s tr is
invoked with two arguments, s, a null terminated array of char, and n,
an int, it prints s or the first n characters in s, whichever is shortest.
Write a main function that invokes p rin t_ s tr several times to demon­
strate that p rin t_ s tr is working properly.

2.6. Given

struct numeric {
long a [ 10 ] ;
>;

struct s trin g {
char a [ 10 ] ;
>;

write a function p rin t which, when invoked with a numeric argument,


prints the 10 longs in a one per line; but, when invoked with a s trin g
argument, prints all chars in a up to but not including ’ \0’ on one line.
Overload ++ so that if n is a variable of type numeric,

n++;

adds 1 to each of the 10 long values in a.


Write a main function that invokes p rin t and exercises the overloaded
increment operator several times to demonstrate that prin t and the over­
loaded increment operator axe working properly.

2.7. Add a function reverse_elephant to the program of Example 2.3.1,


which receives a linked list, reverses the order of the nodes in the list,
and returns the address of the first node in the reversed list. Modify
main so that it invokes reverse_elephant several times to demonstrate
that reverse_elephant is working properly.

2.8. This programming exercise is derived from an example originally due


to Mitchell Feigenbaum and adapted by John Allen Paulos in Beyond
Numeracy (New York: Alfred A. Knopf, 1990).

www.MathSchoolinternational.com
CHAPTER 2 BASIC C + +

Consider the deceptively simple formula

( CurrentYr
NextYr = Rate • CurrentYr » ^1 - ^ 5 5 5 5 5 5 "

which calculates next year’s population of, say, waxwings on the basis
of the current population and the growth rate. The variable Rate con­
trols the growth rate and takes on values between 0 and 4. The variable
CurrentYr gives the current value of the waxwing population and is as­
sumed to have a value between 0 and 1,000,000. The variable NextYr
gives the value of the waxwing population one year later. The formula
guarantees that NextYr will also have a value between 0 and 1,000,000.
For example, if CurrentYr is 100,000 and Rate is 2.6, NextYr is 234,000.
Now suppose that we initialize CurrentYr to 100,000 and Rate to 2.6 and
compute the waxwing population 25 years hence by solving for NextYr,
setting CurrentYr to NextYr, solving again for NextYr, and so on for 25
iterations. The waxwing population turns out to be roughly 615,385. We
get the same result if we initialize CurrentYr to, say, 900,000 but leave
Rate set to 2.6. In fact, the population stabilizes at roughly 615,385 for
any value of CurrentYr so long as Rate is 2.6! For some values of Rate,
the population oscillates. For example, if Rate is 3.14, after about 40
years the waxwing population takes on this pattern from one year to the
next: 538,007 to 780,464 to 538,007 to 780,464, and so on indefinitely.
For Rate equal to approximately 3.57, however, the population does not
stabilize or oscillate but rather varies randomly from one year to the next.
Write a program that prompts the user for Rate, an initial CurrentYr,
and a number of iterations. On each iteration, the program prints the
year and the current waxwing population.

2.9. Simulate the Monty Hall puzzle, which gets its name from the host of
the television game show Let’s Make a Deal. The puzzle involves a game
played as follows. A contestant picks one of three doors; behind one of the
doors is a car, and behind the other two are goats. After the contestant
picks a door, the host opens an unpicked door that hides a goat. (Because
there are two goats, the host can open a door that hides a goat no matter
which door the contestant first picks.) The host then gives the contestant
the option of abandoning the picked door in favor of the still closed and
unpicked door. The puzzle is to determine which of three strategies the
contestant should follow:

• Always stay with the door initially picked.


• Randomly stay or switch (e.g., by flipping a coin to decide).
• Always switch to the unpicked and unopened door.

www.MathSchoolinternational.com
PR O G R A M M IN G EXERCISES 71

The user should be prompted as to which strategy he or she wishes to


follow, as well as for how many times the game should be played. Use a
random number generator to place the car at the start and to simulate the
contestant’s initial pick. If the contestant follows the second strategy, use
a random number generator to determine whether the contestant stays or
switches. The program should print the number of games played and the
percentage of games won. (A game is won if the contestant gets the car.)
Before running the simulation, try to determine whether any of the three
strategies is better than the others. You then can use the simulator to test
your answer. The results may surprise you. (For a technical discussion of
this puzzle, see L. Gillman, “The car and the goats,” Amer. Math. Mo.
99 (1992): 3-7.)

2.10. This programming exercise is based on Lewis Carroll’s system for encod­
ing and decoding text. We assume ASCII representation of characters.
The encoding and decoding use the following table:

Across the top and along the side we list, in order, the (printable) ASCII
characters blank (b l) through ~ (see Appendix A). The first row inside
the table is identical to the list across the top. Thereafter, each row is
the same as the previous row, except that each character is shifted one
position to the left, and the last character of a row is the first character
of the preceding row.
To encode text, a string, called a code string, is chosen arbitrarily. To
illustrate the encoding method, we assume that the code string is Walrus
and the text to encode is

Meet me in S t . Louis

Characters other than blank through ~ are not altered. We write the code
string, repeated as often as necessary, on top of the text to be encoded:

WalrusWalrusWalrusWa
Meet me in S t. Louis

www.MathSchoolinternational.com
CHAPTER 2 BASIC C + +

The pairs of characters WM, ae, l e , ..., one on top of the other, are used
as indexes into the preceding table. The encoded text results from finding
the entries in the table that correspond to these pairs. The entry in row
W and column M is "/,, so the first character of the encoded text is '/,. The
entry in row a and column e is G; the entry in row 1 and column e is R;
and so on. Thus the text is encoded as

7,GRgua=aVauGLol?eiAU

To decode text, we reverse this process.


Write a program that repeatedly prompts the user to encode text, decode
text, or quit. If the user chooses to either encode or decode text, he or she
is prompted for a code string, a file to encode or decode, and an output
file.

www.MathSchoolinternational.com
Chapter 3

Classes

3.1 Creating Classes


3.2 Sample Application: A Stack Class
3.3 A First Look at Constructors and Destructors
3.4 Sample Application: A Zip C ode Class
3.5 A First Look at Class O perator Overloading
3.6 Sample Application: A C om plex N u m b e r Class
3.7 Friend Functions
3.8 Assertions and P ro gram Correctness
3.9 Generic Classes U sin g Templates
Com m on Program m ing Errors
Program m ing Exercises

73

www.MathSchoolinternational.com
74 CH APTER 3 CLASSES

class members

data members function members or methods


(representation) (operations)

Figure 3.1.1 Class members.

Classes are at the center of object-oriented programming in C + + . A C + +


class can contain methods in addition to the class’s internal representation,
which is how C + + implements encapsulation. A C + + class can restrict ac­
cess to its internal representation and even to its methods, which is how C + +
implements information hiding. Classes are the natural way to implement
abstract data types in C + + . Finally, class hierarchies are the foundation for
polymorphism in C + + . Object-oriented C + + begins with classes.
A C + + class is a powerful extension of a C struct. A C + + class,
like a C struct, contains member variables, which C + + calls data mem­
bers. A C + + class, unlike a C struct, also may contain methods or class-
specific functions that provide appropriate operations on the class’s data
members (see Figure 3.1.1). C + + uses function mem ber as a synonym
for “method.” This chapter introduces classes by explaining how data mem­
bers and methods are declared, defined, and used in programs. Chapters 4
and 5 expand on these explanations and introduce related material, such as
inheritance.

3.1 Creating Classes


The syntax of C + + classes is close to the syntax of C structs. A C + +
class, like a C struct, is a user-defined, aggregate data type whose member
variables can differ among themselves in data type. We begin with a class
that has no methods so that we can focus on the similarity to a C struct.

Example 3.1.1. The class declaration


// C++ code
class TestClass {
int integer;
flo a t pseudo_real;
>;
is similar in syntax to the structure declaration
/* C code */
struct TestStruct {
int in tege r;
flo a t pseudo_real;

www.MathSchoolinternational.com
CREATING CLASSES 75

However, the class declaration is an implicit typedef. The user-defined data


type is simply TestClass instead of class TestClass, whereas the data
type for the C structure is struct TestStruct. These variable definitions
underscore the difference:
struct TestStruct bigStructArray[ 100000 ] ; /* C code */
TestClass bigClassArray[ 100000 ] ; // C++ code

A typical class has several data members that provide the internal repre­
sentation and several methods that define operations on the data members.
A data member is defined inside the class declaration, just as a struct mem­
ber is defined inside a struct declaration. A method, too, may be defined
inside the class declaration, in which case the method is inline regardless of
whether the keyword in lin e is used. However, it is legal merely to declare
a method inside the class declaration and then to define the method outside
the class declaration. We illustrate with an example.

Example 3.1.2. The code slice


// Window displayed on a screen
class Win {
in t id ; // unique id e n t ifie r
short x, y; // cartesian coordinates
short width,height; // dimensions in plane
char v is ib le ; // 1 == yes, 0 == no
String name; // user-defined type
public:
void moveWin( short, short ) ;
char* getNameO;
void minWinO { width = height = 0; }
>;
declares class Win. This code does not allocate storage but only describes the
storage required for a variable of type Win. The code is a class declaration
rather than a definition of a class variable. In C + + as in C, declarations
describe storage requirements for a data type, whereas definitions allocate
storage for variables of that type. If we define variables of type Win
// creating 100,002 objects of type Win
Win w inl, win2, lo ts_o f_w in s[ 100000 ] ;
then storage is allocated in accordance with the class declaration. Variable
winl, for example, has storage for in t data member id, short data members
x, y, width, and height, and so on. The class name or tag, in this case Win,
serves as an implicit typedef identifier—the name of the class is simply Win
rather than class Win. Class Win has data members of type int, short,

www.MathSchoolinternational.com
CH APTER 3 CLASSES

char, and String, where String is a user-defined data type. The data type
String must be visible to the declaration of class Win for a variable of type
S tring to be included in class Win.
The class declaration also specifies three methods: moveWin, getName,
and minWin. Method minWin is defined inside the declaration, whereas
methods moveWin and getName are only declared inside the class declara­
tion. Functions declared but not defined inside the class declaration must
be defined outside the declaration. A function defined inside a class decla­
ration is thereby inline, even if the keyword in lin e is not used. Method
minWin is thus inline. (Recall that the code for inline functions, including
methods, is substituted in place.)
The method moveWin is declared but not defined inside Win’s declara­
tion. Therefore, moveWin must be defined elsewhere. The scope resolution
operator must be used to indicate that moveWin is a Win method rather than
a method in some other class or even a toplevel function (i.e., a function that
is not a method in any class). The method’s proper name is Win: :moveWin.
Here is how it might be defined:
// define W in::moveWin
void W in::moveWin( short new.x, short new_y )
{
x = new_x;
y = new_y;
>
Whether to define a method inside or outside a class declaration is judg­
ment that should involve two considerations. First, a method defined inside
a class declaration is automatically inline. Second, defining long methods in­
side a class declaration may clutter the declaration. Even a method defined
outside the declaration can be made inline by using the keyword in lin e. We
can revise our example by moving the definition of method minWin outside
Win’s declaration and keep it inline:
// Window displayed on a screen
class Win {
int id; // unique id e n t ifie r
short x, y; // cartesian coordinates
short width,height; // dimensions in plane
char v is ib le ; // 1 == yes, 0 — no
String name; // user-defined type
p u b lic :
void moveWin( short, short ) ;
char* getName( ) ;
void minWinO ; // declare minWin
>;

www.MathSchoolinternational.com
CREATING CLASSES 77

// define minWin in lin e


in lin e void W in::minWin()

width = height = 0;
}

In this example, the keyword in lin e occurs in minWin’s definition. We could


have placed it in minWin’s declaration and omitted it from the definition or
even have put the keyword in both places.
In the declaration for Win, the keyword public occurs above the methods.
Its occurrence means that the three methods may be invoked from anywhere
in the program where class Win is visible. The keyword public does not
occur above the data members, which are therefore private by default. Data
members that are private can be accessed by only two types of functions:

• Methods of the same class such as moveWin or getName.

• frien d functions (see Section 3.7).

In C + + , the keyword p riva te is a mechanism for information hiding. By


making a data member or method private, we restrict its access and, in
effect, hide it from all but other methods in the same class or selected other
functions known as friends.
We can show the difference between private and public class members
by considering function f , which is not a Win method.

void f ( Win w )

// * * * * * ERROR: w’ s width and height


// are private, not public
cout « "width == " « w.width «
"height == " « w.height « endl;

// le g a l — w’ s minWin method is public


w.minWin( ) ;
>

Our class declarations typically place the data members together and the
methods together, and our declarations typically have the methods follow
the data members. This is a matter of style. Data members and methods
may occur in any order within the declaration. However, it is important to
keep in mind that any class member, data member or method, defaults to
p riva te if a class is declared with the keyword class.

Exam ple 3.1.3. The class declaration

www.MathSchoolinternational.com
78 CH APTER 3 CLASSES

class C {
flo a t r; // defaults to private
p u b lic :
int ml( ) ; // public
flo a t m 2(); // public
p riv a te :
char c; // private
p u b lic :
void m 3(); // public
int z; // public data member!
p riv a te :
char cc; // private
>;
intermixes data members and methods. □

C reating Classes w ith the K eyw ord struct

In C + + the keyword struct can be used instead of the keyword class to


create a class.

Exam ple 3.1.4. The code slice


struct Win_S {
int id; // unique id e n t ifie r
short x, y; // cartesian coordinates
short width, height; // dimensions in plane
char v is ib le ; // 1 == yes, 0 == no
String name; // user-defined type
void moveWin( short, short ) ;
char* getName( ) ;
void minWin( ) { width = height = 0 ; }
>;
Win_S main_window;
creates a class Win_S that has the same members as class Win from Example
3.1.2. The difference between Win and Win_S is that members are private
by default in Win, which is created with the keyword class; but members
are public by default in Win_S, which is created with the keyword struct.
In Win we must use the keyword public to make the methods public, for
otherwise they would be private by default. In Win_S we must use the
keyword private to make the data members private, for otherwise they
would be public by default. Classes created with the keyword struct also
involve an implicit typedef. In the case of Win_S, the class’s name is simply
Win_S instead of struct Win_S. □

www.MathSchoolinternational.com
3.1 CREATING CLASSES 79

To make data members p riva te in a class created with the keyword


struct, we must use the keyword private.

Example 3.1.5. The code slice


struct Win_DataIsPrivate {
p r iv a te :
int id; // unique id e n t ifie r
short x, y; // cartesian coordinates
short width, height; // dimensions in plane
char v is ib le ; // 1 == yes, 0 == no
String name; // user-defined type
p u b lic:
void moveWin( short, short ) ;
char* getNameO ;
void minWin() { width = height = 0 ; }
>;
creates a class equivalent to class Win from Example 3.1.2. Note that key­
word public must occur above the methods so that these are not caught in
the scope of the p riva te that introduces the data members. □

There is no advantage in using struct instead of class to create a class.


If a class’s data members need to be public, they can be made so with the
keyword public. Accordingly, our examples and applications usually use
the keyword class to create classes.

C reating Classes w ith the K eyw ord union

The keyword union has the same effect as struct in creating classes: data
members are public by default. Data members in a class created with the
keyword union share storage, just as union members do in C. The storage
shared is enough to accommodate the largest data member in the union.
Classes might be created with the keyword union when there is a need to
economize on storage.

Class D ecla ra tio n s and Class S cope

A class declaration may occur inside a block such as a function’s body or


outside all blocks. If declared inside a block, a class is visible only within
that block; if declared outside all blocks, a class is visible anywhere in a file
following the declaration.

Exam ple 3.1.6. In the code slice


class Outside {

>;

www.MathSchoolinternational.com
80 CH APTER 3 CLASSES

in t mainO
{
class Inside {

>;

>
class Outside is visible from where its declaration occurs to the end of the
file; hence, Outside is visible in main. By contrast, class Inside is visi­
ble only within main’s body because its declaration occurs only within that
block. We say that class Outside has file scope, whereas class Inside has
only block scope. □

It is customary to put class declarations in a header file and then to


#include the header file in whatever other files require access to the class.
In this way, class declarations have file scope.

Exam ple 3.1.7. Assume that our declarations for classes Cl, C2, and C3
occur in the header file classes, h and that we need access to these classes in
the file code.cc. We include the preprocessor directive
#include " classes.h"
in file code.cc to make the classes visible. The #include gives file scope to
all the class declarations in the #included file. □

Exercises

1. If a class is created with the keyword class, are its data members private
by default and its methods public by default?

2. If a class is created with the keyword struct, are only its data members
public by default?

3. Create a class with the keyword union. Make the data members private
and the methods public.

4. In a class declaration, must the data members come before the methods?

5. Change the class declaration so that the data members are p riva te but
the methods are public:

www.MathSchoolinternational.com
CREATING CLASSES 81

class Dilemma {
enum Horn { Hornl, Horn2 >;
p u b lic:
in t horn_crushed( Horn ) ;
void reso lve_p ea cefu lly ( ) ;
char h orn l[ 100 ] ;
chax horn2[ 100 ] ;
>;
6. Is this class declaration legal?

class Dilemma {
enum Horn { Hornl, Horn2 } ;
p u b lic:
in t horn_crushed( Horn ) ;
p r iv a te :
char h orn l[ 100 ] ;
char horn2[ 100 ] ;
p u b lic:
void re s o lv e _ p e a c e fu lly ();
>;
7. Explain the difference between the visibility of class members in these
two class declarations:

// struct keyword
struct Dilemma {
enum Horn { Hornl, Horn2 } ;
in t horn_crushed( Horn ) ;
char h orn l[ 100 ] ;
char horn2[ 100 ] ;
void r e s o lv e _ p e a c e fu lly ();
>;

// class keyword
class Dilemma ■[
enum Horn { Hornl, Horn2
in t horn_crushed( Horn ) ;
char h orn l[ 100 ] ;
char horn2[ 100 ] ;
void reso lve_p ea cefu lly ( ) ;
>;
8. Write a definition for a variable of type Dilemma (see Exercise 7).

9. Write a code slice that invokes method horn_ crushed on the variable you
defined in Exercise 8.

www.MathSchoolinternational.com
82 CH APTER 3 CLASSES

10. Explain the error.

class Dilemma {
enum Horn { Hornl, Horn2 >;
in t horn_crushed( Horn ) ;
char h orn l[ 100 ] ;
char horn2[ 100 ] ;
void re s o lv e _ p e a c e fu lly ();
>;

i n t m a in O
{
Dilemma d l;
strcpy( d l.h orn l, "The barber shaves h im self." ) ;

>
11. Write a declaration for class Employee that has at least six data members
and at least six methods.

12. Write code slices that define Employee objects and invoke each of the
methods.

13. If a method is defined within a class declaration, is the method thereby


inline even if the keyword in lin e is not used?

14. Must a class have the same number of data members and methods?

15. May a class be declared without any methods?

16. Must the data types of a class’s data members be the same?

17. Explain the error in this class declaration.

class C {
p u b lic:
void w r ite ( ) ;
>

18. What is an alternative term for the C + + phrase “function member” ?

19. Must every class method be public?

3.2 Sample Application: A Stack Class


P r o b le m ______________________________________________________________________

Implement a stack as an abstract data type. A stack is a list of zero or more


elements in which insertions and deletions occur at the same end, known as

www.MathSchoolinternational.com
3.2 SAM PLE APPLIC A TIO N : A STACK CLASS 83

the top.

S o lu tio n _______________________________________

We create a Stack class with methods that allow the user to push an element
onto the Stack (insertion), pop an element off the Stack (deletion), and
print all the Stack elements top to bottom. To ensure Stack integrity,
we first check that the Stack is not full before pushing and that is not
empty before popping. Stack elements are chars. We practice information
hiding by making a Stack’s internal representation private. We practice
encapsulation by including methods within a Stack.

C + + Implementation

const in t MaxStack = 10000;


const chax EmptyFlag = ’ \0’ ;

class Stack {
enum { FullStack = MaxStack, EmptyStack = -1 } ;
enum { False = 0, True = 1 } ;
char item s[ MaxStack ] ;
in t top;
p u b lic:
// methods
void i n i t ( ) ; // set top
void push( char ) ; // add an item
char popO ; // remove an item
in t empty( ) ; // no elements?
in t f u l l O ; // too many elements?
void dump_stack(); // top to bottom
>;

void Stack:: i n i t ()
{
top = EmptyStack;
>

void Stack::push( chax c )

// any room le f t ?
i f ( fu llO )
retu rn;
// i f s o , move top and add item
item s[ ++top ] = c;

www.MathSchoolinternational.com
84 CH APTER 3 CLASSES

char Stack::pop()
-C
// anything to remove?
i f ( empty() )
return EmptyFlag;
e ls e
return item s[ top— ] ;
>

in t S t a c k ::fu ll()

// increment top, then check whether th ere’ s room


i f ( top + 1 == FullStack ) ■[
cerr « "Stack f u l l at " « FullStack « endl;
return True; // f u l l
>
else
return False; // not f u l l
>

in t St ack: : empty()

if ( top == EmptyStack ) {
cerr « "Stack empty" « endl;
return True;
>
else
return False;
>

void Stack::dump_stack()
{
fo r ( in t i = top; i >= 0; i — )
cout « item s[ i ] « endl;
>

D iscu ssion ____________________________________________________________________

After class Stack has been declared, we can define Stack objects in the same
way that we define, say, in t or flo a t objects: through variable definitions.
A code slice such as
Stack s i; // define a Stack object
s l.in itO ; // invoke s i ’ s in it method

www.MathSchoolinternational.com
3.2 SAM PLE A PPLIC A TIO N : A STACK CLASS 85

sl.push( ’ a’ ); // in sert a
si.push( ’ b’ ); // in sert b
cout « s i.p o p ( ) ; // remove and p rin t b
sl.push( , c >) ; // in sert c
cout « s l.p o p O ; // remove and p rin t c
si.push( ’b’ ); // in sert b
sl.push( ’ c ’ ); // in sert c
si.dum p_stack(); // p rin t cba, removingnone

illustrates how a Stack object can be created and then manipulated by


invoking its methods.
After defining the Stack object si, method in it is invoked

s i .in it ();

to initialize member top to EmptyStack. Method in it must be invoked


before using a Stack so that top begins with the correct initial value.
A member declaration may not contain an initializer. For example, it is
not possible to initialize top to EmptyStack by writing

class Stack {

// * * * * * ERROR: I l l e g a l in it ia liz a t io n
int top = EmptyStack;

>;
If a member is to be initialized when it is created, a method must be used.
Normally, the programmer would access Stack objects through methods
push, pop, and dump_stack. All three methods are public, which ensures
that the programmer can invoke them wherever class Stack is visible in the
program. There is no need for the programmer to access directly a Stack’s
internal representation, in this case the array of char cells that store the
Stack’s elements; therefore, items is private, which promotes program
security through information hiding. A code slice such as

void f ( )

Stack s;

s . in it O ;
s.push( ’ A’ ) ; // ok

// * * * * * ERROR: items private


cout « s.item s[ 0 ] « endl;

>

www.MathSchoolinternational.com
86 CH APTER 3 CLASSES

contains an error because function f cannot access a p riva te member of


any Stack object, including s; and s.item s is private. It is legal for f to
invoke s.push because push is public.
The methods f u l l and empty normally are invoked by methods push
and pop, respectively. For example, push invokes f u l l to determine whether
there is room on the Stack for another element. We could have made f u l l
and empty p rivate but instead made them public, like the other methods,
in order to give the programmer maximum flexibility. Were f u l l and empty
p rivate, the programmer could not invoke them directly but only indirectly
by invoking push and pop.
The two enums
class Stack {
enum { FullStack = MaxStack, EmptyStack = -1 } ;
enum { False = 0, True = 1

occur inside the class declaration and so are restricted in scope; because the
enums are private, the constants FullStack, EmptyStack, True, and False
axe accessible only by Stack methods or frien d functions. The enums have
class scope. The enum constant EmptyStack, with value -1, is used to
initialize data member top. Before a char is pushed onto a Stack, method
push first checks whether the Stack is full by invoking method fu ll. If the
Stack is not full, the preincremented top serves as index into items, an
array of char that represents the Stack:
void Stack::push( char c )
{
// any room le ft ?
i f ( f u llO )
return;

// i f so, add item and move top


item s[ ++top ] = c ;
>
Method pop returns the top element on a nonempty Stack. On an empty
Stack, pop returns EmptyFlag, a char that signals an empty condition.
Method dump_stack prints, from top to bottom, whatever elements are on
the Stack but without removing them from the Stack.

Exercises

1. Write a method view_top that prints the top item on the Stack without
removing it from the Stack.

www.MathSchoolinternational.com
3.3 A F IR S T LO O K A T CONSTRUCTORS A N D DESTRUCTORS 87

2. Write a method reverse_stack that inverts a Stack. For example, it


would change Stack ABC to CBA.

3. Take and defend a stand for or against the proposition that methods f u l l
and empty ought to be p riva te in class Stack.

4. Write a method fin d that takes a char argument and returns 1 if the
char is currently somewhere in the Stack and 0 otherwise. The method
must not alter the Stack.

5. Write a method stack_sub with char parameters c and s that substitutes


the character s for every character c on a Stack.

3.3 A First Look at Constructors and Destructors


The programmer can specify two special types of method in a class declara­
tion: constructors and destructors. We consider them in order.

Constructors

We begin with an example that illustrates why constructors are useful.

Example 3.3.1. The Stack class of Section 3.2 has an in it method that
initializes data member top to EmptyStack:
void S tack:: i n i t ( )
{
top = EmptyStack;
>
Correct Stack manipulation through pushes and pops requires that top be
initialized to EmptyStack. The problem is that the programmer is responsi­
ble for invoking in it explicitly in a code slice such as
Stack s i; // create a stack
s i . i n i t ( ) ; // i n i t i a l i z e the stack
... / / d o pushes and pops
If the programmerforgets to invoke in it, then top contains junk;that is,
top contains, instead of EmptyStack, some random value that is almostsure
to cause problems during Stack manipulation. □

Constructors are methods that can automatically initialize a class’s data


members. The term “automatically” needs clarification. Although the pro­
grammer may write the code to implement a constructor, the programmer
typically does not write explicit calls to a constructor. Instead, the pro­
grammer usually lets the compiler generate whatever constructor calls are
appropriate. In particular, a constructor is automatically invoked whenever
an object is created.

www.MathSchoolinternational.com
CH APTER 3 CLASSES

Exam ple 3.3.2. We amend the Stack class by replacing in it with a con­
structor that initializes top to EmptyStack when a Stack is created.

class Stack {
enum { FullStack = MaxStack, EmptyStack = -1 >;
enum { False = 0, True = 1
char item s[ MaxStack ] ;
in t top;
p u b lic :
// methods
StackO; // set top to EmptyStack
void push( char ) ; // add an item
char popO ; // remove an item
in t empty( ) ; // no elements?
in t f u l l O ; // too many elements?
void dump_stack(); // top to bottom

// constructor d e fin itio n


Stack: :StackO

top = EmptyStack;
>

Stack s i; // object created and constructor invoked

Now that Stack has constructor Stack: :Stack, the programmer no longer
has to worry about invoking a method such as in it. Instead, the compiler
ensures that the constructor is invoked whenever a Stack is created. In this
example, the constructor initializes top to EmptyStack. In general, a con­
structor can do whatever initializing work is appropriate. □

A constructor’s name is the class’s name. If the constructor takes no


arguments, as in Example 3.3.2, it is known as the default constructor. So
the default constructor for the Stack class is named Stack: : Stack and has
an empty parameter list. No return value appears in either a declaration or
the definition of a constructor. In Example 3.3.2, the header for the default
constructor’s definition is thus

// constructor d e fin itio n


Stack: : StackO

A class may have more than one constructor, which overloads the con­
structor’s name. Even in this case, the compiler rather than the programmer
typically generates a call to whatever constructor is appropriate.

www.MathSchoolinternational.com
A F IR S T LO O K A T CONSTRUCTORS AN D DESTRUCTORS 89

Example 3.3.3. In the following code slice, the class Color has two con­
structors.
class Color {
flo a t red;
flo a t green;
flo a t blue;
p u b lic:
ColorO { red = green = blue = 0.0; >
C olor( flo a t , flo a t , flo a t ) ; // parameterized
>;

// parameterized constructor d e fin itio n


C olor: :C olor( flo a t r , flo a t g, flo a t b )
{
red = r; green = g; blue = b;
}

// C olor: : ColorO constructor used


Color c l;

// C olor: : C olor( flo a t , flo a t , flo a t ) constructor used


Color c2( 1.0, 0.5, 0.0 ) ;
The definition of the parameterized constructor requires the scope resolution
operator
C o lo r::C olor( . . . )
because this definition occurs outside the declaration of class Color. The
default constructor is defined inside the class declaration and so does not
need the scope resolution operator in its name. Nonetheless, the proper
name for the default constructor is C olor: : Color. It is possible that some
other class, say class Pic, has a method named P ic: :Color that expects no
arguments or that there is a toplevel function (i.e., a function that is not
some class’s method) named Color.
When variable c l is defined, the default constructor
C o lo r::C o lo r()
initializes c l because c l ’s definition does not specify any initial values.
Because the default constructor is invoked when c l is defined, cl.red ,
cl.green, and cl.b lu e are all initialized to 0.0. By contrast, when c2
is defined, parameterized constructor
C o lo r ::C olor( flo a t , flo a t , flo a t )
initializes c2 because c2’s definition includes three initial values. The syntax
of c2’s definition

www.MathSchoolinternational.com
90 CH APTER 3 CLASSES

Color c2( 1.0, 0.5, 0.0 ) ;


makes it look as if c2 itself were a function being invoked with three argu­
ments. □

A constructor may have default arguments. We could amend the defini­


tion of parameterized constructor from Example 3.3.3 so that, for example,
its second and third arguments default to specified values.

Exam ple 3.3.4. If we define the parameterized constructor of Example


3.3.3 as
C o lo r ::C olor( flo a t r , flo a t g = 0.5, flo a t b = 0.0 )

red = r ; green = g; blue = b;


>
when the statement
Color c2( 1.0 ) ;
is executed then c2. red is initialized to 1.0, whereas c2. green is initialized
to 0.5 and c2.blue is initialized to 0.0. Of course, we also could define c2
with either two or three initial values:
Color c2( 1.0, 1.0 ) ; // blue defaults to 0.0
Color c2( 1.0, 0.8, 0.7 ) ;
It would be an error to include more than three values in c2’s definition
// * * * * * ERROR: too many args
Color c2( 0.1, 0.2, 0.3, 0.4 ) ;
because C o lo r::C olor( flo a t , flo a t , flo a t ) expects at most three ar­
guments. □

Constructor Initialization

A constructor can use different styles to initialize data members.

Exam ple 3.3.5. The three classes


class Cl {
in t iC l;
flo a t fC l;
p u b lic :
// in it ia liz a t io n in constructor body
C l( in t a l, flo a t a2 ) { iC l = a l; fC l = a2; >
>

www.MathSchoolinternational.com
A F IR ST LO O K A T CONSTRUCTORS AND DESTRUCTORS 91

class C2 {
in t iC2;
flo a t fC2;
p u b lic:
// in it ia liz a t io n in constructor header
C2( in t a l, flo a t a2 ) : iC2( al ) , fC2( a2 ) { }
>;

class C3 {
in t iC3;
flo a t fC3;
p u b lic:
// mixed in it ia liz a t io n
C3( in t a l, flo a t a2 ) : iC3( al ) { fC3 = a2; }
>;
illustrate different styles of data member initialization. C l’s constructor does
all initialization in its body using the assignment operator. By contrast, C2’s
constructor initializes data members in its header rather than its body. The
expression
iC2( al )
in C2’s header initializes data member iC2 to parameter al. Note that the
two header initializations are separated by a comma and that C2’s body is
empty because all the work has been done already in the header. C3’s con­
structor has a mixed style: iC3 is initialized in the header, whereas fC3 is
initialized in the body. □

Header initialization is required if a class has data members that are


const or objects in some other class.

Example 3.3.6. Class C’s constructor


class C {
const in t x; // const data member
flo a t y;
p u b lic:
C( in t a l, flo a t a2 ) : x ( al ) , y ( a2 ) { }
>;
must use header initialization for x because this in t data member is const.
However, the constructor could use either header or body initialization for
data member y, which is not const. In a similar fashion, class Y’s construc­
tor
class X {
char moniker[ 20 ] ;

www.MathSchoolinternational.com
92 CH APTER 3 CLASSES

p u b lic :
X( char* m ) { strcpy( moniker, m ) ; >
>;

class Y {
in t num;
X x; //X object nested in Y
p u b lic :
Y( in t a l , char* a2 ) : num( a l ) , x ( a 2 ) { >
>;
must initialize data member x in its header since x is an object in another
class. □

C opy Constructors

A class’s default constructor takes no arguments. If the class has additional


constructors, they take arguments. However, an argument to a constructor
must not belong to the same class as the constructor: a class C constructor
must not take an argument of type C.

Exam ple 3.3.7. The code slice


class Color {
flo a t red;
flo a t green;
flo a t blue;
p u b lic :
ColorO { red = green = blue = 0.0; }
C olor( Color ) ; // * * * * * ERROR
>;
contains an error because it attempts to create a constructor for class Color
with an argument of type Color. □

Although a class C constructor must not take an argument of type C, it


may take an argument of type C&, that is, a C reference. Such a constructor
is called a copy constructor.

Exam ple 3.3.8. The code slice


class Color {
flo a t red;
flo a t green;
flo a t blue;
p u b lic :
ColorO { red = green = blue = 0.0; }

www.MathSchoolinternational.com
3.3 A F IR S T LO O K A T CONSTRUCTORS AN D DESTRUCTORS 93

C olor( flo a t , flo a t , flo a t ) ;


C olor( Colorft ) ; // copy
>;

C olor: : C olor( Colorft c ) // copy


{
red = c.red;
green = c.green;
blue = c.blue;
>
is legal because the copy constructor’s argument is a Color object reference
rather than a Color object. The copy constructor does what its name sug­
gests: the new Color object is initialized as a copy of some already existing
Color object through memberwise assignment operations— red is assigned
c.red, green is assigned c.green, and blue is assigned c.blue. The com­
piler would invoke the copy constructor in a code slice such as
Color c l ( 0.1, 0.8, 0.7 ) ;
Color c2( c l ) ; // copy constructor invoked
because c2 is initialized in its definition as a copy of Color object cl. □

The programmer may provide a copy constructor, as Example 3.3.8 illus­


trates. If the programmer does not provide a copy constructor, the compiler
automatically provides it. If we were to omit the copy constructor from
Example 3.3.8, then the compiler would generate code that had the same
effect as our version of C olor:: Color ( Colorfe ): the compiler’s default
constructor would do a member by member assignment just as ours does.

Convert Constructors

A one-parameter (other that type C&) constructor for class C is called a


convert constructor. The convert constructor converts type t to type C.

Exam ple 3.3.9. Class Clock


class Clock {
in t hour;
in t min;
in t ap; // 0 is AM, 1 is PM
p u b lic :
ClockO { hour = 12; min = 0; ap = 0; }
Clock( in t ) ;
void p r in t_ tim e ();
>;

www.MathSchoolinternational.com
CH APTER 3 CLASSES

// time is given as 24-hour time


C lock::C lock( in t time )

min = time '/, 100;


hour = time / 100;
i f ( hour > 12 ) {
hour -= 12;
ap = 1;
>
else
ap = 0;
>

void C lock ::p rin t_tim e()


{
cout « s e t f i l l ( ’ O’ ) « setwC 2 ) « hour
« ’ : } « setw( 2 ) « min
« s e t f i l l ( ’ ’ );
if ( ap )
cout « " PM";
else
cout « " AM";
cout « endl;
>

stores a time and has a method print_tim e to print the time in the form
xx:xx XX

where XX is either AM or PM.


The default constructor
C lo ck ::Clock()
sets the time to 12:00 AM.
The convert constructor
C lo ck ::Clock( in t time )
converts the time, given as the in t parameter time, to a Clock by converting
time to the internal representation in terms of the data members hour, min,
and ap. If c is a Clock object, the convert constructor may be explicitly
invoked to convert an in t to a Clock
c = Clock( 1308 ) ; // invoke convert constructor
c .p r in t_ tim e ();
which is then copied into c. The output is 01:08 PM.
Alternatively, an in t may be cast to a Clock

www.MathSchoolinternational.com
3.3 A F IR S T LO O K A T CONSTRUCTORS A N D DESTRUCTORS 95

c = ( Clock ) 123; // cast


c .p r in t_ tim e ();
which is then copied into c. The difference is only syntactic. The convert
constructor is invoked in this case as well. The output is 01:23 AM.
Implicit type conversion is also possible:
c = 1155; // im p lic it type conversion
c .print _t ime( ) ;
Because variable c is of type Clock, the compiler uses the convert construc­
tor to convert the in t 1155 to a Clock, which is then copied into c. The
output is 11:55 AM. □

Constructors and D ynam ic Storage Allocation

A constructor’s main job is to initialize a class’s data members. A construc­


tor also may dynamically allocate storage and then initialize the storage.

Example 3.3.10. The class C


class C {
char* t e x t ;

p u b lic:
CO;
>;

C: :CO
{
tex t = new char[ 20 ] ; // a llo ca te
strcpy( te x t, // i n i t i a l i z e
"North By Northwest" ) ;
>
y
C c; // create a C object
has a data member te x t of type char*. The constructor C: :C dynamically
allocates storage, sets te x t to the address of the first cell, and then initial­
izes the storage to a character string. □

Classes W ith o u t Constructors

If a class has no constructor, then the class’s data members are not initialized
automatically when a class object is created.

Exam ple 3.3.11. In the code slice

www.MathSchoolinternational.com
96 CH APTER 3 CLASSES

class C {
in t dml;
in t dm2;
>;

C c;
class C has no constructor. Accordingly, data members c . dml and c . dm2 are
not initialized when c is defined. The programmer rather than the compiler
now assumes responsibility for initializing the data members. □

Constructors as Functions w ith N o R eturn Value

Constructors differ from ordinary functions in that no return type is speci­


fied in their declaration or definition. This syntactic difference underscores
that the programmer typically does not call a constructor explicitly and,
in any case, does not expect some return value from a constructor call. A
constructor’s job is limited: it initializes data members and, if appropriate,
dynamically allocates storage.

Constructors as P u blic Functions

Constructors axe invoked whenever and wherever class objects are defined,
which means that constructors should be public so that they can work
correctly.

Exam ple 3.3.12. In the code slice


class C {
in t x;
C() { x = 0; } ; // p riva te!
>;

in t mainO

C c l; // * * * * * ERROR: C ::C () not accessible!

>
the default constructor is private, which means that it cannot be invoked
in main, which is neither a method nor a frien d of C. The obvious solution
is to make all constructors public, including the default constructor. □

Destructors

A destructor, like a constructor, is a special method that the programmer


writes but typically does not call; instead, the programmer usually lets the

www.MathSchoolinternational.com
A F IR S T LO O K A T CONSTRUCTORS AN D DESTRUCTORS 97

compiler generate whatever calls to the destructor are appropriate. A de­


structor is automatically called whenever an object is destroyed (e.g., by
going out of scope or by using the d elete operator). A destructor’s job is
to free any storage that a constructor dynamically allocates before the allo­
cated storage becomes garbage. A destructor’s name consists of a tilde (~)
followed by the class name. White space may occur between the tilde and
the class name, but this is uncommon. For example, the destructor for class
Color may be written as
Color: : "C olorO
or even
C o lo r::" C olorO ;
Nothing but white space may occur between the " and the class name.
We illustrate the rationale behind destructors with an example from C.

Example 3.3.13. In the C code slice


typedef struct b ig_strin g {
char* strin g ;
in t s iz e ;
} BigStr;

#define OneBillion (1000000000)


void big_garbage()
{
BigStr bs;
b s .s ize = OneBillion;
b s.strin g = ( char* )
c a llo c ( b s .s iz e , s iz e o f ( char ) ) ;

/* process b s .s trin g but fo rg e t to fr e e i t . */

>
the programmer dynamically allocates a billion char cells but forgets to re­
lease them (with a call to the library function fr e e ) before control leaves
big_garbage. Therefore, b s. strin g becomes a dangling pointer. Because
structure variable bs has auto as its default storage class, bs and its mem­
ber variables can be accessed only within big_garbage. Once control leaves
big_garbage, the billion char cells to which b s. strin g points remain allo­
cated but the program has no way to access them. Each time big_garbage
exits, it leaves behind big garbage— a billion bytes worth. □

In C + + , we can equip class BigStr with a constructor to allocate dy­


namically the required storage and a destructor to ensure that this storage
does not become garbage.

www.MathSchoolinternational.com
CH APTER 3 CLASSES

Exam ple 3.3.14. In the C + + code slice

const long OneBillion = 1000000000;

class BigStr {
char* strin g;
long size ;
p u b lic :
B ig S trO ; // constructor
"B igS trO { d e le te [ ] strin g; } // destructor
>;

BigStr: :BigStrO

strin g = new char[ OneBillion + 1 ] ;


fo r ( long i = 0; i < OneBillion; i++ )
s tr in g [ i ] = ’ $’ ;
s tr in g [ OneBillion ] = ’ \0’ ;
size = OneBillion;
>
void no_garbage()
{
BigStr bs; // constructor B igS tr: : B igStrO
// a llo ca tes a b illio n + 1 chars
// process b s.strin g

// destructor B igS tr: : “BigStr invoked when


// function e x its to release 1 b illio n + 1 chars
>
the constructor B igS tr: : BigStr is called automatically when variable bs
is defined. The constructor allocates one billion contiguous chars, sets
b s .s trin g to the address of the first char in the billion, and copies the
character $ into the char cells. The constructor thus allocates and ini­
tializes storage. The destructor BigStr: : "BigStr is called automatically
when control leaves function no_garbage. The destructor deallocates all the
storage that the constructor allocated. The compiler, not the programmer,
generates the call to the destructor so that the garbage generated in Ex­
ample 3.3.13 does not get generated here. After writing the destructor, the
programmer lets the compiler worry about releasing any storage that the
constructor allocates dynamically.
Recall the syntax for freeing aggregated storage cells. We write

d e le t e [ ] strin g;

instead of

www.MathSchoolinternational.com
3.3 A FIR ST L O O K A T CONSTRUCTORS AN D DESTRUCTORS 99

d elete strin g;
because strin g points to the first in an aggregate of cells rather than to a
single, standalone cell. □

In Example 3.3.14, destructor B igS tr:: "BigStr is defined inside the


class declaration, whereas the constructor B igS tr: : BigStr is defined outside
the class declaration. This reflects our practice of defining only very short
functions inside a class declaration. The destructor B igS tr: : "BigStr could
be defined outside the class declaration, in which case the scope resolution
operator would have to be used.

Example 3.3.15. We revise Example 3.3.14 so that neither the constructor


nor the destructor is defined inside the class declaration:
const long OneBillion = 1000000000;
class BigStr {
char* strin g;
long s ize ;
p u b lic:
B igS trO ; // constructor
"B igS trO ; // destructor
>;

// constructor
BigStr: :B igStrO
{
strin g = new chart OneBillion + 1 ] ;
fo r ( long i = 0; i < OneBillion; i++ )
s tr in g [ i ] = ’ $’ ;
s tr in g [ OneBillion ] = ’ VO’ ;
size = OneBillion;
>

// destructor
BigStr: : "B igStrO
{
d e le te [ ] strin g;
>

Destructors as Functions with N either Argum ents N o r a R eturn Value

A destructor resembles a constructor in that the name of each includes the


class’s name. The difference is that the first character in a destructor’s name

www.MathSchoolinternational.com
100 CH APTER 3 CLASSES

must be the tilde ("). If a destructor is defined outside the class declaration,
then its name must include the scope resolution operator, as Example 3.3.15
illustrates. A destructor never has arguments. Accordingly, the argument
list must be empty in a destructor’s definition or declaration.

Exam ple 3.3.16. The code slice


const in t MaxQueue = 5000;
class Queue {
in t fr o n t ;
in t re a r ;
in t* queue;
unsigned in t max_size;
public:
Queue( ) ; // constructor
Queue( in t ) ; // constructor
~Queue( in t ) ; // * * * * * ERROR: no args allowed!
>;
contains an error because the argument list in the declaration for destructor
Queue: : "Queue is not empty. The declaration must be written
"QueueO; // empty arg l i s t
Similarly, the definition of Queue:: "QueueO must have an empty argument
list, whether the definition occurs inside or outside the class declaration. A
destructor, like a constructor, has no return value— not even void— specified
in its declaration or definition. □

A class has only one destructor and the programmer is responsible for
writing it. The compiler does not provide a destructor if the programmer
does not write one. Destructors are not allowed to have arguments because
their job is highly specialized: a destructor simply releases any storage that
a constructor dynamically allocates before such storage can become garbage.
To perform this task, a destructor needs no arguments. To prevent a de­
structor from trying to perform any other task, C + + disallows destructors
with arguments. Destructors, again like constructors, perform tasks that
require no return value, especially because the programmer typically does
not explicitly invoke a destructor. Invoking a destructor is normally left to
the compiler.

Exercises

1. Does every constructor dynamically allocate storage?

2. Write the declaration for the default constructor for class Mystery.

www.MathSchoolinternational.com
3.3 A F IR S T LO O K A T CONSTRUCTORS A N D DESTRUCTORS 101

3. Write the declaration for the destructor in class Mystery.

4. Explain the error.

class C {
char* p tr;

p u b lic:
void C ( ) ;

>;

5. Explain the error.

class C {
chax* p tr;

p u b lic:
CO;
C( char* c ) { . . . }
C( in t c ) { . . . >
C( char* c ) { . . . }
>;

6. Can a class have more than one destructor? Explain why or why not.

7. Is this code legal?

class C {
char* p tr;

p u b lic:
CO;
CO;
>;

8. Is this code legal?

class C {
in t x;
p u b lic:
C( in t x = 4, y = 6 ) ;

>;

9. Should a class’s constructors be private? Explain.

10. Should a class’s destructor be private? Explain.

www.MathSchoolinternational.com
102 CHAPTER 3 CLASSES

11. Explain the error.

class C {
in t x;

p u b lic:
C( C ) ; // copy constructor

>;
How should the copy constructor be declared?

12. One destructor might free storage with the d elete operator, whereas
another might free storage with the d elete [ ] operator. Explain the
difference.

13. If the programmer does not write a default constructor for a class, does
the compiler provide one?

14. If the programmer does not write a copy constructor for a class, does the
compiler provide one?

15. Explain the error.

class C {
in t x;
p u b lic:
in t C (); // default

>;

16. If a class does not use any dynamically allocated storage, does it need a
destructor?

17. Explain how the destructor works in this code slice.

class C {
char* p tr;
p u b lic:
CO { ptr = new char[ 100 ] ; }
~C() { d e le te [ ] p tr; }
>;

void f ( )

C c l, c2, c3;

>

www.MathSchoolinternational.com
3.4 SAM PLE APPLIC A TIO N : A Z IP CODE CLASS 103

18. Explain the error.

class C {
char* p tr;

p u b lic:
void ~ C ();

>;

19. How many destructors may a class have?

20. Declare a class C that uses dynamically allocated storage and define the
destructor C: : ~C.

21. Explain the error.

class C {
in t x;
const flo a t y;
p u b lic:
C( in t a l, flo a t a2 ) { x = a l; y = a2; >
>;

3.4 Sample Application: A Zip Code Class


P r o b le m ________________________________________________________________

[mplcnif-nt a zip code as an abstract data type.

S o lu tio n ______________________________________________________________ ________

We create class ZipC with methods to manipulate zip codes of different


lengths (e.g., 5- or 9-digit zip codes). The class constructors allow zip codes
to be generated from either character strings or integers. The class hides from
the user the internal representation of a zip code, thereby allowing the user
to focus on operations appropriate to a zip code. We practice information
hiding by making ZipC’s data member private. We practice encapsulation
by including various methods, including constructors and a destructor, in
ZipC.

CH—b Im p le m e n ta tio n _______________________________________________________

#include <stdio.h>
#include <iostream.h>
#include <string.h>

www.MathSchoolinternational.com
CH APTER 3 CLASSES

const in t MinZip = 5; // .g ., 60607


const in t BigZip = 10; //
const int MaixZip = 32; //
const in t InitChar = >? >t•
const in t Hyphen = y_ y>•
const in t SuffixLen = 4; // as in 60607-1234

class ZipC {
char* code; // representation
p u b lic :
// constructors-destructor
ZipCO; // default
ZipC( const char* ) ; // from char*
ZipC( const unsigned long ) ; // from in teger
"ZipCO ; // destructor
void w r ite ( ) { cout « code « endl; }
void expand( const char* ) ;
>;

// d efau lt constructor
ZipC: :ZipCO
{
code = new char[ MinZip + 1 ] ; // +1 fo r terminator
fo r ( in t i = 0; i < MinZip; i++ )
code[ i ] = InitC har;
code[ i ] = ’ \0’ ;
}

ZipC::ZipC( const char* z ip s tr )

in t len =
( s tr le n ( zip s tr ) < MaxZip ) ? s trle n ( z ip s tr ) :
MaxZip;
code = new char[ len + 1 ] ;
strncpy( code, z ip s tr, len ) ;
code[ len ] = ’ \0 ’ ;
>

ZipC::ZipC( const unsigned long zipnum )

chair bu ffer [ BigZip + 1 ] ;


s p r in tf( b u ffer, "y,0*ld", MinZip, zipnum ) ;
b u ffe r [ MinZip ] = ’ NO’ ;

www.MathSchoolinternational.com
3.4 SAM PLE A PPLIC A TIO N : A Z IP CODE CLASS 105

code = new chair[ s tr le n ( b u ffer ) + 1 ] ;


strcpy( code, b u ffer ) ;
>

ZipC: : "ZipCO

d e le te [ ] code;
>

void ZipC::expand( const char* s u ffix )

chair temp [ BigZip + 1 ] ;


char previou s[ MinZip + 1 ] ;

i f ( s tr le n ( code ) != MinZip I I // ’ sm all’ size?


s tr le n ( s u ffix ) != SuffixLen ) // length ok?
return;

strcpy( previous, code ) ;


d e le te [ ] code;
code = new char[ BigZip + 1 ] ;
s p r in tf( code, "'/0s'/,c7,s", previous, Hyphen, s u ffix ) ;
>

D iscu ssion ____________________________________________________________________

The class ZipC has code, a pointer to chairj as its single data member. The
three constructors set code to storage dynamically allocated with the new
operator. How much storage is allocated depends on the constructor. For
instance, the default constructor
// default constructor
ZipC: :ZipCO

code = new char[ MinZip + 1 ] ; // +1 fo r terminator


fo r ( in t i = 0; i < MinZip; i++ )
code[ i ] = InitChair;
code[ i ] = ’ \0’ ;
>
allocates MinZip + 1 bytes and stores InitChar, currently the character ?,
in MinZip of the cells and the null terminator in the remaining cell. The
compiler would have the default constructor invoked in a code slice such as

www.MathSchoolinternational.com
CHAPTER 3 CLASSES

void f ( )
{
ZipC z l ; // default constructor

}
because z l ’s definition does not include an initializing value. Accordingly,
zl.c o d e points to MinZip + 1 chars, each holding InitChar, except for
the last, which holds a ’ \0 ’ . The constructor adds a null terminator to the
sequence of chaxs so that, for example, the user can invoke library functions
such as strlen or strcpy on the sequence. The user need not worry about
a null terminator because the constructor ensures that one is present.
The constructor ^ ^ O’1
ZipC::ZipC( const char* z ip s tr )

in t len =
( s tr le n ( z ip s tr ) < MaxZip ) ? s tr le n ( z ip s tr ) :
MaxZip;
code = new char[ len + 1 ] ;
strncpy( code, z ip s tr , len ) ;
code[ len ] = ’ \0’ ;
>
converts a traditional C string to a ZipC object. The compiler would have
this constructor invoked in a code sequence such as
void g ()
■C
ZipC z2( "60607" ) ; // convert constructor

}
because z2’s definition includes the initializing value 60607 given as a char­
acter string constant. In this case, the constructor would allocate six char
cells: five for the characters in “60607” and one for the null terminator.
This constructor ensures that the initializing value does not exceed MaxZip
characters in length. The constructor’s single parameter, zip s tr, has a
type qualifier of const to ensure that the constructor does not change the
characters to which z ip s tr points.
The last of the three constructors
ZipC::ZipC( const unsigned long zipnum )
•C
char b u ffe r [ BigZip + 1 ] ;
s p r in tf( b u ffer, "*/,0*ld", MinZip, zipnum ) ;
b u ffe r [ MinZip ] = ’ \0’ ;
code = new char[ s trle n ( bu ffer ) + 1 ] ;

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A Z IP CODE CLASS 107

strcpy( code, b u ffer ) ;


>
creates a ZipC object from an integer. The compiler would have this con­
structor invoked in a code sequence such as
void hO
{
ZipC z3( 60607 ) ; // in teger constructor

>
The constructor uses sp rin tf to convert the integer into a null-terminated
array of char with length MinZip. For this purpose, the constructor uses an
array of BigZip + 1 char cells to ensure sufficient storage for the conver­
sion. The constructor then allocates sufficient char cells to hold the string
that represents the ZipC object, including a cell for the ’\0’.
Class ZipC has three constructors, each named ZipC: :ZipC. Yet there
is never any question about which of the three is invoked in a particular
situation. If an object of type ZipC is defined without an initializing value,
then the default constructor is invoked. If such an object is defined with an
initializing string value, then the char* constructor is invoked. If such an
object is defined with an initializing integer value, then the long constructor
is invoked. In general, the compiler uses the number and type of initial values
to determine which constructor should be invoked. It is an error to have two
constructors that expect identical arguments. In our example, there can be
only one constructor that expects a single long argument.
The destructor for class ZipC
ZipC: : "ZipCO
{
d e le te [ ] code;
>
deallocates whatever storage a constructor allocates before such storage can
become garbage. For example, in the code slice
void f ( )
-C
ZipC z ip ( "60607-1312" ) ; // 11 bytes allocated

} // destructor invoked as control e x its th is block


the ZipC object zip has 11 bytes allocated to represent the zip code: 10
bytes for the characters in the initializing value and one byte for the null
terminator. These 11 bytes are allocated dynamically through the new oper­
ator. If the bytes were not freed before f exited, they would become garbage,
that is, dynamically allocated storage that the program cannot access. The
destructor ZipC: : "ZipC prevents such garbage from being created in the

www.MathSchoolinternational.com
108 CHAPTER 3 CLASSES

first place. The destructor uses the operator d elete [ ] to free whatever
storage the constructor has allocated, which in this code slice is 11 bytes.
The compiler, not the programmer, takes responsibility for invoking the de­
structor.
Class ZipC also has a method, defined within the class declaration and
therefore implicitly in lin e, to print the zip code. This method could be
invoked as follows:
void f ( )

ZipC z ip ( "60607-1312” ) ;
z ip .w r ite O ; // prints 60606-1312

>
As ZipC presently stands, the only way to print z ip . code inside function f
is to use method zip .w rite because
• z i p . code is p riva te and therefore accessible only to ZipC methods or
frie n d functions (see Section 3.7).
• f is neither a method nor a frien d function of ZipC.
By contrast, method zip .w rite is accessible in f because all of ZipC’s meth­
ods are public.
Finally, method expand takes a zip code suffix, such as 1234, and ap­
pends it to a standard zip code such as 60607, inserting a hyphen between
the code and the suffix. Of course, many other methods may be appropri­
ate to support zip code operations. All such methods should provide users
with desired functionality without requiring users to know the underlying
representation of a ZipC. In short, ZipC is an abstract data type.

Exercises

1. Write a method shorten that shortens a 9-digit zip code such as 60607-
1234 to a 5-digit zip code. The 5-digit zip code then becomes the official
zip code.

2. Write a method sameZip that checks whether two ZipCs are identical.

3. Write a method getMainZip that extracts a 5-digit ZipC from a 9-digit


ZipC. However, the 9-digit zip code is still the official zip code.

3.5 A First Look At Class Operator Overloading


Arithmetic operators such as + and / are overloaded even in C. For example,
the / operator means integer division in the expression

www.MathSchoolinternational.com
A F IR S T LO O K A T CLASS O PERATOR OVERLOADING 109

2/3 // d ivid e 2 by 3 ==> 0


and floating-point division in the expression
2.0 / 3.0 // d ivid e 2.0 by 3.0 ==> 0.666667
It would be clumsy to have separate operators for integer division and
floating-point division. Instead, we have a single overloaded operator, that
is, an operator with more than one meaning.
The convenience of operator overloading extends to classes. By overload­
ing operators for class objects, we can ease the manipulation of such objects
and thereby provide the programmer with a higher level interface.

Example 3.5.1. Consider the class


class String {
char* strin g;
p u b lic:

>;
in which strin g points to dynamically allocated storage that can hold null-
terminated chars. Now suppose that we store “bob” in String object s i
and “carol” in String object s2. We would like the expression
s i + s2 // String concatenation
to represent String concatenation. We therefore overload the + operator,
the built-in version of which expects numeric rather than String operands.
Here is a sketch:
class String {
char* strin g;
p u b lic:
Stringfe operator+( const String ) const;

>;

Stringfe S trin g::op era tor+ ( const S tring s ) const


I
... // code that defines operator+ fo r Strings
>
(For now, ignore the consts that occur in the example. We clarify them in
the next section.) The overloaded operator’s name is String: :operator+.
It expects a single const argument of type String and returns a Stringft,
that is, a String reference. Once the + operator has been overloaded for the
String class, it can be used in expressions with S tring operands. □

An operator is overloaded by defining it as an operator function. The


keyword operator occurs in the definition.

www.MathSchoolinternational.com
CHAPTER 3 CLASSES

Exam ple 3.5.2. We expand Example 3.5.1 by overloading the > operator
so that it can be used to compare two String objects on the basis of their
lexicographical order.
class String {
char* strin g;
p u b lic :
in t operator>( const String s ) const; // declaration

>;

// d e fin itio n
in t S tr in g ::operator>( const String s ) const
{
return strcmp( strin g , s .s trin g ) > 0;
}
Suppose that s i.s t r in g points to “lettuce” and that s2 .strin g points to
“lattice” , where s i and s2 are String objects. The expression
s i > s2
would evaluate to 1 because “lettuce” is lexicographically greater than “lat­
tice.” The overloaded > operator could be used in code slices such as
if ( s i > s2 )

e ls e

The overloading extends an operator originally defined for numeric types


such as ints and flo a ts to Strings. □

An overloaded operator such as > may be used in syntactically distinct


ways, which we characterize as operator and method syntax. For example,
the code
if ( si.o p era to r> ( s2 ) ) // method syntax

has the same meaning as


if ( s i > s2 ) // operator syntax

Because method syntax for operators is clumsy, we typically use operator


syntax. Indeed, ease of use is the very motivation behind operator overload­
ing for a class.
C + + does not permit the overloading of these five operators:

www.MathSchoolinternational.com
3.5 A F IR S T L O O K A T CLASS O PERATOR OVERLOADING 111

// class member operator


.* // class member dereference operator
:: // scope resolu tion operator
?: // conditional operator
s ize o f // s ize in bytes operator
All other operators— including even the comma operator ( , ) — may be over­
loaded.

Exercises

1. Explain the error.

class Diet {
char word[ 100 ] ;
char d e fin it io n [ 1000 ] ;
public:
void w r ite ( ) ;
in t operator>( const Diet ) const; // declaration

};

// d e fin itio n
in t D ic t ::> ( const Diet d ) const
{
return strcmp( word, d.word ) ;
}

2. Overload the < operator for class Diet.

3. For the overloaded operator in Exercise 1, give code slices in which the
operator is invoked using method syntax. ;i- /' , , ■!& '/

4. For the overloaded operator in Exercise 1, give code slices in which the
operator is invoked using operator syntax. , ( ( /j J J I s

5. Can you envision any use for an overloaded comma operator?

6. Explain the difference between operator and method syntax for an over­
loaded class operator. Give an example of each.

7. Declare a class C that includes a pointer. Overload the == operator for C.

8. Do you see any potential danger in operator overloading?

9. Explain the error.

www.MathSchoolinternational.com
112 CHAPTER 3 CLASSES

class Diet {
chair word[ 100 ] ;
char d e fin it io n [ 1000 ] ;
p u b lic:
void w r ite ( ) ;
in t o p era to r.( const Diet ) const; // declaration

// d e fin itio n
in t D ie t ::o p era to r.( const Diet d ) const
{

>
v ■ •r
10. Which C + + operators, if any, may not be overloaded? - - •'
s '

3.6 Sample Application: A Complex Num ber Class


P r o b le m ______________________________________________________________________

Implement complex numbers as an abstract data type that supports standard


binary operations such as addition and multiplication of objects in the class.

S o lu tio n ______________________________________________________________________

We create class Complex with two double data members to represent the real
and imaginary components of a complex number. We extend the standard
arithmetic operators such as + and * to Complex objects. We practice infor­
mation hiding by making a Complex’s data members private. We practice
encapsulation by including methods, including constructors and overloaded
operators, in Complex.

C + + Im plem en tation _____________

#include <iostream.h>

class Complex {
double re a l;
double imag;
p u b lic :
// constructors
Complex( ) ; // default
Complex( double ) ; // re a l given
Complex( double, double ) ; // both given

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A C O M PLEX NUM BER CLASS

// other methods
void w r ite ()
{
cout « "re a l == " « re a l «
"imaginary == " « imag « endl
>
// operator methods
Complex operator+( const Complex ) const;
Complex op erator-( const Complex ) const;
Complex operator*( const Complex ) const;
Complex operator/( const Complex ) const;
>;

// default constructor
Complex::Complex()
{
re a l = imag = 0.0;
}

// constructor — re a l given but not imag


Complex::Complex( double r )

rea l = r ; imag = 0.0;


>

// constructor — re a l and imag given


Complex: : Complex( double r , double i )

re a l = r; imag = i ;
>

// Complex + as binary operator


Complex Complex::operator+( const Complex c ) const
{
return Complex( re a l + c .r e a l,
imag + c . imag ) ;
>

// Complex - as binary operator


Complex Complex::op erator-( const Complex c ) const

return Complex( re a l - c .r e a l,
imag - c . imag ) ;

www.MathSchoolinternational.com
114 CHAPTER 3 CLASSES

Complex Complex: : operator*( const Complex c ) const


{
return Complex( re a l * c .r e a l - imag * c.imag,
imag * c .re a l + re a l * c . imag ) ;
>

Complex Complex: : operator/( const Complex c ) const

double abs_sq = c .r e a l * c .r e a l + c.imag * c.imag;

return Complex(
( re a l * c .r e a l + imag * c.imag ) / abs_sq,
( imag * c .re a l - re a l * c . imag ) / abs_sq ) ;
>

D iscu ssion ____________________________________________________________________

The class Complex has three constructors but no destructor because it does
not use dynamically allocated storage. The default constructorinitializes
datamembers re a l and imag to 0.0. The one-argument constructor ini­
tializes re a l to the double parameter and imag to 0.0. The two-argument
constructor initializes the data members to the respective parameter values.
The three constructors could be combined into one as:
Complex: : Complex( double r = 0.0, double i = 0.0 )

re a l = r; imag = i ;
>
Our main interest lies in the arithmetic operators overloaded for the
class. The purpose of the overloading is to extend the standard arithmetic
operators to Complex numbers, thus allowing code such as
int mainO
{
Complex c l ( 7.7, 5.5 ) ;
Complex c2( 4.4, 3.3 ) ;
Complex c3;

c3 = c l + c2; // + fo r Complex variables

>
In the declarations and definitions for the overloaded arithmetic opera­
tors, the keyword const occurs twice, once in the formal parameter list and
then at the end:

www.MathSchoolinternational.com
3.6 SAM PLE A PPLIC A TIO N : A C O M PLEX NUM BER CLASS 115

Complex operator+( const Complex ) const; // declaration

The const in the parameter list ensures that the argument’s value will not
be altered during the addition, and the const at the end ensures that the
method’s invoker— the Complex object whose operator+ is invoked on the
argument— will not be altered during the addition. The operator returns
the Complex result of the addition but does alter the objects involved in the
addition.

Exercises

1. Overload the += operator for class Complex and show a code slice that
uses the overloaded operator.

2. Overload the -= operator for class Complex and show a code slice that
uses the overloaded operator.

3. Overload the /= operator for class Complex and show a code slice that
uses the overloaded operator.

4. Overload the *= operator for class Complex and show a code slice that
uses the overloaded operator.

5. In the declaration

Complex op erator*( const Complex ) const; // declaration

explain what each of consts means.

6. Explain the error.

int mainO

Complex c l ( 1.1, 2.2 ) ;


Complex c2( 2.2, 3.3 ) ;
Complex c3;
c3 = c l .+ ( c2 ) ;

>

7. Give code slices in which Complex: :operator- is used with operator


syntax and method syntax.

www.MathSchoolinternational.com
116 CHAPTER 3 CLASSES

3.7 Friend Functions


A class’s p riva te members, which typically are its data members, are acces­
sible to its methods and to its frien d functions. To make function f a friend
of class C, we declare f within C’s declaration using the keyword friend:
class C {
in t x;
p u b lic :
frien d in t f ( ) ; // frien d function

>;
Except for being able to access one or more class’s private members, a frien d
function is no different from any other function.
There are three main reasons to use frien d functions:
• To allow a library function access to the private members of a class.
• To allow a function to access the private members of two or more classes.
(If a function needs access to the private members of only one class, it
should be a method.)
• To allow more flexible operator overloading.
Because a frien d function is not a method and still has access to p rivate
class members, a frien d function violates a strict interpretation of object-
oriented principles. Accordingly, frien d functions are controversial and
open to misuse. We recommend using frien d functions only when abso­
lutely necessary. We illustrate each use of frien d functions.

L ib rary Function Access to private M em bers

Consider the class C that has a p rivate data member of type char*:
class C {
char* strin g;
p u b lic :
C( char* s )

strin g = new char[ s trle n ( s ) + 1 ] ;


strcpy( strin g, s ) ;
}
>;

in t mainO
{
C c l ( "tcp/ip" );

www.MathSchoolinternational.com
3.7 FR IEN D FUNCTIONS 117

// * * * * * ERROR: strin g is p riva te


in t len = s tr le n ( c l.s t r in g ) ;

We cannot call library functions such as s trle n or strtok with c l.s t r in g


as the argument because strin g is private. One way around the problem is
to make selected library functions friends so that they can access a p riva te
data member such as C: : string:
class C {
char* strin g;
p u b lic:
C( char* s )
{
strin g = new char[ s tr le n ( s ) + 1 ] ;
strcpy( strin g , s ) ;
}
frien d in t s tr le n ( char* ) ;
>;

int mainO

C c l ( "tcp/ip" ) ;
in t len = s tr le n ( c l.s t r in g ) ; // ok

>

frien d s o f T w o or M o re Classes

Example 3.7.1. Consider a version of the Stack class (see Section 3.2)
designed for Complex objects rather than chars:
class Complex; // makes class Complex
// v is ib le to ComplexStack

class ComplexStack {

Complex item s[ MaxStack ] ;


p u b lic :

frien d void dump_stack( ComplexStack ) ;

class Complex {
double re a l;

www.MathSchoolinternational.com
118 CHAPTER 3 CLASSES

double imag;
p u b lic :

frien d void dump_stack( ComplexStack ) ;


};

// note: dump_stack is not a method


void dump_stack( ComplexStack s )
{
fo r ( in t i = top; i >= 0; i — )
cout « s.items [ i ] .re a l
« ’ ’ « s.item s[ i ].im ag;

cout « endl;
>
In the original Stack class, dump_stack is a method. Here dump_stack is
a frie n d of ComplexStack and a frien d of Complex because dump_stack
needs access to ComplexStack’s private member items and to Complex’s
p riv a te members re a l and imag. In the original Stack class, dump_stack
needs access only to p rivate member items in Stack itself and so is best
implemented as a method.
Note that we must declare Complex
class Complex; // makes class Complex
// v is ib le to ComplexStack

class ComplexStack {

before declaring ComplexStack because ComplexStack references Complex.


U sing frien d s in O perator Overloading

Exam ple 3.7.2. Operator functions in class Complex could have been im­
plemented as friends rather than as methods. For example,
class Complex {
double re a l;
double imag;
p u b lic :
// constructors
ComplexO; // default

www.MathSchoolinternational.com
3.7 FRIEN D FUNCTIO NS 119

// friends
frien d Complex operator+( const Complex, const Complex ) ;

};

Complex operator+( const Complex c l, const Complex c2 )

return Complex( c l.r e a l + c 2 .rea l,


cl.im ag + c2.imag ) ;
}

The declaration of operator+ contains the keyword friend, but the op­
erator’s definition does not. Note, too, that the operator’s name is sim­
ply operator+ rather than Complex: : operator+. Whether operator+ is a
method or a friend, its invocation looks exactly like the method’s invocation
when operator syntax is in use:
in t mainO

Complex c l ( 1.0, 1.0 ) ;


Complex c2( 2.0, 2.0 ) ;
Complex c3;

c3 = c l + c2; // eith er frien d or operator syntax

>
If operator+ is a frien d rather than a method, however, then method
syntax cannot be used. This code is illegal for the frie n d version
c3 = c l . operator+( c2 ) ; // * * * * * ILLEGAL fo r frien d
because operator+ is not a c l method. However, the code
c3 = operator+( c l, c2 ) ; // le g a l fo r frien d

is legal. □

The code slice


in t main()

Complex c l ( 2.0, 2.0 ) ;


Complex c2;

c2 = c l + 99.0; // c2 = c l .operator+( 99.0 ) ;

>

www.MathSchoolinternational.com
120 CHAPTER 3 CLASSES

is legal because, if operator+ is a method or a friend, Complex object c l


invokes its operator+ with 99.0 as its argument. Although 99.0 is not a
Complex object, the compiler can convert 99.0 to a Complex type by invoking
the convert constructor Complex: : Complex ( double ) so that the addition
can proceed. However, the code slice
c2 = 99.0 + c l; // * * * * * ERROR: 99.0 not Complex
is in error if operator+ is a method because 99.0 is not a Complex object
with an operator+ that can be invoked with c l as its argument. If we imple­
ment operator+ as frien d that expects two Complex objects as arguments,
then either
c2 = c l + 99.0; //ok
or
c2 = 99.0 + c l; // ok
is legal. □

One class’s method may be another class’s friend, or classes may share a
function as a friend. Yet these and related uses of frien d functions strain
the style of object-oriented programming in C + + . Some object-oriented
languages allow only methods to access data members as a way of enforc­
ing information hiding. C + + gives the programmer flexibility by allowing
frie n d functions as well as methods to access a class’s p riva te members,
but we recommend that frien d functions be used sparingly. The normal
access to a class’s p riva te members should be through its methods.

Exercises

1. Create a class and test whether you can make main a frie n d function of
this class.

2. Overload all the operators for class Complex as frie n d functions rather
than as methods.

3. Explain why the extensive use of frien d functions may compromise the
spirit of object-oriented programming.

4. On your system, find the header file complex.h. (On UNIX systems, it is
likely in the directory /usr/include and in Borland C + + it is likely in the
IN CLUD E subdirectory.) Check whether the arithmetic operators have
been overloaded as frien d functions or as methods.

5. When does it seem useful to have frien d functions?

www.MathSchoolinternational.com
.8 ASSERTIONS A N D P R O G R A M CORRECTNESS 121

6. If we overload * for class Complex as a method rather than as a frien d,


which of these expressions is legal and which illegal? Explain why in each
case.

in t mainO
{
Complex c l ( 111.0, 999.0 ) ;
Complex c2;
c2 = c l + 777.0;
c2 = 888.0 + c l;
c2 = 777.0 + 888.0;

>

7. Assuming + is overloaded as a frien d for Complex, explain the error(s).

in t mainO
{
Complex c l ( 1.0, 1.0 ) ;
Complex c2( 3.0, 3.0 ) ;
Complex c3;

c3 = c l + c2;
c3 = c l .operator+( c2 ) ;
c3 = + ( c2, c l ) ;
c3 = operator+( c l, c2 ) ;

>

.8 Assertions and Program Correctness


A goal of any software development environment is to produce quality soft­
ware. In particular, software should confirm to the specifications. An asser­
tion in a program is a condition that must always be true at some particular
point of the program’s execution. Assertions, which formally represent the
specifications and are embedded in the code, are checked for correctness
when the program is running. When an assertion fails, the system detects
and reports the error. Much attention has been given to formal methods of
producing correct software, especially by developers of object-oriented lan­
guages. C + + and standard C support assertions through macros. Other
object-oriented languages (e.g., Eiffel) support assertions directly in the lan­
guage. In this section, we discuss preconditions and postconditions (as­
sertions that must be true at entrance and exit from methods) and class
invariants (assertions that must be satisfied by the class as a whole). Us­
ing preconditions, postconditions, and class invariants, it is possible to give

www.MathSchoolinternational.com
122 CHAPTER 3 CLASSES

a formal definition of what it means for a class to be correct [see B. Meyer,


Object-oriented Software Construction, (Englewood Cliffs, N.J.: Prentice
Hall, 1988), Chapter 7].
Consider the following variant of the stack class of Section 3.2:
const in t MaxStack = 3;

class Stack {
enum { FullStack = MaxStack, EmptyStack = -1 } ;
char items[ MaxStack ] ;
in t top;
p u b lic :
StackO ;
void push( char ) ;
char p o p ();
in t empty( ) ;
in t f u llO ;
>;

Stack::Stack()
■C
top = EmptyStack;
>

void Stack::push( char c )

item s[ ++top ] = c;
>

char Stack::pop()
{
in t va l;

va l = items [ top— ] ;
return va l;
>

in t S t a c k ::fu ll()

return top + 1 == FullStack;


>

www.MathSchoolinternational.com
ASSERTIONS AN D P R O G R A M CORRECTNESS 123

in t Stack::empty()
{
return top == EmptyStack;
>

In this version of the stack class, the user is responsible for checking for a
full stack before pushing an item on the stack and for checking for an empty
stack before popping an item off the stack.
Consider the method push. Because it is an error to push an item onto
a full stack, we assert that, when push is entered, the stack is not full. A
precondition for push is that the stack is not full. When push is exited, the
stack is not empty. A postcondition for push is that the stack is not empty.
Formally, we write
void Stack: :push( chair c )

// precondition: tru e— OK; fa ls e — error


a ssert( ! f u l l ( ) ) ;

item s[ ++top ] = c;

// postcondition: tru e— OK; fa ls e — error


a ssert( !empty() ) ;
>
When push is invoked, the statement
a ssert( ! f u l l ( ) ) ;
is executed. The expression between the parentheses, ! f u l l ( ) in this case,
is evaluated. If this expression is true, execution proceeds normally to the
next statement; but, if the expression is false, the program terminates with
a message such as
Assertion fa ile d : ! f u l l ( ) , f i l e ASSERT.CPP, lin e 29
Abnormal program termination
Similarly, after the statement
item s[ ++top ] = c ;
is executed, the statement
a s s e rt( !empty() ) ;
is executed. If the expression ! empty ( ) is true, execution proceeds nor­
mally with a return from push; but, if the expression is false, the program
terminates with a message.
We must include the system header file assert.h to use the assert macro.
The following example shows the stack class with preconditions and post­
conditions added to push and pop and some sample output.

www.MathSchoolinternational.com
CH APTER 3 CLASSES

Example 3.8.1. We show two sample runs of the following program:

#include <iostream.h>
#include <assert.h>
#include <stdlib.h>

const in t MaxStack = 3;

class Stack {
enum { FullStack = MaxStack, EmptyStack = -1 } ;
char item s[ MaxStack ] ;
in t top;
p u b lic :
Stack( ) ;
void push( char ) ;
char popO ;
in t empty( ) ;
in t f u l l O ;
>;

Stack::Stack()

top = EmptyStack;
>

void Stack::push( char c )

// precondition: true— OK; fa ls e — error


a s s e rt( ! f u l l ( ) ) ;

item s[ ++top ] = c;

// postcondition: true— OK; fa ls e — error


a s s e rt( ! empty() ) ;
>

chair S ta c k : :p o p ( )
{
in t v a l;

// precondition: true— OK; fa ls e — error


a ssert( !empty() ) ;

va l = item s[ top— ] ;

www.MathSchoolinternational.com
ASSERTIONS AN D P R O G R A M CORRECTNESS

// postcondition: tru e— OK; fa ls e — error


a ssert( ! f u l l ( ) ) ;

return va l;
>

in t S t a c k ::fu ll()
{
return top + 1 == FullStack;
>

in t S tack::empty()

return top == EmptyStack;


}

in t mainO

char resp , v a l;
Stack s;

fo r ( ; ; ) {
cout « "Push (2 ), Pop (1 ), Quit (0 ): 11;
cin » resp;
switch ( resp ) {
case ’ 2’ :
cout « "Push what value? ";
cin » v a l;
s.push( va l ) ;
break;
case ’ 1 ’ :
cout « "Pop va l = " « s.popO « endl;
break;
case ’ O’ :
return EXIT.SUCCESS;
>
}
>

The following is the output of a session in which a push fails:

Push (2 ), Pop (1 ), Quit (0 ): 2


Push what value? a
Push (2 ), Pop (1 ), Quit (0 ): 2

www.MathSchoolinternational.com
CHAPTER 3 CLASSES

Push what value? b


Push (2 ), Pop (1 ), Quit (0 ): 2
Push what value? c
Push (2 ), Pop (1 ), Quit (0 ): 2
Push what value? d
Assertion fa ile d : ! f u l l ( ) , f i l e ASSERT.CPP, lin e 29
Abnormal program termination
The following is the output of a session in which a pop fails:
Push (2 ), Pop (1 ), Quit (0 ): 2
Push what value? a
Push (2 ), Pop (1 ), Quit (0 ): 1
Pop v a l = a
Push (2 ), Pop (1 ), Quit (0 ): 1
Assertion fa ile d : !em pty(), f i l e ASSERT.CPP, lin e 42
Abnormal program termination

Because a certain amount of overhead is associated with checking asser­


tions, C + + makes it possible to disable the assertions by defining the macro
NDEBUG (No DEBUG). The following example illustrates.

Example 3.8.2. If we insert the line


#def ine NDEBUG
just before the line
#include <assert.h>
the assertions will not be checked, as the following output from a sample
session illustrates:
Push (2 ), Pop (1 ), Quit (0 ): 2
Push what value? a
Push (2 ), Pop (1 ), Quit (0 ): 2
Push what value? b
Push (2 ), Pop (1 ), Quit (0 ): 2
Push what value? c
Push (2 ), Pop (1 ), Quit (0 ): 2
Push what value? d
Push (2 ), Pop (1 ), Quit (0 ): 0

A class invariant is an assertion that must be satisfied by the class as


a whole. To be more precise, a class invariant must be satisfied after any
constructor is invoked and it must be satisfied at entrance and exit from each

www.MathSchoolinternational.com
ASSERTIONS AND P R O G R A M CORRECTNESS 127

public method. For example, in the stack class, top must always be greater
than or equal to EmptyStack, and top must always be less than FullStack.
Thus the condition
top >= EmptyStack && top < FullStack
is a class invariant for the class Stack.
The following example shows the stack class with preconditions, post­
conditions, and a class invariant.

Example 3.8.3. The class


const in t MaxStack = 3;

class Stack {
enum { FullStack = MaxStack, EmptyStack = -1 } ;
char item s[ MaxStack ] ;
in t top;
p u b lic:
StackO ;
void push( char ) ;
char popO ;
in t empty( ) ;
in t f u l l O ;
>;

// the class invariant


#define Stack_inv ( top >= EmptyStack && top < FullStack )

Stack: :StackO
{
top = EmptyStack;
a ssert( Stack_inv ) ;
>

void Stack::push( char c )


■C
assert ( I f u llO && Stack_inv ) ;
item s[ ++top ] = c ;
a ssert( !empty() && Stack_inv ) ;
>

www.MathSchoolinternational.com
128 CHAPTER 3 CLASSES

char Stack::pop()

in t v a l;

a s s e rt( ! empty() && Stack_inv ) ;


v a l = item s[ top— ] ;
a ssert( ! f u l l ( ) && Stack_inv ) ;
return v a l;
>

in t S tack::f u l l ()

a s s e rt( Stack_inv ) ;
return top + 1 == FullStack;
>

in t S tack::empty()

a s s e rt( Stack_inv ) ;
return top == EmptyStack;
>

contains preconditions, postconditions, and a class invariant. If we deliber­


ately insert the erroneous statement

top— ;

just before the line

a s s e rt( Stack_inv ) ;

in the constructor and run the program with the main function of Example
3.8.1 added, the output is

Assertion fa ile d : Stack_inv, f i l e ASSERT.CPP, lin e 28


Abnormal program termination

Exercises

1. Supply preconditions, postconditions, and class invariants for the zip code
class of Section 3.4.

www.MathSchoolinternational.com
3.9 GENERIC CLASSES USING TEM PLATES 129

3.9 Generic Classes Using Templates


In Section 3.2, we constructed a stack class that pushed and popped chars
on and off the stack. If we need a stack class that pushes and pops ints, we
could modify our char stack by replacing char by in t, but otherwise the
code would remain the same. If we use this technique, every time we want
a stack to manipulate a different data type, we would have to modify the
code, replacing one data type by another. C + + provides a better way. By
using templates, we can construct a stack class once and for all, and then
construct stack classes for particular data types as needed. Thus templates
directly support code reusability and promote code correctness.
When we use a template to construct a class, particular data types (such
as char in the stack example) are replaced by symbolic names called pa­
rameters. Types constructed using parameters are called parameterized
types. When the programmer requests a class from a parameterized class
by providing arguments for the parameters, the system replaces the param­
eters by the given arguments and produces an actual class. A class that uses
parameters for data types is also known as a generic class. Such a class
is “generic” in the sense that the parameters are merely placeholders for
specific data types. The parameters are not themselves actual data types.
The following example shows a version of the stack class of Section 3.2
rewritten as a generic class using templates.

Example 3.9.1. The code


template< class Typ, in t MaxStack >
class Stack {
enum { FullStack = MaxStack, EmptyStack = -1 } ;
Typ item s[ MaxStack ] ;
in t top;
p u b lic:
StackO ;
void push( Typ ) ;
Typ p o p ();
in t empty( ) ;
in t f u l l O ;
>;

template< class Typ, in t MaxStack >


Stack< Typ, MaxStack > ::S ta ck ()
{
top = EmptyStack;
>

www.MathSchoolinternational.com
CHAPTER 3 CLASSES

template< class Typ, in t MaxStack >


void Stack< Typ, MaxStack >::push( Typ c )

item s[ ++top ] = c ;
>

template< class Typ, in t MaxStack >


Typ Stack< Typ, MaxStack > ::p o p ()
{
return item s[ top— ] ;
>

template< class Typ, in t MaxStack >


in t Stack< Typ, MaxStack > : : f u l l ( )

return top + 1 == FullStack;


>

template< class Typ, in t MaxStack >


in t Stack< Typ, MaxStack >::em pty()
-C
return top == EmptyStack;
>

constructs a generic stack class.


The line
template< class Typ, in t MaxStack >
must be followed by a declaration or definition of a class or function. The
statement makes the parameters given between the angle brackets (Typ and
MaxStack in this case) available to the class or function that follows. The
line
template< class Typ, in t MaxStack >
serves somewhat the same purpose for parameterized types as does a function
header for functions.
Each parameter listed between the angle brackets must be either a type
parameter or a function-style parameter. A type parameter, which is pre­
ceded by the keyword class, is replaced by an actual type when a particular
class is constructed. In our example, Typ is a type parameter that could be
replaced by either a built-in data type such as in t or by a user-defined data
type such as a class. A function-style parameter, which follows the same
syntax as function parameters, is replaced by a value when a particular class
is constructed. In our example, MaxStack is a function-style parameter.
To construct a particular stack class and define an object, we could write

www.MathSchoolinternational.com
GENERIC CLASSES USING TEM PLATES 131

Stack< in t, 1000 > s;


This code creates the class Stack< in t, 1000 > and defines an object s
of this type. The system obtains a declaration of Stack< in t, 1000 > by
replacing the type parameter Typ by in t and the function-style parameter
MaxStack by 1000 in the code
class Stack {
enum { FullStack = MaxStack, EmptyStack = -1 } ;
Typ item s[ MaxStack ] ;
in t top;
p u b lic:
StackO ;
void push( Typ ) ;
Typ popO;
in t empty( ) ;
in t f u llO ;
>;
Note that the expression
Stack< in t, 1000 >
resembles a function call in which parameters receive arguments.
To define the methods, we must also use the keyword template since the
methods are parameterized functions. For example, to define the method
push, we write
template< class Typ, in t MaxStack >
void Stack< Typ, MaxStack > : :push( Typ c )
{
item s[ ++top ] = c ;
>
The line
template< class Typ, in t MaxStack >
makes the parameters Typ and MaxStack available to the function
Stack< Typ, MaxStack > : : p u s h

which follows. As shown, the method must be scoped to the parameterized


class
Stack< Typ, MaxStack >

Templates are similar to parameterized macros and serve the same gen­
eral purpose. Indeed, early versions of C + + did not support templates and
so generic classes had to be constructed using parameterized macros. Tem­
plates are far superior to parameterized macros because templates are part

www.MathSchoolinternational.com
132 CH APTER 3 CLASSES

Generic stack class

of the language and are not handled by the preprocessor, as are parameter­
ized macros. Templates are easier to write than parameterized macros, and
they are also easier to debug.
Generic classes provide yet another level of abstraction for the program­
mer. Just as a class is an abstraction of an object, so a generic class is an
abstraction of a class (see Figure 3.9.1).

Exercises

1. Find the error.

template< class Typ >


class C {

public:
CO;

>;

template< class Typ >


C::C()
I

>

2. Write a line that constructs a stack class using the generic class of Ex­
ample 3.9.1 for a maximum stack size of 500 elements of type char.

3. Write a line that constructs a class stack class using the generic class of
Example 3.9.1 for a maximum stack size of 2000 elements of type String*.

4. Show the changes to the stack class of Example 3.9.1 if we add a method
top that returns, but does not remove, the item at the top of the stack.

www.MathSchoolinternational.com
CO M M O N PR O G R A M M IN G ERRORS 133

5. Revise the stack class of Example 3.9.1 so that it has one type parameter
Typ. Provide a constructor with an in t parameter size that dynami­
cally allocates an array of size elements. Provide a default constructor
that dynamically allocates an array of 100 elements. Also provide an
appropriate destructor.

6. Write a line that constructs a stack class using the generic class of Exercise
5 to a maximum stack size of 100 elements of type flo a t.

7. Write a line that constructs a stack class using the generic class of Exercise
5 to a maximum stack size of 1000 elements of type Complex*.

Com m on Program m ing Errors

1. It is an error to declare a class without using exactly one of the keywords


class, struct, or union in the declaration.

2. The keyword class is not needed to create class objects. For example,
we write

class C {

>;

C c l, c2; // object d e fin itio n s

instead of

class C c l, c2; // keyword class not needed

3. If a class member is private, it can be accessed only by class methods


and frien d functions. For example, this code is in error

class C {
in t x ;

p u b lic :
C() { x = -999; }
>;

void f ( )
{
C c;
cout « c.x ; // * * * * * ERROR
>

www.MathSchoolinternational.com
134 CH APTER 3 CLASSES

because x is p riva te in class C and function f is neither a method nor a


frie n d of C.

4. It is illegal to specify a return type, including void, for a constructor or


a destructor in either its declaration or its definition. If C is a class, for
example, then

void C ::~C () // * * * * * ERROR: void not allowed here

>

is illegal.

5. It is illegal to specify any argument, including void, in either the declara­


tion or definition of a class’s default constructor. In class C, for example,
the default constructor

C ::C ( void ) // * * * * * ERROR

>

cannot be defined with any argument.

6. It is illegal to specify any argument, including void, in either the decla­


ration or definition of a class’s destructor.

7. It is illegal to have two constructors for the same class with exactly the
same argument types because there is then no way for the compiler to
determine which version of the constructor should be invoked.

8. It is illegal for a constructor for class C to have an argument of type C.


However, such a constructor can have an argument of type C&, that is, a
C reference. Such a constructor is called the copy constructor.

9. It is illegal for a member declaration to have an initializer:

class C {
// * * * * * ERROR: i l l e g a l in it ia liz a t io n
in t x = 1;
>;

Initialization must be done by a method, typically, a constructor:

class C {
in t x;

www.MathSchoolinternational.com
COM M ON PRO G RAM M IN G ERRORS 135

p u b lic:
// correct in it ia liz a t io n
CO { x = 1; >
>;

10. It is illegal to initialize a const data member in a constructor’s body.


The initialization must occur in the constructor’s header:

class C {
const in t x;
p u b lic:
// * * * * * ERROR: const in it ia liz e d in body
C( in t a ) { x = a; }
>;

class Z {
const in t x;
pu b lic:
// ok
Z( in t a ) : x ( a ) { }
>;

11. Initialization of an object in another class must be done in a constructor’s


header. The code

class N {
in t x;
p u b lic:
N( in t a ) { x = a; }
>;

class C {
in t y;
// * * * * * ERROR: i l l e g a l in it ia liz a t io n
N n( 0 ) ;
p u b lic:
CO { y = 0; >
>;

contains an error because a member declaration cannot have an initializer.


The correct way to initialize n is through C’s constructor:

class C {
in t y;
N n;
p u b lic:

www.MathSchoolinternational.com
136 CHAPTER 3 CLASSES

// correct
CO : n( 0 ) { y = 0; >
>;

12. It is illegal to declare or define a function operator without the keyword


operator. For example, the overloaded + operator for class Complex must
be declared

Complex Complex: : operator+( const Complex ) const;

rather than

Complex Complex::+ ( const Complex ) const; / * * * * * ERROR

13. It is illegal to use method syntax on an overloaded frie n d operator.


Instead, operator syntax must be used. If + is overloaded as a frien d for
class Complex, it must be invoked as

c l + c2 // OK
operator+( c l, c2 ) // OK

rather than as

c l .operator+( c2 ) // * * * * * ERROR: method syntax

14. It is illegal to overload any of these operators:

// class member
.* // class member dereference
:: // scope resolu tion
?: // conditional
s iz e o f // s iz e in bytes

15. A method in a parameterized class such as

template< class Typ >


class C {

in t f ( char ) ;

>;
is a parameterized function and therefore requires a template. For this
reason,

// * * * * * ERROR: template required


in t C< Typ > : : f ( char c )
{

www.MathSchoolinternational.com
PR O G R A M M IN G EXERCISES 137

is an error.
Also, the class is C< Typ > rather than C. Accordingly, the following is
an error:

// * * * * * ERROR: the class is C< Typ >, not C


template< class Typ >
in t C: : f ( char c )
{

>

The correct syntax is

// correct
template< class Typ >
in t C< Typ > : : f ( chax c )
{

Program m ing Exercises

3.1. Implement a Calendar class. The public interface consists of methods


that enable the user to

• Specify a start year such as 1776 or 1900.


• Specify a duration such as 1 year or 100 years.
• Specify generic holidays such as Tuesdays.
• Specify specific holidays such as the third Thursday in November.
• Specify a month-year such as July-1776, which results in a display
of the calendar for the specified month-year.

To simplify matters, you may ignore leap years. Holidays should be


marked so that they can be readily recognized as such whenever the cal­
endar for a month-year is displayed.

3.2. Implement a CollegeStudent class with appropriate data members such


as name, year, expectedGrad, major, minor, GPA, coursesAndGrades,
maritalStatus, and the like. The class should have at least a half-dozen
methods in its public interface. For example, there should be a method
to compute GPA from coursesAndGrades and to determine whether the
GPA merits honors or probation. There also should be methods to display
a CollegeStudent’s current course load and to print remaining required
courses.

www.MathSchoolinternational.com
CH APTER 3 CLASSES

3.3. Implement a Deck class that represents a deck of 52 cards. The public
interface should include methods to shuffle, deal, display hands, do pair­
wise comparisons of cards (e.g., a Queen beats a Jack), and the like. To
simulate shuffling, you can use a random number generator such as the
library function rand.

3.4. Implement a Profession class with data members such as name, t i t l e ,


credentials, education, avglncome, and the like. The public interface
should include methods that compare Professions across the data mem­
bers. The class should have at least a dozen data members and a dozen
methods.

3.5. A queue is a list of zero or more members. A member is added to a


queue at its rear; a member is removed from a queue at its front. If a
queue is empty, then a removal operation is illegal. If a queue is full,
then an add operation is illegal. Implement a generic Queue class with
preconditions, postconditions, and class invariants.

3.6. A deque is a list of none or more members. It is a generalization of a


stack and a queue in that members may be added or removed from either
end. Implement a generic Deque class with preconditions, postconditions,
and class invariants.

3.7. A semaphore is a mechanism widely used in computer systems to en­


force synchronization constraints on shared resources. For example, a
semaphore might be used to ensure that two processes cannot use a printer
at the same time. The semaphore mechanism first grants exclusive ac­
cess to one process and then to the other so that the printer does not
receive a garbled mix from the two processes. Implement a Semaphore
class that enforces synchronization on files so that a process is ensured
exclusive access to a file. The public interface consists of methods that
set semaphore for a specified file, that release a semaphore protecting a
specified file, and that test to determine whether a semaphore is currently
protecting a specified file.

3.8. Implement an interactive Calculator class that accepts as input an arith­


metic expression such as

25 / 5 + 4

and then evaluates the expression, printing the value. In this example,
the output would be

There should be methods to validate the input expression. For example,


if the user inputs

www.MathSchoolinternational.com
PR O G R A M M IN G EXERCISES 139

25 / 5 +

then the output should be an error message such as

ERROR: operator-operand imbalance.

3.9. Implement a Set class, where a set is an unordered collection of none or


more elements with no duplicates. For this exercise, the elements should
be ints. The public interface consists of methods to

• Create a Set.
• Add a new element to a Set.
• Remove an element from a Set.
• Enumerate the elements in the Set.
• Compute the intersection of two Sets SI and S2, that is, the set
of elements that belong both to SI and to S2.
• Compute the union of two Sets SI and S2, that is, the set of ele­
ments that belong to SI or to S2 or to both.
• Compute the difference of two Sets SI and S2, that is, the set of
elements that belong to SI but not to S2.

3.10. Implement a generic Set class (see Programming Exercise 3.9).

3.11. Implement a Bag class. A bag is like a set except that a bag may have
duplicates. For this exercise, the bag’s elements should be ints. The
public interface should support the counterpart operations given in Pro­
gramming Exercise 3.9.

3.12. Implement a generic Bag class (see Programming Exercise 3.11).

3.13. Create a Spaceship class suitable for simulation. One of the construc­
tors should allow us to specify the Spaceship’s initial position in 3-
dimensional space, its trajectory, its velocity, its rate of acceleration, and
its target, which is another Spaceship. The simulation should track a
Spaceship’s movement every clock tick (e.g., every second), printing such
relevant data as the Spaceship’s identity, its trajectory, and so forth. If
you have access to a graphics package such as UNIX curses or Borland
C + + Graphics, you can add graphics to the simulation.

3.14. Implement a Database class where a Database is a collection of tables,


which in turn are made up of rows and columns. For example, the em­
ployee table

www.MathSchoolinternational.com
140 CH APTER 3 CLASSES

Social Security Last Name Department Boss


Number

1 111-11-1234 Cruz Accounting Werdel


2 213-44-5649 Johnstone InfoSystems Michaels
3 321-88-7895 Elders Marketing B ierski

has numbered records, each of which has four fields (Social Security Num­
ber, Last Name, Department, and Boss). The public interface should
allow a user to

• Create a table.
• Change a table’s structure by adding or removing fields.
• Delete a table.
• Add records to a table.
• Remove records from a table.
• Retrieve information from one or more tables at a time using a suit­
able query language.

3.15. This exercise requires access to a basic graphics package such as UNIX
curses or the Borland C + + graphics library. Implement a GeoFig class
with methods that allow the user to draw on the screen geometric fig­
ures such as lines, squares, circles, and the like. The public interface
also should include methods for creating, moving, enlarging, shrinking,
and destroying such objects. The idea behind this exercise is to take
operations already available in a package such as UNIX curses and to en­
capsulate them with a class so that they enjoy the ease-of-use associated
with object-oriented programming.

www.MathSchoolinternational.com
Chapter 4

More On Classes

4.1 Sample Application: A String Class


4.2 M ore on the C opy Constructor
4.3 Friend Classes
4.4 Sample Application: A B in ary Search Tree Class
4.5 Sample Application: A n Iterator Class
4.6 Static D a ta M em bers and M ethods
Com m on Program m ing Errors
Program m ing Exercises

141

www.MathSchoolinternational.com
142 CHAPTER 4 M O RE O N CLASSES

This chapter begins with a sample application, a String class, that intro­
duces important and subtle issues with respect to constructors, destructors,
and assignment operators. We use the sample application to clarify these
issues in subsequent sections and to introduce related material.

4.1 Sample Application: A String Class


P r o b le m ______________________________________________________________________

Implement a string as an abstract data type.

S o lu tio n _______________________________________________________________________

We create a String class with constructors and a destructor that allows


the user to focus on the manipulation of Strings without concern for their
internal representation. For example, the constructors ensure that the array
of char used in the internal representation is null-terminated and that all
dynamically allocated storage is freed before it can become garbage. We
practice information hiding by making a S tring’s internal representation
private. We practice encapsulation by including methods (including con­
structors, a destructor, and overloaded operators) in a String.

C + + Im plem en tation _______________________________________________________

// header f i l e : strin gs.h

// Use an # ifn d ef to guard against the


// case in which th is header f i l e is
// inadvertently #included more than once
// in some other f i l e
# ifn d ef StringHeaderLoaded_Version_l.0
#define StringHeaderLoaded_Version_l.0

class String {
enum SortOrder { Asc, Desc } ;
enum ErrorsIO { ReadFail, W riteFail } ;
char* s tr; // data member — p rivate
in t len; // actual
p u b lic:
// constructors-destructor
S trin g ( ) ; // defau lt constructor
S trin g ( const Stringfe ) ; // copy constructor
S trin g( const in t ) ; // strin g in it ia liz e d to blanks
StringC const char* ) ; // from a C strin g, chair*

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A STRING CLASS 143

"Strin gO ; // destructor
// other methods as functions
in t w r ite ( ) ;
in t w r ite ( FILE* ) ;
in t r e a d ();
in t read( FILE* ) ;
// operators
String operator+( const Stringfe ) const;
in t operator<( const String ) const;
in t operator>( const String ) const;
Stringfe operator=( const Stringfe ) ;
// friends
frien d void s o r t( S trin g*, in t, in t ) ; // selection sort
>;

#endif // StringHeaderLoaded_Version_l.0

// end of header f i l e : strin gs.h

// source code f i l e : strings.cpp

#include <iostream.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "strin gs.h "

// default constructor — create empty strin g


String: :StringO
{
s tr = new char[ 1 ] ; // a llo ca te
*s tr = ’ \0’ ; // i n i t i a l i z e to n u ll strin g
len = 0;
>

// convert constructor
S t r in g ::S trin g ( const char* cstr )
{
// +1 to ensure room fo r n u ll terminator
s tr = new char[ s tr le n ( cstr ) + 1 ] ;
strcp y( s tr, cstr ) ;
len = s tr le n ( s tr ) ;
}

www.MathSchoolinternational.com
CHAPTER 4 M O RE ON

// copy constructor — String ==> String


S tr in g ::S trin g ( const Stringfe strarg )

// copy an e x is tin g S trin g .s tr into a new


// one a fte r f i r s t a llo c a tin g s u ffic ie n t storage
strcp y( s tr = new chart stra rg .le n + 1 ] ,
s tr a r g .s tr ) ;
len = s tra rg .len ;
>

// blank strin g constructor


S t r in g ::S trin g ( const in t lenarg )

s tr = new char[ lenarg + 1 ] ;


len = lenarg;
char* p tr = s tr;

fo r ( in t i = 0; i < len; i++ )


*ptr++ = ’ ’ ;
*p tr = ’ \0’ ; // n u ll terminate
>

// destructor — deallocate storage


S trin g: : "S trin gO

d e le t e [ ] s tr;
>

// w rite String to standard output


// with a terminating newline
in t S tr in g ::w r ite ()
{
return p rin tf ( "*/,s\n" , s tr ) ;
>

// w rite String to s p ecified f i l e


in t S tr in g ::w r ite ( FILE* o u t file )
{
i f ( ! o u t file )
return W riteFail;
else
return f p r in t f ( o u t file , "7,s\n", s tr ) ;
>

www.MathSchoolinternational.com
SAM PLE APPLIC A TIO N : A STRING CLASS 145

// read String from standard input


in t S tr in g ::read()
{
scanf( "°/.s", s tr ) ;

return len = s tr le n ( s tr ) ;
>

// read String from designated f i l e


in t S trin g ::re a d ( FILE* i n f i l e )
{
i f ( !in file )
return ReadFail;
else {
fs c a n f( i n f i l e , "*/.s", s tr ) ;
return len = s tr le n ( s tr ) ;
>
}

// assignment operator
Stringfe S tr in g ::operator=( const Stringfe strarg )
{
// check whether String assigned to i t s e l f
i f ( th is != festrarg ) {
d e le te [ ] s tr; // fre e current storage
len = stra rg .le n ;
s tr = new char[ len + 1 ] ; // a llo c a te new storage
strcpy( s tr , s tra rg .s tr ) ; // copy contents
>
return *th is ;
}

// < operator fo r less than comparisons


in t S trin g::op era tor< ( const String strarg ) const

return strcmp( s tr, s tra rg .s tr ) < 0;


>

// > operator fo r greater than comparisons


in t S trin g::op era tor> ( const String strarg ) const
{
return strcmp( s tr, s tra rg .s tr ) > 0;
>

www.MathSchoolinternational.com
CHAPTER 4 M O RE O N CLASSES

// + operator fo r concatenations
S tring S trin g::op era tor+ ( const Stringfe strarg ) const

// a llo c a te enough storage fo r 2 strings + J\0’


char* temp = new chart len + stra rg .le n + 1 ] ;

// copy 1st strin g and concatenate second


strcp y( temp, s tr ) ;
s tr c a t( temp, s tra rg .s tr ) ;

// define a variab le of type String to be


// used as a return value
S trin g r e t v a l( temp ) ;

// fre e temporary storage


d e le te t ] temp;

return re tv a l;
>

// selectio n sort an array of String


void s o r t( String at ] , in t s iz e , in t order )

in t next;

// set comparison operator based on order parm


in t ( String::*compare_op ) ( const String ) ;
compare_op =
( order == Asc ) ? compare_op = S tr in g :: operator< :
compare_op = S trin g::op erator>;

// loop thru array, picking biggest or smallest


// element each time, depending on sort order
fo r ( in t i = 0; i < s ize - 1; i++ ) {
next = i ; // assume ith String sm allest-biggest

// compare against remaining elements


fo r ( in t j = i + 1; j < s ize ; j++ )
i f ( ( at j ] • *compare_op ) ( at next ] ) )
next = j ;

// put sm allest-biggest at p osition i


i f ( i != next ) {
String temp = at i ] ;

www.MathSchoolinternational.com
4.1 SAM PLE A PPLIC A TIO N : A STRING CLASS 147

a [ i ] = a [ next ] ;
a [ next ] = temp;
>
>
>

D iscussion .------------------------------------------------------------------------------------------------

The class String has two data members: an in t variable len that stores the
S tring’s length and a pointer s tr to the char cells that hold the S trin g’s
value. The char cells include a cell for a ’ \0 ’ , which means that the total
number of cells is the S trin g’s length + 1. The data members are p riva te
by default and so accessible only to a Strin g’s four constructors, its destruc­
tor, the other four member functions, the four member operators, and the
frien d function sort, which does a selection sort on an array of Strings.
The sort may be done in either ascending or descending order. The data
member len is included as a convenience for defining the constructors. The
class String could be created without the len member, as we request in an
exercise. We now examine how the methods and frien d function work.
The default constructor
// default constructor — create empty strin g
String: :StringO
{
str = new char[ 1 ] ; // a llo ca te
* s tr = ’ \0’ ; // i n i t i a l i z e to empty
len = 0;
>
expects no argument and so is invoked whenever a variable of type S tring
is defined without any initializing value. For example, in a definition such
as
String s tr in g l;
the default constructor is invoked. This constructor dynamically allocates a
single char cell, sets its value to the null terminator, and sets its length to
0. Because class member s tr now points to a null-terminated array of char,
s t r in g l. s tr is an ordinary C string and so can be passed as an argument
to library functions such as strlen, strcmp, s tr cat, and the like.
The convert constructor
// convert constructor
S tr in g ::S trin g ( const char* cstr )
{
// +1 to ensure room fo r n u ll terminator
s tr = new chart s tr le n ( cstr ) + 1 ] ;
strcpy( s tr, cstr ) ;

www.MathSchoolinternational.com
CHAPTER 4 M O RE O N CLASSES

len = s tr le n ( s tr ) ;
>
is invoked whenever a String object is defined with an ordinary C string as
its initializing value. For example, in the definition
S trin g strin g2 ( "mercy!" ) ;
this constructor is invoked. The constructor allocates s tr le n ( cs tr ) +
1 char cells so that there are enough cells to hold the C string and the
’ \0 ’ . The constructor then copies the argument into the cells and sets data
member len to s tr le n ( cstr ). The argument to this constructor is const
to ensure that the constructor does not alter the value of the C string while
initializing str.
The copy constructor
// copy constructor — String ==> String
S t r in g ::S trin g ( const Stringfe strarg )

// copy an ex istin g S trin g .s tr into a new


// one a fte r f i r s t a llo ca tin g s u ffic ie n t storage
strcp y( s tr = new char[ stra rg .le n + 1 ] ,
s tra rg .s tr ) ;
len = s tra rg .len ;
>
initializes a newly created String to an already existing String. For exam­
ple, in the code slice
S trin g strin g2 ( "foo 11 ) ;
S trin g strin g3 ( string2 ) ;
the copy constructor is invoked to initialize string3 from the previously
defined string2, which in turn is initialized from an ordinary C string.
Had we not provided our own copy constructor, the compiler would have
provided one for us; but the compiler’s copy constructor would not have
worked properly because a String object includes a pointer to dynamically
allocated storage. Figures 4.1.1 and 4.1.2 contrast the compiler’s default
copy constructor and our own.
The problem with the compiler’s copy constructor is that it does mem­
berwise assignment:
string3.1en = string2.1en;
s trin g 3 .s tr = strin g 2 .s tr;
The memberwise assignment is proper for the len data member but not for
the s tr data member. After the assignment
s trin g 2 .s tr = strin g 3 .s tr;
the two pointers point to exactly the same storage! Our intent is to have
s trin g 3 . s tr and strin g2 . s tr point to different storage cells but to have

www.MathSchoolinternational.com
4.1 SAM PLE APPLIC A TIO N : A STRING CLASS

st r i n gs

Figure 4.1.1 The compiler’s default copy constructor for a String.

strin g 2

f 0 0 \0

strin g3

f 0 0 \0

Figure 4.1.2 Our copy constructor for a String.

www.MathSchoolinternational.com
150 CHAPTER 4 M O RE O N CLASSES

Constructor Type Sample Invocation


String: :S trin gO Default String s i;
S tr in g :: S trin g ( char* ) Convert String s2( "fo o " ) ;
S t r in g ::S trin g ( in t ) Convert String s3( 1000 ) ;
S t r in g ::S trin g ( Stringft ) Copy String s4( s2 ) ;

Figure 4.1.3 Constructor summary for String.

the same chars in these cells. Our copy constructor achieves this goal, but
the compiler’s does not. Our copy constructor first allocates separate storage
for string3 and then uses strcpy to copy strin g2 . s tr into s trin g 3 . str.
The constructor finishes its work by setting strin g3 . len to s trin g 2 . len.
Whenever we create a class with a pointer as a data member, we typically
provide our own copy constructor. If we create a class without any pointers
as data members, we typically let the compiler generate the copy constructor
for us. Our copy constructor’s single argument is const because the copy
constructor does not alter it.
The convert constructor

// blank strin g constructor


S trin g : : S trin g ( const in t lenarg )

s tr = new char[ lenarg + 1 ] ;


len = lenarg;
char* p tr = s tr;

fo r ( in t i = 0; i < len; i++ )


*ptr++ = ’ ’ ;
*p tr = ’ \0’ ; // null terminate
>
creates a blank string of length len and provides an extra chair cell to hold
a null terminator. This constructor would be invoked in a definition such
as

S tring strin g3 ( 100 ) ; // 100 blanks + null terminator

Figure 4.1.3 summarizes the different types of constructor, using the String
constructors as examples.
The destructor

// destructor — deallocate storage


S trin g: : "S trin gO

d e le te [ ] s tr;
>

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A STRING CLASS 151

deallocates however much storage has been allocated by a constructor. For


example, in the case of
String s tr in g l;
the destructor would free the single cell that the default constructor allocates
for s t r in g l. str2. In the case of
String strin g2 ( "bar foo bar" ) ;
the destructor would deallocate 12 cells for s trin g 2 .s tr— 11 for “bar foo
bar” and one for the null terminator. In a code slice such as
void f ( )
{
String s ( "Weir and Peace" ) ;

} // s ’ s destructor fir e s as control e x its f


the constructor
S tr in g ::S trin g ( char* )
is automatically invoked when f is invoked and the object is created. The
destructor
S tr in g :: ~String()
is automatically invoked when f terminates and the object s is destroyed.
There are four other methods: two versions of w rite and two versions of
read. The following code slice illustrates how each might be used:
in t main()
{
// variab le d e fin itio n s
FILE* out = fopen( "DiskOut", "w" ) ;
FILE* in = fopen( "D iskin", "r " ) ;
String s ( "Once upon a midnight d r e a r y ..." ) ;
s .w r ite O ; // w rites to standard output
s .w r ite ( out ) ; // w rites to disk f i l e DiskOut
s.read O ; // reads from standard input
s.read ( in ) ; // reads from disk f i l e Diskin

Calls to s . w rite ( ) and s . w rite ( out ) print s . s tr to the standard output


and DiskOut, respectively. The calls s . read ( ) and s . read ( in ) read a
string from the standard input and Diskln, respectively, storing the string in
the cells to which s . s tr points. These member functions rely upon fp r in t f
and f scanf.
The class String overloads four operators: >, <, +, and =. The operator
< is overloaded by the code

www.MathSchoolinternational.com
CHAPTER 4 M O RE ON CLASSES

// < operator fo r less them comparisons


in t S trin g::o p era to r< ( const String strarg )

return strcmp( s tr, s tra rg .s tr ) < 0;


>
to simplify the syntax of String comparison. The code slice
in t mainO

String s t r in g l( "bonnie" ) ;
String strin g2 ( "clyde" ) ;
i f ( s trin g l < string2 )
cout « "bonnie" « " < " « "clyde" « endl;
else
cout « "clyde" « " <= " « "bonnie" « endl;

>
illustrates how the operator might be used. The output is
bonnie < clyde
The single argument to the overloaded operator is const because the com­
parison does not alter the argument.
The overloaded operator +
// + operator fo r concatenations
S trin g S trin g::op era tor+ ( const Stringfe strarg ) const

// a llo ca te enough storage fo r 2 strings + ’ XO’


char* temp = new char[ len + stra rg .len + 1 ] ;

// copy 1st strin g and concatenate second


strcp y( temp, s tr ) ;
s tr c a t( temp, s tra rg .s tr ) ;

// define a variable of type String to be


// used as a return value
String r e t v a l( temp ) ;

// fre e temporary storage


d e le t e [ ] temp;

return re tv a l;
>
is more complex. Here is a code slice to illustrate its use:

www.MathSchoolinternational.com
4.1 SAM PLE A PPLIC A TIO N : A STRING CLASS 153

int mainO
{
String s l ( "Get a l i f e , George" ) ;
String s2( " and Martha!" ) ;
String s3;
s3 = s i + s2; // Get a l i f e , George and Martha!
s 3 .w r ite (); // prin ts s3 .s tr to standard output

}
The overloaded + is used for string concatentation. Because the operator
returns a value of type String, the line
String r e t v a l( temp ) ;
is needed to convert the C string temp into a String. When r e tv a l is
defined, temp (which points to the concatenation of s i. s t r and s 2 .s tr)
provides the initializing value stored at r e t v a l. str. The constructor
// C strin g to String constructor
S tr in g ::S trin g ( const char* cstr )

// +1 to ensure room fo r n u ll terminator


s tr = new chart s tr le n ( cstr ) + 1 ] ;
strcpy( s tr , cstr ) ;
len = s tr le n ( s tr ) ;
}
is invoked to initialize retva l. Before the + operator function returns, it
frees the storage to which temp points so that no garbage is left behind. The
operator then returns the required value of type String:
return re tv a l;
The class String also overloads the assignment operator = and thereby
gives us a first look at the C + + keyword th is. The compiler sets the value
of th is to the address of the object whose function or operator member is
invoked. We illustrate with a simple example before looking at the body of
the = operator.
The method w rite with no arguments could be changed from the origi­
nal
// w rite String to standard output
// with a terminating newline
in t S tr in g ::w r ite ()
{
return p r in tf ( "'/,s\n", s tr ) ;
>
to

www.MathSchoolinternational.com
154 CHAPTER 4 M O RE O N CLASSES

si

f r e d \0

this
r i

Figure 4.1.4 The pointer this.

// w rite String to standard output


in t S tr in g ::w r ite ()

return p r in tf ( "'/,s\n", th is -> s tr ) ; // th is


>
The two code slices do the same thing. In the code slice
in t main()
{
String s l ( "fre d " ) ;
s i .w riteO ;

>
th is points to String s i (see Figure 4.1.4). The pointer variable th is has
type qualifier const so that it would be an error to use th is as the target
of an assignment expression. Any statement of the form
th is = . . . ; // *** * * ERROR
is wrong. Also, th is is a C + + keyword, which rules out a user-defined
variable or parameter of the same name.
In the case of the = operator,
// assignment operator
Stringfe S tr in g ::operator=( const Stringft strarg )

// te s t whether object assigned to i t s e l f


i f ( th is != ftstrarg ) {
d e le te [ ] s tr;
len = s tra rg .len ;
s tr = new char[ len + 1 ] ;
strcpy( s tr , s tra rg .s tr ) ;
>
return *th is ;
>

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A STRING CLASS 155

th is is needed to check, in the i f statement, whether a String object is


being assigned to itself. For example, in the code slice
int mainO
■C
String s l ( "fre d " ) ;
s i = s i; // poin tless assignment of s i to s i

}
the body of the i f statement is not executed because the test
i f ( th is != &str ) {
fails. (Exercise 6 asks you to investigate the result of removing the i f test
from the operator.) If the String object is not being assigned to itself,
the body of the i f statement frees the current storage to which s tr points,
allocates new storage to hold stra rg .le n + 1 bytes, and copies the string
into this new storage. The operator uses the statement
return *th is ;
to return the current object, that is, the object to which th is points. It
is important that String: :operator= return a S tring reference to enable
constructs such as
in t main()
{
String s l ( "h e llo , world" ) ;
String s2;
String s3;
s3 = ( s2 = s i ) ; // parentheses optional

>
Recall that the assignment operator is, technically, an operator function,
that is, a function that permits operator syntax. So String: :operator=
may be used with the syntax of a binary operator
s2 = s i; // operator syntax
or the syntax of a method
s2.operator=( s i ) ; // method syntax
The point is that String: :operator= expects a String reference as an
argument. Therefore, the statement
s3 = ( s2 = s i ) ; // parentheses optional
should be understood as
s3.operator=( s2.operator=( s i ) ) ; // method syntax
For this to work, the expression

www.MathSchoolinternational.com
156 CHAPTER 4 M O RE O N CLASSES

s2 = s i
must evaluate to a String reference— the argument to s3’s operators
The frie n d function sort uses one of the overloaded comparison oper­
ators (either > or <), the overloaded assignment operator =, and the copy
constructor. The code slice
in t ( S tr in g :: *compare_op ) ( const String ) ;
compare_op =
( order == Asc ) ? compare_op = S tr in g :: operator< :
compare_op = S tr in g ::operator>;
defines the variable compare_op as a pointer to a String method that ex­
pects a const argument of type String and returns an in t (see Section
2.3). The assignment statement sets compare_op to either operator< or
operator>, depending on the value of parameter order. Once compare_op
has been set, we can use it to compare values during the sort:
if ( ( a [ j ] . *compare_op ) ( a [ next ] ) )
next = j ;
For example, if compare_op is operator>, the i f condition tests whether
a [ j ] .s tr is lexicographically greater than a [ next ] .str.
The code
// put sm allest-biggest at p osition i
i f ( i != next ) {
String temp = a [ i ] ;
a [ i ] = a [ next ] ;
a [ next ] = temp;
>
swaps two Strings so that they are in the correct lexicographical order. In
the next section, we illustrate how the assignment operator and the copy
constructor are both at work in this code.

Exercises

1. Explain which constructor is invoked in each of the object definitions:

in t mainO

String s i;
String s2( 1000 ) ;
String s3( "fred " ) ;
String s4( s3 ) ;

>

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A STRING CLASS 157

2. Rewrite the String class without data member len.

3. Explain why the programmer should provide a copy constructor for a


class with a data member that is a pointer.

4. Rewrite the String class without null-terminating the chars to which


s tr points.

5. Rewrite the read and w rite methods so that they use functions such as
puts, gets, fputs, and fgets.

6. Why would it be dangerous to remove the test

i f ( th is != &str ) {

from String: : operator=? If you do not see any danger, remove the test
and do a sample run.

7. Write a method substitute that substitutes len chars for the blanks
used in the constructor

S tr in g ::S trin g ( const in t lenarg )

Recall that this constructor initializes a newly created String to lenarg


blanks.

8. Overload the == operator for class String.

9. Overload the ! = operator for class String.

10. The method String: :w r ite ( FILE* ) does not make its FILE* argu­
ment a const. Explain why not. If the reason is not clear, make the
argument const and do a sample run that invokes the method.

11. Express in words the data type of compare_op in frien d function sort.

12. What is the scope of enum SortOrder?

13. Change the String class so that enum ErrorsIO has file rather than class
scope.

14. The String method operator+ uses local variable r e tv a l of type String
to hold the operator’s return value. Is this local variable necessary? That
is, could you rewrite the operator so that it uses no local variable of type
String?

15. Explain in words how the expression

( a [ j ] . *compare_op ) ( a [ next ] )

works in the sort function. In particular, is there a function call here?

www.MathSchoolinternational.com
158 CHAPTER 4 M O RE O N CLASSES

16. Would the sort still work if sort were a method rather than a friend?
Explain your answer.

17. Why does the String destructor use the d elete [ ] operator rather than
the d elete operator?

18. Illustrate how th is behaves by rewriting the read and w rite methods so
they all explicitly use this.

19. What is the data type of th is in any of the String methods?

20. In which String methods, if any, must th is be explicitly used?

21. Explain the error:

th is = . . . ;

22. Write a String method that replaces, in the internal representation, one
specified character by another. For example, this method would replace
every blank character by, say, the character ’ !’ in the S trin g’s internal
representation.

23. Explain what each of the consts means in the definition of


String::operator+.

4.2 M ore on the Copy Constructor


A copy constructor for class C has the header
C: : C( const C& )
The compiler provides such a constructor if the programmer does not, and
the programmer typically provides one if class C contains a pointer among
its data members. The copy constructor expects a reference as its argument
and, like all constructors, does not return a value. The compiler invokes
the copy constructor in three contexts, regardless of whether the user or the
compiler furnishes the copy constructor. We consider each context separately
and use the String class of Section 4.1 to illustrate.

O bject Definition with Copy Initialization

At times C + + syntax hides rather than reveals underlying operations. This


is particularly true with respect to object initialization, which may appear
to use the assignment operator rather than one of the class’s constructors.

Exam ple 4.2.1. In the code slice

www.MathSchoolinternational.com
M O RE O N THE C O P Y CONSTRUCTOR 159

int mainO
{
// code s lic e 1
String s l ( "RISC versus CISC" ) ; // convert constructor
String s2( s i ) ; // copy constructor
String s3 = s i; // copy, not assignment!

}
s2 is initialized through the copy constructor because s i is a String. Object
s3 also is initialized through the copy constructor, even though the assign­
ment operator seems to be at work. In C + + , the assignment operator is
never at work when a variable is initialized in its definition. The reason can
be seen by reviewing the String assignment operator:
// assignment operator
Stringft S trin g : : operator=( const Stringft strarg )
{
// te s t whether object assigned to i t s e l f
i f ( th is != ftstrarg ) {
d e le te [ ] s tr;
len = stra rg .le n ;
s tr = new char[ len + 1 ] ;
strcpy( s tr , s tr a r g .s tr ) ;
>
return *th is ;
}
Note that the assignment operator uses the d elete [ ] operator to free the
previously allocated storage to which s tr points. However, when a S trin g
such as s3 is initialized in its definition, s3. s tr does not yet point to dy­
namically allocated storage. Instead, s 3 .s tr contains some random value
that, as luck usually has it, constitutes an illegal address. The d e le te [ ]
operator within the assigns lent operator thus could cause an access violation
on the spot or even more si btle mischief later on. (The prudent programmer
assumes that an uninitialis ed pointer points to an illegal address.) A copy
constructor, which is designed precisely for variable initialization, usually
does not contain a d elete [ ] operator because there is no previously allo­
cated storage to delete. Accordingly, the copy constructor is the appropriate
way to initialize s3.
The code slice
int mainO
{
// code s lic e 2
String s l ( "RISC versus CISC" ) ; // convert
String s2; // default

www.MathSchoolinternational.com
CHAPTER 4 M O RE O N CLASSES

s2 = s i; // assignment

to initialize s2. Afterwards, s i is assigned to s2. The copy constructor is


not involved in code slice 2. The d elete [ ] operator within the assignment
operator is not a problem because the default constructor does dynamically
allocate a single cell and does set s2. s tr to this cell’s address. The assign­
ment operator deletes the cell before allocating new storage of size
s tr le n ( s i . s t r ) + 1
to store the String. □

It bears repeating that the assignment operator is never at work in C+-1-


when objects are initialized in their definition.

Exam ple 4.2.2. In the code slice


in t mainO

S tring s i = S trin g( "judy" ) ; //no assignment


S tring s2 = ( String ) "judy"; // no assignment

>
neither the copy constructor nor the assignment operator is at work. The
two statements are equivalent. They are syntactic variants of
String s l ( "judy" ) ; // convert constructor

Just as an individual object may be initialized in its definition, so may


an array of objects. Initial values are enclosed in braces, just as they would
be in initializing an array in C.

Exam ple 4.2.3. The code


char* Cstrs [ ] = { "black h oles",
"white dwarfs",
" supernovas",
"neutron sta rs",
"warped spacetime" } ;

String s ( C strs[ 4 ] ) ; // convert

String S s trs [ ] = { C strs[ 0 ] , // convert


C strs[ 1 ] , // convert

www.MathSchoolinternational.com
4.2 M O R E O N THE C O P Y CONSTRUCTOR 161

Strin gO , // defau lt
s, // copy
C strs[ 2 ] >; // convert
illustrates how an array of String might be initialized. Array Sstrs has five
members because the compiler allocates one String for every initial value.
The initializations include an explicit call to the default constructor to signal
that the third element, Sstrs [ 2 ], should be initialized with the default
constructor. If we left out the default constructor, the compiler would have
allocated only four String cells for Sstrs because, in that case, only four
initial values would have been provided. However, we could specify an array
size and provide fewer initial values:
String s t r s [ 5 ] = { C strs[ 0 ] , // convert
C strs[ 1 ] } ; // convert
Because there are only two initial values, strs [ 2 ], strs [ 3 ], and
strs [ 4 ] are all initialized with the default constructor. □

Class storage allocated with the new statement is always initialized with
the default constructor. It is an error to try to provide initial values for such
storage.

Example 4.2.4. In the code slice


String* strin gs = new S trin g [ 100 ] ; // defau lt a l l 100

// * * *** ERROR: can’ t i n i t i a l i z e storage allocated with new


String* bad = new S trin g [ 2 ] = { "fr e d ", "frie d a " } ;
strings is set to the address of the first of 100 contiguous String cells, each
initialized with the default constructor. In the case of pointer bad, an error
occurs because it is illegal to provide initial values for cells allocated by the
new operator. □

O bjects Passed by Value to a Function

If an object is passed by value to a function, the compiler invokes the copy


constructor to initialize the temporary storage.

Example 4.2.5. The code


#include <stdio.h>
#include "strin gs.h "

void func( String s )

>

www.MathSchoolinternational.com
162 CHAPTER 4 M O RE ON CLASSES

si

f 0 0 \0

in m ain

f 0 0 \0

in fu n c

Figure 4.2.1 Role of copy constructor in call by value.

in t mainO

String s l ( "fo o " ) ;


func( s i ) ;

>
passes String variable s i by value to function func. The copy constructor is
invoked to initialize the storage cells that hold the copy of s i that is passed
to func (see Figure 4.2.1). □

O bjects R eturn ed by Value from a Function

Whenever a class object is returned by value by a function, the compiler calls


the copy constructor to initialize the receiving storage. Recall the code
// + operator fo r concatenations
String S tr in g ::operator+( const Stringfe strarg ) const
{
// a llo c a te enough storage fo r 2 strin gs + ’ NO’
char* temp = new char [ len + stra rg .le n + 1 J;

// copy 1st strin g and concatenate second


strcpy( temp, s tr ) ;
s tr c a t( temp, s tra rg .s tr ) ;

// define String variable fo r return value


String r e t v a l( temp ) ;

www.MathSchoolinternational.com
4.2 M O R E O N THE C O P Y CONSTRUCTOR 163

si

s2

temp

Figure 4.2.2 Result of copy construction in return by value.

// fre e temporary storage


d e le te [ ] temp;

return r e tv a l; // return String value


>
that overloads the + operator so that it can be used for String concatenta-
tion. Here is a sample use:
in t mainO
{
String s l ( "fre d " ) ;
String s2( " d e r f" ) ;
s i + s2; // value ==> fred derf
s i.w r it e ( ) ; // prin ts fred
s 2 .w rite ( ) ; // prin ts derf

>
The concatenation changes neither s i nor s2, as the calls s i.w r it e ( ) and
s 2 .w rite () illustrate. The concatentation operator does return a String
value, which is stored in a temporary String cell initialized with the copy
constructor (see Figure 4.2.2).
In a code slice such as

www.MathSchoolinternational.com
164 CHAPTER 4 M O RE O N CLASSES

in t main()
{
String s l ( "G alileo was lucky." ) ;
String s2( " Bruno was n o t." ) ;
String s3;
s3 = s i + s2; // s3: G alileo was lucky. Bruno was not.

>
the contents of the temporary String cell that holds s i + s2 are assigned
to s3. The assignment operator returns a String reference rather than a
S trin g value, which explains why the copy constructor is not invoked in the
assignment operation. For example, the copy constructor is not invoked in
this code slice:
in t main()

String s l ( "Mick Jagger is older than the P re s !" ) ;


String s2;
s2 = s i; // operator= returns String reference

>

O verloading the Assignment O perator for Classes

If the user does not overload the assignment operator when creating a class,
then the compiler does so. The compiler’s assignment operator, which does
a member by member copy, is likely to cause problems if the class contains
a pointer as a data member.

Exam ple 4.2.6. The code slice


class C {
char* p tr; // points to dynamically alloca ted storage
p u b lic :
C (); // default
C( const char* ) ; // convert
~C() { d e le te [ ] p tr; }
void w r ite ( ) { cout « ptr « endl; }
frien d void setValue( C, char* ) ;
>;

C ::C ()
{
p tr = new char[ 1 ] ;
*ptr = ’ \0’ ;

www.MathSchoolinternational.com
4.2 M O R E O N THE C O P Y CONSTRUCTOR 165

Cl

Figure 4.2.3 After initialization.

C::C( const char* cstr )


{
p tr = new char[ s tr le n ( cstr ) + 1 ] ;
strcpy( p tr, cstr ) ;
>

void setValue( C o b ject, char* cstr )


{
i f ( s tr le n ( o b je c t.p tr ) >= s tr le n ( c s tr ) )
strcp y( o b je c t.p tr, cstr ) ;
>

in t mainO

C c l ( "Charles, hardware" ) ; // convert


C c2; // default

c2 = c l; // c2.ptr == c l.p t r
setValue( c2, "Ada, software" ) ;

c 2 .w r ite (); // Ada, software


c l.w r it e ( ) ; // Ada, software
return EXIT_SUCCESS;
>
defines two objects c l and c2 of type C. After initialization, c l.p t r points
to the string
Charles, hardware
and c2.ptr points to the null string (see Figure 4.2.3). The assignment
statement
c2 = c l;
sets pointer c2.p tr to the same address as pointer c l .ptr (see Figure 4.2.4).
After the call to setValue, c2 .ptr points to the string

www.MathSchoolinternational.com
166 CHAPTER 4 M O RE O N CLASSES

cl

c h a r 1 e s h a r d w a r e \0

\o

Figure 4.2.4 After the assignment statement c2 = c l;


cl

\0

c2

Figure 4.2.5 After the call to setValue.

Ada, software
However, c l.p t r also points to this string because it points to the same
cell to which c2.ptr points (see Figure 4.2.5)! This is not what we want.
We want c l.p t r and c2.ptr to point to different cells that hold different
strings. We can achieve this goal by overloading the assignment operator:
C& C ::operator=( const C& c )

// check whether c is to be assigned to i t s e l f


i f ( th is != fee ) {
d e le te [ ] p tr;
ptr = new char[ strlen( c.ptr ) + 1 ];
strcpy( p tr, c .p tr ) ; // copy contents, NOT address
}
return *th is;
>
With our own copy constructor in place, after
c2 = c l;
we have the situation in Figure 4.2.6. After the call to setValue, c l.p t r
and c2.ptr point to different cells that hold different strings (see Figure
4.2.7). □

www.MathSchoolinternational.com
4.2 M O RE O N THE C O P Y CONSTRUCTOR 167

c h a r 1 e s h a r d w a r e \0

c h a r 1 e s h a r d w a r e \0

Figure 4.2.6 After the assignment statement with the assignment operator over­
loaded.

C a r 1 e s > h a r d w a r e \0

c2

ptr A d a s 0 f t w a r e \0 a r e \0

Figure 4.2.7 After the call to setValue.

Overloading the Equality and Inequality O perators for Classes

It makes sense to overload the equality (==) and inequality (!= ) operators
whenever it makes sense to overload the assignment operator. The equality
and inequality operators are overloaded so they test for equality as defined
by the assignment operator.

Example 4.2.7. For the class of Example 4.2.6 with the assignment op­
erator overloaded, we could overload the equality and inequality operators
as
in t C : :operator==( const C& c )
{
return strcmp( p tr, c .p tr ) == 0;
>

in t C : :operator!= ( const C& c )


{
return strcmp( p tr, c .p tr ) != 0;
>
Now for the situation in either Figure 4.2.4 or Figure 4.2.6, the expression
c l == c2
evaluates to 1 (true). □

www.MathSchoolinternational.com
168 CHAPTER 4 M O RE O N CLASSES

A Sample Program Run

We conclude this section with a program slice that defines Strings, passes
them by value and by reference to functions, returns them by value and by
reference, assigns them to other Strings, and so on. We add print statements
to the constructors, destructor, and String assignment operator to trace
the program’s run. We also have print statements at function entry and exit
points. The sample run’s output illustrates how the constructors, destructor,
and assignment operator behave for Strings.
// * * * * * * * * * * * * code
in t mainO

cout « "\n\tInto m a in O ..." « endl;

void f ( String ) ;
String s i;
String s tr in g s [ ] =
{ "moe", "cu rly ", "la rry " } ;
S tring s2( strings [ 0 ] ) ;
S tring s3;
s3 = ( String ) "natasha";
f ( s3 ) ;

cout « "\n\tOut of m a in O ..." « endl;

return EXIT_SUCCESS;
>

void f ( String s )

cout « "\n\tInto f ( String ) . . . " « endl;

void g ( String ) ;
S tring lo c a l;
g( s );
g ( lo c a l ) ;

cout « "\n\tOut o f f ( String ) . . . " « endl;


>

void g ( String s )
{
cout « "\n\tInto g ( String ) . . . " « endl;

www.MathSchoolinternational.com
4.2 M O R E O N THE C O P Y CONSTRUCTOR 169

String h( String ) ;
String lo c a l;
lo c a l = h( s ) ;

cout « "\n\tOut of g ( String ) . . . " « endl;


}

String h( String s )
{
cout « "\n\Into h( String ) . . . " « endl;

String k( Stringfe ) ;
String lo c a l;
lo c a l = k( s ) ;

cout « "\n\tOut of h( String ) . . . " « endl;

return s;
}

Stringfe k( String s )
{
cout « "\n\tInto k ( String ) . . . " « endl;
cout « "\n\tOut of k ( String ) . . . 11 « endl;

return s;
}

// * * * * * * * * * * * * sample run
Into mainO . . .
Default — S tr in g ::S trin g ( ) ***
Convert — S tr in g ::S trin g( const char* ) *** moe
Convert — S tr in g ::S trin g ( const char* ) *** curly
Convert — S tr in g ::S trin g ( const char* ) *** la rry
Copy — S tr in g ::S trin g ( const Stringfe ) *** moe
Default — S tr in g ::S trin g ( ) ***
Convert — S tr in g ::S trin g ( in t ) ***
Convert — S tr in g ::S trin g ( const char* ) *** natasha
Assignment — S trin g::o p era to r= ( const Stringfe ) *** natasha
Destructor — S tr in g :: "S trin g () *** natasha
Copy — S tr in g ::S trin g ( const Stringfe ) *** natasha

Into f ( String ) . . .
Default — S tr in g ::S trin g ( ) ***

www.MathSchoolinternational.com
CHAPTER 4 M O RE O N CLASSES

Copy — S tr in g ::S trin g ( const S tring* ) *** natasha

Into g( String )...


Default — S tr in g ::S tr in g () ***
Copy — S tr in g ::S trin g ( const Stringfe ) *** natasha

Into h( String )...


Default — String: :StringO ***

Into k( Stringfe ) . . .

Out of k( Stringfe ) . . .
Assignment — S trin g::op erator= ( const Stringfe ) *** natasha

Out of h( String ) . . .
Copy — S tr in g ::S trin g ( const Stringfe s tr ) *** natasha
Destructor — S tr in g :: "S trin gO *** natasha
Destructor — S tr in g :: "S trin gO *** natasha
Assignment — S tr in g ::operator=( const Stringfe ) ** * natasha
Destructor — String::"StringO *** natasha

Out of g ( String ) . . .
Destructor — S tr in g :: "S trin gO *** natasha
Destructor — S tr in g :: "S trin gO *** natasha
Copy — S tr in g ::S trin g( const Stringfe ) ***

Into g( String )...


Default — String: :StringO ***
Copy — S tr in g ::S trin g ( const Stringfe ) ***

Into h( String ) . . .
Default — String: :StringO ***

Into k( Stringfe ) . . .

Out of k( Stringfe ) . . .
Assignment — String::operator=( const Stringfe ) ***

Out of h( String ) . . .
Copy — String::String( const Stringfe ) ***
Destructor — S tr in g :: "S trin gO ***
Destructor — String::"StringO ***
Assignment — String::operator=( const Stringfe ) ***
Destructor — String::"StringO ***

www.MathSchoolinternational.com
4.2 M O R E O N THE C O PY CONSTRUCTOR 171

Out of g ( String )
Destructor — String: ~String() ***
Destructor — String: ~String() ***

Out of f ( String ) . . .
Destructor — String: ~String() ***
Destructor — String: ~String() *** natasha

Out of mainO . . .
Destructor — String: ~String() ***
Destructor — String: ~String() *** natasha
Destructor — String: ~String() *** moe
Destructor — String: ~String() *** la rry
Destructor — String: ~String() *** curly
Destructor — String: ~String() ♦♦♦ moe
Destructor — String: “ S trin g () ***

E xercises

These exercises refer to the String class of Section 4.1.

1. Next to each expression, indicate which constructor or operator is used,

int mainO
{
String si;
String s2 = si;
String s3;
s3 = si;
String s4( "glory days by bs" );
String s5;
s5 = s4;
String s6 = s5;
String s7 = String( "judy blue eyes" );

2. Define an array of Strings and initialize every other element to “widget.”


Elements not initialized to “widget” should be initialized by the default
constructor.

3. Explain the error:

www.MathSchoolinternational.com
172 CHAPTER 4 M O RE O N CLASSES

S tring* strp tr = new S trin g [ 10 ] = { "a ", "b" } ;

4. Which constructor is at work in this code slice?

String* s = new String;

5. If the programmer does not write his or her own copy constructor, is the
compiler’s copy constructor still at work during call by value?

6. Illustrate with pictures how the copy constructor works during a call by
value with a String object.

7. Why should you not rely on the compiler to overload the assignment
operator for class String?

8. Illustrate with pictures how the copy constructor works during return by
value with a String object.

9. Why does String: :operator= check whether a S tring object is being


assigned to itself? What problem would arise if this check were absent?

10. Illustrate with pictures a problem that could arise if we did not provide
String: :operator= for class String.

11. Can you imagine a situation in which it would be a good idea to write
your own copy constructor but not your own assignment operator?

12. Is the copy constructor at work in return by reference? Explain.

13. Is the copy constructor at work in call by reference? Explain.

14. Explain why the == operator should be overloaded for class String.

15. Explain why the != operator should be overloaded for class String.

4.3 Friend Classes


A class’s p rivate members, typically its data members, are accessible only
to its methods and whatever other functions are designated as friends. A
frie n d function may be either a toplevel function (i.e., a function that is
not a method) or a method in some other class.

Exam ple 4.3.1. Class C


class F {
in t adm; // data member
p u b lic:
in t f ( ) ;
>;

www.MathSchoolinternational.com
FRIEND CLASSES 173

class C {
in t cdm; // data member
p u b lic:
in t m (); // method
frien d in t t ( ) ; // to p le v e l frien d
frien d in t F : : f ( ) ; // method frien d
>;
has two frien d functions, t and F: :f. As the scope resolution operator
makes clear, F : : f is a method in class F. By contrast, t is a toplevel function.
The two frien d functions have the same access to C’s p rivate members,
however. Either frien d can access C: : cdm. □

Making a method in class F a frien d of class C is a piecemeal approach


to granting F access to C. Suppose, however, that F has 20 or so methods,
each of which is to be a frien d to C. We could take the piecemeal approach
and make each of F’s methods a friend. A wholesale approach is to make
F a frien d class.

Example 4.3.2. The code

class F -(
in t adm; // data member
p u b lic:
in t f ( ) ;
... // other methods
>;

class C {
frien d F ; // class F a frien d
in t cdm; // data member
p u b lic:
in t m (); // method
frien d in t t ( ) ; // to p le v e l frien d
>;
makes F a frien d class of C. As a result, any method in F has full access to
all members of C, even p riva te ones. For instance, F : : f can access p riva te
data member C: : cdm. □

The frien d relationship between F and C is not symmetric. Class F


is a frien d to C, which means that all F’s methods have access to all C’s
members. By contrast, class C is not a frien d to F, which means that C’s
methods have no access to F’s p riva te members. Of course, F’s public
members (in this case, its methods) are accessible to C’s methods as they

www.MathSchoolinternational.com
174 CHAPTER 4 M O RE O N CLASSES

are to any other functions. Two classes can befriend each other so that the
relationship is symmetrical.

Exam ple 4.3.3. The code


class C; // forward declaration

class F {
frien d C; // C a frien d to F
in t adm; // data member
public:
// other methods
>;

class C {
frie n d F; / / F a frien d to C
in t cdm; // data member
p u b lic :

>;
makes F a frien d to C, and C a frien d to F. Note that class C is declared in
abbreviated form above F’s declaration so that its name is visible in F when
C is designated a friend. □

The frien d relationship among classes is not transitive. If class P is a


frie n d of class Q, which in turn is a frien d of class R, it does not thereby
follow that P is a frien d of R. If we want P to be a frie n d of R, then we
must explicitly declare P to be so.
A frien d class, like a frien d function, is a C + + convenience that strains
the spirit of object-oriented programming because it compromises the prin­
ciple that a class’s p rivate members should be hidden except within the
class. Nonetheless, as we shall see in Section 4.4, there are situations in
which a frien d class, like a frien d function, makes programming easier.

Exercises

1. Write class declarations for Cl, C2, and C3 so that Cl is a frie n d to C2,
which in turn is frien d to C3.

2. If Cl is a frien d of C2, which in turn is a frien d of C3, is Cl automatically


a frien d of C3 as well?

3. If class C is a frien d of class D, is there any difference in the access rights


of C’s methods and D’s methods with respect to D’s p rivate members?

www.MathSchoolinternational.com
4.4 SAM PLE A PPLIC A TIO N : A B IN A R Y SEARCH TREE CLASS 175

4. Give an example in which it makes more sense to have C and D be mutual


friends instead of combining them into a single class.

5. Is the frien d relationship symmetric? Explain.

6. Does it make sense for a class to be a frien d to itself? Explain.

4.4 Sample Application: A Binary Search Tree Class


P r o b le m ______________________________________________________________________

Implement a binary search tree as a class. A binary search tree is a binary


tree in which each node contains data. The data are arranged so that, for
any node N in binary search tree T, any data item in N’s left subtree is less
than or equal to the data item in N; and any data item in N’s right subtree
is greater than the data item in N.

S o lu tio n _______________________________________________________________________

We use two classes, Node and BST, with BST a frie n d to Node. The class BST
consists of Nodes ordered as described previously. We practice information
hiding by having only priva te data members in BST and Node. Making BST
a frien d to Node eases the programming but does compromise information
hiding. We practice encapsulation by including methods in both classes.

C + + Im plem entation_______________________________________________________

#include <iostream.h>

const char None = ’ ’ ;

class BST; // forward declaration

class Node {
frien d BST; // frie n d class
char v a l; // value == contents
Node* lc ; // l e f t ch ild
Node* rc; // rig h t ch ild
p u b lic:
Node(); // default constructor
in t emptyO; // va l == None
void w r ite ( ) ;
>;

www.MathSchoolinternational.com
CHAPTER 4 M O RE O N CLASSES

class BST {
Node* root;
Node* tre e ;
void addNodeAux( const char ) ;
void inorderAux( Node* ) ;
p u b lic :
BSTO;
void addNode( const char ) ;
void in order( ) ;
>;

Node::Node()

lc = rc = 0;
v a l = None;
>

in t Node: : empty()

return va l == None;
>

void N o d e::w rite()


{
cout « va l;
>

BST::BST()

root = tre e = new Node;


>

void BST: : addNode( const char v )

tre e = root; // sta rt at root


addNodeAux( v );
>

void BST:: addNodeAux( const char v )

// i f root of current subtree is


// empty,
// — store v there
// — create a l e f t and righ t subtree

www.MathSchoolinternational.com
4.4 SAM PLE A PPLIC A TIO N : A B IN A R Y SEARCH TREE CLASS 177

// — return
i f ( tree -> empty() ) {
tree -> va l = v;
tree -> lc = new Node;
tree -> rc = new Node;
return;
>

// otherwise, search eith er l e f t or rig h t


// subtree fo r Node to store v
i f ( v <= tre e -> va l )
tree = tre e -> lc ;
else
tree = tree -> rc;

addNodeAux( v ) ;
>

void BST:: in order()


{
inorderAux( root ) ;
>

void BST:: inorderAux( Node* n )


{
// i f current subtree is empty,
// return as there are no nodes
// to traverse in i t
i f ( n -> emptyO )
return;

inorderAux( n -> lc ) ; // traverse l e f t subtree


n -> w rite O ; // v i s i t Node
inorderAux( n -> rc ) ; // traverse rig h t subtree
>

D iscussion____________________________________________________________________

Classes Node and BST are interdependent: Node has BST as a friend, whereas
BST has two data members of type Node*. To make BST visible to Node’s
declaration, we place the forward declaration
class BST; // forward declaration
above Node’s declaration, which in turn occurs above the declaration for BST.
We make BST a frien d of Node so that BST methods such as addNodeAux

www.MathSchoolinternational.com
CHAPTER 4 M O RE O N CLASSES

and inorderAux can access Node’s p riva te data members lc and rc. As we
add other BST methods, they too will have access to p riva te data members
in Node objects. Making individual BST methods into friends of Node would
be less convenient, especially as we add to these methods.
The default BST constructor
BST::BST()

root = tre e = new Node;


>
dynamically allocates a Node object and has root and tre e point to it. The
default constructor for Node
Node::Node()

lc = rc = 0;
v a l = None;
>
sets pointers lc and rc to null address 0 before setting v a l to value None,
which is used to identify an “empty” Node. Pointer root always points to
the root of the entire binary search tree, whereas tre e points to the current
subtree. Initially, the entire tree is the current subtree. This approach lets
us write BST methods addNode and addNodeAux with only one argument,
the v a l to be stored in a Node:
void BST::addNode( const char v )
■C
tre e = root; // sta rt at root
addNodeAux( v ) ;
>

void BST: : addNodeAux( const char v )


{
// i f root of current subtree is
// empty,
// — store v there
// — create a l e f t and righ t subtree
// — return
i f ( tre e -> empty() ) {
tre e -> v a l = v;
tre e -> lc = new Node;
tre e -> rc = new Node;
retu rn;
>

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A B IN A R Y SEARCH TREE CLASS 179

// otherwise, search eith er l e f t or righ t


// subtree fo r Node to store v
i f ( v <= tree -> va l )
tree = tre e -> lc ;
else
tree = tre e -> rc;

addNodeAux( v ) ;
}

The BST method addNode sets tre e to root to ensure that the search
always starts at the root of the entire tree. Method addNodeAux does the
work. If the current Node (i.e., the root of the current subtree) is empty,
then addNodeAux stores the value there and creates a left and a right child
before returning. Otherwise, the method sets tre e to the current Node’s left
or right subtree before invoking itself. Note that addNodeAux is p riva te in
BST but that addNode is public. There is a similar relationship between
inorderAux, which is private, and inorder, which is public. The two
public methods are the BST’s public interface, whereas the p rivate methods
represent a functional decomposition of the public methods. A user adds
a Node to a BST by invoking the public method only, which then turns the
task over to a p riva te method.
Once a BST has been built, method inorder is invoked to perform an
inorder traversal of the tree. The method traverses Node’s left subtree, prints
Node’s val, and then traverses Node’s right subtree. The recursive method
starts at the tree’s root so that the traversal visits every Node.
The BST methods addNodeAux and inorderAux illustrate two styles of
recursion. Before each recursive call, addNodeAux sets tre e to its left subtree
(lc ) or its right subtree (rc), thereby determining the direction of the search
for an empty Node. The only argument passed is v, the data item to be
stored in the appropriate empty Node. By contrast, inorderAux changes its
parameter to either the left or the right subtree of the current Node. Each
recursive method is p rivate so that the user can remain oblivious to this
dreaded programming technique while benefiting from its power.
Finally, here is a code slice that illustrates the BST methods:

in t main()
{
BST bst; // create binary search tree
bst.addNode( ’ K’ ) ; // add K node
bst.addNode( ’ G’ ) ; // etc.
bst.addNode( >F’ ) ;
bst.addNode( ’ I ’ ) ;
bst.addNode( ’ H’ ) ;
bst.addNode( );

www.MathSchoolinternational.com
180 CH APTER 4 M O RE O N CLASSES

bst.addNode( );
bst.addNode( ’ X’ );
b s t. addNode( ’ S’ );
bst.addNode( ’ A’ );
b s t . in order( ) ; // p rin ts: AFGHIKPRSX

return EXIT.SUCCESS;
>

Exercises

1. If we restrict BST: : v a l to letters, will the inorder traversal print them in


alphabetical order no matter what the order in which they are addNoded?

2. Write a BST method to perform a preorder traversal.

3. Write a BST method to perform a postorder traversal.

4. Rewrite BST using root as the only data member of type Node*.

5. In general, when should a method be made p rivate rather than public?

6. Write BST:: ~BST() so that it systematically deletes all Nodes in the bi­
nary tree. Hint: The destructor should d elete the left and right subtrees
before deleting the root.

7. Explain why

class BST {
const Node* root; // changed from Node* root

>;

will not work in the current implementation of BST.

8. Do a hand trace of

bst.addNode( ’ T ’ ) ;

Assume the statement occurs at the end of the code slice immediately
preceding these exercises.

9. Explain why BST needs to be a frien d to Node.

10. Explain why Node does not need to be a frien d to BST.

11. Write a code slice that creates a BST object.

12. Write a copy constructor for a BST.

www.MathSchoolinternational.com
4.5 SAM PLE A PPLIC A TIO N : A N ITE R A TO R CLASS 181

4.5 Sample Application: A n Iterator Class


P r o b le m ____________________________________________________

An iterator class or cursor class is a class whose objects can scan an


aggregate one element at a time and, in this sense, iterate through the ag­
gregate. The problem here is to implement an iterator class for the BST class
of Section 4.4. An object in this iterator class iterates through the nodes
of a binary search tree. The iteration is to be nondestructive; that is, the
iteration must not alter the binary search tree.

S o lu tio n _______________________________________________________________________

We implement an iterator class IterBST as a frie n d to class BST and class


Node from Section 4.4. The class IterBST treats a BST as a list of Nodes
through which it steps one Node at a time. We practice information hiding
by making all data members private, although the use of frien d classes
BST and IterBST does compromise information hiding in the case of Node.
We practice encapsulation by including methods in all three classes.

C + + Im plem entation________

#include <iostream.h>

const char None = ’ ’;

class BST;
class IterBST;

class Node {
frien d BST; // frien d class
frien d IterBST; // frien d class
char v a l; // value == contents
Node* lc ; // l e f t child
Node* rc; // righ t child
p u b lic:
N o d e(); // constructor
in t empty( ) ; // va . == None
void w rite O ;
>;

class IterBST {
Node** nodeStack; // l i s t of Nodes in BST
in t nextNode; // index into l i s t
in t nodeCount; // to ta l Nodes in BST

www.MathSchoolinternational.com
CHAPTER 4 M O RE ON CLASSES

void stackNodes( BST* ) ;


void stackNodesAux( Node* ) ;
p u b lic :
IterBST( BST* ) ;
"IterBSTO { delete [ ] nodeStack; }
Node* getNextNodeO ;
>;

class BST {
frien d IterBST;
in t count;
Node* root;
Node* tree;
void addNodeAux( const char ) ;
void inorderAux( Node* ) ;
p u b lic :
BSTO;
void addNode( const char ) ;
void inorder( ) ;
>;

IterB S T :: IterBST( BST* bst )

nodeCount = bst -> count;


nodeStack = new Node*[ nodeCount ] ;
nextNode = 0;
stackNodes( bst ) ;
nextNode = 0;
>

void IterB ST:: stackNodes( BST* bst )

stackNodesAux( bst -> root ) ;


>

// inorder traversal
void IterB ST:: stackNodesAux( Node* n )
{
i f ( n -> empty( ) )
return;
stackNodesAux( n -> lc ) ;
nodeStack[ nextNode++ ] = n;
stackNodesAux( n -> rc ) ;

www.MathSchoolinternational.com
4.5 SAM PLE APPLIC A TIO N : A N ITE R A TO R CLASS 183

Node* IterBST::getNextNode()

Node* re tv a l = nodeStack[ nextNode ] ;


nextNode = ( nextNode + 1 ) '/, nodeCount;
return re tv a l;
>

Node: :Node()
{
lc = rc = 0;
val = None;
}

int Node: : empty()


{
return val == None;
>

void Node: :w rit e ()

cout « val;
>

BST::BST()
{
root = tree = new Node;
count = 0; //n o nodes yet
>

void BST: : addNode( const char v )


{
tree = root; // start at root
addNodeAux( v ) ;
>

void BST:: addNodeAux( const char v )

if ( tree -> empty( ) ) {


tree -> val = v;
count++; // increment count
tree -> lc = new Node;
tree -> rc = new Node;
return;
>

www.MathSchoolinternational.com
184 CHAPTER 4 M O RE ON CLASSES

if ( v <= tre e -> v a l )


tre e = tre e -> lc ;
else
tre e = tree -> rc;
addNodeAux( v ) ;

void BST:: inorder0


{
tre e = root;
inorderAux( root ) ;
>

void BST: : inorderAux( Node* n )


{
i f ( n -> emptyO )
return;
inorderAux( n -> lc ) ;
n -> w r ite ( ) ;
inorderAux( n -> rc ) ;
>

D iscu ssion ________________________________

An IterBST has a convert constructor that expects a single argument, a BST.


The code slice
Node* n;
BST bst; // create a BST
bst.addNode( ’ K ’ ) // add a Node. . .
bst.addNode( ’ G’ ) // and another. . .
bst.addNode( ’F ’ ) // and another
IterBST i t e r ( bst ) ; // create inorder ite r a to r
n = i t e r .getNextNodeO ; // get 1st Node — F
n -> w r ite ( ) ; // w rite i t
n = iter.getN extN od eO ; // get 2nd Node — G
n -> w r ite ( ) ; // w rite i t
n = iter.getNextNodeO ; // get 3rd Node — K
n -> w r ite ( ) ; // w rite i t
illustrates how an IterBST might be created and used to iterate through
the Nodes in a BST. This particular IterBST steps through the Nodes in-
order, although it could be modified to step through them preorder or pos­
torder as well (see Exercises 1 and 2). Accordingly, F is the first Node that
getNextNode returns.

www.MathSchoolinternational.com
4.5 SAM PLE A PPLIC A TIO N : A N ITE R A TO R CLASS 185

The iterator’s constructor


IterB S T:: IterBST( BST* bst )
■c
nodeCount = bst -> count;
nodeStack = new Node*[ nodeCount ] ;
nextNode = 0;
stackNodes( bst ) ;
nextNode = 0;
>
takes advantage of a change made to the BST class; namely, a BST keeps a
count of how many Nodes it has. Because IterBST is a frien d class of BST,
IterBST can use this count to allocate a nodeStack that stores a pointer to
each Node in the BST. The IterBST data member nextNode, an index into
nodeStack, is initialized to zero so that it references the first Node in the
iterator list. Each time the iterator accesses a Node, nextNode is updated
so that the subsequent call to getNextNode returns the next Node in the
iterator list:
Node* IterB S T ::getNextNode()

Node* r e tv a l = nodeStack[ nextNode ] ;


nextNode = ( nextNode + 1 ) */, nodeCount;
return r e tv a l;
}
The modulus operator '/, is used so that the iterator steps from the last Node
in the iterator list back to the first.
An IterBST must be a frien d to a BST in order to access BST data
members such as root and count. An IterBST also must be a frien d to a
Node. To build the iterator list, an IterBST traverses a BST. The traversal
requires access to Node data members lc and rc, which point to a Node’s
left and right subtrees, respectively.

Exercises

1. Change IterBST so that the iterator list reflects a preordertraversal.

2. Change IterBST so that the iterator list reflects apostorder traversal.

3. Explain why IterBST is a frien d toBSTand Node.

4. Consider the code slice:

www.MathSchoolinternational.com
186 CHAPTER 4 M O RE ON CLASSES

in t mainO
{
Node* n;
BST bst; // create a BST
b s t.addNode( ’K’ // add a Node. . .
b s t.addNode( ’ G’ // and another..
b s t.addNode( >F > // and another

IterBST i t e r ( bst ) ; // create an ite r a to r


bst.addNode( ’ Z ’ ) ; // add another node

Our IterBST would not see a Node such as Z that is added to the BST
after the BST is created and initialized. Change IterBST so that it would
see Nodes added to the BST after the IterBST is created. Hint: Data
member BST: : count counts every Node added to a BST.

5. Explain why IterBST does not need friends.

6. Overload the assignment operator for IterBST.

7. Rewrite IterBST: :getNextNode so that it returns 0 if there are no more


Nodes. This change would allow code such as

while ( n = it e r .getNextNodeO ) // loop u n til 0


n -> w r ite ( ) ;

4.6 Static Data Members and Methods


A class may have s ta tic data members and methods. We look first at
s ta tic data members and then at s ta tic methods.

Static D a ta M em bers

A s ta tic data member belongs to a class as a whole, not to any particular


object within the class. Storage for a s ta tic data member is allocated
once— in its definition— and this storage is shared by all of the class’s objects.
A nonstatic data member, by contrast, belongs to a particular object in the
class. A s ta tic data member is declared with the keyword s ta tic inside
the class declaration and is then defined without the keyword s ta tic outside
all blocks. In particular, a s ta tic data member must be defined outside the
class declaration. In its definition, a s ta tic data member is referenced
with the scope resolution operator as C: :sdm, where sdm is a s ta tic data
member in class C. A public s ta tic data member sdm in class C can be
referenced outside the class as C: : sdm or obj : : sdm if obj is an object in

www.MathSchoolinternational.com
STATIC DATA MEMBERS AND METHODS 187

class C. Except for its definition, a p riva te s ta tic data member cannot be
accessed outside the class.

Example 4.6.1. The declaration for class C


class C {
s ta tic in t sdm; // s ta tic data member

>;
includes a declaration for s ta tic data member sdm. The declaration does
not allocate storage. Accordingly, C: : sdm must be defined elsewhere as, for
example:
in t C::sdm = -999; // define s ta tic C::sdm and i n i t i a l i z e
// keyword s ta tic NOT used!
in t mainO
{

}
Note that C: : sdm’s definition includes the scope resolution operator. If
the scope resolution operator were omitted
in t sdm = -999; // define an extern va riab le named sdm
// that is NOT the same as C: : sdm
then we would be defining an extern variable named sdm (i.e., : : sdm) rather
than C: : sdm.
A s ta tic data member must be defined outside all blocks. It would be
an error, for example, to try to define C: : sdm inside main:
in t mainO
{
// * * * * * ERROR: s ta tic data member can’ t be
// defined inside a block
in t C :: sdm = -999;

>

Because a s ta tic data member belongs to the class as a whole, not to


any particular object within the class, no constructor is invoked to initialize
a s ta tic data member, and the destructor never destroys a s ta tic data
member. After all, constructors and destructors deal with particular class
objects. We can approach the same point from a different angle. Each object
in a class has its own copies of the class’s data members, which have an auto
storage class by default; an object’s data members come into existence and
go out of existence as the object does.

www.MathSchoolinternational.com
188 CHAPTER 4 M O RE ON CLASSES

Exam ple 4.6.2. Data members for object s

// f ’ s body is the containing block fo r


// String s ’ s d e fin itio n so that s is
// v is ib le only within f
void f ( )
{
String s; // define String object s with
// data members strin g and length

} // s goes out of existence when f e x its

come into existence when s is defined in f . Data members s . s tr and s . len


likewise go out of existence when s does: when control leaves f . Because the
scope of object s is the body of f , the scope of s’s data members is also the
body of f . □

A class’s s ta tic data members do not depend upon particular class


objects. Indeed, a s ta tic data member can be defined before any object
belonging to the class is defined.

Exam ple 4.6.3. In the code slice

class C {
char* name;
in t id;
s ta tic in t sdm;

>

in t C::sdm; // C::sdm is defined


//no object in C exists yet
in t mainO
{
void f 0 ;
f();

>

void f ( )

C s; // 1st C object ex its


C r; // 2nd C object ex ists

>

www.MathSchoolinternational.com
4.6 STATIC DATA MEMBERS AND METHODS 189

C::Sdm

Figure 4.6.1 s ta tic data member shared by two class objects.

the s ta tic data member C :: sdm is defined before the first class object s is
defined. Objects s and r have their own copies of data members name and
id, but the two share the s ta tic data member sdm (see Figure 4.6.1). If a
s ta tic data member is defined without an initial value, then the compiler
initializes it to zero. So, for example, given this definition of C:: sdm

in t C::sdm; // in it ia liz e d by compiler to 0

the compiler initializes C: : sdm to zero. □

A s ta tic data member is useful when we need to track information


about the class as a whole. It is appropriate that such information should
not be stored in any particular class object. It is also appropriate that such
information should be associated with the class and not stored in, say, an
independent global variable.

Example 4.6.4. We amend the declaration for class String to include a


s ta tic data member:

class String {
char* s tr; // data member — p rivate
in t len; // actual
s ta tic in t count; // s ta tic data member
p u b lic :

frie n d void p rin t_c o u n t(); // access count


};

void print_count()
<
cout « "Total strin gs ==> " « S trin g : : count « endl;
}

www.MathSchoolinternational.com
190 CHAPTER 4 M O RE O N CLASSES

Data member count is s ta tic and tracks the total number of String objects
created in the program. Function print_count is a frie n d that can access
this data member in order to print its value. We make count a s ta tic data
member rather than, say, an extern variable in order to associate count with
class String. For count to reflect the current number of S tring objects,
the String constructors should be amended to include the statement
count++;
and the String destructor should be amended to include the statement
count— ;

If a s ta tic data member is private, then it is visible only to function


members and frien d functions. If a s ta tic data member is public, then
it may be accessed using its full class name.

Exam ple 4.6.5. Data member sdm is a s ta tic yet public data member
in class C
class C {
p u b lic :
s ta tic in t sdm; // declare

>

C::sdm = -999; // define


and so can be accessed anywhere in the program using its full name as, for
example, in this code slice:
void f ( )

while ( C::sdm < 0 ) { // C::sdm is public

>

>
Of course, class C must be visible to f . □

Static M ethods

Although a class’s methods (including the constructors and the destructor)


and frien d functions have access to s ta tic data members, these members
may be accessed in another and more restrictive way: through s ta tic meth­
ods. A s ta tic method, like a s ta tic data member, belongs to the class as
a whole and not to any particular object in the class. A s ta tic method may

www.MathSchoolinternational.com
4.6 STATIC DATA MEMBERS AND METHODS 191

access only s ta tic data members. An attempt to access nonstatic data


members is illegal and, indeed, impossible because a s ta tic method does
not have a th is pointer to any particular class object.

Example 4.6.6. In the code slice


class C {
in t id ; // nonstatic data member
s ta tic in t count; // s ta tic data member
p u b lic:
s ta tic void count_up();
>;

in t C::count = 0; // define s ta tic data member

void C :: count_up() // define s ta tic method


{
count++; // le g a l — count s ta tic
id = 999; // * * * * * ERROR: id nonstatic
>
C has two data members, the nonstatic member id and the s ta tic member
count. The s ta tic method C: : count_up is declared inside C’s class dec­
laration and then defined, like C: : count, outside C’s declaration. However,
C: : count_up could have been defined inside C’s declaration. Theerror in
this example occurs because C: : count_up, as a s ta tic method,canaccess
only s ta tic data members and id is not s ta tic . □

A class’s constructors and destructor cannot be s ta tic . If they could


be, they would be unable to initialize and clean up storagefornonstatic
data members and, in this sense, would be worthless. A s ta tic method
is highly specialized in that its sole task is to manipulate whatever s ta tic
data members a class may have.

Example 4.6.7. We amend the String class so that it has a s ta tic method
to increment the s ta tic data member count:
class String {
char* s tr; // data member — p rivate
in t len; // actual
s ta tic in t count; // count declared here
p u b lic :

// s ta tic functions
s ta tic void count_up() { count++ } ;

>;

www.MathSchoolinternational.com
192 CHAPTER 4 M O RE ON CLASSES

void print_count()

cout « "Total strings ==> " « S trin g: : count « endl;


>
The constructor such as String: : ( char* ) is then changed from
// char* constructor
S t r in g ::S trin g( const char* cstr )

// +1 to ensure room fo r n u ll terminator


s tr = new char[ s tr le n ( cstr ) + 1 ] ;
strcpy( s tr , cstr ) ;
len = s tr le n ( s tr ) ;
count++; // another String created
>
to
// char* constructor
S t r in g ::S trin g( const char* cstr )

// +1 to ensure room fo r n u ll terminator


s tr = new char[ s trle n ( cstr ) + 1 ] ;
strcpy( s tr, cstr ) ;
len = s tr le n ( s tr ) ;
count_up(); // increment sta tic data member count
>

As Example 4.6.7 illustrates, nonstatic methods such as constructors


and destructors may manipulate s ta tic data members. The constructor in
Example 4.6.7 increments the s ta tic data member count.

Exercises

1. Can a sta tic data member be defined as well as declared within a class
declaration?

2. Can a s ta tic method be defined as well as declared within a class dec­


laration?

3. Explain the error.

www.MathSchoolinternational.com
STATIC DATA MEMBERS A N D METHODS

class X {
in t x;
s ta tic in t sX;

>;

in t mainO
{
X: :sX = -999;

4. Explain the error.

class X {
in t x;
s ta tic in t sX;

>;
sX = -999; // d e fin itio n of X::sX

5. Explain the error.

class X {
in t x;
s ta tic in t sX;

>;
s ta tic in t X::sX = -999;

6. Must a s ta tic data member be private?

7. Is this code legal?

class Z {
in t z ;
p u b lic:
s ta tic in t sZ;
>;
in t Z: : sZ = 10;

in t main()
{

www.MathSchoolinternational.com
194 CHAPTER 4 M ORE O N CLASSES

8. Explain the error.

class C {
in t c;
s ta tic in t sC;
p u b lic :
s ta tic void fC () { count « c; >

>;

9. Explain the difference in access rights between a s ta tic method and a


frie n d function.

10. Can a constructor be sta tic? Explain.

11. Can a destructor be sta tic? Explain.

12. Explain the error.

class C {
in t c;
s ta tic in t sC;
p u b lic:
s ta tic void fC ()
{ cout « th is -> sC; }

>;

13. Explain the advantage of using a s ta tic data member instead of a global
variable.

Com m on Program m ing Errors

1. Although the programmer is never obligated to write a copy constructor


or to overload the assignment operator, it is usually a mistake not to do
so for a class that contains a pointer as a data member. It also is a good
idea to overload == and ! = for any class with a pointer as a data member.

2. It is an error to have a parameter named this, which is a C + + keyword.

3. It is an error to use th is as the target of an assignment expression such


as

th is = . . . ; // *** * * ERROR: th is is const

because th is is a constant.

www.MathSchoolinternational.com
CO M M O N PRO G RAM M IN G ERRORS 195

4. It is an error for class C to have a constructor that expects an argument of


type C. However, a constructor may take a C reference as an argument:

class C {

>;

C::C(Cc) // * * * * * ERROR
{

C::C( C& c ) // ok
{

>

5. It is an error to provide more initializing values than there are cells in an


array:

// * * * * * ERROR: 2 c e lls , 3 i n it ia liz in g values


String s tr in g s [ 2 ] = { "good", "bad", "ugly" } ;

However, it is legal to provide fewer initializing values than there are cells
in an array:

//ok
String s tr in g s [ 12 ] =
{ "good", "bad", "ugly" } ; // only 3 values

6. It is an error to provide initializing values for an object created with the


new operator:

String* strin gs = new S trin g [ 3 ] =


{ "good", "bad", "ugly" } ; // * * * * * ERROR

The default constructor, if present, is invoked automatically to initialize


the array elements.

7. It is an error to attempt to define a s ta tic data member within a class


declaration, although a s ta tic data member must be declared within the
class declaration. Note the difference:

class C {
s ta tic in t sdm = 6 ; // * * * * * ERROR

>;

www.MathSchoolinternational.com
196 CHAPTER 4 M O RE O N CLASSES

class D {
sta tic int sdm; // ok — declaration

>;

// ok — d efin itio n with in it ia liz a t io n


D :: sdm = 6;

8. It is an error to define a sta tic data member inside a block:

class D {
sta tic int sdm; // ok — declaration

>;

void f ( )
{
// * * * * * ERROR: d efin itio n in a block!
D: : sdm = 6;

>

9. It is an error to define a sta tic data member with the keyword static.
However, the data member must be declared with the keyword static:

class D {
sta tic int sdm; // ok — declaration

>;

// * * * * * ERROR: no keyword static


s ta tic D::sdm = 6;

10. It is illegal to attempt to reference a public sta tic data member with­
out the scope resolution operator:

class C {

p u b lic :
sta tic int sdm; // declaration

>;

C::sdm = 6; // d efin itio n + in it ia liz a t io n

www.MathSchoolinternational.com
PR O G R A M M IN G EXERCISES 197

void f ( )
{
i f ( sdm > 0 ) // * * *** ERROR: name is C::sdm

>

11. It is an error to make a constructor or destructor sta tic.

12. It is an error to use the keyword th is within a s ta tic method.

13. It is an error for a s ta tic method to reference a data member that is not
static.

P rogram m ing Exercises

4.1. Applications in fields such as cryptography and number theory require


integers that are too big or too small to be stored as ints or longs.
Create a B igln t class that can handle signed integers that exceed the
storage limitations of C + + built-in integer types. Provide methods that
support the standard integer operations of +, *, /, and '/, together with
relational operations such as >, ==, and the like. (Recall that operators
may be overloaded as methods.) B iglnt should be implemented as an
abstract data type so that the user needs to know only the class’s public
interface and not its internal representation.

4.2. Implement a BankTransaction class that allows the user to

• Open an account.
• Close an account.
• Add funds to an already open account.
• Remove funds from an already open account.
• Transfer funds from one open account to another.
• Request a report on one or more open accounts.

There should be no upper bound on the number of accounts that a user


may open. The class also should contain a method that automatically
issues a warning if an account is overdrawn.

4.3. Introduce appropriate classes to simulate the behavior of a local area


network, hereafter L A N . The network consists of nodes, which may
be devices such as personal computers, workstations, FAX machines,
telecommunications switches, and so forth. A L A N ’s principal job is
to support data communications among its nodes. The user of the simu­
lation should, at a minimum, be able to

www.MathSchoolinternational.com
198 CHAPTER 4 M O RE O N CLASSES

• Enumerate the nodes currently on the LAN.


• Add a new node to the LAN.
• Remove a node from the LAN.
• Configure a LAN by giving it, for example, a star or a bus topology.
• Specify packet size, which is the size in bytes of message that goes
from one node to another.
• Send a packet from one specified node to another.
• Broadcast a packet from one node to all others.
• Track LAN statistics such as the average time it takes a packet to
reach the most distant node on the LAN.

4.4. Implement a memory-resident HashTable class, where a hash table is


a structure that supports constant-time access to any of its entries.
An entry may be any object whatever: an integer, a string, an array,
a list, or even another HashTable. The access is constant-time in that
it takes the same time to access any item in the table. By contrast, a
linked-list supports only sequential-time access because, for example,
it takes longer to access the last entry than it does to access the first
entry. The HashTable should make use of a hash function that maps
an object into the HashTable. To handle the case in which two or more
objects hash to the same slot in the HashTable, the class must provide
a conflict-resolution policy. The public interface supports operations
such as

• Creating a hash table.


• Destroying a hash table.
• Adding an object to a hash table.
• Removing an object from a hash table.
• Accessing an object from a hash table but without removing it.
• Copying a hash table.

4.5. Implement a list as an abstract data type L ist, where a list is an ordered
collection of none or more elements. In a traditional programming lan­
guage such as C, a list typically is implemented either as an array or as
a linked-list, with shortcomings in either implementation: an array’s size
must be fixed at definition time, and a linked-list usually requires that the
user manipulate pointers. By contrast, the user of a L is t should need to
know only the public interface, which supports the following operations:

• Create a list of none or more elements.


• Destroy a list.
• Insert an item into a specified spot in the list.

www.MathSchoolinternational.com
PR O G R A M M IN G EXERCISES 199

• Remove one or more items from a list.


• Concatentate two or more lists to create a new list.
• Divide a list into two or more sublists.
• Copy a list.

4.6. Implement a L is tlte r a to r class that iterates through L is t elements (see


Programming Exercise 4.5). The L is t lt e r a t o r should be nondestruc­
tive; that is, it should not change any L is t element in the course of its
iteration.

4.7. Implement a Schedule class that produces an optimal conflict-free subset


of activities given an input set of activities together with the start and
finish time for each activity. The conflict-free subset, together with the
start and finish times, is a schedule. The schedule is conflict-free because,
given any two distinct activities, one finishes before the other starts. For
example, given the input set

A c tiv ity Start Finish


Al 6 10
A2 1 5
A3 1 6
A4 9 12
A5 5 7
A6 6 14
A7 3 7
A8 10 14
A9 13 16

e Schedule would be

A c tiv ity Start Finish


A2 1 5
A5 5 7
A4 9 12
A9 13 16

Given the input set, it is impossible to produce a Schedule of five or more


nonconflicting activities. The public interface should include methods
for creating, destroying, revising, and combining Schedules. There also
should be a method that explains exactly why the produced Schedule
cannot be expanded to include any more of the input activities. Hint:
Iterate through the activities, picking in each iteration the activity with
the minimal finish time that does not conflict with any previously selected
activity for the Schedule.

www.MathSchoolinternational.com
CHAPTER 4 M O RE ON CLASSES

4.8. Implement a spreadsheet class. A spreadsheet consists of cells that


can be accessed through row-column indexes. For example, c e l l (11,44)
is at row 11 and column 44. A cell may hold a numeric value or a for­
mula. For example, c e l l (11,44) might hold the value 3.14, whereas
c e l l (34,989) holds the formula

c e l l (11,44) * cell(1 4 ,2 3 )

The value of c e l l (11,44) is 3.14, but the value of c e l l (34,989) is


c e l l (11,44) multiplied by c e ll (14,23). The public interface allows the
user to create, destroy, copy, and compute Spreadsheets. To compute
a spreadsheet is to compute the value of each of its cells. The user
also must be able to specify a c e l l ’s value, of course.

4.9. Implement a Graph class, where a graph is a set of vertices V and a set
of edges E such that each edge in E is associated with an unordered pair
of distinct vertices in V. The following figure

M i s s o u l a ----------M i n n e a p o l i s Detroit

shows a graph in which the vertices represent cities and the edges repre­
sent airline routes between pairs of cities. A graph search is the coun­
terpart of a tree traversal (see Section 4.3) in that the search visits every
vertex in the graph. Two general-purpose graph searches are depth-first
search and breadth-first search, which can be sketched as follows:

// d ep th -first search
1. Designate a vertex as the START. Let V = START.
2. V is it V and mark i t as v is ite d .
3. Choose an unvisited vertex W that is adjacent to V.
Do a d ep th -first search from W.
4. When you reach a vertex U that has no u nvisited
adjacent v e rtic e s , back up to the most recen tly
v is ite d vertex W that does have an unvisited vertex X.
I f there is no such vertex W, terminate the search;
otherwise, do a d e p th -firs t search from X.

// b rea d th -first search


1. Designate a vertex as the START. Let V = START.
2. V is it V and mark i t as v is ite d .

www.MathSchoolinternational.com
PR O G R A M M IN G EXERCISES 201

3. Let W be the most recently v is ite d vertex. V is it and


mark as v is ite d each unvisited vertex adjacent to W.
4. I f a l l vertices have been v is ite d , terminate the
search; otherwise, repeat step 3.

For the graph in the preceding figure, one depth-first search with Chicago
as START is

Chicago Milwaukee Minneapolis Lusk Missoula Peoria D etroit

One breadth-first search with Lusk as START is

Lusk Missoula Minneapolis Peoria Milwaukee Chicago D etroit

The public interface should include methods to

• Add a vertex to a graph.


• Remove a vertex from a graph.
• Depth-first search the graph.
• Breadth-first search the graph.
• Iterate through the graph, one vertex at a time.

4.10. Implement a Polynomial class to represent polynomial expressions. A


polynomial expression may have any number of terms. Class methods
should support the standard operations such as addition, subtraction,
multiplication, and division. The user should be able to create arbitrary
polynomial expressions such as

2a**10 + b**5 + c**3 + 2

where a**b means ab. Also, there should be a method that allows the
user to encode a polynomial as a bit-string. For example, the bit-string

1101

represents the polynomial expression

x **3 + x **2 + x **0 = x **3 + x **2 + 1

because the bit-string contains a 1 in positions 0, 2, and 3.

4.11. Implement a SymbolTable class. A symbol table lists all identifiers (i.e.,
function and variable names) in a program’s source code together with
pertinent information such as the identifier’s data type, its role within the
program (e.g., whether the identifier is a function name, variable name,
or a label), and its position in a source code file (e.g., a line number
designating the source code line in which the identifier occurs). The
public interface should allow the user to specify one or more source files

www.MathSchoolinternational.com
202 CHAPTER 4 M O RE O N CLASSES

from which the SymbolTable is to be built. There also should be methods


for displaying and editing a SymbolTable.

4.12. Implement a RegExp class to represent regular expressions, which are


used in pattern matching. A regular expression is a character string that
consists of ordinary and special characters. For example, the regular
expression

aRgT

matches only other strings with exactly these four characters in this order.
Use the following special characters:

Special Character What It Matches


. Any character.
[< lis t > ] Any character in <list>.
For instance, [aBc] matches
a, B, or c.
[~ < lis t> ] Any character not in <list>.
[<X>-<Y>]] Any character in range <X> to <Y>.
For instance, [a -c ] matches a, b,
or c.
* Zero or more occurrences of the
preceding RegExp.
For instance, ab* matches ab,
abab, ababab, and so on.

The public interface consists of methods to create and destroy RegExps


as well as to match RegExpAs against other strings.

www.MathSchoolinternational.com
Chapter 5

Inheritance

5.1 Basic Concepts and Syntax


5.2 Constructors U n d er Inheritance
5.3 Sample Application: M easuring Com puter
Perform ance
5.4 Polym orphism and V irtu al M ethods
5.5 Sample Application: V irtu al Tree Traversal
5.6 Destructors U n d er Inheritance
5.7 M ultiple Inheritance
Com m on Program m ing Errors
Program m ing Exercises

203

www.MathSchoolinternational.com
204 CHAPTER 5 INHERITANCE

Vehicle base class

Car derived class

Figure 5.1.1 A base class and a derived class.

A class can occur in an inheritance hierarchy, which plays two roles in


C + + as an object-oriented language. First, an inheritance hierarchy allows
code to be reused. Code written for one class— data members and methods—
can be used by other classes in the hierarchy. If the code is written correctly
for a class, then it remains correct in the classes that inherit the code, thus
promoting system robustness. Second, an inheritance hierarchy supports
polymorphism—the run-time binding of a function reference to a particular
body of code— in the form of virtual methods. We begin by clarifying basic
concepts and syntax.

5.1 Basic Concepts and Syntax


Base and D erived Classes

Classes in an inheritance hierarchy stand in superclass/subclass relation­


ships. The C + + name for superclass is base class and the C + + name for
subclass is derived class. Whenever an existing class can be modified to
produce a new class, we can derive the new class from the base class rather
than declare a whole new class, thus promoting reusable code.

Example 5.1.1. Classes Car and Vehicle are related such that every Car
is a Vehicle as well (see Figure 5.1.1). Car is thus a subclass or derived class
of Vehicle, and Vehicle is a superclass or base class of Car. Our figures use
an arrow to point from a derived class back to a base class.
Inheritance relationships are either direct or indirect. Suppose that
Coupe is a subclass of Car (see Figure 5.1.2). Because there is no intermedi­
ate superclass between Coupe and Car, Car is a direct superclass or a direct
base class of Coupe. Conversely, Coupe is a direct subclass or direct derived
class of Car. By contrast, Vehicle is an indirect superclass or indirect base
class of Coupe, and Coupe is an indirect subclass or indirect derived class of
Vehicle. Note that Car is a derived class with respect to Vehicle but a base
class with respect to Coupe.
The terms direct and indirect also apply to inheritance links. For ex­
ample, Coupe inherits directly from Car but only indirectly from Vehicle.
There is a direct inheritance link from Vehicle to Car but only an indirect
inheritance link from Vehicle to Coupe.
C + + supports multiple inheritance in which a subclass has multiple
superclasses. For example, Car also might be a subclass of ExpensiveToy (see

www.MathSchoolinternational.com
5.1 BASIC CONCEPTS AN D SYNTAX 205

direct base class for Car


indirect base class for Coupe

direct derived class from Vehicle


direct base class for Coupe

direct derived class from Car


indirect derived class from Vehicle

Figure 5.1.2 Direct inheritance.

Figure 5.1.3 Multiple inheritance.

Figure 5.1.3). Section 5.7 is devoted to multiple inheritance. Conversely, a


superclass may have multiple subclasses. For example, Vehicle may have
HugeSemiTruck as another subclass (see Figure 5.1.4). □

M e m b er Accessibility

Among object-oriented languages, C + + offers the programmer the richest


control over access to class members within an inheritance hierarchy. The
type of inheritance between a base class and a derived class affects access to
data members and methods inherited by a derived class from a base class.
We begin with the three types of inheritance supported in C ++.
Each member of a class is either

priva te protected public

(We discussed p riva te and public members in Section 3.1.) A p riva te


member may be accessed only by methods within its class or by frie n d
functions. A protected member may be accessed only by methods within
its class hierarchy (i.e., within its class and within certain classes directly
and indirectly derived from its own class) or by frien d functions. A public
member is globally accessible.

Exam ple 5.1.2. In the code

Figure 5.1.4 Multiple subclasses.

www.MathSchoolinternational.com
CH APTER 5 INHERITANCE

class Pen {
p u b lic :
move_pen( a, b ) { x = x + a ; y = y + b ; }
set_pen( a, b ) ( x = a ; y = b ; }
PenO { in it ia l_ p e n (); }
protected:
void in it ia l_ p e n ();
p r iv a te :
in t x;
in t y;
>;

in t main()
{
Pen p;
p.set_pen( 0, 0 ) ; //OK — set_pen is public
p .in it ia l_ p e n (); // *** ERROR: in itia l_ p e n is protected
p.x = 0; // *** ERROR: x is p riva te

>
move_pen and set_pen can access x and y because x and y are members
of the class Pen. Because x and y are p riva te members, they cannot be
accessed outside Pen; thus,
p.x = 0; // * * * * * ERROR: x is p rivate
is an error.
Similarly, the constructor can access in itia l_ p e n because in itia l_ p e n
is a member of the class pen. Because in itia l_ p e n is a protected member,
it cannot be accessed outside pen or classes directly or indirectly derived from
pen; thus,
p . in it ia l_ p e n (); // * * * * * ERROR: in itia l_ p e n is protected

is an error.
Because set_pen is a public member, it can be accessed globally. Ac­
cordingly,
p.set_pen( 0, 0 ) ; //OK — set_pen is public
is legal. □

By making a member of a class private, we restrict its visibility to that


class. Except for a frien d class, no class— derived or otherwise— can access
a p riva te member of another class. A protected member is like a p riva te
member except that it is visible in a derived class. By making a member of
a class public, we make the member visible wherever the class is visible.

www.MathSchoolinternational.com
5.1 BASIC CONCEPTS AN D SYNTAX 207

In C + + , all data members and methods of the base class, except for
constructors, the destructor, and the overloaded assignment operator, are
always automatically included in the derived class. In this sense, the derived
class inherits from the base class. Although the derived class inherits mem­
bers from the base class, the accessibility of an inherited member can change.
For example, a public member in the base class can become p rivate in the
derived class. The C + + programmer has considerable flexibility, not only
in constructing new classes from old through inheritance, but also in con­
trolling the accessibility of the inherited members. In the remainder of this
section, we explain the syntax and semantics of inheritance and accessibility
of members.

Syntax for D eriving a Class

To derive the class D from the class B, we write


class B { // base class

>;

class D : access-specifier B { // derived class

>;
where optional access-specifier is one of private, protected, or public.
The access-specifier controls the type of access provided to the data mem­
bers and methods inherited by the derived class from the base class. (We use
the phrase inheritance link interchangeably with access-specifier.) If access-
specifier is omitted, it defaults to private. (If class is replaced by struct
and access-specifier is omitted, access-specifier defaults to public.)
Although a derived class cannot prohibit any of its base class’s data
members or methods from being inherited, the derived class may specify
additional data members or methods.

Exam ple 5.1.3. In the code slice


class Vehicle { // base class
p u b lic:
flo a t mph; // meters per hour
flo a t cpmph; // cost per mph
flo a t weight; // in kilograms
>;

class Car : public Vehicle { // derived class


public:
char brand_name[ 100 ] ;

www.MathSchoolinternational.com
208 CH APTER 5 INHERITANCE

V eh icle

mph

cpmph

w e ig h t

Car

mph

cpmph • inherited from V e h i c l e

w e ig h t

brand_nam e J- local to G a r

Figure 5.1.5 Class Car is derived from class Vehicle.

class Celt is derived from the base class Vehicle. The access-specifier is
public. Derived class Car inherits the members mph, cpmph, and weight
from superclass Vehicle and adds a fourth member brand_name (see Figure
5.1.5). □

public Inheritance

The most important type of access-specifier is public. In a public deriva­


tion
• Each public member in the base class is public in the derived class.
• Each protected member in the base class is protected in the derived
class.
• Each p rivate member in the base class remains p riva te in the base
class and so is visible only in the base class.

Exam ple 5.1.4. In the code slice


class B { // base class
p u b lic:
in t x;
protected:
in t w;
p r iv a te :

www.MathSchoolinternational.com
5.1 BASIC CONCEPTS AN D SYNTAX 209

in t z;
>;

class D : public B { // public derived class


p u b lic:
in t y;
void set_w( in t a ) { w = a; }
>;
class D is derived from the base class B. The access-specifier is public. The
members of the derived class D are

Member Access Status in D How Obtained


X public From class B
w protected From class B
z Not accessible From class B
y public Added by class D
set_w public Added by class D

In the code
in t mainO
{
D d l;
d l.x = 33; / / ok — x is public
d l.y = 99; / / ok — y is public
dl.w = 77; // * * * * * ERROR: w is protected
d l.z = 88; // * * * * * ERROR: z is accessible only within B

>
we may access the public members x and y of D but not the protected
member w, which is visible only within the class hierarchy. Within class D,
it is legal to access w:
void set_w( in t a ) { w = a; } // ok
It is an error to try to access z outside of B. In particular, z cannot be
accessed even in D:
class D : public B {

// * * * * * ERROR: z is accessible only within B


void s e t_ z( in t a ) { z = a; }

>;

www.MathSchoolinternational.com
210 CHAPTER 5 INHERITANCE

In Example 5.1.4, z is inherited by D from B even though z is not visible


in D. Whenever a D object is created, storage for z is allocated. Although a
p riva te member of a base class is not visible in a derived class and, there­
fore, may not be directly accessed, it might be indirectly accessed through
a derived method as the following example shows.

Exam ple 5.1.5. Given the class declarations


class Point {
in t x;
in t y;
p u b lic :
void set_x( in t x l ) { x = x l; }
void set_y( in t y l ) { y = y l; }
in t g e t_ x () { return x; }
in t g e t_ y () { return y; >
>;

class IntensePoint : public Point {


in t in ten sity;
p u b lic :
void s e t_ in te n s ity ( in t i ) { in ten sity = i ; }
in t g e t_ in te n s ity () { return in ten sity; }
>;
the members of the derived class IntensePoint are
Member Access Status in How Obtained
IntensePoint
X Not accessible From class Point
y Not accessible From class Point
set_x public From class Point
set_y public From class Point
get_x public From class Point
get_y public From class Point
in ten sity private Added by class
IntensePoint
set_in ten sity public Added by class
IntensePoint
g et_in ten sity public Added by class
IntensePoint

Class IntensePoint inherits data members x and y, which are visible


only in class Point. Nevertheless, class IntensePoint can indirectly access
these data members through the methods set_x, set_y, get_x, and get_y,
which are visible in IntensePoint. □

www.MathSchoolinternational.com
5.1 BASIC CONCEPTS AN D SYNTAX 211

A derived class may access protected members that it inherits from a


base class. A derived class may access these protected members precisely
because, once inherited, they belong to the derived class. Yet a derived class
may not access protected members of a base class object, that is, an object
that belongs to the base class but not to the derived class.

Example 5.1.6. In the code slice


class B { // base class
protected:
in t w;
>;

class D : public B { // derived class


p u b lic:
// w belongs to D because i t is in h erited from B
void set_w( in t a ) { w = a; }

// * * * * * ERROR: b.w not v is ib le in D since b.w is


// member of B, not D
void base_w( B b ) { b.w = 0; >
>;
the reference to w in class D
void set_w( in t a ) { w = a; } / / 0 K
is legal because this w is D’s member, which is inherited from B. It is visible
in D because it is protected in B. The reference to b.w in class D
void base_w( B b ) { b.w = 0; >
is illegal because this w is B’s member and it is visible only in B. In other
words, b.w is the data member of a B object that is not a D object. □

A class may be derived from more than one base class. In this case, its
base classes are separated by commas in the derived class’s declaration.

Example 5.1.7. In the code slice


class B1 { // base class 1
protected:
in t x l ;
>;

class B2 { // base class 2


protected:
in t x2;

www.MathSchoolinternational.com
212 CHAPTER 5 INHERITANCE

class D : public Bl, public B2 { // derived class


in t d;
>;

class D is derived directly from two independent base classes, Bl and B2.
D inherits x l from Bl and x2 from B2. Because both access-specifiers are
public, the members x l and x2 are protected. (The added member d is
p rivate.). □

Example 5.1.7 illustrates multiple inheritance, a topic to which we


devote an entire section (Section 5.7). For now, though, our examples use
only single inheritance, that is, inheritance to a derived class from a single
base class.

protected Inheritance

In a protected derivation

• Each public member in the base class is protected in the derived


class.

• Each protected member in the base class is protected in the derived


class.

• Each p riva te member in the base class remains p riva te in the base
class and so is visible only in the base class.

Exam ple 5.1.8. In the code slice

class B { // base class


p u b lic :
in t x;
protected:
in t w;
p r iv a te :
in t z;
>;

class D : protected B { // protected derived class


p u b lic :
in t y;
>;

class D is derived from the base class B. The access-specifier is protected.


The members of the derived class D are

www.MathSchoolinternational.com
5.1 BASIC CONCEPTS A N D SYNTAX 213

Member Access Status in D How Obtained


X protected Prom class B
w protected Prom class B
z Not accessible Prom class B
y public Added by class D

p riv ate Inheritance

In a p rivate derivation
• Each public member in the base class is p riva te in the derived class.
• Each protected member in the base class is p riva te in the derived
class.
• Each p riva te member in the base class remains p riva te in the base
class and so is visible only in the base class.

Example 5.1.9. In the code slice


class B { // base class
public:
in t x;
protected:
in t w;
p r iv a te :
in t z;
>;

class D : p riva te B { // p riva te derived class


p u b lic:
in t y;

class D is derived from the base class B. The access-specifier is private. The
members of the derived class D are

Member Access Status in D How Obtained


X p rivate Prom class B
w p rivate Prom class B
z Not accessible From class B
y public Added by class D

Exam ple 5.1.10. Because priva te is the default value for access-specifier,
the declaration of class D in Example 5.1.9 is equivalent to

www.MathSchoolinternational.com
214 CHAPTER 5 INHERITANCE

Figure 5.1.6 Indirect inheritance.

class D : B { // p riva te (d e fa u lt) derived class


p u b lic :
in t y;
>;

Indirect Inheritance

Data members and methods may traverse several inheritance links as they
are included from a base to a derived class. For example, suppose that B is
D’s base class and that D is X’s base class (see Figure 5.1.6). In this case, X
inherits D’s data members and methods— including whatever data members
or methods D inherits from B. Inheritance thus may be either direct (to a
derived class from a direct base class) or indirect (to a derived class from an
indirect base class).

Exam ple 5.1.11. In the code slice


// d ire c t base class fo r Cat,
// in d irect base class fo r HouseCat
class Animal {
protected:
char speciesInLatin[ 100 ] ;
flo a t lifeE xpectancy;
in t warmBlooded_P; // 0 == False, 1 == True
>;

// d ire c t derived class from Animal,


// d ire c t base class fo r HouseCat
class Cat : public Animal {
protected:
char range[ 100 ] [ 100 ] ;
flo a t fa v o rite P re y [ 100 ] [ 100 ] ;
>;

www.MathSchoolinternational.com
5.1 BASIC CONCEPTS AN D SYNTAX 215

// in d irect derived class from Animal,


// d ire c t derived class from Cat
class HouseCat : public Cat {
char to y s [ 10000 ] [ 100 ] ;
char c a tP s y c h ia tris t[ 50 ] ;
char catD en tist[ 50 ] ;
char catDoctor[ 50 ] ;
char apparentOwner[ 50 ] ;
>;
HouseCat has 10 data members: five are added, two are inherited directly
from Cat, and three are inherited indirectly from Animal by way of Cat. The
inherited data members remain protected in HouseCat. □

Access Declarations

Access to an inherited member may be adjusted to that of the base class


by declaring it public or protected in the derived class.

Example 5.1.12. In the code slice


class BC { // base class
protected:
in t x;
in t y;
pu b lic:
in t z;
>;

class DC : p riva te BC {. // p riva te inheritance


protected:
BC: : x ;
p u b lic:
BC::z;
in t w;
};
the declaration
BC::x;
adjusts x in DC to the same protected status that this data member has in
base class BC. The adjustment is done by placing the declaration
BC: : x ;
in the protected section of DC. Without the declaration, x would be p riva te
in DC because the inheritance link from BC to DC is private. Similarly, the
declaration

www.MathSchoolinternational.com
216 CHAPTER 5 INHERITANCE

BC:: z ;
adjusts z to its public status in the base class BC by declaring it in the
public section of the derived class DC. Inherited member y is p riva te in DC
because DC is obtained by p rivate inheritance from BC and y’s status was
not adjusted. Added member w is public. □

It is an error to try to use an access declaration to change the status of


a base class member.

Exam ple 5.1.13. In the code slice


class BC { // base class
p r iv a te :
in t y;
protected:
in t x;
p u b lic :
in t z;
>;
class DC : p rivate BC { // p rivate inheritance
protected:
BC::y; // * * *** ERROR: can’ t change p riva te to protected
BC::z; // * * *** ERROR: can’ t change public to protected
>;
the statement
BC::y; // * * *** ERROR: can’ t change p riva te to protected
is an error because p rivate member y in BC cannot have its status changed.
(In fact, y is not even visible in DC.) The statement
BC::z; // * * *** ERROR: can’ t change public to protected
is an error because public member z in BC cannot have its status changed
through a declaration, (z is public if it is declared public in DC, and z is
p riva te if it is not declared in DC.) □

N am e H idin g

If a derived class adds a data member with the same name as a data member
in the base class, the local data member hides the inherited data member.

Exam ple 5.1.14. In the code slice


class B { // base class
p u b lic:
in t x; // B::x
>;

www.MathSchoolinternational.com
5.1 BASIC CONCEPTS AN D SYNTAX 217

class D : public B { // derived class


p u b lic:
in t x; // * * * * * DANGER: hides B ::x
>;

in t mainO

D d l;

d l.x = 999; // D ::x, not B ::x


d l.B ::x = 4; // ok — but clumsy!

>

class D inherits x from B. However, D also has a local data member named x,
which means that the local data member hides the inherited data member
in the sense that B: : x is not in class D’s scope. Theonly way toaccess B: : x
in D is by using the scope resolution operator, as inthe second assignment
statement. □

Similarly, if a derived class adds a method with the same name in the
base class, the added method hides the base class’s method.

Example 5.1.15. In the code slice

class B { // base class


p u b lic:
void h( flo a t ) ; // B::h
>;

class D : public B { // derived class


p u b lic:
void h( char* ) ; // * * * * * DANGER: hides B::h
>;

in t mainO

D d l;

d l.h ( "B o ffo !" ) ; // D::h, not B::h


d l.h ( 707.7 ) ; // * * * * * ERROR: D::h hides B::h
d l.B ::h ( 707.7 ) ; // OK: invokes B::h

>

www.MathSchoolinternational.com
218 CHAPTER 5 INHERITANCE

The error occurs because method D: :h expects a char* argument rather


than a flo a t argument. The inherited B: : h, which does expect a char*
argument, is hidden in d by D: :h and so must be invoked with the scope
resolution operator. □

Exercises

1. Draw a classhierarchy in which a base class has multiple derived classes.

2. Draw a classhierarchy in which a derived class has multiple base classes.

3. Draw a classhierarchy in which a base class has multiple derived classes,


at leastone of which has a second base class.

4. Draw a class hierarchy that illustrates the distinction between direct and
indirect inheritance.

5. Explain the relationship among the terms superclass, subclass, base class,
and derived class.

6. Is it possible for class C to be both a base class and a derived class? If


so, draw a figure to illustrate. If not, explain why not.

7. Draw a class hierarchy that is at least five deep. List the directly and
indirectly derived classes of the top class in the hierarchy.

8. In the code slice

class A {
in t x;

class B : A {
in t y;

B b l;

how many data members does bl have?

9. Explain the error.

class A {
in t x;

www.MathSchoolinternational.com
5.1 BASIC CONCEPTS AN D SYN TAX 219

class B : A {
in t y;
void f ( ) { y = x; >
>;

10. Is the inheritance link from A to B p riva te or public?

class A {

>;

class B : A ■[

};

11. Explain the error.

class A {
protected:
flo a t f l , f2 ;
p r iv a te :
in t x;

in t main()

A a l;
A* p tr = &al;

p tr -> f l = 3.14;

>

12. Explain the difference between p riva te and protected with respect to
a class’s data members and methods.

13. Write the code for a class hierarchy in which there is public inheritance
from base class B to derived class D.

14. Change the inheritance link from A to B from p riva te to public.

class A {

www.MathSchoolinternational.com
220 CHAPTER 5 INHERITANCE

class B : A {

>;

15. Draw an inheritance hierarchy in which P has a direct inheritance link to


base class Q and an indirect inheritance link to base class R.

16. Explain the advantage of protected over p rivate data members or meth­
ods.

17. Explain the advantage of protected over public data members or meth­
ods.

18. Suppose that data member B: :x is public. Use an inheritance link from
B to D to deny public access to D: :x.

19. Explain the error.

class A {
protected:
flo a t f l , f2;
p u b lic :
in t x;
>;

class B : p riva te A {
p u b lic:
void p flO { cout « fl « endl; }
>;

in t mainO

B b l;
b l . f l = 3.14;
b l.p flO ;
b l.x = ( in t ) b l . f l ;

>

20. Explain the error.

class A {
protected:
in t x;
>;

www.MathSchoolinternational.com
5.1 BASIC CONCEPTS AN D SYNTAX 221

class B : public A {
in t y;
void f ( A a ) { y = a . x ; >
>;

21. Explain the error.

class A {
protected:
in t x;
>;

class B : public A {
p u b lic:
in t y;
>;

in t main()
{
B b l;
b l .x = 8;
b l.y = 9;

22. In the code slice

class B { // base class


protected:
in t numl;
in t num2;
>;
class D : public B { // derived class
in t num3;
>;
D d l;

how many data members does dl have?

23. Write the body of in it, which initializes D’s members.

class B { // base class


protected:
in t numl;
in t num2;
>;

www.MathSchoolinternational.com
222 CHAPTER 5 INHERITANCE

from the added by


base class derived class

Figure 5.2.1 A derived class as a specialization of a base class.

class D : public B { // derived class


in t num3;
p u b lic:
void i n i t ( i n t , i n t , in t ) ;
>;
void D : : in it ( in t n l, in t n2, in t n3 )
{

24. Explain the difference between inheritance and access. Write rules that
summarize the difference.

25. Explain the error.

class A {
protected:
in t x;
>;
class B : public A {
p u b lic :
void f l ( ) { x = 6; }
void f 2 ( A a ) { a . x = l ; }
>;

5.2 Constructors Under Inheritance


A derived class is a specialization of a base class. An object in a derived
class inherits characteristics from the base class but also has characteristics
that are specific to the derived class (see Figure 5.2.1). For this reason,
a base class constructor (if any) is invoked when a derived class object is
created. The base class constructor handles initialization, dynamic storage
allocation, and the like for the “from the base class” part of the object. If the
derived class has a constructor of its own, then this constructor can handle
the “added by the derived class” part of the object.

www.MathSchoolinternational.com
CONSTRUCTORS UNDER INHERITANCE 223

Exam ple 5.2.1. In the code slice

class B { // base class


protected:
in t x;
in t y;
p u b lic:
B () { x = y = -1; } // base constructor
>;

class D : public B { // derived class


p u b lic:
void w r ite ( ) { cout « x * y « endl; }
>;

int mainO
{
D d l; // B ::B () invoked
d l.w r it e O ; // 1 w ritten to standard output

>
B’s default constructor is invoked when dl is defined because D is derived
from B. The constructor initializes d l.x and d l.y to -1. Note that d l in­
herits its only data members from B. □

Base class constructors are often sufficient for the derived class. Some­
times, however, it makes sense for a derived class to have its own construc­
tors. A constructor specific to a derived class may invoke a base class con­
structor, if one exists.

Example 5.2.2. The code slice

const in t MaxName = 100;

class Animal {
protected:
char species [ MaxName + 1 ] ;
p u b lic :
Animal() { strcpy( species, "Animal" ) ; >
Animal( char* s ) { strcpy( species, s ) ; }
>;

class Primate: public Animal {


in t heart_cham;
p u b lic:

www.MathSchoolinternational.com
224 CH APTER 5 INHERITANCE

Primate( ) : Animal( "Primate" ) { }


Primate( int n ) : Animal( "Primate" )
{ heart_cham = n; >
>;

Animal slug; // Animal()


Animal tweety( "canary" ) ; // Animal( char* )

Primate g o d zilla; // Primate()


Primate human( 4 ) ; // Primate( int )
has four constructors: two for base class Animal and two for derived class
Primate.
The two Primate constructors invoke a base class Animal constructor in
their headers:
PrimateO : Animal( "Primate" ) { >
Prim ate( in t n ) : Animal( "Primate" )
{ heart_cham = n; }
The syntax indicates that each Primate constructor invokes an Animal con­
structor before executing its own body. For instance, the default Primate
constructor invokes the base class constructor Animal ( char* ) before exe­
cuting its own body, which happens to be empty. In the case of the Primate
constructor with one argument, the body contains an assignment of n to
heart_cham. □

In a deep inheritance hierarchy, creation of an object belonging to a de­


rived class may have a domino effect with respect to constructor invocation.

Exam ple 5.2.3. In the code slice


const in t MaxName = 100;

class Animal {
protected:
char species [ MaxName + 1 ] ;
p u b lic :
AnimalO { strcpy( species, "Animal" ) ; }
Animal( chair* s ) { strcpy( species, s ) ; }

class Primate: public Animal {


protected:
int heart_cham;
p u b lic :
PrimateO : Animal( "Primate" ) { }

www.MathSchoolinternational.com
5.2 CONSTRUCTORS UNDER INHERITANCE 225

A n i m a l: :A n i m a l ( ... ) executes first

I
P r im a t e ::P r im a t e ( ... ) executes second

1
H u m a n ::H u m a n ( ... ) executes third

Figure 5.2.2 Constructor firing in Animal-Primate-Human hierarchy.

Primate( int n ) : Animal( "Primate" )


{ heart_cham = n; }
>;

class Human : public Primate {


p u b lic :
HumanO : Primate( ) {
Human( int c ) : Primate( c ) { >
>;

Human j i l l ( ) ; // HumanO
Human fr e d ( 4 ) ; // Human( int )
the inheritance hierarchy is now three deep. Human inherits heart_cham di­
rectly from Primate and species indirectly from Animal by way of Primate.
Each of the Human constructors invokes the direct base class constructor
Primate before executing an empty body. The Primate constructor, in turn,
invokes the Animal constructor before executing its own body. The effect is
that the constructors are executed in a top-down order, where Animal is at
the top, Primate in the middle, and Human at the bottom of the inheritance
hierarchy (see Figure 5.2.2). □

D erived Class Constructor Rules

If a base class has constructors but no default constructor, then a derived


class constructor must explicitly invoke some base class constructor.

Exam ple 5.2.4. The code slice


// B has constructors but no
// default constructor
class B { // base class
int x;
int y;
p u b lic :
B( int a ) { x = a; y = 999; >
B( int a l, int a2 ) { x = a l; y = a2; >
>;

www.MathSchoolinternational.com
C H A PTE R S INHERITANCE

// D has a constructor (any constructor


// w i l l do fo r the example)
class D : public B { // derived class
int z;
p u b lic :
// * * ** * ERROR: D( int ) must e x p lic it ly
II invoke a B constructor
D( int n ) { z = n; }
>;

is in error because B does not have a default constructor and D’s constructor
does not explicitly invoke a B constructor. We can avoid the error in two
ways: by having D’s constructor explicitly invoke, in its header, one of B’s
constructors or by giving B a default constructor. We amend the code slice
to illustrate the two approaches:

// approach 1: have D’ s constructor e x p lic it ly


I I invoke one of B’ s constructors

class B i l l base class


in t x;
int y;
public:
B( int a ) { x = a ; y = 999; >
B( int a l, int a2 ) { x = a l; y = a2; >
>;

// D’ s constructor e x p lic it ly invokes a


// B constructor in it s header
class D : public B -C // derived class
in t z;
p u b lic :
// ok: D( int ) e x p lic it ly invokes B( in t, int )
D( int n ) : B ( n , n + 1 ) { z = n; >
>;

// approach 2: give B a default constructor

class B { // base class


int x;
int y;
p u b lic :
B () { x = 1; y = 2 ; > / / default
B( int a ) { x = a ; y = 999; >

www.MathSchoolinternational.com
CONSTRUCTORS UNDER INHERITANCE 227

B( int al, int a2 ) { x = al; y = a2; >


>;

/ / D ’s constructor need not invoke a B


// constructor because B how has a
// default constructor
class D : public B { // derived class
int z;
public:
//ok: B has a default constructor
D( int n ) { z = n; }
>;
There is rarely a good reason for a base class not to have a default con­
structor. Giving a base class a default constructor avoids the problem and
still allows a derived class constructor to invoke any base class constructor.
Accordingly, we recommend that every base class have a default constructor.

Suppose that a base class has a default constructor and that a derived
class has constructors, none of which explicitly invokes a base class construc­
tor. In this case, the base class default constructor is invoked automatically
whenever a derived class object is created.

Example 5.2.5. The output for the code slice


class B { // base class
int x;
public:
B() { cout « "B::B() fires..." « endl; }
>;

class D : public B { // derived class


int y;
public:
D() { cout « "D::D() fires..." « endl; >
>;

int main()
{
D d;

>
is
B::B() fires.

www.MathSchoolinternational.com
CHAPTER 5 INHERITANCE

D ::D () f i r e s . ..
It is legal but unnecessary for D’s constructor to invoke B’s default constructor
explicitly:
// le g a l but unnecessary
DO : B () { . . . }

We now summarize the rules, using D as a class derived from B.


• If D has constructors but B has no constructors, then the appropriate D
constructor (if any) fires automatically whenever a D object is created.
• If D has no constructors but B has constructors, then B must have a
default constructor so that B’s default constructor can fire automatically
whenever a D object is created.
• If D has constructors and B has a default constructor, then B’s default
constructor fires automatically whenever a D object is created unless the
appropriate D constructor explicitly invokes, in its header, some other
B constructor.
• If D and B have constructors but B has no default constructor, then
each D constructor must explicitly invoke, in its header, a B constructor,
which then fires when a D object is created.
It makes sense that the creation of a derived class object should cause
some base class constructor, if any, to fire. A derived class constructor may
depend upon actions from a base class constructor. For example, the derived
class may assume storage dynamically allocated by the base class constructor
or data member initializations performed by the base class constructor. Also,
a derived class object is a specialization of a base class object, which means
that a base class constructor, if present, should execute first when a derived
class object is created. The more specialized constructor, which is the local
derived class constructor, then can handle any special details.

Exam ple 5.2.6. In the code slice


const in t MaxLen = 100;

class B { // base class


protected:
char* name;
in t maxlen;
p u b lic :
BO
{
maxlen = MaxLen;

www.MathSchoolinternational.com
5.2 CONSTRUCTORS UNDER INHERITANCE 229

B •■B ( ) executes first

I
D ::D ( c h a r * ) executes second

Figure 5.2.3 Firing of base class and derived class constructors,

name = new char[ maxlen ] ;


>
>;

class D : public B { // derived class


p u b lic:
D( char* n ) : B () { strcpy( name, n ) ; }
>;

D fo o ( "fo o " ) ;

D’s constructor is called when foo is defined with the initializing value “foo” ,
which is copied into the dynamically allocated storage to which name points.
The copy occurs in D’s body. The dynamically allocated storage consists
of maxlen char cells. It is B’s default constructor, not D’s parameterized
constructor, that initializes maxlen and dynamically allocates the storage.
Therefore, B’s default constructor must be invoked before the body of D’s
parameterized constructor executes. D’s parameterized constructor does in­
voke B: :B () and, as a result, fo o ’s creation and initialization work properly
(see Figure 5.2.3). □

Exercises

1. Explain the error.

class B {
in t x;
p u b lic:
B( in t a ) { x = a; }
>;

class D : public B {

p u b lic:
DO { . . . }
>;

www.MathSchoolinternational.com
CHAPTER 5 INHERITANCE

2. In Example 5.2.3, the default constructor for Human explicitly invokes the
default constructor for Primate but does not explicitly invoke the default
constructor for Animal. Why not?

3. Create a class hierarchy at least three deep and write constructors for
each class. The constructors should contain print statements so that you
can trace their firing. Create objects that belong to each class and trace
the constructor firings.

4. What is the output?

class B {

p u b lic:
B () { cout « "B" « endl; }
>;

class Dl : public B {

p u b lic:
D l() : BO { cout « "Dl" « endl; >;
>;

class D2 : public Dl {

p u b lic:
D2() : DIO { cout « "D2" « endl; } ;
>;

D2 d2;

5. Is it mandatory that a derived class have a constructor?

6. Is it mandatory that a base class have a constructor? Write and compile


sample code to confirm your answer.

7. Is it possible for a derived class to have a constructor but its base class
not to have a constructor? Write and compile sample code to confirm
your answer.
8. Suppose that base class B has two constructors, the default constructor
B: :B( ) and the constructor B: :B( in t ). D is derived from B and has a
single constructor, D: :D( in t ). Must D invoke both of B’s constructors
before executing its own body?

9. C + + requires that when a derived class object is created, a base class


constructor, if one exists, be invoked. Explain the reasoning behind this
rule.

www.MathSchoolinternational.com
5.3 SAM PLE APPLIC A TIO N : MEASURING CO M PUTER PERFO RM AN C E 231

Figure 5.3.1 Hierarchies for measuring computer performance.

10. Extend the class hierarchy of Example 5.2.3 at least two more levels,
writing constructors for each of the additional classes.

5.3 Sample Application: Measuring Computer Perfor­


mance
P r o b le m ______________________________________________________________________

Simulate the measurement of computer performance.

S o lu tio n _______________________________________________________________________

We use three inheritance hierarchies anchored on base classes BMark (bench­


mark program), Computer, and Test. BMark has WetRock, DryRock, and
PetRock as derived classes. Computer has Desktop and Mainframe as de­
rived classes, and Desktop has WS (workstation) and PC (personal computer)
as derived classes. Test does not occur in an inheritance hierarchy. Figure
5.3.1 illustrates.
A Test consists of a Computer running a BMark, the results of which are
printed to the standard output. Test is a frien d to BMark and Computer
so that Test can access relevant attributes of BMark and Computer in order
to measure a Computer’s performance in executing a BMark.

C-|—|- Im p le m e n ta tio n ______________________________________________________

#include <iostream.h>
#include <string.h>
#include <math.h>

const in t MaxName = 100;


const flo a t Tolerance = 0.01;

class Test;

www.MathSchoolinternational.com
232 CH APTER 5 INHERITANCE

class BMark { // benchmark


frien d Test;
protected:
// instruction breakdown by
// categories — in percentages
flo a t alP; // arithm etic-logic
flo a t mP; // memory
flo a t cP; // control
flo a t ioP; // io
flo a t t lP ; // tigh t-loo p
flo a t ic ; // executed instruction count
char name[ MaxName + 1 ] ;
p u b lic :
BMarkO // base class constructor

in it () ;
strcpy( name, "???" ) ;
>

BMark( char* n )

in it O ;

i f ( s tr le n ( n ) < MaxName )
strcpy( name, n ) ;
else
strncpy( name, n , MaxName ) ;
>

void re p o rt()

cout « "Benchmark "


« name « endl;
cout « "Total instructions executed
« ic « endl;
cout « 11 Arithm etic-logic == "
alP « endl;
V
V

cout « " Memory == "


mP « endl;
V
V

cout « " Control == "


cP « endl;
V
V

cout « " Input/Output == "


ioP « endl;
V
V

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : M EASURING C O M PU TER PER FO R M A N C E 233

// Check whether percentages sum within


// Tolerance to 1.0.
int ok()
{
return fa b s ( 1.0 - ( alP + mP + cP + ioP ) )
<= Tolerance;
>

// Print error message in case percentages


// do not sum within Tolerance to 1.0.
void in it _ e r r o r ()
{
cout « name « 11 — in it e r r o r ! " « endl;
>

private:
// I n it ia liz e percentages to 0.0.
void in it O
{
alP = cP = mP = ioP = ic = 0.0;
>
>;

// Benchmark emphasizes a rith -lo g ic and


// control statements with moderate
// memory t r a f f i c and low i/o.
class WetRock : public BMark {
p u b lic :
WetRockO : BMark( "WetRock" ) // WetRock constructor
{
alP = 0.50;
cP = 0.20;
mP = 0.20;
ioP = 0.10;
ic = ( flo a t ) 4500301;

if ( !ok() )
in it _ e r r o r ( ) ;
>
>;

// Benchmark emphasizes a rith -lo g ic and


// control with lig h t memory t r a f f i c and

www.MathSchoolinternational.com
CHAPTER 5 INHERITANCE

// no i/o.
class DryRock : public BMark {
public:
DryRockO : BMark( "DryRock" ) // DryRock constructor

alP = 77.0;
cP = 16.6;
ioP = 0.0;
mP = 1.0 - alP - cP - ioP;
ic = ( flo a t ) 6700909;

if ( !ok() )
i n i t _ e r r o r () ;
>
>;

// Benchmark emphasizes memory t r a f f i c


// with moderate i/o and low a rith -lo g ic
// and control.
class PetRock : public BMark {
p u b lic :
PetRockO : BMark( "PetRock" ) // PetRock constructor

alP = 12.2;
mP = 57.7;
ioP = 23.9;
cP = 1.0 - mP - ioP - alP;
ic = ( flo a t ) 10400500;

i f ( !ok () )
i n i t _ e r r o r () ;
>
>;

class Computer {
frien d Test;
protected:
// cpi = cycles per instruction
flo a t a lc p i; // arithm etic-logic cpi
flo a t ccpi; // control cpi
flo a t iocpi; // input-output cpi
flo a t mcpi; // memory cpi
flo a t ct; // cycle time (nanoseconds)
char name[ MaxName + 1 ] ;

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : MEASURING CO M PUTER PERFO RM AN C E 235

// cost range: upper - lower


flo a t costU;
flo a t costL;

protected:
Computer( flo a t a l, flo a t c, flo a t io,
flo a t m, flo a t t ,
char* n, flo a t 1, flo a t u )
{
alcp i = a l;
ccpi = c;
iocpi = io;
mcpi = m;
ct = t;
i f ( s tr le n ( n ) < MaxName )
strcpy( name, n ) ;
else
strncpy( name, n, MaxName ) ;
costU = u;
costL = 1;
}

void re p o rt()
{
cout « "Computer " << name
« " at cost ranging from $" « costL
« " to $" « costU « « endl;
cout « " Clock cycle (nanoseconds): "
« ct « endl;
cout « " CPIs: " « endl;
cout « " A rith-Logic: " « alcp i « endl;
cout « " Control: " « ccpi « endl;
cout « " Memory: " « mcpi « endl;
cout << " I/O: " « iocpi « endl;
>
>;

// Desktop machines comprise workstations and


// personal computers.
class Desktop : public Computer {
protected:
Desktop( flo a t a l, // a rith -lo g ic
flo a t c, // control
flo a t m, // memory

www.MathSchoolinternational.com
CHAPTER 5 INHERITANCE

flo a t io , // io
flo a t t, // ct
char* n, // name
flo a t 1, // lower bound
flo a t u ) // upper bound
Computer( a l, c, m, io, t , n,
>;

PC : public Desktop { // personal computer

flo a t a l = 1.8, // airith-logic


flo a t c = 2.3, // control
flo a t m = 5.6, // memory
flo a t io = 9.2, // io
flo a t t = 230, // ct
chair* n = "PC", // name
flo a t 1 = 800.00, // lower bound
flo a t u = 14500.00 ) // upper bound
Desktop( a l, c, m, io , t , n, 1, u ) { >
>;

class WS : public Desktop { // workstation


p u b lic :
WS( flo a t a l = 1.3, // a rith -lo g ic
flo a t c = 1.7, // control
flo a t m = 2.1, // memory
flo a t io = 5.8, // io
flo a t t = 15, // ct
char* n = "WS", // name
flo a t 1 = 4500.00, // lower bound
flo a t u = 78900.00 ) // upper bound
: Desktop( a l, c, m, io , t , n, 1, u ) { }
>;

class Mainframe : public Computer { // mainframe


p u b lic :
Mainframe( flo a t al = 1.2, // a rith -lo g ic
flo a t c = 1.5, // control
flo a t m = 3.6, // memory
flo a t io = 3.2, // io
flo a t t = 50, // ct
char* n = "$$", // name

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : M EASURING C O M PU TER PER FO R M A N C E 237

flo a t 1 = 310000.00, // lower bound


flo a t u = 20000000.00 ) // upper bound
: Computer( a l, c, m, io , t , n, 1, u ) { }
>;

class Test {
flo a t r t ; // response time in nanoseconds
void r e s u lt s ( Computer c, BMark b ) ;
p u b lic :
Test( Computer c, BMark b ) ;
>;

// Compute response time of running benchmark b


// on computer c, where
// rt = response time
// ct = clock cycle time
// ic = instruction count
// cpi = clock cycles per instruction
// Then rt = ic * cpi * ct
// The response time is given in nanoseconds,
int T e st::T est( Computer c, BMark b )
{
// instruction counts by type
flo a t a l_ r t , c _rt, m_rt, io _ rt;
// compute response times per type
a l_ rt = b .a lP * b .ic * c .a lc p i * c .c t;
c_rt = b.cP * b .ic * c.ccpi * c .c t;
m_rt = b.mP * b .ic * c.mcpi * c .c t;
io _rt = b.io P * b .ic * c.io cp i * c .c t;
rt = a l_ r t + c_rt + m_rt + io _ r t;
re s u lt s ( c, b ) ;
}

void T est: : r e s u lt s ( Computer c, BMark b )

cout « "\nBenchmark resu lts:\n " « endl;


cout « " RT (ns) fo r " « c.name
<< " running " << b.name
« " = 11 << rt << endl;
cout « "\nBenchmark and computer d e ta ils follow .\n"
« endl;
b .re p o rt( ) ;
c .rep o rt( ) ;
>

www.MathSchoolinternational.com
238 CH APTER 5 INHERITANCE

D iscussion____________________________________________________________________

A Test simulates the measurement of response time for a Computer run­


ning a BMark. Response time is the time that it takes a computer to run a
program from start to finish. A benchmark is a special program designed to
measure computer performance. For a discussion of performance in general
and the pitfalls of using benchmarks to measure it, see John L. Hennessy
and David A. Patterson Computer Architecture: A Quantitative Approach
(San Mateo, CA: Morgan Kaufmann Publishers, Inc., 1992).
Computer designers define response time as the product

R T = IC * C P I * CT

where

Metric Definition
RT Response time
IC Instruction count, the number of instructions executed
CPI Average clock cycles per instruction
CT Clock cycle time, usually in nanoseconds

Viewing R T in this way allows designers to focus on different strategies for


lowering R T and thereby improving the computer’s performance. For ex­
ample, improvements in compiler technology can lower IC and, therefore,
R T as well. Improvements in instruction set design together with accelera­
tion techniques such as pipelining and cache memory can lower CPI. R T is
generally measured in nanoseconds, as this is the unit typically used for CT.
BMark has data members to store the benchmark’s IC as well as the
instruction breakdown by category. Four categories of instruction are used:
• Arithmetic/logic instructions such as add, multiply, test for equality,
and logical or.
• Control instructions such as if-then-else and jump.
• Memory instructions such as read and write.
• Input/output instructions such as print.
Data members alP, cP, mP, and ioP store the breakdown of IC by percentage
into the four categories. For example, a cP value of 0.40 means that 40
percent of the benchmark’s instructions fall into category Control. Computer
has data members to store the machine’s average C PI for each instruction
category. For example, a ccpi value of 1.8 means that the machine’s average
C P I for control instructions is 1.8; that is, the machine averages 1.8 clock
cycles to execute a control instruction such as if-then-else. Given a BMark
and a Computer, a Test simulates running the benchmark on the computer
to measure RT, which Test stores in data member rt.

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : MEASURING C O M PU TER PER FO R M A N C E 239

Test is a frien d to Computer and BMark. Test accesses the Computer’s


data members alcpi, ccpi, mcpi, and io cp i to get the cycles per instruction
for each type of instruction. Test accesses the BMark’s data member ic to get
the benchmark’s instruction count and data members alP, cP, mP, and ioP to
get the breakdown of benchmark instructions for each instruction category.
Test then computes separately the response time for the arithmetic-logic,
the control, the memory, and the input-output instructions. By summing
these, Test computes the run time for the entire benchmark. Test prints its
own report to the standard output and then invokes BMark and Computer
methods, each named report, to print reports on the benchmark and the
computer particulars.
The inheritance hierarchy with BMark as its base class has WetRock,
DryRock, and PetRock as derived classes. The constructor BMark: :BMark
initializes data member name for each benchmark. Constructors for the de­
rived classes initialize data members such as alP, which are inherited from
the base class. Each derived constructor invokes the base class constructor
before executing its own nonempty body, as we see in the PetRock example:

class PetRock : public BMark {


p u b lic:
PetRockO : BMark( "PetRock" ) // PetRock constructor
{
alP = 12.2;
mP = 57.7;
ioP = 23.9;
cP = 1.0 - mP - ioP - alP;
ic = ( flo a t ) 10400500;

if ( !o k () )
in it _ e r r o r ( ) ;
>
>;

Inherited method BMark: : ok checks whether the percentages sum within


Tolerance to 1.0, and inherited method BMark: : in it_ e r r o r prints an error
message if they do not. Inheritance from BMark to each derived class is
public. BMark’s data members are all protected and all of its methods
but one are public. The exception is method in it, which is meant to be
invoked only by the constructor and so is private. The derived classes thus
have access to all the base class data members and to all the methods except
one, which is invoked within the accessible base class constructor.
The inheritance hierarchy with Computer as its base class differs in or­
ganization from the BMark hierarchy. In the case of the Computer hierarchy,
the derived classes have no data members except the ones inherited from the
base class; and each derived class has only one constructor among its local

www.MathSchoolinternational.com
CH APTER 5 INHERITANCE

methods. For example, the declaration for Desktop is:


class Desktop : public Computer {
protected:
Desktop( flo a t a l, // a r ith -lo g ic
flo a t c, // control
flo a t m, // memory
flo a t io , // io
flo a t t , // ct
char* n, // name
flo a t 1, // lower bound
flo a t u ) // upper bound
: Computer( a l, c, m, io , t , n, 1, u ) { >
>;
The declaration for PC, which is derived from Desktop, follows the pattern:
class PC : public Desktop { // personal computer
public:
PC( flo a t a l = 1.8, // a r ith -lo g ic
flo a t c = 2.3, // control
flo a t m = 5.6, // memory
flo a t io = 9.2, // io
flo a t t = 230, // ct
char* n = "PC", // name
flo a t 1 = 800.00, // lower bound
flo a t u = 14500.00 ) // upper bound
: Desktop( a l, c, m, io , t , n, 1, u ) { }
>;
The local constructor’s body is empty, which means that its base class’s
constructor does all the work. Defining a PC object
PC my_pc;
invokes the PC constructor, which immediately invokes the Desktop construc­
tor before executing its own body; and the Desktop constructor immediately
invokes the Computer constructor before executing its own body. As a result,
the constructors complete in the left-to-right order
Computer Desktop PC
Technically, the PC constructor is invoked first, Desktop constructor is in­
voked second, and the Computer constructor is invoked third. Yet the call­
ing sequence behaves as if the Computer constructor were invoked first, the
Desktop constructor were invoked second, and the PC constructor were in­
voked third (see Figure 5.3.2). Note that a low-level constructor such as
PC: :PC has default values for its arguments. These can be overwritten, of
course, in a call such as

www.MathSchoolinternational.com
5.3 SAM PLE A PPLIC A TIO N : M EASURING CO M PUTER PERFO RM AN C E 241

C o m p u ter::C om p u ter( ... ) executes first

J
D e s k to p ::D e s k to p ( ... ) executes second

I
PC::PC( ... ) executes third

Figure 5.3.2 Order of constructor invocation.

PC p c l( 1.9, // a rith m etic-lo gic cpi


2.3, // control cpi
3.8, // memory cpi
6.5, / / i/ o cpi
160, // clock cycle time
"S ecret", // name
850.00, // low-end price
850.00 ) ; // high-end p rice
The constructors for PC, WS, and Mainframe are public so that they
may be invoked anywhere in the program where their respective classes are
visible. By contrast, the constructors Computer and Desktop are protected
rather than public so that they cannot be invoked except from within the
inheritance hierarchy anchored on base class Computer. A public construc­
tor such as PC invokes a protected constructor such as Desktop.
The program generates an output report that summarizes the computer’s
response time when running the benchmark. A code slice such as
int mainO
{
WetRock wr;
PC pc; // use default values
Test t e s t ( pc, wr ) ; // te s t pc running wr

}
generates the report
Benchmark r e s u l t s :

RT (ns) in fo r PC running WetRock = 4.254135e+09

Benchmark and computer d e ta ils fo llo w .

Benchmark WetRock
Total instructions executed == 4500301
A rith m etic-logic == 0.5
Memory == 0.2
Control == 0.2

www.MathSchoolinternational.com
242 CHAPTER 5 INHERITANCE

Input/Output = = 0 . 1
Computer PC at cost ranging from $800 to $14500.
Clock cycle (nanoseconds): 230
CPIs:
A rith -L o gic: 2.5
Control: 2.3
Memory: 9.2
1/0: 5.6

Exercises

1. Is Desktop a base or a derived class? Explain.

2. Add Minicomputer as a derived class of Computer. Be sure to write a


constructor for it.

3. Add data member superscalar to Desktop. The data member has value
true (1) if the machine is superscalar and false (0) otherwise. A computer
is superscalar if

average CPI < 1

Some workstations achieve superscalar status by executing integer and


floating-point instructions in parallel.

4. Add protected data members or methods to each of Computer’s derived


classes. For example, derived class Desktop might have a data mem­
ber fo o tp rin t that indicates how much desk space is required for the
machine.

5. Computer technology changes so fast that the default clock cycle time
and C PI values listed in a constructor such as PC: :PC may already be
out of date and, in any case, soon will be. Determine accurate values for
an actual machine and change the default values.

6. Throughput—work done per unit of time— is a second measure of com­


puter performance. Extend the sample application so that Test can test
either throughput or response time.

7. Throughput varies inversely with response time. For example, if response


time goes down, then throughput goes up. This means that an improve­
ment in response time entails an improvement in throughput. Is the
converse true? Does an improvement in throughput automatically bring
an improvement in response time?

www.MathSchoolinternational.com
5.4 PO LYM O RPH ISM AND VIRTUAL METHODS 243

8. Add a method balanced to BMark. Its value is true (1) if the percentages
of the four instruction categories are approximately equal and false (0)
otherwise. For example, balanced would be true if

alP = 25.0;
cP = 25.0;
mP = 26.0;
ioP = 24.0;

Be sure to define “approximately equal.”

9. For the code slice

WS workstation;

trace the order in which the relevant constructors are invoked and the
order in which they complete.

10. Computer vendors often are selective in making public how their machines
run different benchmarks. Explain why a given machine might fare far
better running, say, PetRock than WetRock.

11. M IP S is a commonly used indicator of computer performance that stands


for Millions of Instructions Per Second. Add data member mips to Test
and have a Test method that sets its value.

12. MIPS is a very suspicious measure of computer performance for various


reasons. For one thing, it is possible that a drop in a computer’s response
time when running a given program may bring a drop in its MIPS rating
as well! In order words, the MIPS rating gets worse as the machine’s
performance gets better. How is this possible? Hint: MIPS is highly
sensitive to a program’s instruction mix.

5.4 Polymorphism and Virtual Methods


Polymorphism, refers to the run-time binding of a pointer to a method. For
example, suppose that classes C ircle and Rectangle are both derived from
base class Figure. Suppose further that the two derived classes redefine
method draw. Finally, assume that pointer ptr may point to either a C ircle
object or a Rectangle object. Under polymorphism, the statement
// draw a C ircle or Rectangle
// depending on the type of object
/ / t o which p tr points
ptr -> d ra w ();

www.MathSchoolinternational.com
244 CH APTER 5 INHERITANCE

executes differently depending on whether p tr currently points to a C ircle


object or a Rectangle object. Polymorphism delays until run-time the de­
termination of whether a C irc le ’s draw or a Rectangle’s draw is invoked.
Accordingly, the programmer can use the same code
p tr -> drawO ;
to invoke different draw methods simply by having p tr point to different
types of objects such as Circles and Rectangles.
C>—I—(—supports polymorphism through v ir tu a l methods and pointers.
In the previous example, draw would be a v irtu a l method and p tr would
be a pointer to a Figure (Figure*), which means that p tr could point to
both Circles and Rectangles. There is a sharp difference between v irtu a l
methods such as draw and nonvirtual functions (including nonvirtual
methods) that happen to have the same name. For nonvirtual functions
with the same name, the system determines at compile-time which of the
functions to invoke. For v irtu a l methods with the same name, the system
determines at run-time which of the methods to invoke. The invoked method
is determined by the type of object to which a pointer points. For example,
if p tr currently points to a C ircle object, then a C ir c le ’s v ir tu a l method
draw is invoked by
p tr -> d ra w ();
If p tr currently points to a Rectangle object, then a Rectangle’s v irtu a l
method draw is invoked by the same statement.
“Pure” object-oriented languages such as Smalltalk have only run-time
binding. C + + inherits compile-time binding from C and then adds run­
time binding. In this sense, C + + is a hybrid language. Run-time binding
in C + + is restricted to v irtu a l functions and C + + allows only methods to
be v irtu a l. Pointers are also central to polymorphism in C + + . To enable
polymorphism, C + + allows a pointer to a base class (e.g., a pointer of type
Figure*) to point to either a base class object (e.g., a Figure) or to any
derived class object (e.g., a C ircle or a Rectangle). No explicit cast is
required to have the pointer point to a derived class object; and, of course,
the derived class may be either directly or indirectly derived. The code slice
in t mainO

Figure* p tr; // pointer to Figure


C ircle c; // C ircle derived from Figure
p tr = &c; // ptr points to a C ircle

>
illustrates. By contrast, a pointer to a derived class object(e.g., a pointer of
type C irc le *) may not point to a base class object withoutexplicit casting.
The code slice

www.MathSchoolinternational.com
5.4 PO LYM O RPH ISM AN D VIRTUAL METHODS 245

in t mainO
{
Figure f i g ;
C ircle* p tr;
ptr = & fig; // * * * * * ERROR: derived class pointer can’ t
// point to base class object

>
is illegal. It would be legal if a cast were included:
ptr = ( C irc le * ) & fig;
We review these points with another example.

Example 5.4.1. Given


class B {

};

class D : public B {

>;

B b;
D d;
B* ptr;
each of these assignment statements
ptr = &b;
ptr = &d;
is legal.
Given
class B {

>;

class D : public B {

>;

B b;
D d;
D* p tr;
the assignment

www.MathSchoolinternational.com
246 CHAPTER 5 INHERITANCE

p tr = fed; // OK

is legal, but the assignment


// * * * * * ERROR: derived class pointer can’ t
// point to base class object
p tr = &b;
is illegal. □

A method to be bound at run-time must be flagged using the keyword


v irtu a l; otherwise, compile-time binding is in effect as usual. After a
v ir tu a l method is defined for a base class, derived classes may tailor the
v ir tu a l method to their own needs by redefining it.

Exam ple 5.4.2. In the code slice


class B ■[
p u b lic :
v ir tu a l void g ( ) ;
in t h ( ) ;
>;

class D : public B {
p u b lic :
void g ( ) ;
in t h ( ) ;
>;

in t mainO

D d;
B* p tr = &d;

p tr -> h (); // B::h invoked


p tr -> g ( ) ; // D ::g invoked

>
the base class B contains a v irtu a l method g, which derived class D then
redefines, and a nonvirtual method h. Since h is nonvirtual, compile-time
binding is in effect in the statement
p tr -> h () ;
Because p tr is of type B*, B’s h is invoked. On the other hand, because g is
v irtu a l, run-time binding is in effect in the statement
p tr -> g ( ) ;

www.MathSchoolinternational.com
PO LYM O RPH ISM AN D VIRTU AL METHODS 247

Because p tr points to an object of type D, D’s g is invoked. It is legal for


p tr to point to a D object even though p tr is of type B* because D is derived
from B.
In compile-time binding, the data type of the pointer resolves which
method is invoked. In run-time binding, the type of the object pointed to
resolves which method is invoked. □

A derived class need not redefine a v ir tu a l method in the base class. If


the v irtu a l method is not redefined, it is simply inherited in the usual way
by the derived class.

Example 5.4.3. In the code slice


class B {
p u b lic:
v irtu a l void g O ;
>;

class D : public B {
p u b lic:
int f ( char ) ;
>;

int mainO
{
D d;
d .g O ; // D: :g (in h erited from B) is invoked

}
method g in class B is inherited by D in the usual way. □

If a derived class redefines a v ir tu a l method in the base class, the


redefined method must have exactly the same prototype as the base class
method; otherwise, the derived class method hides (see Example 5.1.15) the
v irtu a l method in the base class and compile-time binding is used.

Example 5.4.4. In the code slice


class B {
p u b lic :
v irtu a l void f ( in t ) ;
>;

www.MathSchoolinternational.com
CH APTER 5 INHERITANCE

class D : public B {
p u b lic :
void f ( flo a t ) ; // hides B’ s f
>;

in t mainO
{
D d;
B* p tr = fed;
p tr -> f ( 3.3 ) ; // B ::f invoked

the parameter types of B: : f and D: : f differ; hence, D does not redefine f , but
rather defines a new f that hides B’s f . Furthermore, compile-time binding
is used. Because p tr is of type B*, B: : f is invoked in the statement
p tr -> f ( 3.3 ) ;
Because B: : f expects an in t argument, 3.3 is converted to the in t value 3
by dropping the fractional part and 3 becomes the argument to B: :f. □

It is a compile-time error to attempt to redefine a v ir tu a l method and


change the return type.

Exam ple 5.4.5. In the code slice


class B {
p u b lic :
v ir tu a l void f ( in t ) ;
>;

class D : public B {
p u b lic :
// * * * * * ERROR: Can’ t change the return type
in t f ( in t ) ;
>;
D’s attempt to change the return type of f from void to in t results in an
error. □

Exam ple 5.4.6. Consider the class hierarchy


class One {
p u b lic :
void whoamiO { cout « "One" « endl; }
>;

www.MathSchoolinternational.com
5.4 PO LYM O RPH ISM AN D VIRTUAL METHODS 249

One

Two

Three

Figure 5.4.1 The One-Two-Three hierarchy.

class Two : public One {


p u b lic :
void whoami { cout « "Two" « endl; }
};

class Three : public Two {


public:
void whoami( ) { cout « "Three" « endl; }
>;
(see Figure 5.4.1). Each class has a method whoami that prints its name to
the standard output. When main executes
int m a i n O
{
// define three objects
One one;
Two two;
Three three;

// define an array of pointers to One


One* arra y [ 3 ] ;

// in it i a liz e array
arra y [ 0 ] = Stone;
arra y [ 1 ] = &two;
arra y [ 2 ] = ftthree;

// have each object print it s name


fo r ( int i = 0; i < 3; i++ )
arra y [ i ] -> whoami( ) ;

return EXIT_SUCCESS;
>
the output is

www.MathSchoolinternational.com
CHAPTER 5 INHERITANCE

One
One
One
It would be nice if the output were
One
Two
Three
instead. However, the system determines at compile-time which of the
whoami methods to invoke. Because the array elements are of type pointer
to One (One*), the system invokes One: :whoami in each loop iteration de­
spite the fact that array [ 1 ] points to a Two and array [ 2 ] points to a
Three. □

Exam ple 5.4.7. We slightly amend the code of Example 5.4.6 to get sig­
nificantly different behavior:
// This time, make whoami a v irtu a l method,
class One {
p u b lic :
v ir tu a l void whoami( ) { cout « "One" « endl; }
>;

class Two : public One {


p u b lic :
v ir tu a l void whoami { cout « "Two" « endl; }
>;

class Three : public Two {


p u b lic :
v ir tu a l void whoami( ) { cout « "Three" « endl; }
>;

in t mainO
{
// define three objects
One one;
Two two;
Three three;

// define an array of pointers to One


One* array[ 3 ] ;

// i n i t i a l i z e array

www.MathSchoolinternational.com
5.4 POLYM O RPH ISM AN D VIRTUAL METHODS 251

array[ 0 ] = &one;
array[ 1 ] = &two;
array[ 2 ] = fethree;

// have each object prin t it s name


fo r ( in t i = 0; i < 3; i++ )
a rra y[ i ] -> whoami( ) ;

return EXIT_SUCCESS;
>
This time the output is
One
Two
Three
The only difference is that whoami is now a v ir tu a l method, which means
that the system determines at run-time—not at compile-time— which of the
three whoami to invoke. Because array [ 0 ] points to a One, the system
invokes One:: whoami for it. Because array [ 1 ] points to a Two, the system
invokes Two: :whoami for it. Because a rra y[ 2 ] points to a Three, the
system invokes Three: :whoami for it.
The output in this example would be the same if the keyword v ir tu a l
were omitted in the definitions of Two: :whoami and Three: :whoami. C+-1-
adheres to the principle
• Once virtual, always virtual!
for v irtu a l methods. □

The next example shows the usefulness of polymorphism.

Example 5.4.8. Suppose that we have a database of books that is sup­


ported by the class hierarchy
class Book {
p u b lic:
v irtu a l void p r in t( ) ;

>;

class Textbook : public Book {


p u b lic:
v irtu a l void p r in tO ;

>;

www.MathSchoolinternational.com
252 CHAPTER 5 INHERITANCE

class Referencebook : public Book {


public:
v ir tu a l void p r in t( ) ;

>;
Each class derived from Book specializes v irtu a l method p rin t for its class.
For example, Textbook’s prin t prints information about the book as well
as courses for which the book is appropriate, the level of the book (e.g.,
elementary, high school, college), and so on. Similarly, Referencebook’s
p rin t prints information about the book as well as subject areas to which
it is relevant, type of reference book (e.g., dictionary, journal), and so on.
Periodically, output is produced that lists all of the books in order to­
gether with information about each, which is supplied through the print
methods. To facilitate the sort, the array
Book* l i s t [ NoBooks ] ;
is defined and initialized to the various objects from the classes Textbook
and Referencebook representing the books. After sorting, the output is
produced by the loop
fo r ( in t i = 0; i < NoBooks; i++ )
l i s t [ i ] -> p r in t( ) ;
Because method prin t is virtu a l, if l i s t [ i ] points to a Textbook ob­
ject, Textbook’s prin t is invoked. If l i s t [ i ] points to a Referencebook
object, Referencebook’s prin t is invoked.
If the kinds of books are expanded by deriving additional classes from
Book, the code for producing the output need not be changed. An added
class would require only code for its version of print.
Sorting also could be supported through polymorphism. For example, if
each class contained a v irtu a l method key that returned the key on which
to sort, the array l i s t could be sorted by accessing the sort key as
l i s t [ i ] -> keyO
Now if the kinds of books are expanded by deriving additional classes from
Book, the code for sorting would not have to be changed. An added class
would require only code for its version of key. □

P u re v ir t u a l M ethods and A bstract Classes

An abstract class is a base class that is required to have a derived class. An


abstract class is also called a partial class. The mechanism for declaring a
class to be abstract is, however, subtle: we declare a pure virtu al m ethod
in the abstract class’s declaration.

Exam ple 5.4.9. The code slice

www.MathSchoolinternational.com
PO LYM O RPH ISM AN D VIRTU AL METHODS 253

class AC { // abstract class

p u b lic:
v irtu a l void f ( in t ) = 0 ; // pure v ir tu a l method
>;
declares AC to be an abstract class, namely, a base class that must have a
derived class. AC is made abstract by declaring f as a pure virtual method.
This is done through the assignment statement
v irtu a l void f ( in t ) = 0 ; // f == 0
As a pure v ir tu a l method, f must be defined in a class derived from AC.
For example, the code slice
class D : public AC {

public:
v irtu a l void f ( in t i ) { . . . }
>;
satisfies the requirement that f be defined in a class derived from AC. □

Objects may not be created for an abstract class. Further, it is an error


to invoke a pure v ir tu a l method if it has not been defined in a derived
class.

Example 5.4.10. The code slice


class AC { // abstract class
p u b lic:
v irtu a l void f ( ) = 0 ; // pure v ir tu a l method

>;

class D : public AC {
p u b lic:
v irtu a l void f ( ) // f defined

>;

AC ac; // * * * * * ERROR: AC is abstract


D d; // ok
contains an error because it defines ac as an object in an abstract class AC.
Object d is defined legally. □

Abstract classes are a means of expressing program design requirements


within C + + . For example, suppose that a team of programmers is working

www.MathSchoolinternational.com
254 CHAPTER 5 INHERITANCE

on a project. The manager partitions the project, giving each program­


mer a part of the system to code. The manager further specifies the project
requirements by declaring abstract classes, which is a way of forcing the pro­
grammers to code at least one derived class for each abstract class. Further,
the programmers are required to define, somewhere in the derived classes,
the pure v irtu a l methods that make a base class abstract in the first place.
An abstract class may have its own definition for the pure v irtu a l
method that makes it abstract, although this would be pointless because
an abstract class is not allowed to have objects that belong directly to it.
Nonetheless, a code slice such as
class AC {
p u b lic :
v ir tu a l void f ( ) = 0 ; // declare f pure

>;

class D : public AC {
p u b lic :
v ir tu a l void f ( ) { . . . } // define f

>;

void A C : : f ( ) // le g a l but pointless

>
is legal.

Miscellany

A v ir tu a l method may not be s ta tic because a v irtu a l method is always


a member of a particular object in a class rather than a member of the class
as a whole. So a v irtu a l method is nonstatic by itsvery nature.
To make a method virtu a l, we use the keyword v ir tu a l in the method’s
declaration but not in its definition if this occurs outside the class declaration.

Exam ple 5.4.11. In the code slice


class X {
p u b lic :
v irtu a l int f ( ) ; // declare
v irtu a l int g ( ) { . . . } // define in line
>;

www.MathSchoolinternational.com
5.4 PO LYM O RPH ISM AND VIRTU AL METHODS 255

int X : : f ( ) // v irtu a l does NOT occur


{

f and g are both v irtu a l. Technically, g is declared and defined inside X’s
declaration; but the keyword v ir tu a l is part of g ’s declaration, not its inline
definition. In the case of f , the declaration and definition are separated: the
keyword v irtu a l occurs in the declaration but not in the definition, which
occurs outside X’s declaration. It would be an error for the keyword v ir tu a l
to occur in X : :f ’s definition. □

Exercises

1. What is the output?

class A {
p u b lic :
void f ( ) { cout « "A!" « endl; >
>;

class Z : public A {
p u b lic :
void f ( ) { cout « "Z!" « endl; }
>;

int mainO
{
A* ptr;
Z z;
ptr = &z;
ptr -> f ( ) ;
return EXIT_SUCCESS;
}

2. What is the output?

class A {
p u b lic :
void f ( int ) { cout « "A!" « endl; }
>;

www.MathSchoolinternational.com
256 CHAPTER 5 INHERITANCE

class Z : public A {
p u b lic :
void f ( double ) { cout « "Z!" « endl; }
>;

int mainO

A* p tr;
Z z;
ptr = &z;
ptr -> f ( 9999999.9999 ) ;
return EXIT_SUCCESS;
>

3. What is the output?

class A {
p u b lic :
v irtu a l void f ( ) { cout « "A!" « endl; }
>;

class Z : public A {
p u b lic :
v irtu a l void f ( ) { cout « "Z!" « endl; }
>;

int m a i n O

A* p tr;
Z z;
p tr = &z;
p tr -> f ( ) ;
return EXIT_SUCCESS;
>

4. What is the output?

class A {
p u b lic :
v irtu a l void f ( int ) { cout « "A!" « endl; >
>;
class Z : public A {
p u b lic :
v irtu a l void f ( double ) { cout « "Z!" « endl; }
>;

www.MathSchoolinternational.com
5.4 PO LYM O RPH ISM AN D VIRTUAL METHODS 257

int main()
{
A* p tr;
Z z;
p tr = &z;
p tr -> f ( 27 ) ;
return EXIT_SUCCESS;
>

5. What is the error?

class A {
p u b lic :
void f ( ) -[ cout « "A!" « endl; }
};

class Z : public A {
p u b lic :
void f ( ) { cout « "Z!" « endl; }
>;

int m a i n O

A* p tr;
Z z;
p tr = &z;
p tr -> f ( ) ;
ptr -> Z : : f ( ) ;
return EXIT_SUCCESS;
}

6. Explain the difference between compile-time and run-time binding.

7. Is compile-time or run-time binding at work in this code slice?

class B {
p u b lic :
int f ( int ) ;
>;

class D : public B {
p u b lic :
int f( float );
>;

www.MathSchoolinternational.com
258 CHAPTER 5 INHERITANCE

in t mainO
{
D d l;
B* ptr = &dl;

p tr -> f ( ) ; // run-time or compile-time?

>

8. Explain why it does not make sense for a v ir tu a l method to be sta tic.

9. Explain the error, (g and f are not methods.)

void g ( in t, in t, double ) ;
v ir tu a l in t f ( double ) ;

10. Explain the error.

class A {
p u b lic :
s ta tic v irtu a l void f ( ) { . . . }
>;

11. What is an abstract class? Write code to illustrate.

12. What is a pure v irtu a l method? Write code toillustrate.

13. Explain therelationship between an abstract class and a pure v irtu a l


method.

14. Explain the error.

class A {
p u b lic :
v irtu a l void f ( ) = 0;
>;

A a l , a2, a3;

15. Is this code legal?

class A {
... //no v irtu a l methods here
>;
class B : public A {
p u b lic :
v irtu a l void f ( ) ;
>;

www.MathSchoolinternational.com
5.4 PO LYM O RPH ISM AN D VIRTUAL METHODS 259

class C : public B {
p u b lic:
v irtu a l void f ( ) ;
>;

16. Is Z: : f a v ir tu a l method?

class A {
p u b lic:
v ir tu a l void f ( ) ;
};

class Z : public A {
p u b lic:
void f ( ) ;
>;

17. What is the error?

class A {
p u b lic:
v ir tu a l void g ( ) ;
>;

v irtu a l void A : : g ( )
{

18. What is the error?

class A {
p u b lic:
void v ir tu a l h ( ) ;
>;

void A: :h ()

>

www.MathSchoolinternational.com
260 CHAPTER 5 INHERITANCE

5.5 Sample Application: Virtual Tree Traversal


P r o b le m ______________________________________________________________________

Implement the standard binary tree traversals (inorder, preorder, and pos­
torder) as v irtu a l functions.

S o lu tio n _______________________________________________________________________

We revise the binary search tree of Section 4.4 so that class BST is now an
abstract or partial class that declares traverse as a pure v ir tu a l function.
There are three derived classes that then define traverse: InBST, PreBST,
and PostBST. The BST methods that create a binary search tree and add
Nodes to it remain unchanged.

C + + Im plem en tation _______________________________________________________

#include <iostream.h>

class BST;

class Node {
frie n d BST; // abstract class
frie n d InBST; // derived from BST
frie n d PreBST; // derived from BST
frie n d PostBST; // derived from BST
char v a l; // value == contents
Node* lc ; // l e f t ch ild
Node* rc; // righ t child
p u b lic :
Node(); // constructor
in t empty( ) ; // va l == None
void w r i t e ( ) ;
>;

class BST { // abstract class


protected:
in t count;
Node* root;
Node* tree;
void addNodeAux( const char ) ;
p u b lic :
BSTQ;

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : VIRTUAL TREE TRAVERSAL

BSTC BST& ) ; // copy constructor


void addNode ( const chair ) ;
v irtu a l void trav erse( ) = 0; // pure v irtu a l
};

class InBST : public BST {


void inorderAux( Node* ) ;
p u b lic :
void inorder( ) ;
v irtu a l void tra v e rse O ; // declaration
InBSTO : BST() { >
InBST( BST& bst ) : BST( bst ) { >
};

void InBST::traverseO

inorder( ) ;
>

void InBST: : inorder( )

tree = root;
inorderAux( root ) ;
>

void InBST: : inorderAux( Node* n )


{
i f ( n -> emptyO )
return;
inorderAux( n -> lc ) ;
n -> w rite ( ) ;
inorderAux( n -> rc ) ;
>

class PreBST : public BST {


void preorderAux( Node* ) ;
p u b lic :
void preorderO ;
v irtu a l void tra v e rse O ; // declaration
PreBSTO : BST() { >
PreBST( BST& bst ) : BST( bst ) { >
>;

www.MathSchoolinternational.com
CHAPTER 5 INHERITANCE

void PreBST::trav erse()

preorder( ) ;
>
void PreBST::preorder()

tree = root;
preorderAux( root ) ;
>

void PreBST: : preorderAux( Node* n )


{
if ( n -> emptyO )
return;
n -> w rite ( ) ;
preorderAux( n -> lc ) ;
preorderAux( n -> rc ) ;
>

class PostBST : public BST {


void postorderAux( Node* ) ;
p u b lic :
void postorderO ;
v ir tu a l void traverse( Node* ) ; // declaration
PostBSTO : BST() { >
PostBST( BST& bst ) : BST( bst ) { }
>;

void PostBST: :postorderO

// stub function
}

void PostBST::postorderAux( Node* n )


{
// stub function
>

BST::BST()
{
root = tree = new Node;
count = 0;
}

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : VIRTUAL TREE TRAVERSAL

BST::BST( BST& bst )


{
root = b st.ro o t;
tree = b s t.tre e ;
count = b s t . count;
>

void BST: : addNode( const char v )


{
tree = root; // start at root
addNodeAux( v ) ;
}

void BST::addNodeAux( const char v )

i f ( tree -> empty( ) ) {


tree -> val = v;
count++;
tree -> lc = new Node;
tree -> rc = new Node;
return;
}
i f ( v <= tree -> val )
tree = tree -> lc ;
else
tree = tree -> rc;
addNodeAux( v ) ;
>

Node::Node()
{
lc = rc = 0;
val = None;
>

int Node::empty( void )

return val == None;


>

void N ode::w rite( void )


■C
cout « val;
>

www.MathSchoolinternational.com
264 CHAPTER 5 INHERITANCE

D iscussion____________________________________________________________________

There are moderate changes to the original code (see Section 4.4). The
most significant is that BST is now an abstract class with traverse as its
pure v ir tu a l method. BST has InBST, PreBST, and PostBST as three de­
rived classes, each of which defines traverse. For example, InBST defines
tra verse to perform an inorder traversal of the BST
void InBST: :traverseO

in order( ) ;
>
by invoking local method inorder. PreBST has a comparable local method
preorder that produces a preorder traversal of the BST.
BST now has a copy constructor in addition to the default constructor.
The code for copy constructor is straightforward:
BST::BST( BST& bst )

root = b s t.ro o t;
tre e = b s t.tre e ;
count = b s t.count;
>
Each derived class also has a default and a copy constructor with empty
bodies. The constructors merely invoke their BST counterpart. Here is the
InBST copy constructor:
InBST( BST& bst ) : BST( bst ) { }
The copy constructors have been added to allow code slices such as this:
int mainO

BST* ptr; // pointer to BST


InBST in; // create InBST object
// add Nodes to the InBST
ptr = &in; // set pointer
p tr -> trav erse O ; // invoke v irtu a l function
// InBST: : traverse called to
// perform inorder traversal
PreBST p re( in ) ; // create PreBST object as a copy
// of InBST object
p tr = &pre; // set pointer
p tr -> trav erse O ; // invoke v irtu a l function

>

www.MathSchoolinternational.com
5.5 SAM PLE A PPLIC A TIO N : VIRTU AL TREE TRAVERSAL 265

The new program is more “object driven” than the original. In the
original program, we build a BST and then explicitly invoke method inorder
to perform an inorder traversal. If we want a preorder traversal instead, then
we must explicitly invoke preorder. In the new program, we can invoke the
v irtu a l function traverse through a pointer, as in the code slice
in t mainO
{
BST* p tr; // pointer to BST
// build a BST of one fla v o r
// or another, e . g . , PostBST
ptr -> tr av er se O ; // object determines what sort
// of tra versa l is performed

}
The object to which p tr points determines what sort of traversal is per­
formed. The programmer simply invokes traverse and lets the system
determine at run-time the appropriate v ir tu a l function to call.

Exercises

1. The new code makes BST, InBST, PreBST, and PostBST all friends of
Node. Why must BST still be a friend?

2. Name a major consequence of our decision to make BST an abstract class.

3. What is the mechanism by which BST is made an abstract class?

4. Must the keyword v ir tu a l be used in declaring InBST::traverse in


order for the function to be virtu a l?

5. Is the keyword v ir tu a l used in the definition of PreBST:: traverse?

6. Write the code for PostBST: :postorder.

7. Write the code for PostBST: :postorderAux.

8. Why does the new code have a copy constructor for BST?

9. Explain why the default and the copy constructor for PreBST have empty
bodies.

10. How many definitions of traverse are required so that the pure v ir tu a l
function requirement is met?

11. Summarize the difference in approach between the original BST program
and the new one. Which strikes you as more object-oriented in design?
Justify your answer.

www.MathSchoolinternational.com
CH APTER 5 INHERITANCE

Destructors Under Inheritance

Constructors in an inheritance hierarchy fire in a


• base class to derived class

order. Destructors in an inheritance hierarchy fire in a


• derived class to base class

order. So the destructors fire in the reverse order of the constructors.

Exam ple 5.6.1. The output for the code slice


c la s s A {
p u b lic :
A ( ) { cout « "A ’ s con stru ctor"; }
~ A () { cout « "A ’ s d estru ctor"; }
>;

c l a s s B : p u b lic A {
p u b lic :
B () : A () { cout « "B ’ s c o n s t r u c t o r " ; }
~B () { cout « "B ’ s d e s t r u c t o r " ; }
};

i n t mainO

vo id f ( ) ;

f();

r e t u r n EXIT_SUCCESS;
}

vo id f ( )
{
B b;
}
is
A’ s c o n s tru c to r
B’ s c o n s tr u c t o r
B’ s d estru ctor
A’ s d estru ctor

www.MathSchoolinternational.com
5.6 DESTRUCTORS UNDER INHERITANCE 267

A destructor’s main job is to free storage that a constructor dynamically


allocates. By firing in the reverse order of constructors, destructors ensure
that the most recently allocated storage is the first storage to be freed.

Example 5.6.2. The output for the code slice


class B { // base class
char* sB;
public:
B ()
{
sB = new char[ 3 ] ;
cout « "B allo cates 3 bytes" « endl;
}

"BO
{
d e le te [ ] sB;
cout « "B frees 3 bytes" « endl;
}
>;

class D : public B { // derived class


char* sD;
p u b lic :
DO : B ()

sD = new char[ 5 ] ;
cout « "D allocates 5 bytes" « endl;
>

~D()
{
d e le te [ ] sD;
cout << "D frees 5 bytes" « endl;
>
>;

int mainO

D d l;

return EXIT_SUCCESS;
>
is

www.MathSchoolinternational.com
268 CHAPTER 5 INHERITANCE

B allo cates 3 bytes


D allo cates 5 bytes
D frees 5 bytes
B frees 3 bytes
D’s five bytes are allocated after B’s three bytes, and D’s five bytes are freed
before B’s three bytes. Note that the storage allocated by B’s constructor is
freed by B’s destructor and that the storage allocated by D’s constructor is
freed by D’s destructor. □

V irtu a l Destructors

Constructors may not be v irtu a l but destructors may be. The need for
v ir tu a l destructors is best explained through an example.

Exam ple 5.6.3. The output for the code slice


class B { // base class
char* ptrB;
p u b lic :
B ()

ptrB = new char[ 5 ] ;


cout « "B allocates 5 bytes" « endl;
>

~B() // base class destructor


{
d e le te [ ] ptrB;
cout « "B frees 5 bytes" « endl;
>
>;

class D : public B { // derived class


char* ptrD;
p u b lic :
DO : B()
{
ptrD = new char[ 1000 ] ;
cout « "D allocates 1000 bytes" « endl;
>

~D() // derived class destructor


{
d e le te [ ] ptrD;

www.MathSchoolinternational.com
DESTRUCTORS UNDER INHERITANCE 269

cout « "D fre e s 1000 bytes" « endl;


}
>;

int mainO
{
const in t Forever = 1;
void f ( ) ;

// generate lo ts of garbage
while ( Forever ) {
f();
>

return EXIT_SUCCESS;
>

void f ( )
{
B* p = new D; // p points to a D object

d elete p;
>
is

B a llo ca tes 5 bytes


D a llo ca tes 1000 bytes
B frees 5 bytes
for each iteration of the while loop. On each entry to f, B’s constructor
allocates five bytes and D’s constructor allocates a thousand bytes. When f
exits, the thousand bytes allocated by D’s constructor are not freed; instead,
only the five bytes allocated by B’s constructor are freed.
The problem is that B’s destructor is nonvirtual, which means that the
system must bind p at compile-time. Because p is of type B*, p is bound
to B’s data members and methods, including constructors and destructors,
rather than to D’s data members and methods. When f exits, B: : ~B frees
the five bytes. Yet D: : ~D fails to fire, thus leaving behind another thousand
bytes of garbage.
The solution is to make B: : ~B a v ir tu a l destructor:
class B {
char* ptrB;
p u b lic:

www.MathSchoolinternational.com
270 CHAPTER 5 INHERITANCE

BO
{
ptrB = new char[ 5 ] ;
cout « "B a lloca tes 5 bytes" « endl;
>

v ir tu a l ~B() // v irtu a l destructor

d e le te [ ] ptrB;
cout « "B frees 5 bytes" « endl;
>
>;

In this case, p is bound at run-time to the data members and methods of the
object to which it currently points. So if p points to a D object, as in f , then
D: : ~D fires when control exits. B: : ~B also still fires when f exits because
the D object to which p points is, by virtue of inheritance, a B object as well.
With B: : ~B as a v irtu a l destructor, our output becomes
B a llo ca tes 5 bytes
D a llo ca tes 1000 bytes
D fre e s 1000 bytes
B fre e s 5 bytes

The rule of thumb is that a destructor should be v ir tu a l whenever two


conditions are met:
• Constructors for the base and the derived class dynamically allocate
separate storage. In Example 5.6.3, for instance, B’s constructor allo­
cates storage to which ptrB points and D’s constructor allocates sepa­
rate storage to which ptrD points.
• The program dynamically allocates a class object. In Example 5.6.3,
for instance, f dynamically allocates a D object.

Exercises

1. What is the output?

class X {
p u b lic :
XO { cout « "X: :X" « endl; }
~X() { cout « " X: : ~X" « endl; >
>;

www.MathSchoolinternational.com
5.7 M U LT IP LE INHERITANCE 271

class Y : public X {
p u b lic:
Y( ) : X () { cout « "Y ::Y " « endl; >
~Y() { cout « "Y : : ~Y" « endl; }
>;

class Z : public Y {
pu b lic:
Z () : Y ( ) { cout « "Z ::Z " « endl; >
~Z() { cout « "Z::~Z" « endl; }
>;

int mainO
{
Y y;
Z z;

return EXIT_SUCCESS;
}

2. In Exercise 1, make all the destructors virtu a l. Does the code run any
differently than before?

3. For Exercise 1, list all the methods that Z has.

4. Must a derived class constructor invoke a base class constructor if one


exists?

5. Must a derived class destructor invoke a base class destructor if one exists?

6. Can a constructor be virtu a l?

7. Give an example where a v irtu a l destructor is required.

8. Must every destructor in an inheritance hierarchy be virtu al?

9. What is the rule of thumb about whether a destructor should be made


virtu a l?

10.Explain the connection between run-time binding and v irtu a l destruc­


tors.

5.7 Multiple Inheritance


In single inheritance, a derived class has a single base class. In multiple
inheritance, a derived class has multiple base classes (see Figure 5.7.1). In

www.MathSchoolinternational.com
272 CHAPTER 5 INHERITANCE

single multiple
inheritance inheritance

Figure 5.7.1 Single versus multiple inheritance.

technical jargon, a hierarchy with only single inheritance is a tree, whereas


a hierarchy with multiple inheritance is a graph.
In a single inheritance hierarchy, a derived class typically represents a
specialization of its base class. Because classes are user-defined data types
in C + + , such a derived class is a specialization or refinement of the more
general data type that its base class represents. In a multiple inheritance hi­
erarchy, a derived class typically represents a combination of its base classes.

Exam ple 5.7.1. In the single inheritance hierarchy


// Window base class — no s c r o ll bars
class Win { // base class
protected:

>;

// S crolled window derived class — s c r o ll bars


class ScrollWin : public Win {
Widget horizontalSB;
Widget verticalSB;

>;
a ScrollWin is a specialization of a Win. In particular, a ScrollWin has a
horizontal and a vertical scrollbar, which are not present in a Win. □

Exam ple 5.7.2. In the multiple inheritance hierarchy


// Popup menu class — no s c r o ll bars
class PopupMenu {_
protected:
in t menuChoices;
Win* menuSubWins;

>;

www.MathSchoolinternational.com
5.7 M U LTIPLE INHERITANCE 273

// Scrolled window class — not a popup


class ScrollWin : public Win {
Widget horizontalSB;
Widget verticalSB ;

>;

// Combination of popup and scro lled


class ScrollPopupMenu :
public PopupMenu, public ScrollWin {

>;

a ScrollPopupMenu combines a PopupMenu and a ScrollWin to form a new


class that inherits features from each of its base classes. □

Example 5.7.3. In the multiple inheritance hierarchy


class istream { // input

class ostream { // output

>;

class iostream :
public istream, public ostream { // input-output

>;
an iostream is a class that supports input and output operations. It com­
bines istream, which supports only input operations, and ostream, which
supports only output operations. These classes and others are declared in
the C + + header file iostream. h. Chapter 7 looks closely at the multiple in­
heritance hierarchy that C + + provides for input/output. □

Inheritance and Access

The rules of inheritance and access do not change from a single to a multiple
inheritance hierarchy. A derived class inherits data members and methods
from all its bases classes, regardless of whether the inheritance links are
private, protected, or public.

Example 5.7.4. The code slice

www.MathSchoolinternational.com
274 CHAPTER 5 INH ERITANCE

// a l l public
class Bl { // base class 1

>;

class B2 { // base class 2

>;

class B3 { // base class 3

>;

class D :
public B l, public B2, public B3 { // derived class

>;

// a l l private
class BB1 { // base class 1

>;

class BB2 { // base class 2

class BB3 { // base class 3

>;

class DD :
private BB1, private BB2, private BB3 { // derived class

>;

// mixed
class BBB1 { // base class 1

>;

class BBB2 { // base class 2

>;

www.MathSchoolinternational.com
5.7 M U LTIPLE INHERITANCE 275

class BBB3 { // base class 3

>;

class DD :
private BBB1, public BBB2, public BBB3 { // derived class

>;

illustrates three possible combinations of inheritance. □

With multiple inheritance comes increased opportunities for name con­


flicts. The conflict can occur between the derived class and one of the base
classes or between the base classes themselves. It is up to the programmer
to resolve the conflict.

Example 5.7.5. In the code slice

class Bl { // base class 1


protected:
int x;
>;

class B2 { // base class 2


protected:
int x;
>;

class D : public B l, public B2 { // derived class


protected:
int x;
p u b lic :
friend void testerO;
>;

void testerO
{
D dl;
d l.x = 999; // lo c a l x
d l. B l: :x = 111; // inherited from Bl
d l.B 2 ::x = 222; // inherited from B2

>

www.MathSchoolinternational.com
276 CHAPTER 5 INHERITANCE

the local x hides the inherited Bl: :x and B2: :x. Further, Bl and B2 each
has a data member named x. The only way to resolve these conflicts is to
use the scope resolution operator in referencing either of the inherited x’s. □

V irtu a l B ase Classes

Multiple inheritance hierarchies can be complex, which may lead to the


situation in which a derived class inherits multiple times from the same
indirect base class.

Exam ple 5.7.6. In the code slice


c la s s B { // base c la s s
p r o te c t e d :
i n t x;

>;

c la s s Dl : p u b lic B { // path 1 through Dl


p r o te c t e d :

>;

c la s s D2 : p u b lic B { // path 2 through D2


p r o te c t e d :

>;

c la s s Z : p u b lic D l, p u b lic D2 { // x comes tw ic e

>;
Z inherits x twice from B: once through Dl and again through D2 (see Figure
5.7.2). This is wasteful and confusing. We correct the problem by changing
Dl and D2 into v irtu a l base classes for Z:
c la s s B { // base c la s s
p r o te c t e d :
in t x;

>;

c la s s Dl : p u b lic v i r t u a l B { // path 1 through Dl


p r o te c t e d :

www.MathSchoolinternational.com
5.7 M U LT IP LE INHERITANCE 277

Figure 5.7.2 The need for v ir tu a l inheritance.

class D2 : public v irtu a l B { // path 2 through D2


protected:

class Z : public Dl, public D2 { // x comes once

>;
There is now a single copy of x in Z. By making Dl and D2 into v ir tu a l base
classes for Z, we tell them in effect to send to Z only one copy of whatever
they inherit from their own common ancestor B. □

Exercises

1. Explain the difference between using inheritance (1) to specialize or refine


a class or data type and (2) to combine classes or data types. Which type
of C + + inheritance is best suited for each of (1) and (2)?

2. Change the code so that multiple rather than single inheritance is at


work.

class A -[

>;

class B : public A {

3. How many data members does R have?

www.MathSchoolinternational.com
278 CHAPTER 5 INH ERITANCE

class P {
protected:
in t x;
in t y;
>;

class Q {
protected:
flo a t a;
flo a t b;
>;

class R : public P, public Q {


p u b lic :
void f ( ) { . . . }
>;

4. Can an inheritance link be p rivate under multiple inheritance? If so,


illustrate with an example. If not, explain why not.

5. Write code to assign values to each of R’s data members.

class P {
protected:
in t x;
in t y;
>;
class Q {
protected:
flo a t a;
flo a t b;
>;
class R : public P, public Q {
p u b lic :
void f ( ) { . . . }
>;

6. Under multiple inheritance, can derived class D have a public inheritance


link to one base class and a p rivate inheritance link to another base class?

7. How many copies of x does D inherit?

class B {
protected:
in t x;
>;

www.MathSchoolinternational.com
CO M M O N PR O G R A M M IN G ERRORS 279

class Bl : public B {
protected:
flo a t y;
>;

class B2 : public B {
flo a t z;
>;

class D : public Bl, public B2 {

};

8. How many copies of x does D inherit?

class B {
protected:
in t x;

class Bl : v irtu a l public B {


protected:
flo a t y;
>;

class B2 : v ir tu a l public B {
protected:
flo a t z;
>;

class D : public B l, public B2 {

>;

Com m on Program m in g Errors

1. It is an error to access a p riva te data member outside its class except


through a frien d function. For example, the code slice

class C {
in t x;

www.MathSchoolinternational.com
280 C H A PTE R S INHERITANCE

in t mainO
{
C c l;
c l.x = 6; // * * * * * ERROR: x is p riva te in C

>

contains an error because x is p riva te in C and, therefore, accessible only


to C’s methods or friends.

2. It is an error to access a protected data member outside its class hier­


archy except through a frien d function. For example,

class C { // base class


protected:
in t x;
>;

class D : public C { // derived class

>;

in t mainO

C c l;
c l.x = 9; // * * * * * ERROR: x is protected in C

>

contains an error because x is accessible only to C’s methods and friends


and to methods and friends of certain classes, such as D, that are derived
from C.

3. It is an error for a derived class to have more than one access-specifier


per base class. The code slice

class BC { // base class

>;

// * * * * * ERROR: only one a ccess-sp ecifier fo r


// each base class
class DC : public, p rivate BC { // derived class

>;

www.MathSchoolinternational.com
COM M ON PR O G R A M M IN G ERRORS 281

contains an error because DC’s access to BC must be either p riva te or


protected or public. However, a derived class may have different access-
specifiers for different base classes:

class BC1 { // base class 1

class BC2 { // base class 2

>;

// ok
class DC : protected BC1, public BC2 {

>;

4. If a base class has constructors but no default constructor, then a derived


class constructor must explicitly invoke a base class constructor in its
header:

class B {
in t x;
in t z;
p u b lic:
// constructors — but no defau lt constructor
B( in t a ) { x = a ; z = -1; >
B( in t a l , in t a2 ) ■[ x = a l ; z = a2; }
>;

class Dl : public B {
in t y;
p u b lic:
// * * * * * ERROR: D( in t ) must e x p lic it ly invoke
// one of B’ s constructors
D l( in t a ) { y = a; }
>;

class D2 : public B {
in t y;
p u b lic:
// ok — D2 e x p lic it ly invokes a B
// constructor in it s header
D2( in t a ) : B ( a ) { y = a ; >
};

www.MathSchoolinternational.com
282 CH APTER 5 INHERITANCE

5. If a derived class has more than one base class, then it is an error to omit
a comma that separates the access-specifiers for the base classes. Here
are two examples:

class BC1 { // base class 1

class BC2 { // base class 2

>;

// * * * * * ERROR: access-specifiers must be


// separated by commas
class DC1 : public BC1 public BC2 {

>;

// correct
class DC2 : public BC1, public BC2 {

>;

6. It is an error to use an access declaration to change the status of a base


class member. The code slice

class BC { // base class


p riv a te :
int y; // private in BC
p u b lic :
int z; // public in BC
>;
class DC : public BC { // derived class
protected:
BC::y; // * * * * * ERROR: can’ t change to protected
BC::z; // * * * * * ERROR: can’ t change to protected
>;

7. If a derived class has a methodwith the same name as a base class


method, then the derivedclass’s method hides the base class method. It
is therefore an error to invoke the base class method:

class BC { // base class

p u b lic :
void f ( double ) ; // method f
>;

www.MathSchoolinternational.com
COM M ON PR O G R A M M IN G ERRORS 283

class DC : public BC { // derived class

p u b lic:
void f ( char* ) ; // CAUTION — hides BC::f
>;

in t mainO
{
DC d;

// * * * * * ERROR: D C ::f, which hides B C ::f,


// expects a char* and not a double
d . f ( 3.14 ) ;

>

8. It is an error to declare a nonmethod v irtu a l. Only methods may be


virtu a l. For example, the code slice

// * * * * * ERROR: f not a method


v irtu a l in t f ( i n t , in t ) ;

contains an error because f is not a method.

9. A v irtu a l method in a base class can only be redefined in a derived


class if the derived class declares the method with the same prototype as
the base class method. If the arguments are changed, the result is a new
method that hides the v ir tu a l method in the base class. Furthermore,
run-time binding is disabled. For example, in the code slice

class BC { // base class


p u b lic:
virtual int f ( char*, double );
>;

class DC : public BC { // derived class


p u b lic:
v irtu a l in t f ( in t ) ; // DC’ s f hides BC’ s f
>;

BC:: f is not in the scope of class DC. BC: : f can only be accessed within
class DC by using the scope resolution operator. Furthermore, compile­
time binding will be used.

www.MathSchoolinternational.com
284 CH APTER 5 INHERITANCE

10. It is an error to declare a method in a derived class with the same pa­
rameter types as a v ir tu a l method in the base class but with a different
return type:

class BC { // base class


p u b lic :
v irtu a l in t f ( char*, double ) ;
>;

class DC : public BC { // derived class


p u b lic :
// * * * * * ERROR: d iffe r e n t return type
v irtu a l double f ( char*, double ) ;
>;

11. It is an error for a v ir tu a l method to be declared s ta tic .

12. It is an error to include the keyword v irtu a l in a v ir tu a l method’s


definition if the definition occurs outside the class declaration. The code
slice

class C {
p u b lic :
v irtu a l in t f ( ) ; // declaration — ok
v irtu a l in t g ( ) in lin e d e fin itio n — ok
>;

// * * * * * ERROR: v irtu a l must not appear in


// d e fin itio n outside class declaration
v ir tu a l in t C : : f ( )

>

13. It is an error to make a constructor virtu a l. However, a destructor may


be virtu a l.

14. It is an error for an abstract or partial class not to have a derived class.

15. It is an error to define an object in an abstract class. However, objects


may belong to a class derived from the abstract class. In the code slice

class AC { // abstract class


p u b lic :
v ir tu a l in t f ( ) = 0; // pure v irtu a l function

>;

www.MathSchoolinternational.com
PR O G R A M M IN G EXERCISES 285

class DC : public AC { // derived class

>;

// * * * * * ERROR: can’ t define AC objects


AC a cl;

DC d e l; // ok — DC derived from AC

the attempted definition of acl is thus illegal, whereas the definition of


del is legal.

Program m ing Exercises

5.1. Implement a Library hierarchy with at least a dozen classes. For pur­
poses of the exercise, consider a library to be a collection of literary or
artistic materials that are not for sale. In addition to the constructors
and destructors, classes should include methods that describe the class
much in the way that a human librarian might describe a class or subclass
of materials among library’s holdings.

5.2. Implement a LAN (local area network) hierarchy by using the LAN class
of Programming Exercise 4.3 as a base class. Subclasses may be derived
to represent different topologies such as star, ring, bus, and hub. Data
members should represent properties such as transmission medium, access
control method, data frame format, standards, data rate, and the like. For
at least one subclass, simulate the activity of nodes in such a LAN.

5.3. Implement an Employee hierarchy for any type of business with which you
are familiar. The hierarchy should have at least four levels, with inheri­
tance of data members and methods. There should be methods for hiring,
firing, promoting, demoting, transferring, and retiring Employees. Also,
there should be methods for calculating raises and bonuses for Employees
in keeping with both their category (e.g., hourly-wage versus salaried)
and their performance. The inheritance hierarchy also should be used to
provide different types of access to Employees. For example, the type of
access granted the general public presumably would differ from the type
of access provided to an Employee’s supervisor, to the payroll depart­
ment, or to the FBI. Use inheritance to distinguish among at least four
different types of access to Employee information.

5.4. Implement a ProgLang hierarchy in which classes represent types of pro­


gramming language. The hierarchy should begin with an abstract class

www.MathSchoolinternational.com
CH APTER 5 INHERITANCE

that has a v irtu a l method to place a sample language in the hierarchy.


For example, given C + + as a sample language, the method would iden­
tify it as an object-oriented language; given LISP as a sample language,
the method would identify it as a functional or applicative language. The
hierarchy also should include a method that identifies code slices. For
example, given the code slice

fo r ( i = 0; i < n; i++ ) {

>

the method would identify the language as C_Type, where C_Type is a


base class with C and CPlusPlus as derived classes. Given the code slice

fo r ( in t i = 0; i < n; i++ ) {

>

the method would identify the language as CPlusPlus. The hierar­


chy should include at least six classes in addition to the abstract class
ProgLang.

5.5. Implement an Automobile within a multiple inheritance hierarchy. In


addition to being a Vehicle, an Automobile is also a Commodity, a
StatusSymbol, a ModeOfTransport, and so on. Automobile should be
have at least three base classes and at least three derived classes. Use
v ir tu a l inheritance wherever appropriate.

5.6. Implement a Graph hierarchy with Graph as the base class (see Program­
ming Exercise 4.9). Sample derived classes are

• UndirectedGraph in which each edge is associated with an unordered


pair of vertices.
• DiGraph (Directed Graph) in which each edge is associated with a
an ordered pair of vertices.
• WeightedGraph in which each edge is labelled with a number that
represents its weight, which is the cost of the edge.
• CompleteGraph in which there is an edge between each distinct pair
of vertices.
• AcyclicGraph in which there are no cycles.
• ConnectedGraph in which there is a path between each pair of dis­
tinct vertices.

The hierarchy should have methods appropriate to all graphs (e.g., search
methods) as well as methods particular to a specific type of graph (e.g.,
methods to compute the cost of a path in a WeightedGraph).

www.MathSchoolinternational.com
PR O G R A M M IN G EXERCISES 287

5.7. Implement a hierarchy of numeric data types that extends the fundamen­
tal data types such as in t and flo a t available in C + + . You could begin
with classes such as B iglnt (see Programming Exercise 4.1) and Complex
(see Section 3.6). Other candidate classes include Fraction, Vector, and
Matrix. Each class in the hierarchy should have methods that extend,
where appropriate, the built-in arithmetic operators to the class.

5.8. Implement a GeoFig class that represents geometrical figures such as


point, line, rectangle, triangle, and the like. Provide v irtu a l methods
to draw, enlarge, move, and destroy such objects. The hierarchy should
consists of at least a dozen classes.

5.9. Implement a Task hierarchy that provides classes suitable for simulation.
A Task has basic temporal properties such as

• Earliest start time.


• Latest start time.
• Earliest finish time.
• Latest finish time.
• Duration.

A Task also requires one or more resources. For example, the Task of
mowing the lawn requires a lawn mower, trimming shears, and so on. So a
Task should have data members to reflect resource requirements. A Task
also has temporal constraints with respect to other Tasks. For example,
in steel making, the Task of casting the steel into a bar cannot begin until
the Task of heating the steel has finished. Temporal constraints on Tasks
can be implemented as relations such as

• Start-before-start (e.g., Tj must start before T j starts).


• Start-after-finish (e.g., Tj must start after T j finishes).
• Finish-after-finish (e.g., Tj must finish after T j finishes).

Provide at least half a dozen temporal relations by which the user can
constrain Tasks. Document the methods that define a Task’s public in­
terface and include a graphical depiction of the class hierarchy.

5.10. Implement a Sequence hierarchy where a sequence is an ordered collec­


tion of none or more items. Vectors, character strings, linked-lists, stacks,
queues, and dequeues then can be derived directly or indirectly from
Sequence. The hierarchy should contain a rich assortment of methods
to handle operations such as creating and destroying Sequences, adding
objects and removing them from Sequences, and so forth. Wherever pos­
sible, v irtu a l methods should be used so that the user has the same
public interface whatever the particular type of Sequence. For instance,

www.MathSchoolinternational.com
CH APTER 5 INHERITANCE

a method such as in sert should be v irtu a l so that the user invokes


in sert regardless of the particular Sequence type (e.g., StackSeq or
QueueSeq).

5.11. Zoology offers rich examples of inheritance hierarchies from phyla to


species to levels of subspecies (see Example 5.2.2). Implement a zoological
hierarchy that has at least six levels of derivation and twelve classes.

5.12. Implement an ErrorCode hierarchy. Error codes are used in data com­
munications to detect and to correct errors that occur when digital in­
formation is transmitted over a channel from a sender to a receiver. Two
broad classes are ForwardErr and BackwardErr codes: the former codes
include sufficient information so that the receiver can fix a detected error
in place, whereas the latter codes provide only enough information so that
the receiver can detect an error and “fix” it by requesting retransmission
of the data. Subclasses of ErrorCode include parity, block sum, Hamming,
and cyclic redundancy codes. For further details, consult a text in data
communications such as Fred Halsall, Data Communications, Computer
Networks, and Open Systems (New York: Addison-Wesley, 1993).

www.MathSchoolinternational.com
Chapter 6

Operator Overloading

6.1 Basic O perator Overloading


6.2 Sam ple Application: Bounds Checking
6.3 Sample Application: A n Associative A rra y
6.4 T yp e Conversions
6.5 Sample Application: File Subscripts
6.6 M em ory M anagem ent Operators
Com m on Program m ing Errors
Program m ing Exercises

289

www.MathSchoolinternational.com
290 CH APTER 6 OPERATOR OVERLOADING

In C + + , built-in operators such as + and [ ] may be overloaded so that they


extend beyond primitive built-in data types to apply to classes as well. For
example, the + operator can be extended beyond ints and flo a ts so that it
applies to Strings for concatenation (see Section 4.1). In this chapter, we
cover the basics of operator overloading and then illustrate more advanced
uses that make it easier to write applications in C ++.

6.1 Basic Operator Overloading


C + + allows any operator to be overloaded except for these five:
// class member operator
.* // class member dereference operator
:: // scope resolu tion operator
?: // conditional operator
s iz e o f // s ize in bytes operator
Every overloaded operator in a base class, except the assignment operator
(=), is inherited in a derived class.
An overloaded operator is a user-defined function that retains the con­
venience of operator syntax. The technical name for an overloaded opera­
tor, operator function, underscores the point. In general, an overloaded
operator must be either a method or include a class object among its ar­
guments. The exceptions are the memory-management operators, that is,
the new, delete, and d elete [ ] operators. These three operators may be
overloaded as toplevel operator functions that need not take class objects
as arguments. Particular operators such as the subscript operator [ ] and
function call operator ( ) must be implemented as methods.

Exam ple 6.1.1. The code slice


// * * * * * ERROR: neither a method nor a
// a function that takes a class argument
void operator'/,( const flo a t f l , const flo a t f2 )

contains an error because an overloaded operator must be either a class


method or take a class object as an argument. □

It makes sense for C + + to require that an integer operator such as '/,


either be overloaded as a method or take at least one class object as an
argument. Otherwise, in an expression such as
in t x = 11, y = 3 , z;
z = x % y;

www.MathSchoolinternational.com
6.1 BASIC OPERATOR OVERLOADING 291

the system could not distinguish between the built-in */, and some user-
defined If */. is overloaded either as a method or as an operator function
that takes a class object as an argument, then the system can determine
which "/, operator to invoke in a particular context.

Example 6.1.2. The code slice


// *** * * ERROR: [ ] must be overloaded as a method
void operator[ ] ( String s ) { . . . }
contains an error because the subscript operator [ ] must be overloaded as
a method rather than as a toplevel function. The same holds for the function
call operator 0 . If these operators could be overloaded as nonmethods, then
the system could not determine in a particular context whether to invoke
the built-in or some user-defined version. □

Example 6.1.3. The code slice


Complex Complex: : op erator-( const Complex c ) const
{
return Complex( re a l - c .r e a l,
imag - c . imag ) ;
>
comes from the Complex class (see Section 3.6). The example meets both
requirements for overloading an operator because operator- is a method in
the Complex class and also takes a Complex object as an argument. □

Example 6.1.4. Because code slice


Stringfe operator+( const String s i, const String s2 ) const
{

}
has a class object— a String— as an argument, it does not contain an error
even though operator+ is overloaded as a toplevel function rather than a
method. One String argument would be sufficient to meet the requirement.

O perator Precedence and Syntax


Operator precedence and syntax cannot be changed through overloading.
For example, the binary operator I I always occurs between its two argu­
ments, whether built-in or overloaded; and I I retains its original precedence
even when overloaded.

Example 6.1.5. In the code slice

www.MathSchoolinternational.com
292 CH APTER 6 OPERATOR OVERLOADING

in t mainO

Complex c l ( 1, 1.1 ) ;
Complex c2( 2, 2.2 ) ;
Complex c3( 3, 3.3 ) ;
Complex ans;

ans = c l + c2 * c3;

>
the expression
ans = c l + c2 * c3;
is equivalent to
ans = c l + ( c2 * c3 ) ;
There is no way to change the precedence of the built-in operators + and *
so that, for example, Complex: :+ had higher precedence than Complex::*.
Also, the binary operator + always occurs between its two arguments. There
is no way to overload the binary + so that it occurred either before or after
its two arguments. In similar fashion, the unary + in C + + always occurs
before its argument, even if the operator is overloaded. □

O perator A rity

If a built-in operator is unary, then all overloads remain unary. If a built-in


operator is binary, then all overloads remain binary. In technical terms, over­
loading an operator cannot change its arity. Operator arity can be tricky
because overloaded operators have two different syntactic forms: method
syntax and operator syntax. We illustrate with a series of examples.

Exam ple 6.1.6. The code slice


// * * * * * ERROR: overloaded + must be binary
Complex Complex::operator+ ( const Complex c l,
const Complex c2 )

>
overloads operator+ as a method in class Complex. The code contains an
error because the built-in addition operator + is binary, not ternary. There­
fore, the overloaded Complex: : operator+ also must be binary, which means
that it must take one rather than two arguments. Recall that an expression
such as
c3 = c2 + c l; // operator syntax

www.MathSchoolinternational.com
BASIC O PERATOR OVERLOADING 293

is a convenient alternative to
c3 = c2.operator+( c l ) ; // method syntax
In order to be a binary operator, the overloaded Complex: :operator+ thus
needs a single argument because the other operand is a class object such as
c2.
If we overloaded operator+ as a frien d to Complex rather than as a
Complex method, then the operator would take two arguments:
class Complex {

frien d:
void operator+( Complex c l, Complex c2 )
{

};
Note that the + operator is binary as either a method or a toplevel friend.
The issue is one of syntax only. □

Example 6.1.7. The code slice


void Complex: :operator'/,()
{

}
contains an error because the built-in modulus operator */, is binary, not
unary. Therefore, the overloaded Complex: : operator'/, must be binary as
well, which means that Complex: : operator'/,— a method—needs exactly one
argument. We can correct the error as follows
void Complex: :operator'/,( Complex c )
{

>

As these examples illustrate, care must be taken in distinguishing be­


tween operators defined as class methods and operators defined as toplevel
functions (for instance, as friends) that expect class objects as arguments.
The point to keep in mind is that a binary operator overloaded as a class
method takes one argument, as the other argument is the class object itself.
A binary operator overloaded as a a toplevel function takes two arguments.

www.MathSchoolinternational.com
294 CHAPTER 6 OPERATOR OVERLOADING

A unary argument overloaded as a class method takes no arguments, whereas


a unary operator overloaded as a toplevel function takes one argument.

Exam ple 6.1.8. Here is an overload of the binary operator && as a class
method:

// && overloaded as a class method


class C {
p u b lic :
// method — 1 argument
in t operator&&( C c )

>

>;

in t mainO
{
C c l;
C c2;

if ( c l && c2 ) // same as: i f ( c l . operator&&(c2 ) )

>

The operator function takes a single argument, as the other argument is the
class object itself—in this case, cl. Here is the same operator overloaded as
a frie n d to C, but implemented as a toplevel function:

class C {

frie n d :
// not a method — 2 arguments
in t operator&&( C compl, C comp2 )

>

in t mainO

C c l, c2;
i f ( c l && c2 ) // same as: i f ( operator&&( c l, c2 ) )

>

www.MathSchoolinternational.com
6.1 BASIC OPERATOR OVERLOADING 295

Now the operator function takes two arguments, as it is not a method but a
toplevel function. □

The increment ++ and decrement operators — are among those that can
be overloaded in C + + . Recall that each operator comes in a prefix and a
postfix flavor:
int x = 6;
// p re fix ++
// p o stfix ++

Accordingly, we can overload the prefix ++, the postfix ++, the prefix — ,
and the postfix — . Such overloads can be used to ensure, for example, that
pointers to array cells always point to a cell within the array’s bounds. We
begin with an example that overloads the prefix increment operator. We
give the code and then explain it:
#include <iostream.h>
#include <string.h>
#include <stdlib.h >

// safe strin g — ptr can’ t f a l l o ff the end


// when incremented with the S afeStr: : operator++
class SafeStr {
char* s tr;
int size;
p u b lic :
char* p tr;
SafeStr( ) ;
SafeStr( char* ) ;
~SafeStr( ) ;
char* o p e ra to r+ + ();

chair* SafeStr: :operator++()

if ( *ptr != ’ \0’ ) // bounds check


return ++ptr; // ok to increment
else

S a fe S t r::SafeStr()
-C
str = ptr = new char[ 1 ] ;
*s t r = ’ \0’ ;

www.MathSchoolinternational.com
CHAPTER 6 OPERATOR OVERLOADING

s ize = 0;
>

S afeS tr: : S afeS tr( char* s )


{
s ize = s trle n ( s ) ;
s tr = p tr = new char[ size + 1 ] ;
strcpy( s tr, s ) ;
>

S a feS tr::~ S a feS tr()

d e le t e [ ] s tr;
>
SafeStr has two variables of type char*: s tr is a p riva te data member
that always points to the first char in a character string, whereas p tr is a
public data member that may point to any char in the string. The code
slice
in t mainO
{
SafeStr s l ( "foo" ) ; // length is 3

// * * * * * NOTE: 100 increments fo r a


// strin g of length 3
fo r ( in t i = 0; i < 100; i++ ) {
cout « s i.p t r « endl;
++sl; // increment ptr
}

>
produces the output
foo
oo
o
. . . // empty strings with lin e feeds
After the loop’s third iteration, s i .p tr points to the cell that holds the null
terminator. The i f test in the overloaded ++ operator
char* S a fe S tr::operator++()
{
i f ( *ptr != ’ NO’ ) // bounds check
return ++ptr; // ok to increment
else

www.MathSchoolinternational.com
BASIC O PERATOR OVERLOADING 297

return p tr; // not ok to increment


>
therefore fails because the value of *p tr is indeed the null terminator. Ac­
cordingly, s i.p t r is not incremented during the remaining loop iterations
as this would result in its pointing to cells beyond those allocated to hold
foo and a null terminator. □

We now give an example that overloads both prefix and postfix versions
of the increment operator ++. The declaration
op erator+ + ();
with no parameters describes the prefix operator and the declaration
operator++( in t ) ;
with a single in t parameter describes the postfix operator. (Similar com­
ments apply to the decrement operator.) The in t parameter in the postfix
form serves to distinguish this form from the prefix form. The parameter
itself may be but need not be used.
If class C overloads the prefix increment operator and obj is an object in
class C, the expression
++obj
is equivalent to
o b j.operator++()
Either expression may be used.
If class C overloads the postfix increment operator and obj is an object
in class C, the expression
obj++
is equivalent to
o b j.operator++( 0 )
Either expression may be used. The method may also be invoked with an
arbitrary argument n:
o b j.operator++( n )

Example 6.1.9. Class Clock overloads both the prefix and postfix ++
operator:
class Clock {
in t hour;
in t min;
in t ap; // 0 is AM, 1 is PM
p u b lic:
Clock( in t, in t, in t ) ;

www.MathSchoolinternational.com
298 CHAPTER 6 OPERATOR OVERLOADING

Clock tic k O ;
void p rin t_tim e ();
Clock o p e rato r+ + (); // ++c
Clock operator++( int ); // C++

>;

C lo ck ::Clock( int h = 12, int m = 0, int ap _flag = 0 )

hour = h;
min = m;
ap = ap _flag;
>

Clock C lo c k ::tic k ()
{
++min;
i f ( min == 60 ) {
hour++;
min = 0;
>

i f ( hour == 13 )
hour = 1;

if ( hour == 12 && min == 0 )


ap = lap;

return *th is;


>

Clock C lock ::operator++()

return t ic k O ;
>

Clock Clock::operator++( int n )

Clock c = *th is;


tic k O ;
return c;

www.MathSchoolinternational.com
BASIC O PERATOR OVERLOADING 299

void C lock ::p rin t_tim e()


{
cout « s e t f i l l ( ’ O’ ) « setw( 2 ) « hour
« * : ’ « setw( 2 ) « min
« s e t f ill( ’ ’ );
i f ( ap )
cout « " PM";
else
cout « " AM";
cout « endl;
>

The prefix and postfix increment operators are overloaded so as to act


on Clocks just as the built-in prefix and postfix increment operators act on
numeric types such as ints. If c is a Clock, c++ advances the time one
second. The value of the expression C + + is the original Clock. Executing
++c also advances the time one second, but the value of theexpression ++c
is the updated Clock.
The default constructor sets the clock to 12:00 AM. Method tic k adds
one second to the Clock and then returns it. Method operator++() over­
loads the prefix increment operator. It advances the time one second by
invoking tic k and returns the updated Clock.
Method operator++( in t ) serves to overload the postfix increment
operator. It saves the current Clock, referenced as *th is, in c. It then ad­
vances the time one second by invoking tic k and returns c, the (unchanged)
original Clock. The parameter n is not used.
The method print_tim e prints the time in the form
xx:xx XX
where XX is either AM or PM.
The output of the code
in t main()

Clock c, d;
c = d++;
cout « "Clock c: 11;
c .p r in t_ tim e ();
cout « "Clock d: ";
d .p r in t_ tim e ();

>
is
Clock c: 12:00 AM
Clock d: 12:01 AM

www.MathSchoolinternational.com
300 CHAPTER 6 OPERATOR OVERLOADING

The output of the code


in t mainO

Clock c, d;
c = ++d;
cout « "Clock c: ";
c .p rin t_ tim e ();
cout « "Clock d:
d .p rin t_ tim e ();

>
is

Clock c: 12:01 AM
Clock d: 12:01 AM

Exercises

1. Explain the error.

class C {
void op era to r.( C c l, C c2 ) ;

>;

2. Explain the error.

in t op era to r,( in t i l , in t i2 , in t i3 )

>

3. Explain the error.

class C {
in t operator I|( C c l, C c2, C c3 ) ;

>;

4. For class C, write code that overloads the unary + as a method.

5. Overload the && operator as a toplevel frien d to C.

www.MathSchoolinternational.com
6.2 SAM PLE A PPLIC A TIO N : BOUNDS CHECKING 301

6. For class C, write code that overloads the binary + as a method.

7. Overload the binary + operator as a toplevel frie n d to C.

8. Overload the /= operator for class Complex. Implement the overloaded


operator as a method.

9. Does this code slice

class C {
p u b lic:
void operator->*( const char* a ) { . . . }

>;

contain an error?

10. List the C + + operators that cannot be overloaded.

11. Must an overloaded operator always be either a class method or take at


least one class object as an argument?

12. Define a class Odd that consists of odd integers. Overload the ++ operator
so that it increments the integer by two.

13. Take a stand for or against the C + + policy that the s ize o f operator
cannot be overloaded.

14. If an operator is binary, must an operator function that overloads it take


two arguments? Explain.

15. For class C, write code that overloads the comma operator.

16. Add to class SafeStr an overload of the postfix increment operator.

6.2 Sample Application: Bounds Checking


P r o b le m ______________________________________________________________________

Implement bounds checking that detects illegal array subscripts.

S olu tion _______________________________________________________________________

We create a Matrix class, where a m atrix is a two-dimensional array with


the same number of rows and columns. We overload the function call opera­
tor ( ) to act as subscript operator that checks whether indexes into a Matrix
fall within bounds. If indexes are out of bounds, then an error message is
printed.

www.MathSchoolinternational.com
302 CHAPTER 6 OPERATOR OVERLOADING

C + + Implementation___________________________________

#include <iostream.h>
#include <stdlib.h>

const int MaxSide = 1000;


int OverflowFlag = -999;

class Matrix {
in t* c e lls ;
int side;
void toobig( int ) ;
void overflow( in t, int ) ;
p u b lic :
M atrix( int ) ; // constructor
"M atrixO ; // destructor
void dumpO; // print contents
intfe operator( ) ( in t, int ) ; // row-col access
>;

M a trix ::Matrix( int s )

// allowable size?
i f ( s > MaxSide ) {
toobig( s ) ;
return;
>

// allo cate s * s matrix c e lls


c e lls = new i n t [ s * s ] ;
side = s;
}

Matrix: : "MatrixO
{
// deallocate storage from constructor
d e le te [ ] c e lls ;
>

void M atrix::too big( int s )

cout « s « " exceeds MaxSide of "


« MaxSide « « endl;
>

www.MathSchoolinternational.com
6.2 SAM PLE A PPLIC A TIO N : BOUNDS CHECKING 303

void M atrix::overflow( int r , int c )

cout « "\n\tRow-Col reference " « r


« << c << " is out of bounds."
« endl;
>

// overload ( ) fo r use as bounds-checking


// subscript operator
int& M atrix::operator( ) ( int r , int c )

// row-col reference in bounds?


if ( r < 0 II
c< 0 II
r >= side II
c >= side ) {
overflow( r , c ) ;
return OverflowFlag;
>
else
return c e lls [ r * side + c ] ;
}

D iscu ssion ____________________________________________________________________

The user creates a bounds-checked Matrix through code such as


Matrix ml ( 8 ) ; // 8 by 8 matrix
and manipulates it through code such as
ml( 1, 1 ) = 26; // m l[ 1 ] [ 1 ] = 26

// store i * j in each matrix c e ll


fo r ( int i = 0; i < 8; i++ )
fo r ( int j = 0 ; j < 8 ; j++ )
ml ( i , j ) = i * j ;

cout « ml( 4, 6 ) ; // cout « ml[ 4 ] [ 6 ]


Because ml is an 8 x 8 Matrix, the legal indexes for either a row or a column
range between 0 and 7. An expression such as
ml( 8, 4 ) = -999; // 8 is i l l e g a l index
causes the warning message
Row-Col reference 8-4 is out of bounds.

www.MathSchoolinternational.com
304 CHAPTER 6 OPERATOR OVERLOADING

to be printed to the standard output. The definition of the overloaded ()


operator
// overload ( ) fo r use as bounds-checking
// subscript operator
intfe M a trix ::operator( ) ( in t r , in t c )

// row-col reference in bounds?


i f ( r< 0 II
c < 0 II
r >= side I I
c >= side ) {
overflow ( r , c ) ;
return OverflowFlag;
>
else
return c e l l s [ r * side + c ] ;
>
includes a test to ensure that the row and the column index (parameters
r and c, respectively) fall within bounds, which means that theindex can
take values between 0 and side - 1. The operator function returns an in t
reference rather than an in t value so that a Matrix can occur on either side
of an assignment expression:
in t num;
Matrix ml( 8 ) ;
ml( 4, 4 ) = 1234; // l e f t side
num = ml( 4, 4 ) ; // righ t side
If Matrix: :op erator() returned a value rather than a reference, then a
Matrix such as ml could occur only on the right side of an assignment ex­
pression, which would not be very useful.
A Matrix’s internal representation is hidden from the user, who sees
a Matrix as a two-dimensional object with the same number of rows as
columns. In its internal implementation, a Matrix is one-dimensional: c e lls
points to the first of side * side dynamically allocated in t cells. An as­
signment expression such as
ml( 4, 3 ) = 77; //
causes 77 to be stored in in t cell
4 * side + 3
Assuming side is 8, then 77 is stored at position 35:
4 *8 + 3 // c e lls [ 35 ] = 7 7
The Matrix class also has a destructor to deallocate the storage to which
c e lls points. The public method dump prints to the standard output each

www.MathSchoolinternational.com
6.2 SAM PLE A PPLIC A TIO N : BOUNDS CHECKING 305

cell’s value. The p riva te method toobig ensures that a Matrix’s side
does not exceed MaxSide, and p riva te method overflow produces the error
message for out-of-bounds references.

Exercises

1. Write the code to implement Matrix: :dump.

2. Change the appropriate Matrix methods so that, in case of overflow, the


offending index is highlighted. For example, if ml is an 8 x 8 Matrix, then
an expression such as

ml( 3, 9 ) = 88;
\

should single out 9 as the offending index. The index 3 is within bounds.

3. Write the Matrix method copy that copies one Matrix’s cells to the
other’s. The method must ensure that the two Matrixes are of the same
size.

4. Write the method Matrix: :change_dim that changes a Matrix’s dimen­


sions:

Matrix ml( 8 ) ; // 8-by-8

ml.change_dim( 12 ) ; // 12-by-12

ml. change_dim( 3 ) ; // 3-by-3

It is understood that data may be lost when a Matrix changes its dimen­
sions.

5. Explain why Matrix: : operator( ) returns an in t reference rather than


an in t value.

6. The method Matrix: : operator( ) returns a reference to OverflowFlag


in case a Matrix reference is out-of-bounds. Why is anything returned
at all? Take out the return statement in the if-body and test your
compiler’s reaction.

7. Could OverflowFlag be made const as the implementation now stands?


Explain.

8. Why do you suppose that we overloaded the function call operator ()


instead of the subscript operator [ ] in this application? Could we have
overloaded the subscript operator instead? Explain.

www.MathSchoolinternational.com
306 CHAPTER 6 OPERATOR OVERLOADING

9. Is there any other C + + operator that we could have overloaded to serve


as a bounds-checking subscript operator for a Matrix?

10. Modify the Matrix class so that the user can specify, as optional in t
arguments to the constructor, the range of legal indexes. For example,
code such as

Matrix ml( 8, 1, 8 ) ;

means that ml is an 8 x 8 Matrix with legal indexes that range between


1and 8 instead of between 0 and 7.

6.3 Sample Application: A n Associative Array


P r o b le m ______________________________________________________________________

Implement an associative array, that is, an array that accepts as indexes


noninteger expressions such as character strings.

S o lu tio n _______________________________________________________________________

We create a dictionary class implemented as an array of word-definition pairs


together with appropriate methods. In particular, one method overloads the
subscript operator [ ] so that a word (character string) may be used as an
index into the array. The overloaded operator prints the word’s definition if
the word occurs in the dictionary. We also overload the function call operator
( ) for use as an iterator (see Section 4.5). Our implementation is inspired
by an example in Bjarne Stroustrup, The C + + Programming Language, 2nd
ed., (Reading, Mass.: Addison-Wesley, 1992).

C + + Im p lem en tation _______________________________________________________

#include <iostream.h>
#include <string.h>

const in t MaxWord = 100;


const in t MaxDef = 1000;
const in t MaxEntries = 100000;

class Entry {
char word[ MaxWord + 1 ] ;
chax def [ MaxDef + 1 ] ;
p u b lic :

www.MathSchoolinternational.com
6.3 SAM PLE A PPLIC A TIO N : A N ASSOCIATIVE A R R A Y 307

EntryO
{
strcpy( word, 11" ) ;
strcpy( def, "" ) ;
}

void w rite () const;


void add( const char*, const char* ) ;
int match( const char* ) const;
>;

void E n try::w rite ( ) const

cout « word « " defined as 11


« def « « endl;
>

void Entry::add( const char* w, const char* d )

strcpy( word, w ) ;
strcpy( def, d ) ;
>

int Entry::match( const char* key ) const

return strcmp( key, word ) == 0;


>

class D ic tlte r;

class Diet {
friend class D ic tlte r;
Entry e n trie s [ MaxEntries + 1 ] ;
int count; // how many entries
p u b lic :
D ictO -[ count = 0; }
void dumpO ;
void add( const char*, const char* ) ;
void operator[ ] ( const char* ) const;
>;

www.MathSchoolinternational.com
CHAPTER 6 OPERATOR OVERLOADING

void D ie t: : dump()
{
fo r ( int i = 0; i < count; i++ )
e n trie s [ i ] . w r i t e ( ) ;
>

void D ic t::a d d ( const char* w, const char* d )


{
if ( count < MaxEntries )
e n trie s [ count++ ] .add( w, d ) ;
else
cout « count « " exceeds MaxEntries." «
endl;
>

void D i e t ::operator[ ] ( const char* k ) const

int i = 0;

// sequential search — in e ffic ie n t


while ( i < count )
i f ( e n trie s [ i ] .match( k ) ) {
e n trie s [ i ] . w r i t e ( ) ;
break;
>
else
i++;
i f ( i == count )
cout « k « " not in diction ary." « endl;
>

class D ic tlte r {
int current;
Diet* diet;
p u b lic :
D ic t lt e r ( Dictfe d )

diet = &d;
current = 0;
>
Entry* operator( ) ( ) ;
>;

www.MathSchoolinternational.com
6.3 SAM PLE A PPLIC A TIO N : A N ASSOCIATIVE A R R A Y 309

Entry* D i c t l t e r : :operator( ) ( )

if ( current >= d ie t -> count ) / / a t end?


return 0;
return fedict -> e n tr ie s [ current++ ] ;
>

D iscu ssio n ____________________________________________________________________

Class Diet contains as data members an array of Entry objects, where Entry
is itself a class, and an in t variable count that tracks how many entries (up
to a maximum of MaxEntries) are currently in the dictionary. An Entry
consists of a word and its definition, each implemented as a character string.
Diet and Entry have identically named methods called add, each expecting
a word and its def. A typical code sequence for adding pairs and then
printing them is:
in t mainO
■C
// Create a diction ary of MaxEntry w ord-definition pairs
Diet d;

// Add some pairs


d.add( "residu al fm ", "in cid en tal fm" ) ;
d.add( "diode", "two-element s o lid -s ta te device" ) ;
d.add( " p ix e l" , "picture element" ) ;
d.add( "in cid en ta l fm ", "residual fm" ) ;
d.add( "recu rsion", "See recursion" ) ;

// Print a l l pairs in the d iction ary.


d.dumpO ;

>
Method D iet: :add simply invokes Entry: :add on the first open slot in the
dictionary. This slot is indexed by D iet: : count, which is incremented after
each add operation:
void D ict::a d d ( const char* w, const char* d )
{
i f ( count < MaxEntries )
en tries [ count++ ] .add( w, d ) ;
else
cout « count « " exceeds MaxEntries." «
endl;
>

www.MathSchoolinternational.com
310 CHAPTER 6 OPERATOR OVERLOADING

Diet overloads the [ ] operator so that character strings can be used as


indexes. A code slice such as
d [ "diode" ] ; // what’ s the d e fin itio n of diode?
initiates a search of D ie t: : en tries for a word that matches diode. If there
is a match, the word’s definition is printed. If not, a failure message is
printed.
D ic tlte r is the iterator class for Diet. A code slice such as
in t mainO

Diet d; // create a dictionary


D ic tlte r d i( d ) ; // create an ite r a to r fo r d
Entry* e; // return value of d i .operator()
// add items to diction ary
while ( e = d i ( ) ) // loop thru diction ary elements
e -> w r ite ( ) ; // equivalent to d.dumpO

>
shows how D ic tlte r : : operator( ) may be used to iterate through the dic­
tionary entries. The loop condition works because D ic t lt e r : : operator ()
returns 0 when it reaches the end of entries.
The syntax
Entry* D ic t lt e r ::o p e r a t o r ()()

>
may look peculiar in that there are two pairs of parentheses right next to
each other. The first pair represents the operator that we are overloading,
which is the function call operator (). The second pair of parentheses is the
argument list for our overloaded function call operator. The list is empty
because our overload of () does not expect any arguments.
Overloading the [ ] and () operators provides the applications program­
mer with convenient, intuitive tools for manipulating a dictionary. Such
overloading coats the C + + language with a layer of syntactic sugar so as to
make the language more palatable to its programmers.

Exercises

1. Change class Entry so that its data members are of type String (see
Section 4.1 ) rather than char*.

www.MathSchoolinternational.com
6.4 T Y P E CONVERSIONS 311

2. Change the appropriate methods so that each new Entry preserves sorted
order among the Diet entries.

3. Why must D ic tlte r be a frien d to Diet?

4. Why does D ic tlte r not have to be a frien d to Entry?

5. Explain the syntax

Entry* D ic t lt e r ::o p e r a t o r ()()


{

>

In particular, explain what each pair of ( ) means in the operator func­


tion’s header.

6. If we overload operator [ ], how many arguments must the overloaded


operator take?

7. If we overload operatorO , how many arguments must the overloaded


operator take?

8. Implement

void D ie t: : remove( char* w )

which removes word w from the dictionary if w occurs there.

9. Implement

void D iet::append( char* w, char* d )

which appends d to w’s definition if w occurs in the dictionary.

10. Implement

void D ie t: : change( char* w, chax* d )

which changes w’s definition to d if w occurs in the dictionary.

6.4 Type Conversions


Recall the String class of Section 4.1, which implements a string as an
abstract data type. A S trin g’s internal representation includes a pointer
of type char*, which is a suitable argument for library functions such as
strlen , strs tr, strtok, and the like. However, a String itself is not a
suitable argument to such a library function.

Example 6.4.1. The code slice

www.MathSchoolinternational.com
312 CHAPTER 6 OPERATOR OVERLOADING

String s l ( "foo bar" ) ;


in t len = s trle n ( s i ) ; // * * * * * ERROR: s i is wrong type
contains an error because strlen expects an argument of type char*, not
of type String. □

Of course, we could write String methods that mimic s trle n and the
other library functions, but this would be tedious and wasteful. Library
functions are meant to be used, not reinvented. A solution is to overload a
typ e conversion operator so that a String behaves as if it were char*
in contexts that require char*.

Exam ple 6.4.2. We amend the String class to include a type conversion
operator so that a String may be passed to a library function such as
strlen:
class String {
enum SortOrder { Asc, Desc } ;
enum ErrorsIO { ReadFail, W riteFail } ;
char* s tr; // data member — p rivate
in t len; // actual
p u b lic :

operator const ch ar*() const; // declaration

>;

// type conversion operator: String to char*


S tr in g ::operator const ch ar*() const

return s tr;
>;
Note that neither the declaration nor the definition of the overloaded oper­
ator shows a return value, not even void, although the function operator’s
body contains the return statement
return s tr;
It is illegal to give a return value, even void, in either the declaration or the
definition of a type conversion operator.
The first const in the type conversion operator signals that code outside
the String class should not modify the storage to which data member s tr
points. The second const signals that the type conversion operator itself
does not change this storage. The operator returns str, which points to a
null-terminated vector of char. In short, s tr is of type char*. This means
that s tr is a suitable argument to library functions such as strlen. Code
such as

www.MathSchoolinternational.com
T Y P E CONVERSIONS 313

String s i; // s i is String, not char*


in t len;
len = s tr le n ( s i ) ; // s i converted to char*
is legal with the type conversion operator. Because s trle n expects a char*
rather than a String, the compiler issues a call to the type conversion oper­
ator, which returns s i . s t r — of type char*— to strlen. Note that the user
does not explicitly invoke the type conversion operator, leaving that task to
the compiler. The user thus enjoys the benefits of type conversion without
the associated work. □

The syntax of a type conversion operator indicates the conversion that


it performs. For example,
S tr in g ::operator const ch ar*() const
{
return s tr;
}
indicates a conversion from a String to a char* (technically, to a const char*).
Had we a reason to convert Strings to ints, our type conversion operator
would look like this:
S tr in g ::operator i n t ()

>
Type conversion operators occur in built-in and user-defined classes.
We take an example from the iostream class (see the system header file
iostream.h and Chapter 7).

Example 6.4.3. This code slice


char next;

while ( cin » next ) // read from standard input


cout « next; // w rite to standard output
copies the standard input to the standard output until end-of-file is encoun­
tered. The while condition must evaluate to an integer or pointer value: a
value of zero means false and any nonzero value means true. However, the
input expression
cin » next
evaluates to an istream, where istream is a class from the iostream library.
A type conversion operator handles the conversion from iostream to void*,
thus supporting the convenience of the while construct. □

www.MathSchoolinternational.com
314 CHAPTER 6 OPERATOR OVERLOADING

Despite their convenience, type conversion operators should be used with


caution because the compiler, not the programmer, typically invokes a type
conversion operator. The normal call to a type conversion operator is thus
hidden from the programmer, who may not have anticipated all the situations
in which such an operator would be invoked.

Exercises

1. Explain the error.

// type conversion operator


char* S tr in g ::operator const ch ar*() const

return s tr; // type char*


>

2. Assume that we would like to use code such as

String s l ( "0 sole mio" ) ;

i f ( si ) { // i f ( s tr le n ( s i . s t r ) )

>

The problem is that an i f condition must evaluate to an integer or pointer


expression, not to a String expression. Write a type conversion operator
that allows this programming construct for Strings.

3. Explain the meaning of each const in the function header

S tr in g ::operator const char*() const


•C

>

4. Does the compiler or the programmer normally invoke a type conversion


operator?

5. Explain how the compiler determines whether to invoke a type conversion


operator in a particular context.

6. A type conversion operator’s definition and declaration must not give a


return value, even void. What do you think is the point of this rule?

7. Why should type conversion operators be used cautiously, despite their


convenience?

www.MathSchoolinternational.com
6.5 SAM PLE A PPLIC A TIO N : F IL E SUBSCRIPTS 315

8. If we had not provided a type conversion operator that converts a Strin g


to a const char*, then a code slice such as

String s l ( "agnes" ) ;
int len = s tr le n ( s i ) ;

would cause the compiler to issue a type violation warning. With the
type conversion operator in place, the compiler is silent. Explain the
disadvantages of silencing such compiler warnings.

6.5 Sample Application: File Subscripts


P r o b le m ______________________________________________________________________

Extend array syntax to files so that the user can access a file for reading and
writing as if the file were an indeterminately large, one-dimensional array of
char. In other words, implement a file subscript that behaves as if it were
an array subscript.

S o lu tio n _______________________________________________________________________

We create a F ile class that supports the standard operations of opening,


closing, reading, and writing a file. The frie n d class F ileR ef handles some
implementation details. Together the two classes overload the subscript
operator [ ] and the assignment operator =, and define a type conversion
operator to convert a FileRefft to a char. The overall result is an extension
of array syntax to files. The example is adapted from James O. Coplien,
Advanced C + + (Reading, Mass.: Addison-Wesley, 1992).

C + + Im plem entation_______________________________________________________

#include <stdio.h>
#include <iostream.h>

class F ile ;
class FileRef {
Fileft f i l e ;
char b u ffer;
unsigned long index;
p u b lic :
F ile R e f( File& f , unsigned long i ) :
f i l e ( f ) , index( i ) { }
F ileR ef& operator=( char c ) ;
operator c h a r();
>;

www.MathSchoolinternational.com
316 CHAPTER 6 OPERATOR OVERLOADING

class F ile {
frien d class FileR ef;
p riv a te :
FILE* fp tr;
p u b lic :
F i le ( const char* name ) { fp tr = fopen( name, "wb+" ) ; }
~ F ile () { fc lo s e ( fp tr ) ; }
FileRef operator[ ] ( unsigned long ) ;

FileRef F i l e : : operator[ ] ( unsigned long index )

return F ileR ef( *th is , index ) ;


>

F ileR ef& F ile R e f: :operator=( char c )

fseek ( f i l e . f p t r , index, SEEK_SET ) ;


fp u tc( ( int ) c, f i l e . f p t r ) ;
return *th is;
>

F ile R e f: : operator char()


{
fseek ( f i l e . f p t r , index, SEEK_SET ) ;
b u ffe r = fg e tc ( f i l e . f p t r ) ;
return bu ffer;
>

Discussion

Here is a code sequence that illustrates file subscripts:


in t mainO
{
char c = ’ A’ ;
F ile f l ( "foo" ) ; // create and open f i l e
F ile f2 ( "bar" ) ; // create and open another

for ( int i = 0; i < 26; i++ )


fl[ i ] = C++; // write next character

c = f l [ 23 ] ; // read ’X’ from f l


cout « f 1[ 24 ] « endl; // read ’Y’

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : FILE SUBSCRIPTS 317

f l [ 23 ] = ’ x ’ ; // overw rite ’ X’ with ’ x ’


f l [ 5 ] = f 2 [ 99 ] = ’ Z ’ ; // w rite ’ Z ’ to two f i l e s

>
At the syntax level, a F ile now looks like an array of indeterminate
size. A F ile is written or read as if it were an array of char. The C + +
implementation is compact and contains some subtle features.We begin with
a straightforward feature.
The F ile constructor opens the named file in mode wb+, which means
that the file is created if it does not already exist and may be read as well
as written. The F ile destructor closes the file. Once created, a F ile may
be treated as if it were an indeterminately long one-dimensional array. The
file’s actual size is system-dependent, of course. In any case, the file may be
read or written using array syntax. The index must be an integer expression,
however, as the overload of the subscript operator makes no provision for,
say, Strings as indexes. Expressions such as
in t x = 10;
in t y = 20;
char c;

f 1[ 6 ] = ’ A’ ;
f 1C x + y ] = ’ Z>;
c = f l [ y - x ] ;
are all legal.
The overloaded subscript and assignment operators together with the
type conversion operator serve one purpose: to handle situations in which
a subscripted F ile occurs as the source and as the target in an assignment
expression. As a source, a subscripted F ile occurs on the left side of an
assignment; as a target, a subscripted F ile occurs on the right side of an
assignment:
c = fl[ 23 ] ; I I F ile as source
f 1 [ 23 ] = c; // F ile as target
We begin with a subscripted F ile as a target.
In the expression
f 1 [ 10 ] = ’ Q’ ; // F ile as target
the subscript operator
F ileR ef F i l e : : operator[ ] ( unsigned long index )
{
return F ile R e f( *th is , index ) ;
>
returns the value o f the constructor call

www.MathSchoolinternational.com
318 CHAPTER 6 OPERATOR OVERLOADING

F ile R e f( *th is , index )


Recall that *th is evaluates to f l . The FileR ef constructor thus creates
a dummy F ileR ef object, sets its data member f i l e to the first argument
(in this case f l ) , and sets its data member index to the second argument
(in this case 10). The dummy F ileR ef object is returned. It is this dummy
F ileR ef object whose assignment operator is invoked. Note that the over­
loaded assignment operator is a method in class F ileR ef and therefore re­
quires a F ileR ef object. The overloaded assignment operator treats the
index, in this example 10, as an offset into the F ile . An fseek is done to
the specified position, after which the character Q is written. The overloaded
assignment operator returns a F ile R e f&, which enables such constructs as
F ile f l ( "foo" ) ;
F ile f2 ( "bar" ) ;
f l [ 6 ] = f 2 [ 978 ] = ’ B’ ;
When a F ile occurs as the source or right side of an assignment and the
target is a char variable, then the type conversion operator comes into play.
In the assignment
char c;
c = f l [ 23 ] ;
the subscript operator once again evaluates to a FileReffe. However, the
context now requires a char value because c is of type char, not FileReffe.
The compiler thus invokes the type conversion operator
F ile R e f: : operator char()
{
fseek ( f i l e . f p t r , index, SEEK_SET ) ;
b u ffer = fg e t c ( f i l e . f p t r ) ;
return bu ffer;
>
so that a char is stored in c. The type conversion operator also does the
required fseek and fg e tc to read the char at index 23.
The example has instructive subtleties at the implementation level. The
point worth noting, in the spirit of object-oriented programming, is that the
user can ignore these subtleties and manipulate files as if they were arrays
of indeterminate size.

Exercises

1. Explain why FileRef is a friend to File.

2. Explain why F ile is not a friend to FileRef.

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : FILE SUBSCRIPTS 319

3. The body of the overloaded assignment operator ends with

return *th is ;

instead of with

return th is ;

Explain why *th is is returned.

4. Explain how the assignment expression

f 1 [ 2 ] = f 2 [ 3 ] = ’ A’ ;

works, where f 1 and f2 are Files.

5. The assignment operator is overloaded as a F ileR ef method, whereas


the subscript operator is overloaded as a F ile method. Explain why the
overloads are done this way.

6. Does the type conversion operator come into play when a subscripted
F ile occurs as the target or right side of an assignment expression? Ex­
plain.

7. Does the overloaded assignment operator come into play during F ile
reads and writes? Explain.

8. Explain the error.

char F ile R e f: : operator char()

fseek ( f i l e . f p t r , index, SEEK_SET ) ;


b u ffer = fg e t c ( f i l e . f p t r ) ;
return b u ffer;
}

9. In this section, we talk about a dummy F ileR ef being returned by the


overloaded subscript operator. Explain the sense in which this F ileR ef
object is a dummy or intermediate value.

10. Implement bounds checking for the overloaded subscript operator. An


index value should not be less than zero or greater than MaxFileSize, a
const variable that you should set to some appropriate value.

www.MathSchoolinternational.com
320 CHAPTER 6 OPERATOR OVERLOADING

6.6 Mem ory Management Operators


The memory management operators new, delete, and d elete [ ] may be
overloaded as either methods or toplevel operator functions. Such overload­
ing is useful when an application needs to take control of its own memory
management. An embedded system (for example, a microprocessor system
that regulates a refrigerator or an automobile engine) may have very limited
memory resources that the application needs to manage directly. Some envi­
ronments, such as Microsoft Windows, have memory management schemes
that application programs are advised to follow, thus requiring direct mem­
ory management.

Exam ple 6.6.1. The code slices


// overloaded as method in class C
void* C::operator new( s ize _ t size )

>
and
// overloaded as to p le v e l operator function
void* operator new( s ize _ t size )
■C

>
illustrate two overloads of the operator new. The overloaded new operators
both return a void*, just as the built-in new operator does. In this example,
each overloaded operator function expects a single argument of type size_t.
A typical invocation of C: : new would be
C* c l = new C; // a llo ca te a C object
The system uses the overloaded C: :new operator. If such an operator were
not defined, then the system would use the built-in new operator. □

The initial parameter in the overloaded new operator must be of type


s ize _t. The value of this parameter is equal to the size in bytes of the
object being created. Other parameters are optional and depend on the
body of the overloaded new. In any case, an overload such as
void* C::new( void* ptr ) // * * *** ERROR: no s ize _ t
{

>
contains an error because the first parameter is not of type size_t.

Exam ple 6.6.2. The code slices

www.MathSchoolinternational.com
M E M O R Y M A N A G E M E N T OPERATORS 321

// overloaded as method
void C::operator d e le te ( void* objPtr )
{

>
and
// overloaded as to p le v e l function operator
void operator d e le te ( void* objPtr )
{

}
illustrate two overloads of the d elete operator. The operator returns void
and expects an argument of type void*, which points to the storage to be
freed. A typical invocation of C: : d elete would be
C* c l = new C; // a llo c a te

d elete c l; // fre e
The system again uses the overloaded d elete if one is present and the built-
in d elete otherwise. □

The initial parameter in the overloaded d elete or d elete [ ] must be


of type void*. Other parameters are optional.
Next we take a longer example to illustrate an overload of new as a
method. The overloaded new allocates storage from a fixed pool available to
the program.

Example 6.6.3. We define a Frame class, which represents data frames to


be transmitted in a data communications application:
#include <iostream.h>
#include <stdio.h>
#include <string.h>

const in t MaxFrames = 48;


const in t DataSize = 128;
const in t NameSize = 4;

class Frame {
s ta tic Frame* allFrames;
Frame* nextFrame;
char name[ NameSize ] ;
char data[ DataSize ] ;
p u b lic :

www.MathSchoolinternational.com
322 CHAPTER 6 OPERATOR OVERLOADING

Frame( ) ;
Frame( const char* ) ;
Frame( const char*, const char* ) ;
void p rin t( ) ;
void* operator new( size_t ) ;
>;

Frame* Frame: : allFrames = 0 ; //n o Frames yet


unsigned char framePool[ MaxFrames * sizeof ( Frame ) ] ;
const unsigned char* poolEnd =
framePool + MaxFrames * sizeof ( Frame ) ;

Frame::Frame()
■C
name[ 0 ] = d ata[ 0 ] = ’ \0’ ;
>

Frame: : Frame( const char* n )

strcpy( name, n ) ;
d ata[ 0 ] = ’ \0’ ;
p r in t ( ) ;
>

Frame: : Frame( const char* n, const char* d )

strcpy( name, n ) ;
strcpy( data, d ) ;
p r in t ( ) ;
>

void Frame::p r in t ()
{
cout « name « " created." « endl;
>

void* Frame: : operator new( size_t size )


■C
// allo catin g a new Frame?
i f ( size != sizeof ( Frame ) ) {
cout « " !! ERROR — not a Frame!. "
« endl;
return 0;
}

www.MathSchoolinternational.com
M E M O R Y M A N A G E M E N T OPERATORS 323

// any storage le f t ?
i f ( allFrames == ( Frame* ) poolEnd ) {
cout « "No more storage fo r Frames."
« endl;
return 0;
>

// storage a llocated yet?


i f ( ! allFrames ) {
allFrames = ( Frame* ) framePool;
fo r ( in t i = 0; i < MaxFrames - 1; i++ )
allFrames[ i ] .nextFrame = ftallFrames [ i + 1 ] ;
allFrames [ i ] .nextFrame = ( Frame* ) poolEnd;
>

Frame* temp = allFrames; // current chunk


allFrames = allFrames -> nextFrame; // next chunk
return temp;
}
A Frame has data member name, which we use to trace the program’s exe­
cution. Data member data holds DataSize bytes to be transmitted in a data
communications application. All storage for Frames comes from framePool,
an array of unsigned char with size
MaxFrames * s iz e o f ( Frame )
Accordingly, framePool points to storage for up to MaxFrames objects of
type Frame. The objects may be allocated one at a time
Frame* f l = new Frame( " f l " ) ; // a llo c a te one
Frame* f2 = new Frame( "f2" ) ; // a llo c a te another
or as a Frame vector
// a llo ca te a vector of three Frames
Frame* f l = new Frame[ 3 ] ;
The allocation syntax for the overloaded new operator is the same as for the
built-in new operator.
The class Frame has a s ta tic data member allFrames that points to
framePool. The data member is s ta tic because there is only one storage
pool shared by the whole class, rather than a storage pool for each individual
Frame object (see Figure 6.6.1).
When a Frame object is created dynamically, the overloaded new operator
allocates storage from framePool if any is still available. The first time
new allocates storage from framePool, it breaks the storage into chunks

www.MathSchoolinternational.com
324 CHAPTER 6 OPERATOR OVERLOADING

Frame:: a l l F r a m e s

framePool

Figure 6.6.1 Storage pool for Frames.

framePool
s i z e o f ( F r a me ) bytes

Figure 6.6.2 Allocating from Frame storage pool.

(each chunk with s iz e o f ( Frame ) bytes) so that the chunks then can be
allocated one per Frame (see Figure 6.6.2):
// storage a llocated yet?
i f ( ! allFrames ) {
allFrames = ( Frame* ) framePool;
fo r ( in t i = 0; i < MaxFrames - 1; i++ )
allFrames[ i ] .nextFrame = &allFrames[ i + 1 ] ;
allFrames [ i ] .nextFrame = ( Frame* ) poolEnd;
>
Data member nextFrame points to the next available storage, if any,
in framePool. After MaxFrames have been allocated, nextFrame points to
poolEnd, which means that no more Frames can be allocated. The allocation
itself occurs at the end of new:
Frame* temp = allFrames; // current chunk
allFrames = allFrames -> nextFrame; // next chunk
return temp;
The code slice
in t mainO
{
Frame* f l = new Frame( " f l " ) ;
Frame* f2 = new Frame( "f2 " ) ;

Frame* f48 = new Frame( "f48" ) ;


Frame* f49 = new Frame( "f49" ) ;

produces the output


f l created.
f2 created.

www.MathSchoolinternational.com
CO M M O N PRO G RAM M IN G ERRORS 325

f48 created.
No more storage fo r Frames,
because MaxFrames is currently set to 48.

Exercises

1. Assume that the new operator is overloaded for class Cl but not for class
C2. Explain which new operator, Cl: :new or built-in new, is invoked in
each case:

Cl* c l = new Cl;


C2* c2 = new C2;
in t* i l = new in t;
Cl* cs = new C l[ 10 ] ;

2. Explain the error.

void* C::new( in t howmany )

>

3. Explain the error.

void d e le te ( char* p tr )
{

>

4. Explain the role of poolEnd in Example 6.6.3.

5. Is an overload of the new operator restricted to a single parameter?

6. Overload the d elete operator for class Frame in Example 6.6.3. A delete
operation should recover the storage so that it can be used to allocate yet
another Frame.

Com m on Program m ing Errors

1. It is an error to overload these five operators:

www.MathSchoolinternational.com
CHAPTER 6 OPERATOR OVERLOADING

// class member operator


.* // class member dereference operator
:: // scope resolu tion operator
?: // conditional operator
s iz e o f // size in bytes operator

2. Except for memory management operators new, delete, and d elete [ ] ,


an operator must either be overloaded as a method or have atleast one
class object among its arguments. For example,

// * * * * * ERROR: neither a method nor a


// function that takes a class argument
void operator+( const in t numl, const in t num2 )

>

has an error because the overloaded operator is not a method and does
not have a class object among its arguments.

3. It is an error to overload operator [ ] except as a method.

4. It is an error to overload operator ( ) except as a method.

5. It is an error to change an operator’s arity when overloading it. For


example,

// * * * * * ERROR: + must remain binary


Complex Complex:: operator+( const Complex c l,
const Complex c2 )
{

>

contains an error because operator+ must remain binary when over­


loaded. Recall that an expression such as

c3 = c2 + c l; // c3, c2, and c l are Complex

is shorthand for

c3 = c2.operator+( c l ) ;

The overloaded Complex: : operator+ therefore should take one argu­


ment, not two, to remain binary:

www.MathSchoolinternational.com
PRO G RAM M IN G EXERCISES 327

// ok — + remains binary
Complex Complex::operator+( const c )
{

>

6. It is an error to specify, in either a declaration or a definition, the return


data type— even void— for a type conversion operator despite the fact
that such an operator contains a return statement. For example,

// * * * * * ERROR: can’ t give return data type


char* S tr in g :: operator const ch ar*() const
{
return s tr; // s tr is of type char*
}

7. It is an error to have the overloaded new operator return any value except
void*.

8. It is an error if the first argument to the overloaded new operator is not


of type size_t. The overloaded operator may have additional arguments
of any type, however.

9. It is an error to have the overloaded d elete and d elete [ ] operators


return any value except void.

10. It is an error if the first argument to the overloaded operators d elete


or d elete [ ] is not of type void*. The overloaded operators may have
additional arguments of any type, however.

Program m ing Exercises

6.1. Overload a unary operator for the Deck class (see Programming Exercise
3.3) whose invocation results in a Deck’s being shuffled. For example, if
the bitwise complement operator ~ is overloaded, then the code slice

Deck d;
~d; // sh u ffle the Deck

shuffles d.

6.2. Overload the preincrement operator ++ and the predecrement operator —


for the Spaceship class (see Programming Exercise 3.13). The following
code slice illustrates how the operators work:

www.MathSchoolinternational.com
CHAPTER 6 OPERATOR OVERLOADING

// create a spaceship — default v e lo c ity is zero


Spaceship s;
++s; // increment v e lo c ity by constant s.velUp
++s; // do i t again
— s; // decrement v e lo c ity by constant s.velDown

6.3. Overload the binary operators +, and * as Set methods (see Program­
ming Exercise 3.9). The following code slice illustrates how the operators
work:

Set s e t l( a, b, c) ; //s e tl = {a ,b ,c >


Set set2( x, y ) ; //set2 = {b ,x ,y >
Set set3; // set3 = {>
// compute union o f s e tl and set2
set3 = s e tl + set2; // set3 = {a , b , c , x , y }
// compute in tersection of s e tl and set2
set3 = s e tl * set2; // set3 = {b>
// computer d ifferen ce of s e tl and set2
set3 = s e tl - set2; // set3 = { a , c }

6.4. Overload the negation operator ! for the String class (see Section 4.1).
A code slice such as

S tring s ( "Tish Hinojosa" ) ;


// convert uppercase to lowercase and
// lowercase to uppercase in s
! s;

has the effect of converting any uppercase character in s to a lowercase


character and vice-versa. After the expression ! s evaluates, s . s tr points
to the character string

tISH hINOJOSA

An expression such as

!!s; // double negate s

leaves s unchanged, as pairs of negations cancel each other out. Also


overload the unary + and - operators for Strings. An expression such
as

String s ( "Kim" ) ;
// convert to uppercase: KIM
+s;

converts any lowercase characters in s to uppercase. Similarly, the ex­


pression

www.MathSchoolinternational.com
PRO G RAM M IN G EXERCISES 329

String s ( "Kim" ) ;
// convert to lowercase: kirn
-s;

converts any uppercase characters in s to lowercase.

6.5. Overload the + operator as a LAN method (see Programming Exercise


4.3). The code slice

LAN lan l // create one LAN


LAN lan2 // create another
LAN lan3 // create a th ird
lan3 = la n l + lan2;

makes lan3 a LAN that includes la n l and lan2 as subLANs. A combined


LAN includes all the nodes from the two or more LANs that it combines.
However, its topology may differ from theirs because, for example, we
might combine a star and a bus LAN into a new LAN with a hybrid topology.

6.6. The BST (binary search tree) class of Section 4.4 has a method addNode
to add nodes to a BST. To add three nodes to a BST requires a code slice
such as

BST bst; // create a BST


bst.addNode( ’ K’ ) ; // add one node
bst.addNode( ’ G’ ) ; // add a second
bst.addNode( JR’ ) ; // add a th ird

Overload the function call operator ( ) so that expressions such as

b s t( "KGR" ) ; // add three nodes at once


b s t( "MAQP" ) ; // add four more

adds nodes represented as characters in a string. The expression

b s t( "" ) ; // remove a l l nodes

removes all nodes from the BST leaving an empty BST.

6.7. Provide a type conversion operator for BSTs (see Section 4.4) so that they
can be passed to a string library function such as strlen. For example,
in the code slice

BST bst; // create a BST


b s t( "KGRMAQP" ) ; // add 7 nodes
cout « "Node count fo r bst is "
« s tr le n ( bst ) « endl;

www.MathSchoolinternational.com
330 CHAPTER 6 OPERATOR OVERLOADING

7 is printed to the standard output because bst has seven nodes. The
second statement uses the overloaded function call operator as described
in Programming Exercise 6.6.

6.8. Overload the relational operators <, <=, ==, >=, and > for the Graph class
of Programming Exercise 4.9. If g l and g2 are two Graphs, then the
expression

g l < g2

evaluates to true ( l ) if the total number of vertices and edges in g l is less


than the total number of vertices and edges in g2. The other relational
operators also should be overloaded to extend their built-in meanings to
Graphs.

6.9. Overload the preincrement operator ++ and the predecrement operator —


for the Spreadsheet class (see Programming Exercise 4.8). Add a data
member currCell to Spreadsheet, which points to the current or active
cell in the Spreadsheet. A cell is current or active if the user is currently
editing it. When the Spreadsheet is first opened, the current cell is the
first cell. When the user is not actively editing the Spreadsheet, the
current cell is the most recently edited cell or, in case no editing has been
done, the first cell. The code slice

// create a Spreadsheet
Spreadsheet taxes; // current c e ll is 1st
++taxes; // current c e ll is 2nd
++taxes; // current c e ll is 3rd
— ta xes; // current c e ll is 2nd

illustrates how the operators can be used to navigate the Spreadsheet


one cell at a time. Also, overload the ! operator so that an expression
such as

!taxes

has the effect of computing the Spreadsheet, which means computing


the value of each cell in it.

6.10. Create a class hierarchy with Int as an abstract base class that has In ti,
Int2, and Int4 as derived types. In t i represents a signed integer in one
byte, Int2 represents a signed integer in two bytes, and Int4 represents
an unsigned integer in four bytes:
/-N /■—

s ize o f ( In ti
II II
II II

s ize o f ( Int 2
CN
s

s ize o f ( Int4 ) == 4

www.MathSchoolinternational.com
PR O G R A M M IN G EXERCISES 331

Assume further that your application must do its own memory manage­
ment because, for example, it is part of an embedded system with very
limited memory resources. When your application begins to run, it re­
ceives m bytes of memory (e.g., 64K bytes). The application then must do
its own memory management. In particular, storage for integers must be
dynamically allocated and freed. Overload the new, the delete, and the
delete [ ] operators as v ir tu a l operator functions that manage alloca­
tion and release of storage for In ti, Int2, and Int4. The new operator
should ensure that no more than m bytes of memory have been allocated.

www.MathSchoolinternational.com
www.MathSchoolinternational.com
Chapter 7

The CH—|- Input/Output


Class Hierarchy

7.1 O verview
7.2 The Class ios
7.3 The H igh-Level Input/O utput Classes
7.4 M anipulators
7.5 The File Input/O utput Classes
7.6 Sample Application: A R andom Access File Class
7.7 The Character A rra y Input/O utput Classes
7.8 Sample Application: A H igh-Level C opy Function
7.9 The Buffer Classes
Com m on Program m ing Errors
Program m in g Exercises

333

www.MathSchoolinternational.com
CH APTER 7 THE C + + IN P U T / O U T P U T CLASS H IERARCHY

Input and output facilities are not part of the C + + language but instead
are furnished through a class library. In this chapter, we explore this class
library in detail.
The class hierarchy for C + + ’s input/output library is complex. We
examine the hierarchy in detail for three reasons. First, this hierarchy il­
lustrates the power available by combining polymorphism and multiple in­
heritance. Even programmers with no particular interest in the details of
C + + ’s input/output hierarchy can learn important object-oriented design
and coding lessons by attending to these details. Second, most C + + envi­
ronments extend the language by providing class libraries (e.g., Microsoft
Visual C + + ’s Foundation Classes, Borland C + + ’s ObjectWindows). Part
of learning C + + is learning how to use class libraries so that time and en­
ergy are not wasted in recreating what is already available. Because the
input/output class library is standard within C + + , it is an excellent case
study for the use of class libraries in general. Third, through inheritance
a programmer can extend the input/output classes (see Section 7.6). Such
extensions require a clear understanding of the details of the classes.

Overview

We begin by discussing the object-oriented design of the input/output class


library. Object-oriented design begins by identifying objects and continues
by defining the operations (methods) appropriate to the objects. In C + +
input and output, a central object is the stream, which is a sequence of
bytes (see Figure 7.1.1). Operations on a stream include reading from and
w ritin g to the stream. A clean design can be achieved by having a common
base class ios for the two derived stream classes istream, the input stream
class, and ostream, the output stream class (see Figure 7.1.2). The base
class ios has members that the derived classes istream and ostream inherit.
These inherited members are used to describe and to modify various stream
attributes. Such attributes include the mode (e.g., whether the stream is
opened for reading only or for both reading and writing); the format (e.g.,
the number of digits of floating-point precision); and the status of operations
on the stream (e.g., whether end-of-file was encountered, whether the last
operation failed). Class ios has data members to describe these attributes
and methods to read, set, and clear the attribute flags.
Class istream adds methods for reading and moving around in the
stream. Also, the right-shift operator » is overloaded within istream for
reading the built-in types. Similarly, ostream contains methods for writing
and moving around in the stream. The left-shift operator « is overloaded
within ostream for writing the built-in types.
Class iostream is derived from both istream and ostream so that it
inherits methods for both reading and writing a stream. Class iostream has

www.MathSchoolinternational.com
7.1 OVERVIEW 335
t

cm \ Stream
y Plan 9 From Outer Space
Input

Plan 9 From Outer Space

Stream
Plan 9 From Outer Space )
Output

Figure 7.1.1 Stream input/output.

istream ostream

istream w ith a ssig n o strea m _w ith a s sig n

iostream

ios t re a m _ w ith a s sign

Figure 7.1.2 Basic stream classes.

www.MathSchoolinternational.com
336 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

Write a Write 1
character buffer

Data to Buffer Output


write

Figure 7.1.3 Buffered output.

no additional data members and, except for constructors and destructors, it


has no additional methods either.
The classes

istream_withassign
ostream_withassign
iostream_withassign

are directly derived from istream, ostream, and iostream, respectively. As


the names suggest, each overloads the assignment operator. The system
defines the objects cin, which is a member of istream_withassign, and
cout and cerr, which are members of ostream_withassign.
The C + + input/output class library provides for buffered input/out-
put, in which data are not directly read or written but rather pass through
intermediate storage (e.g., an array of char) called a buffer.

Exam ple 7.1.1. Figure 7.1.3 depicts buffered output. When a request is
received to write a character, the character is not written directly to the
output but rather to the buffer. Periodically the buffer is written to the
output. Writing the buffer is called flushing the buffer. In C + + a newline
typically flushes the output buffer. □

A buffer is another essential object in the C + + class input/output li­


brary, and reading and writing the buffer are among the required operations.
Various buffer classes (e.g., f ile b u f, the file buffer class, and strstreambuf,
the character array buffer class) are derived from a common base class
streambuf (see Figure 7.1.4). These derived classes serve as interfaces to
the actual sources and destinations of input and output. The base class
streambuf has methods (e.g., reading and writing the buffer) needed by
both file b u f and strstreambuf, which file b u f and strstreambuf then
obtain through inheritance. A buffer object is connected to class ios through
a pointer-to-streambuf data member in ios (see Figure 7.1.4).
Class streambuf contains constructors and methods for creating an array
of chax or unsigned char to use as a buffer. In addition, it has methods for
reading and writing the buffer and for moving around in the stream. These
reading and writing methods are sometimes called low-level input/output
methods because they provide direct access to the buffer. By contrast,

www.MathSchoolinternational.com
7.1 OVERVIEW 337

ios
■— ► stream bu f

f il e b u f s trs trea m b u f

Figure 7.1.4 The C + + buffer classes.


ios

fstreambase istream ostream strstreambase

Figure 7.1.5 The C + + stream classes.

the methods in the classes istream and ostream are called high-level in­
put/output methods because they access the stream indirectly— by making
calls to the low-level methods in streambuf.
Class streambuf also contains the v ir tu a l methods underflow, which
is responsible for dealing with an attempted read from an empty buffer, and
overflow, which is responsible for dealing with an attempted write to a full
buffer. A class derived from streambuf typically overrides underflow and
overflow so that action appropriate to the specific source or destination
is taken. For example, when the buffer is full, overflow might ignore new
writes or return an error flag, or, if the destination is a file, it might flush
the buffer.
The class file b u f contains constructors and methods for associating a
file b u f object with a file, and the class strstreambuf contains constructors
and methods for associating a strstreambuf object with a character array.
Each overrides underflow and overflow.
One reason that the buffer class hierarchy (Figure 7.1.4) is distinct from
the stream class hierarchy (Figure 7.1.5) is to separate the high-level and
low-level input/output methods. If these hierarchies were combined into a
single hierarchy, some class would necessarily contain both high-level and
low-level input/output methods.
Two additional classes are derived from ios:

fstreambase strstreambase

www.MathSchoolinternational.com
CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

(see Figure 7.1.5). These classes serve as base classes for derived classes that
provide a high-level interface to file and character array input and output.
The class fstreambase contains a buffer member (a member of type
file b u f). The base class ios is connected to this buffer object through a
pointer (see Figure 7.1.4); in this way, fstreambase establishes a connection
to a physical stream— a file. The class fstreambase provides constructors
and methods for opening, attaching, and closing a file.
Class ifstream is derived from both fstreambase and istream; thus,
it contains a file buffer object and methods for reading and moving around
within the stream. Therefore ifstream provides high-level, indirect access
to the file. Class ifstream also inherits from fstreambase methods for
attaching and closing a file. It redefines the method open to assure that the
file is opened in read mode.
Similarly, class of stream is derived from both fstreambase and ostream;
thus, it contains a file buffer object and methods for writing and moving
around within the stream. Class of stream also inherits from fstreambase
methods for attaching and closing a file. It redefines the method open to
assure that the file is opened in write mode.
Class fstream is derived from both fstreambase and iostream; thus, it
contains a file buffer object and methods for reading, writing, and moving
around within the stream. Class fstream also inherits from fstreambase
methods for attaching and closing a file. It redefines the method open to
remove any default protection.
The strstream classes (strstreambase, ostrstream, istrstream , and
strstream), which read and write arrays of characters, are designed similarly
to their f stream counterparts, which read and write files.
The class strstreambase contains a buffer member (a member of type
strstreambuf). The base class ios is connected to this buffer object through
a pointer (see Figure 7.1.4); in this way, strstreambase establishes a connec­
tion to a physical stream— an array of characters. The class strstreambase
provides a constructor for establishing a connection with a character array.
Class istrstream is derived from both strstreambase and istream;
thus, it contains an strstreambuf object and methods for reading and mov­
ing around within the buffer. Therefore istrstream provides high-level,
indirect access to the character array.
Class ostrstream is derived from both strstreambase and ostream;
thus, it contains an strstreambuf object and methods for writing and mov­
ing around within the buffer.
Class strstream is derived from both strstreambase and iostream;
thus, it contains an strstreambuf object and methods for reading, writing,
and moving around within the buffer.
The header file iostream. h declares the classes

www.MathSchoolinternational.com
7.1 OVERVIEW 339

ios
streambuf
istream
ostream
iostream
istream_withassign
ostream_withassign
iostream_withassign
the objects cin, cout, and cerr; and the manipulators endl, ends, flush,
dec, hex, oct, and ws. The header file fstream.h #includes the file iostream.h,
if it was not already included, and declares the classes
file b u f
fstreambase
ifstream
ofstream
fstream
The header file strstream.h #includes the file iostream.h, if it was not already
included, and declares the classes
strstreambuf
strstreambase
istrstream
ostrstream
strstream
Figure 7.1.6 summarizes the classes and functionality given in this sec­
tion. In the remainder of this chapter, we look at these classes in detail.

Exercises

1. Print the header files iostream.h, fstream.h, and strstream.h, and identify
the data members and methods referred to in this section.

2. The C + + input/output class hierarchy assumes the stream model for


input and output. Give an example of input or output for which some
other model might be more suitable.

3. Argue that any input or output can be viewed as a stream.

4. Suggest how an input/output class hierarchy might be organized in the


absence of multiple inheritance.

5. Why not replace class of stream with a class derived from ostream and
file b u f?

www.MathSchoolinternational.com
340 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

Attributes of i/o class

File i/o classes High-level i/o classes Array i/o classes

fstrea m ba se istream strstreambase


of stream ostream ostrstream
ifs t r e a m iostream istrs trea m
fstrea m is t re a m _ w it lia s s ig n strstream
o strea m _w ith a s sig n
io s t r e a m _ w it has sign

Buffer classes

stream buf
fileb u f
strstrea m bu f

Figure 7.1.6 Summary of the C + + input/output class hierarchy.

7.2 The Class ios

In this section we look closely at the base input/output class ios (see Figure
7.1.5). The names used in this section are fairly standard across C + + sys­
tems, but the implementation is less standardized. Thus the reader should
be able to use the flags and methods as described herein; the implementation
should be considered merely as an example.
Class ios defines several protected integer members whose bits are used
as flags. For example, the integer member state could be used to indicate
the status of the stream: the first bit is set to indicate end-of-file; the second
bit is set to indicate that the last input/output operation failed; and so on.
The bits are named by using a public enum:

class ios {
p u b lic :

enum io_state {
goodbit = 0x00, // ok — no b it is set
eo fbit = 0x01, // set = eof
fa ilb it = 0x02, // set = la s t i/o operation fa ile d
badbit = 0x04, // set = in valid operation
h a rd fa il = 0x80 // set = unrecoverable error
>;

>;

www.MathSchoolinternational.com
7.2 THE CLASS ios 341

Method Returns
rd stateO the stream state (state)
goodO nonzero if state is zero; otherwise, returns zero
eof () nonzero if e o fb it is set; otherwise, returns zero
fa ilO nonzero if f a i l b i t is set; otherwise, returns zero
badO nonzero if badbit is set; otherwise, returns zero

Figure 7.2.1 Methods to access the status of the stream.

Because the names names are public, they can be used by the programmer.
However, the programmer cannot directly access the integer member state
containing the flags because this member is protected. Instead, ios fur­
nishes public methods to access the status of the stream (see Figure 7.2.1).

Example 7.2.1. Because cin is a member of istream_withassign, a class


inherited from ios, we may apply the methods of Figure 7.2.1 to cin. The
statement
cur_state = cin .rd sta teO ;
saves the current state for the standard input in cur_state. □

Example 7.2.2. The code


in t i ;

do {
cin » i ;
i f ( Ic in .e o fO )
cout « i « endl;
} while ( Ic in .e o fO ) ;
echoes integers in the standard input to the standard output, one per line.
An attempted read beyond the end of the stream is required to set e o fb it;
thus, the following code that attempts to echo integers in the standard input
to the standard output is not correct; an extra line is printed:
in t i ;

// * * * * * ERROR: extra lin e is printed


while ( Ic in .e o fO ) {
cin » i ;
cout « i < endl;
>

www.MathSchoolinternational.com
CH APTER 7 THE C + + IN P U T / O U T P U T CLASS H IERARCH Y

The member clea r replaces the stream state with the value passed or 0
if no argument is passed.

Exam ple 7.2.3. On many systems, a control character (e.g., control-D in


UNIX, control-Z in MS-DOS or VAX/VMS) is interpreted as end-of-file. To
receive additional input after an end-of-file signal from the standard input,
some systems require the end-of-file flag to be cleared. The statement
c in .c le a r ();

clears all of the standard input status flags, including the end-of-file flag. □

Class ios overloads the not operator ! and provides a conversion from
ios to void*. The declarations are
class ios {
p u b lic :

operator v o id * ();
in t o p era to r!( ) ;

>;
The operator void* converts ios to the pointer value zero if state is
nonzero, and it converts ios to a nonzero pointer value if state is zero.
Similarly, the overloaded not operator returns nonzero if state is nonzero,
and it returns zero if state is zero.

Exam ple 7.2.4. In the code


if ( cin ) { // standard input ok?
// process input
>
if there is an error in the standard input, state is nonzero, so cin is con­
verted to zero and we do not process input. If there is no error in the
standard input, state is zero, so cin is converted to nonzero and we process
the input. □

Class ios declares an enum to give names to flag bits to indicate modes
as shown in Figure 7.2.2. These flags are typically used in classes derived
from ios.

Exam ple 7.2.5. The class of stream has a constructor whose first argument
is a file name (see Section 7.5), and whose second argument is ored with
i o s : : out (to open the file for output) after which it becomes the initial
mode. Thus
ofstream fo u t( "ou t.d at", ios::app ) ;

www.MathSchoolinternational.com
7.2 THE CLASS ios 343

Name Purpose
in Open for reading
out Open for writing
ate Open and move to end-of-stream
app Open for appending
trunc Discard stream if it already exists
nocreate If stream does not exist, open fails
noreplace If stream exists, open for output fails unless ate or app is set
binary Open as a binary stream

Figure 7.2.2 Mode flags.

Name Purpose
beg Seek from the beginning
cur Seek from the current position
end Seek from the end

Figure 7.2.3 Seek flags.

creates an of stream object associated with the file out.dat, which is opened
for appending (writing at the end of the file). □

Class ios declares an enum to give names to flag bits to indicate how
to seek (move) in the stream as shown in Figure 7.2.3. These flags are also
typically used in classes derived from ios (see Section 7.3).

Example 7.2.6. The method seekp is defined in the output class ostream
(see Section 7.3). One form of seekp has two arguments: the first is an
offset, and the second is the direction. If out is an object in class ostream,
the expression
out.seekp( 10, io s ::c u r )
moves the current position in the stream 10 bytes forward. □

Class ios declares an enum to give names to flag bits to indicate how to
format the stream as shown in Figure 7.2.4. Also, ios furnishes methods to
read, set, and clear the format flags (see Figure 7.2.5) and methods to read
and change the field width, the fill character, and the number of digits of
floating-point precision (see Figure 7.2.6).

Exam ple 7.2.7. The statement


old _fla gs = c o u t.fla g s ( i o s : : l e f t
I io s : :hex

www.MathSchoolinternational.com
344 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS H IERARCH Y

Name Purpose
skipws Skip white space
le ft Left-justify
rig h t Right-justify
in tern al Padding after sign or base flag
dec Decimal
oct Octal
hex Hexadecimal
showbase Use base indicator on output
showpoint Print trailing zeros in floating-point numbers
uppercase Use uppercase letters on hex output
showpos Use + with positive integers
s c ie n t ific Use scientific notation for floating-point numbers: d.dddEdd
fix e d Use fixed notation for floating-point numbers: d.ddd
unitbuf Flush any stream after write
stdio Flush standard output and standard error after write

Figure 7.2.4 Format flags.

I ios::showpoint
I ios::uppercase
I ios::fixed );

saves the old flags in o ld _fla gs and formats the standard output as left-
justify output, print integers in hexadecimal, print trailing zeros, print hex­
adecimal output using uppercase letters, and print floating-point output as
dd.dddddd. □

Exam ple 7.2.8. The code

o l d _ f i l l = c o u t . f i l l ( ’ 0’ ) ;
// w rite to standard output
c o u t .fill( o ld _ fill );

changes the fill character to zero and saves the old fill character in o ld _f i l l .
After writing to the standard output, it restores the original fill character.
The same effect could be obtained more easily by using manipulators (see
Section 7.4). □

Exam ple 7.2.9. The method setf, with one argument, sets the specified
format flags without changing the other flags. For example, the statement

cout.setf( ios::showbase );

www.MathSchoolinternational.com
7.2 THE CLASS ios 345

Method Purpose
f l a g s () Return a long that shows the
format flags
f l a g s ( long ) Set the format flags to the
long passed and return the
old flags
s e t f ( long ) Set specified flags and return
the old flags
s e t f ( ios: :dec, i o s : : basefield ) Set integer base to decimal
and return the old flags
s e t f ( i o s : :o ct , i o s : : basefield ) Set integer base to octal and
return the old flags
s e t f ( ios::hex, i o s : : b a s e f i e l d ) Set integer base to hex and re­
turn the old flags
s e t f ( i o s : : l e f t , i o s : : ad ju s tf i el d ) Set left justification and re­
turn the old flags
s e t f ( i o s : : r i g h t , i o s : : ad ju s tf i el d ) Set right justification and re­
turn the old flags
s e t f ( i o s : : i n ter nal , i o s : :ad ju s tf i el d ) Put fill character between sign
and value and return the old
flags
setf( io s :sc ie n t ific , ios::floatfield ) Set scientific notation and re­
turn the old flags
s etf( ios::fixed, io s : :fl o a t fi e l d ) Set fixed notation and return
the old flags
s e t f ( 0, i o s : : f l o a t f i e l d ) Set default notation and re­
turn the old flags
unsetf( long ) Clear specified flags and re­
turn the old flags

Figure 7.2.5 Methods to read, set, and clear the format flags.

Method Purpose
width ( ) Return the field width
width( int ) Change the field width and return the old width (the width
reverts to 0 after the next number or string is written)
fillO Return the fill character
f i l l ( char ) Change the fill character and return the old fill character
precisionQ Return the precision (number of digits of floating-point
precision)
p recisio n ( in t ) Change the precision and return the old precision

Figure 7.2.6 Methods to read and change the field width, the fill character, and the
precision.

www.MathSchoolinternational.com
346 CHAPTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

causes subsequent output to show the integer base; that is, octal integers
will be printed with a leading zero, hexadecimal integers will be printed
with a leading Ox, and decimal integers will be printed in the usual way.
The preceding statement is equivalent to
c o u t.fla g s ( c o u t.fla gs() I i o s : : showbase ) ;

Exam ple 7.2.10. Example 7.2.9 shows how s e tf can be used to change
the format when only a single flag is involved. The situation is more complex
when several flags are involved. For example, to format integer output as
hexadecimal, the hex bit must be set and the dec and oct bits cleared.
To simplify changing the format when several flags are involved, s e tf can
take a second argument. For example, output can be formatted as decimal,
hexadecimal, or octal by using b a sefield as the second argument to setf.
The output from the following code
c o u t.s e tf( io s::h ex , io s : : b a sefield ) ;
cout « "Hex: 11 « 168 « endl;
c o u t.s e tf( io s ::o c t , io s : :b a sefield ) ;
cout « "Octal: " « 168 « endl;
is
Hex: a8
Octal: 250

Exam ple 7.2.11. We can modify the code of Example 7.2.10 so that the
output shows the base, and uppercase letters are used on hexadecimal out­
put:
c o u t.s e tf( io s : : showbase | i o s : :uppercase ) ;
c o u t.s e tf( io s::h ex , i o s : :b a sefield ) ;
cout « "Hex: " « 168 « endl;
c o u t.s e tf( io s ::o c t , io s ::b a s e fie ld ) ;
cout « "Octal: " « 168 « endl;
The output is now
Hex: 0XA8
Octal: 0250

Exam ple 7.2.12. We can left- or right-justify output by using adju stf ie ld
as the second argument to setf. The output from the following code

www.MathSchoolinternational.com
THE CLASS ios 347

cout.width( 6 ) ;
cout « -100 « endl; // d efau lt is r ig h t - ju s t ify

cout.width( 6 ) ;
c o u t.s e tf( i o s : : l e f t , io s ::a d ju s t fie ld ) ;
cout « -100 « endl;

cout.width( 6 ) ;
c o u t.s e tf( io s ::r ig h t , io s : : a d ju s tfie ld ) ;
cout « -100 « endl;

// width reverts to 0 (d e fa u lt)


c o u t.s e tf( io s ::r ig h t , i o s : : a d ju s tfie ld ) ;
cout « -100 « endl;
is

-100
-100
-100
-100
In C ++, if the output is larger than the width, the output is written anyway
in a field whose width is equal to the width of the item to write. Thus, in
the last line, the output is right-justified in a field of width four.
Another option that can be used with a d ju s tfie ld is internal, which
uses the fill character to pad between the sign and the value. For example,
the output from the following code
cout.width( 6 );
cout.fill( ’ 0 ’ );
c o u t.s e tf( i o s : : in te rn a l, i o s : : a d ju s tfie ld ) ;
cout « -100 « endl;
is

-00100

Exam ple 7.2.13. Floating-point output can be formatted in fixed or sci­


entific notation by using f l o a t f i e l d as the second argument to setf. The
output from the following code
const flo a t lo g l0 _p i = .497149872;
c o u t.s e tf( io s : : s c ie n t ific , i o s : : f l o a t f i e l d ) ;
cout « lo g l0 _p i « endl;
c o u t.s e tf( io s ::fix e d , i o s : : f l o a t f i e l d ) ;
cout « lo g l0 _p i « endl;

www.MathSchoolinternational.com
CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

on our system is
4 .971499e-01
0.49715
On our system, the default precision (which can be changed using the
method precision ) is six. Thus, in the first line of output, six digits are
printed to the right of the decimal point. Notice that the value is rounded.
In the second line of output, the sixth digit to the right of the decimal point
is zero and, by default, is not printed. (Trailing zeros can be printed by
setting the showpoint flag.) Again, the value is rounded. □

Exam ple 7.2.14. The method unsetf always takes only one argument. It
clears the specified flags. For example, the statement
c in .u n s e tf( ios::skipw s ) ;
clears the skipws flag so that white space is not skipped. (The default is to
skip white space.) □

The method t i e in ios “ties” an input stream to an output stream.

Exam ple 7.2.15. After the statements


istream in;
ostream out;
i n . t i e ( out ) ;
are executed, the streams in and out are tied, which means that the ostream
out is flushed whenever an input operation is attempted on the istream in.
The value of i n . t i e ( out ) is the stream to which in was previously tied.
If in was not tied to a stream, the value is 0. When invoked with argument
0, t i e breaks the tie, if any. When invoked with no argument
i n . t ie O ;
t i e simply returns the stream to which in is tied, or 0 if in is not tied to a
stream. □

Class ios contains a s ta tic member sync_with_stdio that synchro­


nizes the C + + input/output with the standard C input/output functions.
Anytime the C and C + + input/output libraries are intermixed, the method
sync_with_stdio should be invoked
io s ::s y n c _ w ith _ s td io ();
before doing any input or output.

www.MathSchoolinternational.com
7.2 THE CLASS ios 349

Exercises

In Exercises 1-4, assume that io_obj is an object in class ios.

1. Write an expression whose value is 1 if io _ o b j’s e o fb it is 1, and 0 if


io _o b j’s e o fb it is 0.

2. Write a statement that clears io _ o b j’s e o fb it and does not change any
other status bit.

3. Explain how the expression ! cin is evaluated in the code

i f ( !cin ) {

4. Write a statement to create an object flo u t of type of stream, where


flo u t is associated with the file data.dat, which is opened as a binary
file. Discard the file if it already exists.

5. Write a statement that moves the current position in the file associated
with the object f out in class ostream 10 bytes from the end of the file.

6. Write a statement that formats the standard output as follows: skip white
space, right justify, use decimal conversion, use + with positive integers,
and use fixed notation for floating-point numbers.

7. Use the method s e tf to set the showpoint flag on the standard output.

8. Using only the method fla g s, set the showpoint flag on the standard
output. Do not change any other format flag.

9. Use the method unsetf to clear the showpoint flag on the standard
output.

10. Using only the method fla g s, clear the showpoint flag on the standard
output. Do not change any other format flags.

11. Use the method s e tf to set the integer base to octal on the standard
output.

12. Using only the method fla g s, set the oct flag, and clear the dec and hex
flags on the standard output.

13. Use the method s e tf to restore the default notation for floating-point
output on the standard output.

14. What is the output?

www.MathSchoolinternational.com
350 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

cout. s e t f ( i o s : : showpos ) ;
c o u t.s e tf( i o s : : l e f t , i o s : : a d ju s tfie ld ) ;
c o u t . f i l l ( ’ X’ ) ;
cout.width( 6 ) ;
cout « 66;
cout.width( 6 ) ;
cout « 66 « 66 « 66 « endl;

15. What is the output?

c o u t.s e tf( io s ::h e x , io s : :b a sefield ) ;


c o u t.s e tf( io s : : showbase | io s : : uppercase ) ;
c o u t.s e tf( i o s : : l e f t , io s : : a d ju s tfie ld ) ;
c o u t.fill( );
cout.width( 6 ) ;
cout « 66;
cout.width( 6 ) ;
cout « 66 « 66 « 66 « endl;

16. What is the output?

c o u t.s e tf( i o s : : showpoint ) ;


c o u t.s e tf( i o s : : l e f t , io s : : a d ju s tfie ld ) ;
c o u t.s e tf( io s ::fix e d , i o s : : f l o a t f i e l d ) ;
cou t.p recision ( 4 ) ;
flo a t x = 8.72;
cout.width( 8 ) ;
cout « x;
cout.width( 8 ) ;
cout « x « x « x « endl;

7.3 The High-Level Input/Output Classes


In this section we discuss the classes
istream
ostream
iostream
istream_withassign
ostream_withassign
iostream_withassign

which provide a high-level interface to input and output.

istream

The class istream is publicly derived from ios

www.MathSchoolinternational.com
7.3 THE HIGH-LEVEL IN P U T / O U T P U T CLASSES 351

class istream : v ir tu a l public ios {

>;
and provides high-level methods for input streams.
The method get is overloaded and so can be invoked in various ways.
When the version of get whose declaration is
istreamft g e t ( signed char* a, in t m, char c = ’ \n’ ) ;
is invoked, characters are read from the stream into the array a until the
character c (whose default value is ’ \n’ ) is encountered, until end-of-stream,
or until m - 1 characters have been read into a, whichever happens first.
The character c is not placed in the array a, nor is it removed from the
stream. The method get adds a null terminator ’ \0\ Notice that get
never stores more than m characters in a. This version of get resembles the
C function fgets. It is different from f gets in that f gets stores the newline
terminator and removes it from the stream. The method get returns the
(updated) stream that invoked it.

Example 7.3.1. The value returned by get may be used as a true/false


condition to test for end-of-file:
while ( c in .g e t ( a, 81 ) ) {
// process lin e stored in a

>
The stream return value is converted to type void*, which is zero on an
error condition such as end-of-file, and nonzero, otherwise. □

Another version of get behaves exactly as the version described previ­


ously except that the first parameter has type unsigned char*.
When the version of get whose declaration is
istreamfe g e t( signed charft c ) ;
is invoked, the next character, white space or not, is read into the char c,
and get returns the (updated) stream that invoked it.
Another version of get behaves exactly as the version described in the
last paragraph except that the first parameter has type unsigned charfe.

Exam ple 7.3.2. The following code reads a line (defined as ending with a
newline) and then checks to see whether the line read was too long to store:
const in t lin e _ le n = 81;
char a [ lin e _ le n ] , c;
c in .g e t( a, lin e _ le n ) ;
c in .g e t( c ) ;

www.MathSchoolinternational.com
352 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

if ( c != ’ \nJ )
cout « "Line too lon g." « endl;

When the version of get whose declaration is


in t getO ;
is invoked, the next character in the stream, white space or not, is returned,
or if no characters remain to be read, get returns EOF. This version of get
resembles the C function fgetc.
When the version of get whose declaration is
istreamft g e t( streambufft b u ff, char = ’ \n’ ) ;
is invoked, characters are read from the stream into the streambuf object
b u ff until the character c, whose default value is ’ \n’ , is encountered. The
character c is not placed in buff, nor is it removed from the stream.
The method read whose declaration is
istreamft read( signed char* a, in t n ) ;
works similarly to get, except that no terminator character such as ’ \n’ is
provided and no null terminator ’ \0 ’ is placed in the array a. The method
read, which is used to read binary data, returns the stream which invoked it.
The method gcount can be used to obtain the number of characters actually
read. The method read resembles the C function fread.
Another version of read behaves exactly as the version described in the
last paragraph except that the first parameter has type unsigned char*.
The method peek whose declaration is
in t p e e k ();
returns the next character from the stream but does not remove it from the
stream. If no characters remain to be read, peek returns EOF.
The method putback whose declaration is
istreamft putback( char c ) ;
returns the character c to the stream. The method putback returns the
stream which invoked it. It resembles the C function ungetc.
The method ignore whose declaration is
istreamfe ign ore( in t count = 1, in t stop = EOF ) ;
removes count characters from the stream or all characters from the stream
up to stop, whichever comes first. The removed characters are not stored
but simply discarded. The method ignore returns the stream which invoked
it.
The method gcount whose declaration is
in t gcount( ) ;

www.MathSchoolinternational.com
THE HIGH-LEVEL IN P U T / O U T P U T CLASSES 353

returns the number of characters last read.


There are separate stream position markers— one for input and one for
output. The methods seekg and t e l l g set and read the position within
the input stream (g in seekg stands for “get” ). The output stream class
ostream has methods seekp and t e llp to set and read the position within
the output stream (p in seekp and t e llp stands for “put” ). These methods
resemble the C functions fseek and f t e l l .
There are two versions of seekg. The first, whose declaration is
istreamfe seekg( streamoff o f f , seek_dir d ir ) ;
moves the input stream position marker o f f bytes from dir, which must be
one of the following: beg (from the beginning of the stream), cur (from the
current position), or end (from the end of the stream). A typedef makes
streamoff an actual type— typically, long. The method seekg returns the
stream which invoked it. When this version of seekg is used with a file, the
file should be opened as a binary file.
The method t e l l g whose declaration is
streampos t e l l g O ;
returns the location of the input stream position marker. A typedef makes
streampos an actual type— typically long.
The version of seekg whose declaration is
istreamfe seekg( streampos pos ) ;
sets the input stream position marker to location pos as returned by t e llg .
When this version of seekg is used with a file, it is not necessary to open
the file as a binary file.
The class istream overloads the right-shift operator for formatted input
of built-in types. A typical declaration is
istreamfe o p e r a t o r » ( intfe ) ;
Here an in t is read into a variable passed by reference, after which the
stream is returned.

Example 7.3.3. Because o p e r a to r » returns the stream, » can be


chained:
cin » i » j ;
Because » associates from the left, first the expression
cin » i
is evaluated. An integer is read into i and the expression is replaced by its
value, the updated stream. This updated stream then acts on j
cin » j
and an integer is read into j . □

www.MathSchoolinternational.com
CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

Among the types handled by » are

signed char* unsigned shortfe


unsigned char* unsigned intfe
signed charfe unsigned longfe
unsigned charfe flo a tft
shortfe doublefe
intfe long doublefe
longfe

When » is used for input, the default action is to skip white space, even
for type char (unlike scanf in C).

Exam ple 7.3.4. The type charfe is used to read one character. For the
input
x y z
the code
char c;

while ( cin » c )
cout « c;
cout « endl;
prints
xyz
since white space is skipped. □

The type char* is used with » to read a string. If the field width is
zero (the default), white space is skipped, after which all non-white space
characters up to the next white space character are read and stored. If
the width is set to n, white space is skipped, after which all non-white
space characters up to the next white space character, or n - 1 characters,
whichever occurs first, are read and stored. In either case, a null terminator
is added.

Exam ple 7.3.5. The code


char a [ 80 ] ;
cin.w idth( 80 ) ;
cin » a;
avoids overflow in the array a. □

The skip white space option can be disabled by clearing the skipws flag.

Exam ple 7.3.6. The following code echoes the standard input to the stan­
dard output:

www.MathSchoolinternational.com
THE HIGH-LEVEL IN P U T / O U T P U T CLASSES 355

char c;

// clear skip white space fla g


cin .u n setf( ios::skipw s ) ;

while ( cin » c )
cout « c;
cout « endl;

For integer types, if the first non-white space character is not a digit or a
sign, f a i l b i t is set and no further data can be read until f a ilb it is cleared.
Similarly, for floating-point types, if the first non-white space character is
not a digit, a sign, or a decimal point, f a i l b i t is also set and no further
data can be read until f a i l b i t is cleared.

Example 7.3.7. The following program accepts integer input and echoes
it in hexadecimal. If the input is not an integer, a message is printed and
the user is prompted to reenter the item:
#include <iostream.h>
#include <std lib.h >

enum { fa ls e , true } ;

in t mainO
{
int v a l , ok;
char l i n e [ 80 ] , c ;

cout « hex;

fo r ( ; ; ) {
cout « "Enter an integer (negative to q u i t ) : "
« flush ;
ok = true;
cin » v a l; // i f val is i l l e g a l , state != 0
i f ( cin .rd sta te O ) {
cout « "Bad input. Redo." « endl;
c in .c le a r ( ) ; // clear state so input can be read
c in .g e t( lin e , 80 ) ; // read and dump bad input
c in .g e t( c ) ; // read and dump ’ \n’
ok = fa ls e ;
}

www.MathSchoolinternational.com
356 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

if ( ok )
i f ( va l < 0 )
break;
else
cout « va l « endl;
}

return EXIT_SUCCESS;
}

The operator » can be overloaded for user-defined types.

Exam ple 7.3.8. Given the declaration


struct String {
char s t r [ 100 ] ;
void s to re ( char* ) ;
>;
we can overload » to read a C string into a String object as follows:
istreamfe o p e r a t o r » ( istreamfe in , Stringfe s )
{
return in » s .s tr ;
>
The overloaded input operator could be used as
S trin g s;
cin » s;
The second statement
cin » s;
is equivalent to
o p e r a t o r » ( cin, s ) ;

Class istream has one constructor


istream ( streambuf* ) ;
which can be used to establish a connection to a buffer object.

ostream

The class ostream is publicly derived from ios:

www.MathSchoolinternational.com
THE HIGH-LEVEL IN P U T / O U T P U T CLASSES 357

class ostream : v ir tu a l public ios {

and provides high-level methods for output streams.


The method put whose declaration is
ostreamft put( char ) ;
writes the character passed to the output stream. The method put returns
the (updated) stream which invoked it. It resembles the C function fputc.

Example 7.3.9. Given the definition


char c = ’ $ ’ ;
the statements
cout « c;
and
cout.put( c ) ;
are equivalent. □

The method w rite whose declaration is


ostreamft w r ite ( const signed char* a, in t m ) ;
writes m characters from the array a to the output stream. The method
write, which is used to write binary data, returns the stream which invoked
it. It resembles the C function fw rite.
Another version of w rite behaves exactly as the version described in the
last paragraph except that the first parameter has type const unsigned
char*.
The methods seekp and t e l l p whose declarations are
ostreamft seekp( streampos ) ;
ostreamft seekp( stream off, seek_dir ) ;
streampos t e llp O ;
set and read the output stream file position marker. They behave similarly
to their istream counterparts seekg and t e llg .
When the version
ostreamft seekp( stream off, seek_dir ) ;
is used with a file, the file should be opened as a binary file. The version
ostreamfe seekp( streampos ) ;
should take as an argument a value returned by te llp . When this latter
version is used with a file, it is not necessary to open the file as a binary file.
The method flu sh whose declaration is
ostreamfe flu sh Q ;

www.MathSchoolinternational.com
358 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

flushes the buffer. The method flush returns the stream which invoked it.
It resembles the C function fflush.
The class ostream overloads the left-shift operator for formatted output.
A typical declaration is
ostreamfe o p e r a t o r « ( short ) ;
Here a short is written, after which the stream is returned.
Among the types handled by « are

const signed char* flo a t


const unsigned char* double
signed char long double
unsigned char unsigned short
short unsigned int
int unsigned long
long void*

Either version
ostreamft o p e r a t o r « ( const signed char* ) ;
ostreamft o p e r a t o r « ( const unsigned char* ) ;
writes a null-terminated string to the output stream.
The version
ostreamft o p e r a t o r « ( void* ) ;
writes the value of a pointer to the output stream.

Exam ple 7.3.10. In our system, the code


f lo a t x = 146.25;
cout « "x = " « x « endl « "&x = "
« ( void* ) &x « endl;
printed
x = 146.25
&x = 0 x efffef9 0

The operator « can be overloaded for user-defined types.

Exam ple 7.3.11. Given the declaration


struct String {
char s t r [ 100 ] ;
void s to re ( char* ) ;

we can overload « to write the string stored in a String object as follows:

www.MathSchoolinternational.com
7.3 THE HIGH-LEVEL IN P U T / O U T P U T CLASSES 359

ostreamfe o p e r a t o r « ( ostreamfe out, Stringfe s )


{
return out « s .s tr ;
>
The overloaded output operator could be used as
String s;
s .s to r e ( "Friends of B ill\ n " ) ;
cout « s;

Class ostream also has one constructor


ostream( streambuf* ) ;
which permits a connection to a buffer object.

iostream

Class iostream is publicly inherited from both istream and ostream. Its
entire declaration is
class iostream : public istream, public ostream {
p u b lic:
iostream( streambuf* ) ;
v irtu a l ~ iostream ();
protected:
iostreamO ;
>;
thus it provides high-level methods for input/output streams.

T h e withassign Classes

Class istream_withassign is publicly inherited from istream. Its entire


declaration is
class istream_withassign : public istream {
p u b lic :
istream _w ithassign();
v irtu a l ~istream _w ithassign();
istream_withassignfe operator=( istreamfe ) ;
istream_withassignfe operator=( streambuf* ) ;
>;
The default constructor does no initialization. The overload of the assign­
ment operator with parameter type istreamfe gets a buffer from the istream
object passed to it and does a complete initialization. The overload of
the assignment operator with parameter type streambuf* associates the

www.MathSchoolinternational.com
360 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS H IERARCH Y

streambuf object whose address is passed with the stream and does a com­
plete initialization.
Similarly, ostream_withassign is publicly inherited from ostream and
it adds an overload of the assignment operator; iostream_withassign is
publicly inherited from iostream and it adds an overload of the assignment
operator.

Exercises

1. Write a statement that reads the next 100 bytes from the standard input
(white space or not) and stores them in the char array a.

2. Write a statement that reads and discards the next 100 bytes from the
standard input (white space or not).

3. What is the output?

c in .ig n o re ( 50 ) ;
cout « cin.gcount( ) « endl;

Assume that the standard input is abcde (with e as the last character).
Tell which character would next be read after each sequence of statements
in Exercises 4-8 is executed.

4. cin .seekg( 1, io s ::b e g ) ;

5. cin .seekg( -1, ios::en d ) ;

6. cin .seek g( 2 ) ;
cin .seek g( 1, io s ::c u r ) ;

7. cin .seek g( -2, i o s : :end ) ;


cin .seek g( 1, io s ::c u r ) ;

8. cin .seek g( 1,io s ::b e g ) ;


p = c in .t e llp O ;
cin .seek g( -2, ios::en d ) ;
cin .seekg( p ) ;

9. Assume that a method read hasbeenadded to the zip code class of


Section 3.4 and overload » to read azip code.

10. Write a code slice that reads strings, delimited by single quotes, from the
standard input until end-of-file and stores the strings in a two-dimensional
array of char. Example: If the input is

www.MathSchoolinternational.com
7.4 M A N IPU LA TO R S 361

’ Marty K a lin ’ ’ Don Knuth’

Marty Kalin would be read and stored as the first string and Don Knuth
would be read and stored as the second string. Assume that there are no
errors in the input.

11. Write a code slice that reads lines (terminated by ’ \n’ ) from the standard
input and stores them in a two-dimensional array of char. Reading stops
when a line containing only ’ \n’ is encountered. This newline is removed
from the standard input but is not stored. Example: If the input is

Marty Kalin
Don Knuth

Grace S lick

the strings Marty Kalin and Don Knuth would be read and stored. The
file position marker would be on G in Grace Slick. Assume that the
standard input begins with a nonblank line.

12. Write a statement to write 50 characters from the char array a to the
standard output.

13. Overload « to print a zip code for the zip code class of Section 3.4.

7.4 Manipulators
We introduced manipulators in Section 2.4; here, we look at them in detail.
A manipulator is a function that either directly or indirectly modifies a
stream. For example, the system manipulator hex causes subsequent input
or output to be hexadecimal. A manipulator is used with the overloaded
input operator » or the overloaded output operator « . For example,
in t i = 10;
cout « hex « i « endl;
prints the value 10 in hexadecimal.
Several manipulators are predefined (see Figure 7.4.1). To use predefined
manipulators with no arguments (e.g., endl), include iostream.h. To use
predefined manipulators with arguments (e.g., s e tf i l l ) , include iomanip.h.

Example 7.4.1. We can rewrite Example 7.2.11 in which the output shows
the base, and uppercase letters are used on hexadecimal output:
cout « s e tio s fla g s ( i o s : : showbase I i o s : : uppercase )
« hex « "Hex: " « 168 « endl
« oct « "Octal: " « 168 « endl;

www.MathSchoolinternational.com
362 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

Manipulator Acts On Purpose


endl ostream Write newline and flush
stream
ends ostream Write null terminator in
string
flush ostream Flush output stream
WS istream Skip white space
dec ios Read or write integers in
decimal
oct ios Read or write integers in
octal
hex ios Read or write integers in
hexadecimal
setbase( in t n ) ostream Set integer base to n (0
means default)
s e tf i l l ( in t c ) ostream Set fill character to c
setp recisio n ( in t n ) ios Set precision to n
setw( in t n ) ios Set field width to n
s e tio s fla g s ( long ) ios Set specified format bits
r e s e tio s fla g s ( long ) ios Clear specified format bits

Figure 7.4.1 Manipulators.

We use the terminology manipulator with no arguments to refer to ma­


nipulators such as hex or endl because, when used with « or » , these
manipulators appear without arguments. Here is an example:
cout « endl;
In this example, the overloaded « operator invokes the manipulator endl
and, at this time, endl is passed an argument and performs its modifications
to the stream.
Consider how we might write the code to implement a manipulator such
as endl with no arguments. Because endl modifies the output stream
ostream, it should take an argument of type ostreamfe and return an argu­
ment of type ostreamfe. Now consider a statement such as
cout « endl;
where endl is referenced. The name of a function by itself is of type pointer
to function; thus, endl is of type “pointer to a function with one argument
of type ostreamfe that returns type ostreamfe.” Because the statement
cout « endl;
is equivalent to
c o u t.o p e r a to r «( endl ) ;

www.MathSchoolinternational.com
M A N IPU LA TO R S 363

we need an overload of o p e r a to r « that takes this type of argument: “pointer


to a function with one argument of type ostreamfe that returns type
ostreamfe.” Fortunately for our needs, class ostream contains the follow­
ing version of the overloaded « operator:
ostreamfe ostream: :o p e r a t o r « ( ostreamfe ( * f ) ( ostreamfe ) )

return ( * f ) ( *th is ) ;
>
The argument f to o p e r a to r « is of type “pointer to a function with one
argument of type ostreamfe that returns type ostreamfe.” In practice, f is
a pointer to a manipulator. The method o p e r a to r « simply invokes the
manipulator to which f points.

Example 7.4.2. The manipulator endl might be written as


ostreamfe endl( ostreamfe os )

os « ’ \n’ ;
return o s .flu s h O ;
}
When the statement
cout « endl;
is executed, o p e r a to r « is invoked as
c o u t.o p e r a to r «( endl ) ;
The body of o p e r a to r «
return ( * f ) ( *th is ) ;
is equivalent to
return endl( cout ) ;
which in turn is equivalent to
cout « ’ \n’ ;
return cout.f l u s h ( ) ;

Using the technique illustrated in Example 7.4.2, it is possible to write


our own manipulators.

Example 7.4.3. The manipulator


ostreamfe b e l l ( ostreamfe os )
{
return os « "\a";
>

www.MathSchoolinternational.com
364 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

rings the bell. It could be used as


cout « b e ll;

Consider writing a manipulator that takes an argument. Suppose, for


example, that we want a manipulator b e l l ( n ) that rings the bell n times.
Such a manipulator could be invoked as
cout « b e l l ( 10 ) ;
Because the function call operator has greater precedence than « , when
this statement executes, the function b e ll is invoked with argument 10,
after which o p e r a to r « is invoked as
cou t. o p e r a to r « ( val ) ;
where val is the value returned by b e ll.
So that the compiler can unambiguously choose the correct version of
o p e r a t o r « to invoke, the type of the value returned by b e ll must be
different from the type of argument expected by all of the other versions of
o p e r a to r «. The trick is to define a class for use with manipulators and
to have a manipulator return an object in this class. The returned object
is then responsible for invoking some function that actually modifies the
stream. We illustrate with the b e ll example.
The argument to the manipulator b e ll is of type int, but other ma­
nipulators might have different types of arguments. For this reason, the
class for use with manipulators is a parameterized class; the parameter type
matches the type of the argument to the manipulator.t The templates for
these classes are declared in iomanip.h. We illustrate how to write a manip­
ulator that takes an argument by writing the manipulator b e l l ( n ) that
rings the bell n times.

Exam ple 7.4.4. We use the following template from iomanip.h that facil­
itates writing one-argument manipulators:
template< class Typ > class OMANIP {
Typ n;
ostreamfe ( * f ) ( ostreamfe, Typ ) ;
p u b lic :
OMANIP( ostreamfe ( * f l ) ( ostreamfe, Typ ) , Typ nl )
: f ( f 1 ) , n( nl ) { }
frien d ostreamfe o p e r a t o r « ( ostreamfe os, OMANIPfe oman )
{ return om a n .(*f)( os, oman.n ) ; }
>;
The parameterized type Typ describes the type of the manipulator’s argu­
ment. In our case, the type is int.
^Sorne older versions o f C + + may use macros rather than a parameterized class.

www.MathSchoolinternational.com
M A N IPU LATO RS 365

The constructor has two parameters: the first f l is a pointer to a sec­


ondary function that actually modifies the stream, and the second nl is the
argument to * f 1. The constructor simply initializes the private members to
the arguments passed to it. The frien d function o p e r a to r « invokes the
function that modifies the stream.
We can write the secondary bell ringer function as
ostreamfe b e ll_ r in g e r ( ostreamfe os, in t n )
{
fo r ( in t i = 0; i < n; i++ )
os « "\a";

return os;
}
and the b e ll manipulator as
0MANIP< in t > b e l l ( in t n )
■C
return 0MANIP< in t >( b e ll_ rin g e r, n ) ;
>
The b e ll manipulator can be invoked as
cout « b e l l ( 10 ) ;
which is equivalent to
o p e r a t o r « ( cout, b e l l ( 10 ) ) ;
Because cout is a type derived from ostream and b e ll returns type
0MANIP< in t >, the correct version of « is invoked (i.e., the frien d version
in class 0MANIP< in t > that takes such an argument).
When the expression
0MANIP< in t >( b e ll_ rin g e r, n )
in b e ll is evaluated, the constructor for 0MANIP< in t > is invoked, and an
object temp of type 0MANIP< in t > comes into existence. The constructor
initializes f to b e ll_ rin g e r and n to 10. The function b e ll then returns
temp, which becomes the second argument to operator<<.
When o p e r a to r « executes with arguments cout and temp, it in effect
returns
b e ll_ r in g e r ( cout, 10 )
The bell is rung 10 times and the updated cout is returned. □

Although our examples in this section have dealt with ostream, exactly
the same techniques can be used to write manipulators for ios and istream.
The templates
SMANIP< class Typ >

www.MathSchoolinternational.com
366 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

for use with ios, and


IMANIP< class Typ >
for use with istream, are supplied in iomanip.h.

Exercises

1. Write a statement to clear the showpoint flag on cout. Use a manipula­


tor.

2. Write a statement to set the fill character on cout to ’ O’ . Use a manip­


ulator.

3. Provide an implementation of the manipulator ws.

4. Provide an implementation of the manipulator hex. Hint: This manipu­


lator takes an iosfe argument and returns type iosfe.

5. Write a manipulator scien that sets scientific notation for floating-point


numbers on ostream.

6. Write a manipulator tab that writes a tab in ostream.

7. Provide an implementation of the manipulator s e tf i l l .

8. Provide an implementation of the manipulator re s e tio s fla g s . Hint:


This manipulator takes an iosfe argument and returns type iosfe.

9. Write a manipulator s e to ff ( streamoff n ) that sets the file position


marker in istream to n.

10. Write a manipulator s k ip lin e ( in t n ) that writes n newlines to


ostream and then flushes the stream.

11. Explain the difference between a manipulator and a method.

7.5 The File Input/Output Classes


The classes

fstreambase
ofstream
ifstream
fstream

www.MathSchoolinternational.com
7.5 THE FILE IN P U T / O U T P U T CLASSES 367

are declared in fstream.h. They provide a high-level interface to file input


and output. The header file fstream.h includes iostream.h, if the latter was
not already included.

fstream base

The class fstreambase is publicly inherited from ios:


class fstreambase : v irtu a l public ios {

Its constructors, destructor, and methods contain code for associating files
with fstreambase objects.
Class fstreambase serves as a base class for the file input/output classes
of stream, ifstream, and f stream, which are the classes typically used by a
programmer to manipulate files. Each of of stream, ifstream, and f stream
is also derived from the corresponding stream class (ostream, istream,
or iostream) and, so, has methods for associating files with of stream,
ifstream, or fstream objects as well as methods for moving around in
files and for reading or writing files.
The derived classes of stream, ifstream, and f stream contain construc­
tors, destructors, and methods with behavior similar to those in fstreambase.
Indeed, the definitions of the constructors, destructors, and methods in these
derived classes typically contain calls to the corresponding constructors, de­
structors, and methods in fstreambase, where the actual implementation
takes place. Because of the similarity of the constructors, destructors, and
methods in fstreambase and the derived classes, we give examples only for
the derived classes.
The default constructor whose declaration is
fstreambase( ) ;
is used when an fstreambase object is created, but a file is not attached to
the object.
The constructor whose declaration is
fstreambase( const char* filename,
in t mode,
in t prt = f i l e b u f : : openprot ) ;
is used to associate an fstreambase object with the file filename, which
is opened in mode mode. The mode is described by oring mode bits de­
fined in ios (see Figure 7.2.2). The static member openprot is declared
in class file b u f; it provides default protection when a file is opened. This
constructor provides a system defined buffer.
Among other things, the destructor
"fstreambase( ) ;

www.MathSchoolinternational.com
368 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

closes the file, if any, attached to an fstreambase object.


The method open
void open( const char* filename,
in t mode,
in t prt = f i l e b u f : : openprot ) ;
is used to open the file filename in mode mode and associate it with an
already existing fstreambase object.
The method close
void c lo s e O ;
closes the file, if any, attached to the fstreambase object,

ofstream

The class of stream is publicly inherited from fstreambase and ostream:


class ofstream : public fstreambase, public ostream {

>;
It contains constructors, a destructor, and methods to associate output files
with o f stream objects. The class of stream inherits methods from ostream
to write and move within files.
The default constructor whose declaration is
of streamO ;
is used when an of stream object is created, but a file is not attached to the
object.
The constructor whose declaration is
ofstream ( const char* filename,
in t mode = io s ::o u t,
in t prt = f i l e b u f : : openprot ) ;
is used to associate an of stream object with the file filename, which is
opened in mode mode. The default mode is output. If a mode is specified,
it is automatically ored by the constructor with io s: :out.

Exam ple 7.5.1. The statement


ofstream fo u t( "data.out" ) ;
opens the file data.out for output and creates an ofstream object fout as­
sociated with this file. □

Recall (see Section 7.2) that an ios object can be used as a condition
in a statement. Since ofstream is indirectly derived from ios, an ofstream
object can be used as a condition.

Exam ple 7.5.2. The code

www.MathSchoolinternational.com
THE FILE IN P U T / O U T P U T CLASSES 369

ofstream fo u t( "data.out" ) ;
i f ( Ifout )
cerr « "Can’ t open data.out\n";
attempts to open the file data.out for output. If the file cannot be opened,
an error message is written to the standard error. □

Typically the constructor whose declaration is


ofstream( const char* filename,
in t mode = io s : : out,
in t prt = f i l e b u f : : openprot ) ;
is implemented by simply invoking the corresponding constructor in the base
class fstreambase:
ofstream :: ofstream( const char* filename,
in t mode = i o s : : out,
in t prt = f i l e b u f : : openprot ) :
fstreambase( filename, mode I io s ::o u t, prt )
{
>
Among other things, the destructor
“ of streamO ;
closes the file, if any, attached to an ofstream object.
The method open
void open( const char* filename,
in t mode = io s : : out,
in t prt = f i l e b u f : : openprot ) ;
is redefined in ofstream so that the file filename is opened in mode mode
and associated with an already existing ofstream object. The default mode
is output. If a mode is specified, it is automatically ored with io s : :out.

Example 7.5.3. The code


ofstream fou t;
fout.open( "data.out" ) ;
has the same effect as that of Example 7.5.1. The file data, out is opened for
output and the ofstream object fout is associated with this file. □

The method close, which is inherited from fstreambase, closes the file,
if any, attached to the ofstream object.

Example 7.5.4. The file data.dat associated with the ofstream object
fout of Example 7.5.3 may be closed with the statement
fo u t. c lo s e ( ) ;

www.MathSchoolinternational.com
370 CHAPTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

ifstream

The class ifstream is publicly inherited from fstreambase and istream:


class ifstream : public fstreambase, public istream {

It contains constructors, a destructor, and methods to associate input files


with ifstream objects. The class ifstream inherits methods from istream
to read and move within files.
The default constructor whose declaration is
i f streamO ;
is used when an ifstream object is created, but a file is not attached to the
object.
The constructor whose declaration is
ifstream ( const char* filename,
in t mode = io s ::in ,
in t prt = f i l e b u f : : openprot ) ;
is used to associate an ifstream object with the file filename, which is
opened in mode mode. The default mode is input. If a mode is specified, it
is automatically ored by the constructor with i o s : : in.

Exam ple 7.5.5. The statement


ifstream f i n ( "d ata.in " ) ;
opens the file data.in for input and creates an ifstream object f in associ­
ated with this file. □

Among other things, the destructor


~ ifs tre a m ();
closes the file, if any, attached to an ifstream object.
The method open
void open( const char* filename,
in t mode = io s ::in ,
in t prt = f i l e b u f : : openprot ) ;
is redefined in ifstream so that the file filename is opened in mode mode
and associated with an already existing ifstream object. The default mode
is input. If a mode is specified, it is automatically ored with i o s :: in.

Exam ple 7.5.6. The code


ifstream fin ;
fin .op en ( "d ata.in " ) ;

www.MathSchoolinternational.com
7.5 THE FILE IN P U T / O U T P U T CLASSES 371

has the same effect as that of Example 7.5.5. The file data.in is opened for
input and the ifstream object fin is associated with this file. □

The method close, which is inherited from fstreambase, closes the file,
if any, attached to the ofstream object.

Example 7.5.7. The following program copies one file to another. The
files, whose names are supplied on the command line, are opened in binary
mode so that the program performs a byte-for-byte copy.
#include <stdlib.h>
#include <fstream.h>

in t main( in t argc, char** argv )

ifstream f i n ( argv[ 1 ] , io s ::b in a ry ) ;


i f ( !fin ) {
cerr « "Can’ t open " « argv[ 1 ] « endl;
return EXIT_FAILURE;
>

ofstream fo u t( argv[ 2 ] , io s ::b in a ry ) ;


i f ( !fou t ) {
cerr « "Can’ t open " « argv[ 2 ] « endl;
return EXIT_FAILURE;
}

char c;
while ( f i n . g e t ( c ) )
fo u t.p u t( c ) ;

return EXIT_SUCCESS;
>

fstream

The class fstream is publicly inherited from fstreambase and iostream:


class fstream : public fstreambase, public iostream {

It contains constructors, a destructor, and methods to associate input/out­


put files with f stream objects. The class f stream inherits methods from
iostream to read, write, and move within files.

www.MathSchoolinternational.com
CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

The default constructor whose declaration is


f streamO ;
is used when an f stream object is created but a file is not attached to the
object.
The constructor whose declaration is
fstream ( const char* filename,
in t mode,
in t prt = f i l e b u f : : openprot ) ;
is used to associate an fstream object with the file filename, which is
opened in mode mode. The mode must be specified since no default value is
supplied.

Exam ple 7.5.8. The statement


fstream fin o u t( "data.dat", io s ::in I io s ::o u t ) ;
opens the file data.dat for input and output, and creates an fstream object
fin ou t associated with this file. □

Among other things, the destructor


~f stream O;
closes the file, if any, attached to an fstream object.
The method open
void open( const char* filename,
in t mode,
in t prt = f ile b u f: : openprot ) ;
is redefined in fstream so that the file filename is opened in mode mode
and associated with an already existing fstream object. The mode must be
specified since no default value is supplied.

Exam ple 7.5.9. The code


fstream fin ou t;
finou t.open( "data.dat", io s ::in I io s ::o u t ) ;
has the same effect as that of Example 7.5.8. The file data.dat is opened for
input and output, and the fstream object finout is associated with this
file. □

The method close, which is inherited from fstreambase, closes the file,
if any, attached to the fstream object.
The next section gives a major example involving the class fstream.

www.MathSchoolinternational.com
7.6 SAM PLE A PPLIC A TIO N : A R A N D O M ACCESS FILE CLASS 373

Exercises

1. Write two statements. The first statement creates an ifstream object


fin . The second statement opens the file weather.in and associates it
with fin .

2. Write two statements. The first statement creates an ofstream object


fout. The second statement opens the file news.out for appending and
associates it with fout.

3. Write one statement that creates an f stream object finout associated


with the binary file stars.dat, which is opened for input and output. The
open should fail if the file does not exist.

4. Since we could open a file for input and output with the statement

ifstream fin o u t( "d ata.d at", io s ::o u t ) ;

why do we need class f stream?

5. The following code attempts to open the file data.dat for input, do some
processing, close it, reopen it for output, and then do more processing.
What is the error?

// open f i l e fo r reading
ifstream f ( "data.dat" ) ;
// process
f .c lo s e ();
// open f i l e f o r w ritin g
ofstream f ( "data.dat" ) ;
// process

6. Correct the error in Exercise 5.

7. Write a complete program that copies files in the order listed on the
command line (the first file— the executable— is skipped) to the standard
output. Files that cannot be opened for reading are ignored. (This pro­
gram is similar to the UNIX utility cat.)

7.6 Sample Application: A Random Access File Class


P r o b le m ______________________________________________________________________

A random access file is a file in which we can access records in any order
whatsoever and not necessarily in physical order. We implement a random

www.MathSchoolinternational.com
374 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS H IERARCH Y

access file class that allows the user to create and use a random access file.
In particular, the user can add a record, find a record, or remove a record.
In our implementation, the data read and written are binary data, so
that any kind of data can be stored and retrieved from the file. The user
can directly manipulate the binary data, or a class could be created as an
interface to the binary data.

Sam ple Input / O u t p u t ______________________________________________________

The following sample input/output shows the random access file class in
action. We first create a new file. Although the random access file class
allows records and keys of any size, the records here are five bytes long to
simplify the example. The first three bytes make up the key. User input is
underlined.
New f i l e (Y/N)? y

[A] dd
[F]ind
[R]emove
[Q ]uit? a
Which record to add? 125xx
Record added

[A] dd
[F]ind
[R]emove
[Q]uit? f
Key? 125
Record found: 125xx

[A] dd
[F]ind
[R]emove
[Q ]uit? f
Key? 130
Record not found

[A] dd
[F]ind
[R]emove
[Q]uit? r
Key? 125
Record removed

www.MathSchoolinternational.com
7.6 SAM PLE APPLIC A TIO N : A R A N D O M ACCESS FILE CLASS 375

File Header... T150aaF T167bb...


t T T T
Byte 0 256 262 268
Relative address 0 1 2

Figure 7.6.1 A relative file.

[A] dd
[F]ind
[R]emove
[Q]uit? f
Key? 125
Record not found

[A] dd
[F]ind
[R]emove
[Q]uit? q

S o lu tio n _______________________________________________________________________

We use a relative file, that is, a file in which a record’s relative address
(as opposed to its physical address) is its position in the file: first, second,...
Given a key, we can translate it into a relative address. Once we have the
relative address, we can quickly determine approximately where the record is
located in the file and then access it directly. A relative file is thus analogous
to an array. Just as each of the array’s elements has a position relative to
the first, so each element in a relative file has a position relative to the first.
The records in a relative file are stored contiguously following the file
header, whose purpose we will explain later. The first byte of a record,
which is not part of the logical record, holds a status flag that indicates
whether a record is stored (T for “taken” ), whether a record was stored but
deleted (D for “deleted” ), or whether a record was never stored (F for “free” ).
The remaining bytes store the key and the rest of the record. If, as in the
sample input/output, the logical records are five bytes, the file appears as
in Figure 7.6.1, assuming a 256-byte header.
To access a record, we define a hash function h that, given a key,
produces a relative storage address:

h( key ) = Record’s relative address

Although there are many different ways to define hash functions, our imple­
mentation uses the division-remainder method. We define the hash function

www.MathSchoolinternational.com
376 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

14 42 31 6
Relative address: 0 1 2 3 4 5 6 7 8 9 10 11 IS

Inserting in a relative file.

14 43 31 6 135
Relative address: 0 1 2 3 4 5 6 7 8 9 10 11 IS

Figure 7.6.3 Resolving a collision in a relative file.

h by the rule
h{ key ) = key % divisor

where the modulus operator % yields the remainder after dividing key by
divisor. For example, if key is 134 and divisor is 13, the record’s relative
address is 4. If divisor is ra, the relative addresses range from 0 through
n — 1.

When two distinct keys hash to the same relative storage address, we say
that a collision occurs. For example, if our hash function is

h( key ) = key % 13

we have
h( 134 ) = 4 = h( 147 )

There is a collision: The keys 134 and 147 map to the same relative address.
Any hashing system must provide a collision resolution policy— a
way of handling collisions. Our collision resolution policy is called linear
probing. When a collision occurs, we simply move to the next highest
relative address (with the first record position assumed to follow the last
record position). For example, if we insert the keys 42, 6, 31, and 14 in
the relative file with relative addresses 0 through 12 using the hash function
h( key ) = key % 13, we obtain the situation shown in Figure 7.6.2. Now
suppose that we insert the key 135. Because

h( 135 ) = 5 = h( 31 )

a collision occurs. Using linear probing, we insert 135 in the next highest
unoccupied spot, 7. We obtain the situation shown in Figure 7.6.3.
Divisors should be chosen to minimize collisions. Research and experi­
ence show that divisors with no small prime factors do reasonably well at
avoiding collisions. Avoiding collisions requires more than a good divisor,
however. As more and more records are added to a relative file, collisions
become more likely. A file’s load factor, defined as

, , . number of records m file


load factor = ——------------------------
file s maximum capacity

www.MathSchoolinternational.com
7.6 SAM PLE APPLIC A TIO N : A R A N D O M ACCESS FILE CLASS 377

is the percentage of occupied cells. Research and experience show that a


relative file’s load factor should not exceed 70 to 80 percent.
To delete a record, we mark it D (deleted) rather than physically delete
it.
When we search for a record with a given key k, we first hash to relative
address addr — k% divisor, where divisor is the number of slots in the file.
If the record at relative address addr has status T (taken), we check whether
this record has key k. If so, the search terminates successfully; otherwise,
we continue the search by checking the record at relative address

( addr + 1 ) % divisor

If the record at relative address addr has status F (free), the search termi­
nates unsuccessfully since, if the record were present, we would have found
it before reaching the free slot. If the record at relative address addr has
status D (deleted), we must continue the search by checking the record at
relative address
( addr + 1 ) % divisor

since the record we are searching for may have been inserted before the
record at address addr was deleted and would thus be found after further
probing.
Distinguishing between free and deleted slots usually allows us to termi­
nate the search for a nonexistent record before searching the entire file.

CH—|- Im plem entation______________________________________________

#include <stdio.h>
#include <fstream.h>
#include <string.h>
#include <stdlib.h >
#include <ctype.h>

const int header_size = 256;


const char Taken = ’ T’ ;
const char Free = ’ F’ ;
const char Deleted = ’ DJ;
const int Success = 1;
const int F a il = 0;

class frandom : public fstream {


protected:
int isopen; // 1 i f f i l e is open, else 0
long slo ts;
long record_size; // includes 1-byte f la g

www.MathSchoolinternational.com
378 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

long key_size;
long total_bytes;
long no_records; // no of records stored
long loc_address; // computed by locate
char* b u ffe r; // holds one record
char* stored_key; // holds one key
long get_address( const char* ) ;
int lo c a te ( const char* ) ;
p u b lic :
frandomO ;
frandom( const char* ) ; // open existing f i l e
frandom( const char*, in t, in t, int ) ; // open new f i l e
"frandomO ;
void open( const char* ) ; // open existing f i l e
void open( const char*, in t, in t, int ) ; // open new f i l e
void c lo se O ;
long g e t _ s lo t s () { return s lo ts; }
long get_record _size() { return record_size; >
long get_key_size() { return key_size; }
long ge t_to ta l_b y te s() { return total_bytes; }
long get_no_records( ) { return no_records; }
in t add_record( const char* ) ;
in t find_record( char* ) ;
in t remove_record( const char* ) ;
>;

frandom: : "frandomO

if ( isopen ) {
delete [ ] stored_key;
delete [ ] bu ffer;
char b u f f [ header_size ] ;
fo r ( int i = 0; i < header_size; i++ )
b u ff[ i ] = ’ ’ ;
s p r in t f( b u ff, "‘/.Id ‘/.Id %ld ’/.Id",
slo ts , record_size,
key_size, no_records ) ;
seekp( 0, i o s : :beg ) ;
w rite ( b u ff, header_size ) ;
}
>

www.MathSchoolinternational.com
7.6 SAM PLE A PPLIC A TIO N : A R A N D O M ACCESS FILE CLASS 379

frandom: :frandomO : fstreamO


{
bu ffer = stored_key = 0;
slo ts = record_size = key_size = 0;
total_bytes = no_records = 0;
isopen = fstream base::rdbuf() -> is _ o p e n ();
>

frandom::frandom( const char* filename ) : fstreamO


{
bu ffer = stored_key = 0;

fstream::open( filename,
i o s : : in I
i o s : :out |
io s : : binary |
io s : :nocreate ) ;

isopen = fstreambase: :rd buf0 -> is _ o p e n ();

if ( isopen ) {
char b u f f [ header_size ] ;
read( b u ff, header_size ) ;
sscanf ( b u ff, "‘/.ldV.ld’/.ld'/.ld" ,
ftslots, &record_size, &key_size,
&no_records ) ;
total_bytes = slo ts * record_size + header_size;
// get_address needs \0
stored_key = new chair [ key_size + 1 ] ;
bu ffer = new char [ record_size ] ;
>
>

frandom::frandom( const char* filename,


int si,
int actual_record_size,
int ks ) : fstreamO
{
bu ffer = stored_key = 0;
fstream ::open( filename,
i o s : : in |
i o s : : out |
i o s : : binary |
io s : : noreplace ) ;

www.MathSchoolinternational.com
CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

isopen = fstreambase: :rd buf( ) -> is_o p en ();

if ( isopen ) {
char b u f f [ header_size ] ;
slo ts = s i;
record_size = actual_record_size + 1;
key_size = ks;
total_bytes = slo ts * record_size + header_size;
no_records = 0;
// get_address needs \0
stored_key = new char [ key_size + 1 ] ;
fo r ( int i = 0; i < header_size; i++ )
b u ff[ i ] = ’ ’ ;
s p r in t f( b u ff, "*/.ld ’/.Id ’/.Id */.ld",
slo ts , record_size,
key_size, no_records ) ;
w rite ( b u ff, header_size ) ;
bu ffer = new char [ record_size ] ;
fo r ( i = 1; i < record_size; i++ )
b u ffe r [ i ] = ’
b u f fe r [ 0 ] = Free;
fo r ( i = 0; i < s lo ts; i++ )
w rite ( b u ffe r, record_size ) ;
>
>

// hash function
long frandom::get_address( const char* key )

memcpy( stored_key, key, key_size ) ;


stored_key[ key_size ] = ’ \0’ ;
return ( a t o l( stored_key ) */, slo ts )
* record_size + header_size;
>

// locate searches fo r a record with the specified key.


/ / I f successful, locate returns 1.
/ / I f unsuccessful, locate returns 0.
// locate sets data member loc_address to the address
// of the record i f the record is found. This
// address can be then used by find_record
// or remove_record.
//

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A R A N D O M ACCESS FILE CLASS 381

// I f the record is not found, locate sets loc_address


// to the f i r s t D or F slo t encountered in it s search
// fo r the record. This address can be used by add_record.
// I f there is no D or F slo t ( f u l l f i l e ) , locate sets
// loc_address to the hash address of key.
int frandom::lo c a te ( const char* key )
{
// address = current o ffse t in f i l e
// start_address = hash o ffs e t in f i l e
// unocc_address = f i r s t D slo t in f i l e
// = start_address, i f no D slo t
long address, start_address, unocc_address;

// d e le te _fla g = F a il, i f no D slo t is found


// = Success, i f D slo t is found
int d e le te _fla g = F a il;

address = get_address( key ) ;


unocc_address = start_address = address;
do {
seekg( address, io s ::b e g ) ;
switch( g e t () ) {
case Deleted:
i f ( !d e le te _fla g ) {
unocc_address = address;
d e le te _fla g = Success;
}
break;
case Free:
loc_address = d e le te _fla g ? unocc_address : address;
return F a il;
case Taken:
seekg( address + 1, io s ::b e g ) ;
read( stored_key, key_size ) ;
i f ( memcmpC key, stored_key, key_size ) == 0 ) {
loc_address = address;
return Success;
>
break;
>
address += record_size;
i f ( address >= total_bytes )
address = header_size;
> while ( address != start_address ) ;

www.MathSchoolinternational.com
CH APTER 7 THE C + + IN P U T / O U T P U T CLASS H IERARCH Y

loc_address = unocc_address;
return F a il;
>

int frandom::add_record( const char* record )

if ( no_records >= slo ts I I lo c ate( record ) )


return F a il;

seekp( loc_address, io s ::b e g ) ;


w rite ( ftTaken, 1 ) ;
w rite ( record, record_size - 1 ) ;
no_records++;
return Success;
>

in t frandom ::find_record( char* record )

if ( lo c a te ( record ) ) {
seekg( loc_address + 1, io s ::b e g ) ;
read( record, record_size - 1 ) ;
return Success;
>
else
return F a il;
>

in t frandom::remove_record( const char* key )

if ( lo c a te ( key ) ) {
— no_records;
seekp( loc_address, io s ::b e g ) ;
w rite ( ftDeleted, 1 ) ;
return Success;
>
else
return F a il;
>

void frandom::open( const char* filename )

fstream ::open( filename,


i o s : : in I

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A R A N D O M ACCESS FILE CLASS

i o s : : out I
i o s : : binary I
i o s : :nocreate ) ;

int open_fl = fstream base::rdbuf() -> is _ o p e n ();

if ( open_fl ) {
isopen = open_fl;
char b u f f [ header_size ] ;
read( b u ff, header_size ) ;
sscanf( b u ff, "%ld%ld%ldXld",
feslots, &record_size, &key_size,
&no_records ) ;
total_bytes = slo ts * record_size + header_size
// get_address needs \0
stored_key = new char [ key_size + 1 ] ;
bu ffer = new char [ record_size ] ;
}
}

void frandom:: open( const char* filename,


int s i,
int actual_record_size,
int ks )
{
fstream::open( filename,
i o s : : in I
i o s : : out I
i o s : : binary I
i o s : :noreplace ) ;

int open_fl = fstream base::rdbuf() -> is_o p en ();

i f ( open_fl ) {
isopen = open_fl;
char b u f f [ header_size ] ;
slo ts = s i;
record_size = actual_record_size + 1;
key_size = ks;
total_bytes = slo ts * record_size + header_size
no_records = 0;
// get_address needs \0
stored_key = new char [ key_size + 1 ] ;

www.MathSchoolinternational.com
384 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

fo r ( int i = 0; i < header_size; i++ )


b u ff[ i ] = ’ ’ ;
s p r in t f( b u ff, "’/.Id ’/.Id ’/.Id '/.Id",
s lo ts , record_size,
key_size, no_records ) ;
w rite ( b u ff, header_size ) ;
b u ffe r = new char [ record_size ] ;
fo r ( i = 1; i < record_size; i++ )
b u f fe r [ i ] = ’ ’ ;
b u f fe r [ 0 ] = Free;
fo r ( i = 0; i < s lo ts ; i++ )
w rite ( b u ffe r, record_size ) ;
>
>

void frandom:: c lo se ()

if ( isopen ) ■[
delete [ ] stored_key;
delete [ ] b u ffer;
char b u f f [ header_size ] ;
fo r ( int i = 0; i < header_size; i++ )
b u ff[ i ] = ’ ’ ;
sp rin tf ( b u f f , "'/.Id ‘/.Id ’/.Id */,ld" ,
slo ts , record_size,
key_size, no_records ) ;
seekp( 0, io s ::b e g ) ;
w rite ( b u ff, header_size ) ;
fstream base::close() ;
>
>

int mainO
■C
char b [ 10 ] , c;

frandom finout;

cout « "New f i l e (Y/N)? " « flush;


cin » c;
i f ( toupper( c ) == ’Y’ ) {
finout.open( "data.dat", 15, 5, 3 ) ;
i f ( !finout ) {
cerr « "Couldn’ t open f i l e " « endl;

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A R A N D O M ACCESS FILE CLASS 385

return EXIT_FAILURE;
}
}
else {
finout.open( "data.dat" );
if ( !finout ) {
cerr « "Couldn’t open f i l e " « endl;
return EXIT_FAILURE;
}

do {
cout « "\n\n[A]dd\n[F]ind\n[R]emove\n[Q]uit? " « flu sh ;
cin » c;
switch ( toupper( c ) ) {
case ’ A’ :
cout « "Which record to add? " « flush;
cin » b;
i f ( fin o u t.add_record( b ) )
cout « "Record added" « endl;
else
cout « "Record not added" « endl;
break;
case ’ F ’ :
cout « "Key? " « flush;
cin » b;
i f ( fin o u t.fin d _reco rd ( b ) ) {
b [ 5 ] = ’ \0’ ;
cout « "Record found: " « b « endl;
>
else
cout « "Record not found" « endl;
break;
case ’R’ :
cout « "Key? " « flush ;
cin » b;
i f ( finout.remove_record( b ) )
cout « "Record removed" « endl;
else
cout « "Record not removed" « endl;
break;
case ’ Q’ :
break;

www.MathSchoolinternational.com
386 C H APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

Member Purpose
isopen 1, if file is open; 0, if file is closed.
s lo ts Number of slots.
record_size Actual record size + 1 (for flag).
k ey.size Size of key.
tota l_b ytes s lo ts * record_size
no_records Number of records stored.
loc_address Set by method locate. loc_address is where a record was
found or where a record can be stored.
b u ffer Storage for one record.
stored_key Storage for one key plus null terminator.

Figure 7.6.4 Data members of frandom.

d e fa u lt:
cout « " I l l e g a l choice" « endl;
break;
>
} while ( toupper( c ) != ’ Q’ ) ;

return EXIT_SUCCESS;
>

D iscu ssion ____________________________________________________________________

We derive our random access file class from the library input/output file
class fstream:
class frandom : public fstream {

>;
We provide constructors and a destructor, with behavior similar to those in
the base class fstream, for associating a random access file with an frandom
object. We also provide methods for associating a random access file with an
already existing frandom object; for adding, finding, and removing records;
and for obtaining information about the file (e.g., the number of slots, the
record size, etc.).
The data members of frandom and their purposes are listed in Figure
7.6.4. The first 256 bytes of the file are reserved as a header for the file. The
values of slots, record_size, key_size, and no_records, in this order,
are stored in the file header.
The default constructor

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A R A N D O M ACCESS FILE CLASS 387

frandom: :frandom() : fstreamO


{

>
invokes the base class constructor, initializes certain members to zero, and
sets the isopen flag:
isopen = fstreambase: :rdbuf() -> is _ o p e n ();
The member rdbuf in fstreambase returns a pointer to a buffer object. The
buffer class contains the method is_open that returns 1 if the file is open,
or 0 if the file is not open. The default constructor does not associate a file
with an frandom object. If an frandom object is created with the default
constructor, the method open could be used to open a file and associate it
with the frandom object.
The constructor
frandom::frandom( const char* filename,
in t s i ,
in t actual_record_size,
in t ks ) : fstreamO

>
creates an frandom object and associates the new file filename with it.
After invoking the base class default constructor and initializing b u ffe r
and stored_key, the file is opened using the method open from fstream:
fstream ::open( filename,
io s : : in I
io s : : out |
io s : : binary |
i o s : :noreplace ) ;
The file is opened as a binary file for input and output, thus allowing any
kind of data to be read and written. The flag
io s : : noreplace
causes open to fail if the file exists, unless ate or app is set, which is not the
case here. Thus the file is successfully opened only if it is a new file.
The next statement initializes is_open
isopen = fstream base::rdbuf0 -> is _ o p e n ();
just as in the default constructor.
If the file is opened
if ( isopen ) {

www.MathSchoolinternational.com
388 CHAPTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

we initialize data members, allocate storage, write the file header, and ini­
tialize the file.
We first allocate storage for the header
char b u ff[ header_size ] ;
and initialize slots, record_size, key_size, total_bytes, and no_records.
We then allocate storage for the key:
stored_key = new char [ key_size + 1 ] ;
Next we place the text for the header in buff and then write the header to
the file:
fo r ( in t i = 0; i < header_size; i++ )
b u ff[ i ] = * ’ ;
s p rin tf ( b u ff, "*/.ld ‘/.Id ‘/.Id */.ld",
s lo ts , record_size,
key_size, no_records ) ;
w r it e ( b u ff, header_size ) ;
We then create storage for a record and write s lo ts Free records to the
file:
b u ffe r = new char [ record_size ] ;
fo r ( i = 1; i < record_size; i++ )
b u ffe r [ i ] = ’ ’ ;
b u ffe r [ 0 ] = Free;
fo r ( i = 0; i < s lo ts ; i++ )
w r ite ( b u ffer, record_size ) ;
We use the methods read and w rite throughout since their purpose is to
read and write binary data.
The constructor
frandom::frandom( const char* filename ) : fstreamO

>
creates an frandom object and associates an existing file filename with it.
After invoking the base class default constructor and initializing b u ffer
and stored_key, the file is opened using the method open from fstream:
fstream ::open( filename,
i o s : : in I
i o s : : out I
i o s : :binary |
ios::nocreate ) ;
The file is opened as a binary file for input and output. The flag
io s : :nocreate

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A R A N D O M ACCESS FILE CLASS 389

causes open to fail if the file does not exist. Thus the file is successfully
opened only if it is an existing file.
The next statement initializes is_open as in the other two constructors.
If the file is opened, we create storage for the header, read the header,
initialize slots, record_size, key_size, total_b ytes, and no_records:

char b u ff[ header_size ] ;


read( b u ff, header_size ) ;
sscanf ( b u ff, "7,ld7.1d7.1d7.1d" ,
feslots, &record_size, &key_size,
&no_records ) ;
total_b ytes = s lo ts * record_size + header_size;
We conclude by creating storage for a key and a record:
stored_key = new char [ key_size + 1 ] ;
b u ffer = new chair [ record_size ] ;
The two versions of open work similarly to the two preceding construc­
tors. Each associates a file with an existing frandom object.
The destructor checks whether a file is open. If a file is open, it deletes
the dynamically allocated storage for the key and record and writes an up­
dated header to the file:
if ( isopen ) {
d elete [ ] stored_key;
d elete [ ] b u ffer;
char b u ff[ header_size ] ;
fo r ( in t i = 0; i < header_size; i++ )
buff [ i ] = ’ ’ ;
sp rin tf ( b u ff, "%ld 7.1d 7.1d 7.1d",
s lo ts , record_size,
key_size, no_records ) ;
seekp( 0, io s ::b e g ) ;
w r ite ( b u ff, header_size ) ;
}
The file is closed when the destructor executes since the destructor implicitly
calls the base class (fstream) destructor, which closes the file.
The method close works similarly to the destructor. It adds a call to
f streambase’s method close to close the file:
f streambase: : c lo s e ( ) ;
The protected method locate is used to search for a record. It is used
by the public methods add_record, find_record, and remove_record.
First locate initializes d e le te _ fla g to Fail:
in t d e le te _ fla g = F a il;

www.MathSchoolinternational.com
CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

The flag d e le te _ fla g is reset to Success if locate encounters a D (delete)


slot. A D slot could be used by add_record to store a record.
Next locate calls the hash function get_address:
address = get_address( key ) ;
This value is then copied into unocc_address and start_address:
unocc_address = start_address = address;
The value of start_address is never changed. It is used to check whether
all slots in the file have been examined and to terminate the loop if all slots
have been examined:
do {

> while ( address != start_address ) ;


The variable unocc_address is used to save the first occurrence of a D slot.
In the loop, locate seeks to address
seekg( address, io s ::b e g ) ;
and reads the first byte
sw itch( g e t () ) {
If this slot is D and this is the first occurrence of a D slot, the address of
this slot is saved and d e le te _ fla g is changed to Success:
case Deleted:
i f ( !d e le te _ fla g ) {
unocc_address = address;
d e le te _ fla g = Success;
>
break;
and the search continues.
If this slot is F (free), loc_address is set to the first occurrence of an F
or D slot
case Free:
loc_address = d e le te _ fla g ? unocc_address : address;
return F a il;
and F a il is returned to signal an unsuccessful search. If locate was called by
add_record, add_record will insert the new record at address loc_address.
If this slot is T (taken), locate reads the record and checks whether it
is has the desired key:
case Taken:
seekg( address + 1, io s ::b e g ) ;
read( stored_key, key_size ) ;
i f ( memcmp( key, stored_key, key_size ) == 0 ) {

www.MathSchoolinternational.com
SAM PLE A PPLIC A TIO N : A R A N D O M ACCESS FILE CLASS 391

loc_address = address;
return Success;
>
break;

If the key matches, loc_address is set to the address of the record and
Success is returned to signal a successful search. If locate was called by
f ind_record, f ind_record will store the record at address loc_address. If
locate was called by delete_record, delete_record will change the flag
to D at address loc_address.
If the record was not found and an F slot did not terminate the search,
address is updated to the next slot and the search continues:

address += record_size;
i f ( address >= tota l_b ytes )
address = header_size;

If all the slots are searched, loc_address is set to unocc_address, and


locate returns F ail.
The method add_record checks whether all slots are filled and, if so, it
returns F a il; it also returns F a il if there is already a record stored with the
given key:

if ( no_record >= s lo ts I I lo c a te ( record ) )


return F a il;

Duplicate keys are not allowed.


If locate cannot find a record with the given key, add_record stores the
record at loc_address (computed by locate), updates no_records, and
returns Success:

seekp( loc_address, io s ::b e g ) ;


w rite ( ftTaken, 1 ) ;
w r ite ( record, record_size - 1 ) ;
no_records++;
return Success;
The method find_record calls locate. If locate finds the record,
find_record copies it at the address passed and returns Success; other­
wise, find_record simply returns F ail:

if ( lo c a te ( record ) ) {
seekg( loc_address + 1, io s ::b e g ) ;
read( record, record_size - 1 ) ;
return Success;
>
else
return F a il;

www.MathSchoolinternational.com
392 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS H IERARCH Y

The method remove_record calls locate. If locate finds the record,


remove_record updates no_records, flags the record as D, and returns
Success; otherwise, remove_record simply returns Fail:
if ( lo c a te ( key ) ) {
— no_records;
seekp( loc_address, io s ::b e g ) ;
w r ite ( ftDeleted, 1 ) ;
return Success;
>
else
return F a il;
The function main creates an frandom object (using the default construc­
tor):
frandom fin ou t;
The user is then asked whether a new or existing file is to be opened:
cout « "New f i l e (Y/N)? " « flush;
If a new file is to be opened, the version of open for new files is invoked.
An attempt is made to open the new file data.dat with 15 slots, 5-byte
records, and 3-byte keys:
finou t.open( "data.dat", 15, 5, 3 ) ;
If the file cannot be opened, a message is printed and the program is termi­
nated:
if ( ! finout ) {
cerr « "Couldn’ t open f i l e " « endl;
e x it ( EXIT.FAILURE ) ;
>
(Recall that this version of open was written so that opening the file would
fail if it already exists.)
If an existing file is to be opened, the version of open for existing files is
invoked. An attempt is made to open the existing file data.dat:
finou t.open( "data.dat" ) ;
As in the previous code, if the file cannot be opened, a message is printed
and the program is terminated. (Recall that this version of open was written
so that opening the file would fail if it did not already exist.) A loop then
allows the user to repeatedly add, find, or remove records.

7.7 The Character Array Input/Output Classes


The classes

www.MathSchoolinternational.com
7.7 THE CHARACTER A R R A Y IN P U T / O U T P U T CLASSES 393

strstreambase
ostrstream
istrstream
strstream

are declared in strstream.h. They provide a high-level interface to character


array input and output in much the same way that their counterparts, the
fstream classes, provide a high-level interface to file input and output. The
header file strstream.h includes iostream.h, if it was not already included.

strstream base

The class strstreambase is publicly inherited from ios:


class strstreambase: v ir tu a l public ios {

>;
It serves as a base class for the character array input/output classes o s tr­
stream, istrstream, and strstream, which are the classes used by a pro­
grammer to manipulate character arrays. Each of ostrstream, istrstream,
and strstream is also derived from the corresponding stream class (ostream,
istream, or iostream) and, so, has methods for moving around in charac­
ter arrays and for reading and writing character arrays. The derived classes
provide constructors for associating user-defined character arrays or system-
defined (dynamic) character arrays with objects in these derived classes.
The only public member in strstreambase is rdbuf. Its declaration is
strstreambuf* rdbuf( ) ;
The method returns a pointer to the strstreambuf buffer object associated
with the strstreambase object.
Class strstreambase has a protected constructor that contains code
for associating a user-specified character array buffer with an strstream
object. A second protected (default) constructor permits the system to
dynamically allocate the character array buffer. Class strstreambase also
contains a protected destructor that, among other things, deallocates any
dynamically allocated storage. These protected constructors and destruc­
tor are typically invoked by public constructors, destructors, and methods
in derived classes.

ostrstream

The class ostrstream is used to write formatted output to character arrays.


It performs somewhat the same functionality as the C function s p r in tf.
The entire declaration of the class ostrstream is

www.MathSchoolinternational.com
394 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

class ostrstream : public strstreambase, public ostream {


p u b lic :
ostrstream( char* b u ff,
in t len,
in t mode = io s : : out ) ;
ostrstreamO ;
"ostrstream O ;
char* s t r O ;
in t pcount( ) ;
>;
Its constructors and destructor make calls to the protected constructors
and destructor in strstreambase to allocate and deallocate character array
buffers. It inherits public methods from ostream to write and move within
character arrays.
The default constructor is used when an ostrstream object is created
but a character array is not attached to the object. In this case, the system
uses its own dynamically created character array buffer.

Exam ple 7.7.1. The statement


ostrstream sout;
defines the ostrstream object sout. Output is written to a system-generated,
dynamic character array. □

The constructor whose declaration is


ostrstream ( char* bu ff,
in t len,
in t mode = io s : : out ) ;
is used to associate an ostrstream object with the user-specified character
array b u ff of length len. The character array is opened in mode mode. The
default mode is output. If mode is io s : : app (append) or i o s : : ate (at end),
a null terminator ’ \0’ must occur in the array and the file position marker
is placed at this null terminator.

Exam ple 7.7.2. The statement


char name[ 10 ] ;
ostrstream sout( name, 10 ) ;
opens the character array name for output and creates an ostrstream object
sout associated with this character array. □

Exam ple 7.7.3. A safer version of the code of Example 7.7.2 is


char name[ 10 ] ;
ostrstream sout( name, s ize o f ( name ) ) ;

www.MathSchoolinternational.com
7.7 THE CHARACTER A R R A Y IN P U T / O U T P U T CLASSES 395

because, if we change the size of the array name, the definition is automati­
cally updated to the new size. □

The method pcount returns the number of characters currently stored


in the character array.

Example 7.7.4. The output from the code


ostrstream sout;
sout « "G od zilla vs. the Bionic Monster" « ends;
cout « sout.pcount( ) « endl;
is 32.
The manipulator ends writes a null terminator to the output. Notice
that the null terminator is included in the count 32. The statement
sout « "G od zilla vs. the Bionic Monster";
does not append a null terminator. □

The method s tr returns the address of the character array buffer. If


the buffer has been allocated by the system, the user is now responsible for
deallocating it.

Example 7.7.5. The output from the code


in t i = 6;
ostrstream sout;
sout « "Santa Claus Conquers " « i « 11 Martians" « ends;
char* ptr = s o u t.s tr( ) ;
cout « p tr « endl;
d elete [ ] p tr;
is
Santa Claus Conquers 6 Martians
After writing to the standard output, the storage for the character array
buffer is deallocated. □

is trs tre a m

The class istrstream is used to read formatted input into character arrays.
It performs somewhat the same functionality as the C function sscanf.
The entire declaration of the class istrstream is
class istrstream : public strstreambase, public istream {
p u b lic :
istrstream ( char* buff ) ;

www.MathSchoolinternational.com
CHAPTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

istrstream ( char* b u ff, in t len ) ;


"istrstream O ;
>;

It inherits public methods from istream to read and move within character
arrays. Notice that no default constructor is supplied; the user must specify
the character array to read.
The constructor whose declaration is
istrstream ( char* buff ) ;
is used to associate an istrstream object with the user-specified character
array buff. In this case, a null terminator marks the end of the data, and
this null terminator itself is never read. Here, buff is an ordinary null-
terminated C string.

Exam ple 7.7.6. The output from the code


flo a t x;
char* s = "37.805” ;
istrstream s in ( s ) ;
i f ( sin » x )
cout « "Input succeeded, x = " « x « endl;
else
cout « "Input fa ile d ." « endl;
i f ( sin » x )
cout « "Input succeeded, x = " « x « endl;
else
cout « "Input f a ile d . " « endl;
is
Input succeeded, x = 37.805
Input fa ile d .
The first time the expression
sin » x
is evaluated, the istrstream object sin reads 37.805 from the character ar­
ray to which s points into the flo a t variable x. Because the stream position
marker is now at the null terminator, the next attempt to read from s fails. □

The constructor
istrstream ( char* b u ff, in t len ) ;
is used to associate an istrstream object with the user-specified, len-byte,
character array buff. In this case, the byte at index len — 1 marks the end
of the data.

Exam ple 7.7.7. The output from the code

www.MathSchoolinternational.com
7.7 THE CHARACTER A R R A Y IN P U T / O U T P U T CLASSES 397

char c , s [ 5 ] ;
in t i ;
fo r ( i = 0, c = ’ a ’ ; i < 5;i++, C + + )
s [ i ] = c;
istrstream s in ( s, s iz e o f ( s ) ) ;
while ( sin » c )
cout « "Input succeeded, c = " « c « endl;
cout « "at end of array" « endl;
is
Input succeeded, c= a
Input succeeded, c= b
Input succeeded, c= c
Input succeeded, c= d
Input succeeded, c= e
at end of array

strstream

The class strstream is used to read and write formatted input and output
in character arrays.
The entire declaration of the class strstream is
class strstream : public strstreambase, public iostream {
p u b lic:
strstreamO ;
strstream ( char* b u ff, in t len, in t mode ) ;
“ strstreamO ;
char* s t r ( ) ;
>;
It inherits public methods from iostream to read, write, and move within
character arrays.
The default constructor is used when an strstream object is created but
a character array is not attached to the object. As for class ostrstream,
the system uses its own dynamically created character array buffer.
The constructor whose declaration is
strstream( chax* b u ff, in t len , in t mode ) ;
is used to associate an strstream object with the user-specified character
array buff of length len. The character array is opened in mode mode. If
mode is ios: :app (append) or io s : :ate (at end), a null terminator ’ \0’
must occur in the array and the stream position marker is placed at this null
terminator.

www.MathSchoolinternational.com
398 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

Exam ple 7.7.8. The statements


char name[ 80 ] ;
strcpy( name, "Hercules" ) ;
strstream sinout( name, s ize o f ( name ) ,
io s : : in | io s ::o u t | io s ::a t e ) ;
open the character array name for input and output and create an strstream
object sinout associated with this character array. Initially reading and
writing occur at byte 8, where the null terminator following Hercules is
stored. □

The method s tr behaves exactly like the method with the same name
in ostrstream.

Exercises

1. Rewrite the code of Example 7.7.5 replacing the system-generated char­


acter buffer with a user-defined character buffer.

2. Use an ostrstream object to convert a long value to a character string.

3. Use an istrstream object to convert a character string that represents


a long double value to type long double.

4. A character array contains


g 11 II II It

Use an appropriate object from this section to place the characters be­
tween the first pair of double quotes in the character array f i r s t and
the characters between the second pair of double quotes in the character
array second.

7.8 Sample Application: A High-Level Copy Function


P r o b le m ______________________________________________________________________

Write a function that copies from an arbitrary istream object to an arbitrary


ostream object.

S o lu tio n _______________________________________________________________________

We copy character by character using the overloaded » and « operators.


We must be careful to change the default from “skip white space” to “do

www.MathSchoolinternational.com
7.8 SAM PLE A PPLIC A TIO N : A HIGH-LEVEL C O P Y F U N C TIO N 399

not skip white space,” because we want to copy all of the characters— white
space or not. Before returning from the function, we restore the format flags.
After presenting the C + + implementation, we give several examples using
classes discussed in this chapter.

CH—b Im plem entation___________________________________________________ ___

# in clu d e < iostream .h >

// copy from is tre a m t o ostream


v o id cop y( istreamfe in , ostreamfe out )

char c;
lo n g f l ;

// don’ t s k ip w h ite space


// save o ld f l a g s in f l t o r e s t o r e a t end
f l = i n . u n s e t f ( io s ::s k ip w s ) ;

w h ile ( in » c )
out « c;

// r e s t o r e f l a g s
in .fla g s ( f l );
>

D iscussion _____________________________________________________ _______________

We present several main programs to show how copy can be used to copy
one object to another when the objects are in classes derived from is tre a m
and ostream.
The following code copies the standard input to the standard output:
# in clu d e < iostream .h >
# in clu d e < s t d lib .h >

v o id c o p y ( istream fe, ostreamfe ) ;

in t mainO
{
c o p y( c in , cout ) ;

re tu rn EXIT_SUCCESS;
}
The next code copies from the file data.in to the file data.out:

www.MathSchoolinternational.com
400 CHAPTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

#include <fstream.h>
#include <stdlib.h>

void copy( istreamfe, ostreamfe ) ;

int mainO
{
ifstream f i n ( "data.in " ) ;
ofstream fo u t( "data.out" ) ;

copy( fin , fout ) ;

return EXIT_SUCCESS;
>
The next code copies a character array to a character array and then
writes to the standard output to confirm the copy:
#include <strstream.h>
#include <stdlib.h>

void copy( istreamfe, ostreamfe ) ;

in t mainO
{
char b u ff[ 30 ] ;
istrstream s in ( "Junior G-Men of the A ir" ) ;
ostrstream sout( b u ff, s ize o f ( buff ) ) ;

copy( sin, sout ) ;


sout « ends;
cout « buff « endl;

return EXIT_SUCCESS;
}
Notice that it is necessary to append a null terminator because the null
terminator is not copied by the copy function.
Our last example copies an array to the file data. out-.
#include <fstream.h>
#include <strstream.h>
#include <stdlib.h>

void copy( istreamfe, ostreamfe ) ;

www.MathSchoolinternational.com
7.9 THE BUFFER CLASSES 401

int m a i n O
{
istrstream s in ( "Junior G-Men of the A ir" ) ;
ofstream fo u t( "data.out" ) ;

copy( sin, fout ) ;

return EXIT_SUCCESS;
>

Exercises

1. Write a main function that copies a file to an array.

2. Write a main function that copies an array to the standard output.

3. Write a main function that copies a file to the standard error.

4. Write a high-level copy function that converts each lowercase character to


an uppercase character and passes non-lowercase characters unchanged.

5. Write a high-level copy function that replaces each tab character by the
appropriate number of spaces. The copy function should have a third
parameter that describes the tab setting (e.g., if this parameter is 3, the
tabs are set at columns 4, 7,10,..., where column 1 is the first column).

7.9 The Buffer Classes


The classes
streambuf
file b u f
strstreambuf

provide support for the programmer to manipulate buffers. Class streambuf


is declared in iostream.h-, class file b u f is declared in fstream.h; and class
strstreambuf is declared in strstream.h. Class streambuf provides con­
structors and methods for setting up character arrays to use as buffers, for
moving around in buffers, and for reading from and writing to buffers. Class
file b u f, which is derived from streambuf, provides buffered access to files.
Class strstreambuf, which is also derived from streambuf, provides access
to character arrays. The high-level classes discussed previously in this chap­
ter make use of these buffer classes (recall that class ios contains a pointer
to a buffer object) and provide a transparent user interface to the buffer

www.MathSchoolinternational.com
402 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

classes. For this reason, many programmers never directly use these buffer
classes.
Each object in a buffer class uses a character array as a buffer. We refer
to this array as a character buffer and the classes or objects themselves as
buffer classes or buffer objects.

streambuf

Class streambuf is the base buffer class. The default constructor creates
a streambuf object without assigning a character buffer. The constructor
whose declaration is
streambuf( char* b u ff, in t len ) ;
creates a streambuf object, which uses the len-byte array bu ff as a char­
acter buffer.
The methods
streambuf* setbuf( signed char* b u ff, in t len ) ;
streambuf* setbu f( unsigned char* b u ff, in t len ) ;
associate a streambuf object, created with the default constructor, with the
len-byte array buff, which serves as the character buffer.
The method
in t in _ a v a il();
returns the number of characters remaining in the input character buffer and
the method
in t o u t_ a v a il();
returns the number of characters remaining in the output character buffer.
The method
in t sbumpcO;
returns the current character in the input character buffer and advances the
buffer position marker, which designates the character to read or write, one
byte. If sbumpc fails (e.g., we are at the end of the file), it returns EOF. This
low-level method resembles the high-level method get in class istream.
The method
in t sg etcO ;
returns the next character in the character buffer. The buffer position marker
is unchanged. If sgetc fails, it returns EOF. This low-level method resembles
the high-level method peek in class istream.
The method
in t sgetn( char* store, in t n ) ;
returns the next n characters from the character buffer and stores them in
store. It returns the number of characters actually retrieved. The buffer

www.MathSchoolinternational.com
THE BUFFER CLASSES 403

position marker is moved the number of bytes retrieved. This low-level


method resembles the high-level method read in class istream.
The method
in t snextcO;
advances the buffer position marker one byte and returns the character in
the character buffer. If snextc fails, it returns EOF.
The method
in t sputbackc ( chair c ) ;
returns the character c to the character buffer. The value returned by
sputbackc is EOF on error. This low-level method resembles the high-level
method putback in class istream.
The method
in t sputc( in t c ) ;
writes the character c to the output character buffer. It returns EOF on
error. This low-level method resembles the high-level method put in class
ostream.
The method
in t sputn( const chair* store, in t n ) ;
writes n characters, beginning at address store, into the character buffer.
It returns the number of characters actually placed into the character buffer.
This low-level method resembles the high-level method w rite in class
ostream.
The method
void stosscO ;
advances the buffer position marker one byte.
The method
streampos s e e k o ff( streamoff o f f ,
i o s : : seek_dir d ir ,
int pt = ( i o s : : in | i o s : : out ) ) ;
moves the input or output stream position marker (or both) o f f bytes from
dir, which must be one of the following: beg (from the beginning of the
stream), cur (from the current position), or end (from the end of the stream).
If pt is io s: :in, the input marker is moved; if pt is io s : :out, the output
marker is moved; and if pt is i o s : : in I i o s : : out, both markers are moved.
The method
streampos seekpos( streampos o f f ,
int pt = ( i o s : : in | i o s : : out ) ) ;
moves the input or output stream position marker (or both) to position o ff.
As in method seek o ff, the second argument determines which markers are

www.MathSchoolinternational.com
404 CHAPTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

moved. The low-level methods seekof f and seekpos resemble the high-level
methods seekg and seekp in classes istream and ostream.
We defer examples to the next subsections, which consider the derived
classes file b u f and strstreambuf, as it is these classes that provide the
facilities for associating buffer objects with files and character arrays.

file b u f

The class file b u f is publicly inherited from streambuf:


class file b u f : public streambuf {

>;
It provides buffered access to files.
The default constructor creates a file b u f object that is not associated
with any file.
The method open
file b u f* open( const char* filename,
in t mode,
in t pr = f i l e b u f : : openprot ) ;
opens the file filename in mode mode and associates it with the file b u f
object. The file receives default protection openprot if a protection is not
specified.

Exam ple 7.9.1. The code


file b u f fin ;
fin .o p en ( "d a ta .in ", io s ::in )
creates the file b u f object fin . It then opens the file data.in for input and
associates this file with the object fin . □

The method setbuf


streambuf* setbu f( char* b, in t len ) ;
establishes the array b as the character buffer for the file b u f object.

Exam ple 7.9.2. The code


file b u f fou t;
char b [ 256 ] ;
fo u t.s e tb u f( b, sizeof ( b ) ) ;
fout.open( "data.out", i o s : :out ) ;
creates the file b u f object fout. It next establishes the array b as the char­
acter buffer for fout. It then opens the file data, out for output and associates
this file with the object fout. Output to data.out takes place through the
buffer b. □

www.MathSchoolinternational.com
THE BUFFER CLASSES 405

In Example 7.9.1, the system supplies a character buffer because none


was specified. In Example 7.9.2, if the last two lines are interchanged
file b u f fou t;
char b [ 256 ] ;
fout.openC "data.ou t", io s ::o u t )
fo u t.setb u f( b, s iz e o f ( b ) ) ;
the system supplies its own character buffer. The statement
fout.open( "data.ou t", io s ::o u t )
establishes a system-defined character buffer and the next statement
fo u t.setb u f( b, s iz e o f ( b ) ) ;
is, in effect, ignored.

Example 7.9.3. The following program copies file data.in to data.out:


#include <fstream.h>
#include <stdlib.h>

const in t b u ffs ize = 256;

in t mainO
{
char in b u ff[ b u ffs ize ] , ou tb u ff[ b u ffs iz e ] ;
file b u f f in , fou t;
in t c;

fin .s e tb u f( in b u ff, s iz e o f ( inbuff ) ) ;


fin .op en ( "d a ta .in ", io s : : in ) ;

fo u t.s e tb u f( outbuff, s iz e o f ( outbuff ) ) ;


fout.openC "data.ou t", io s ::o u t ) ;

c = f in .s g e tc O ;

while ( c != EOF ) {
fou t.sp u tc( c ) ;
c = fin .s n e x tc O ;
}

return EXIT_SUCCESS;
>
After setting up the character buffers inbuff and outbuff and opening
the files, we read a character from the input character buffer inbuff, but
the input buffer position marker does not move:

www.MathSchoolinternational.com
CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

c = fin .s g e tc O ;
If we are not at the end of the file, we enter the body of the while loop. We
write one character to the output character buffer out b u ff and move one
byte in the input character buffer and return that character:
fou t.sp u tc( c ) ;
c = f in. snextcO ;
We continue until all of the characters have been read from the input file. □

The method
file b u f* c lo s e O ;
closes the file associated with the file b u f object and flushes the character
buffer. The destructor also closes the file and flushes the character buffer.

Exam ple 7.9.4. When the program of Example 7.9.3 terminates, the de­
structor is automatically called for fin and fout. The destructor closes the
files. The destructor for fout flushes the output character buffer; that is,
data still in the output character buffer but not yet written to the file is now
written to the file. □

The methods overflow and underflow take action when the character
buffer is full or empty. The method
in t overflow ( in t stop = EOF ) ;
flushes the character buffer. More precisely, it writes all the data in the
character buffer, up to but not including stop, to the destination file.
The method
in t underflowO;
reads data from the source file into the character buffer.

Exam ple 7.9.5. In this example, we look more closely at what happens
when the code of Example 7.9.3 executes. For the purposes of illustration,
we assume that b u ffsize is 8, rather than 256.
When we first execute
c = fin .s g e tc O ;
the input character buffer is empty, so method underflow is invoked and
the input character buffer is filled (see Figure 7.9.1).
Next we execute
fou t.sp u tc( c ) ;
and c is written to the output character buffer (see Figure 7.9.2). We then
execute
c = fin .sn ex tcO ;

www.MathSchoolinternational.com
7.9 THE B UFFER CLASSES 407

d a ta .in : I am not a crook!


Buffer underflow
position
marker
inbuff
i
I a m L-. n 0 t

sg e t c

Figure 7.9.1 Result of invoking fin .s g e tc Q .

Buffer
position ------------
marker
outbuff
I

sputc

e I

Figure 7.9.2 First call of fou t.sp u tc( c ).

Buffer
position
marker
I - a m L-J n O t

^^snextc

Figure 7.9.3 First call of fin .sn ex tcQ .

www.MathSchoolinternational.com
408 CHAPTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

Buffer
position ■
marker
int>uff
I a m i_j n 0 t

snextc

sputc

o u t b u ff
I a m n 0 t
Buffer
position
marker

Figure 7.9.4 Full buffers.


data.in: I am not a crook!
Buffer underflow
position
marker
in b u ff
i
- a c r 0 0 k

snextc

I
Figure 7.9.5 Refilling the input buffer the first time.

and the next character in the input character buffer is read (see Figure 7.9.3).
Figure 7.9.4 shows the situation somewhat later—just after we execute
fo u t.sp u tc( c ) ;
for the eighth time. At this point, both the input and output character
buffers are full. When we next execute
c = fin .sn e xtcO ;
no characters are available in the input character buffer, so method under­
flow is invoked and the input character buffer is refilled (see Figure 7.9.5).
Next we execute
fo u t.sp u tc( c ) ;
Since the output character buffer is full, the method overflow is invoked
and the output character buffer is flushed (see Figure 7.9.6).
Figure 7.9.7 shows the situation when the statement
c = fin .sn e xtcO ;
is executed and the last character ! is read; underflow again occurs. Next
fo u t.sp u tc( c ) ;

www.MathSchoolinternational.com
7.9 THE BUFFER CLASSES 409

Buffer
position
marker
o u tb u ff
1 a m n 0 t
-

overflow

i
d a ta .o u t : I am not

Buffer
position '
marker

i
o u tb u ff
L-J a m n 0 t

spu tc

Figure 7.9.6 Flushing the output buffer the first time.

d a ta .in : I am not a crook!

Buffer ) underflow
position
marker
in b u ff
! a c r 0 0 k

s n e x tc

A!

Figure 7.9.7 Refilling the input buffer the last time.

is executed; overflow occurs and the output character buffer is flushed (see
Figure 7.9.8).
When we next execute

c = fin .s n e x tc O ;
since we are at the end of the file, snextc returns EOF and the w h ile loop
terminates. When the program terminates and the destructor for fo u t is
called, the output character buffer is flushed and the last character ( ! ) is
written to the file data.out (see Figure 7.9.9).
If the program terminated without the destructor for fout being called,
the output character buffer would not be flushed the last time and data.out
would contain
I am not a crook
For example, the destructor would not be called if the program terminated
with a call to exit:

www.MathSchoolinternational.com
410 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

Buffer
position ■
marker
o u t b u ff
a c r 0 0 k

H "
data.out: I am n ot a crook!

Buffer
position
marker
o u t b u ff
i
] a c r 0 O k

sputo
1

Figure 7.9.8 Flushing the output buffer the second time.

Buffer
position
marker
outbuff
1 a c r 0 O k

Flushing via destructor


J
data.out: I am not a crook!

Figure 7.9.9 The result of the destructor flushing the output buffer.

www.MathSchoolinternational.com
7.9 THE BUFFER CLASSES 411

in t mainO
{

while ( c != EOF ) {
fout.sputc( c );
c = fin.snextc();
}
e x it ( EXIT.SUCCESS ) ;
>

The method
in t is _ o p e n ();
returns nonzero if the file is open and zero if the file is not open.
The method seekoff behaves exactly like the method with the same
name in streambuf.

strstream buf

The class strstreambuf is publicly inherited from streambuf:


class strstreambuf : public streambuf {

>;
It provides access to character buffers.
The default constructor creates an strstreambuf object that uses dy­
namic storage allocation; that is, the character buffer is allocated as needed.
Similarly, the constructor
strstreambuf( in t n ) ;
creates an strstreambuf object that uses dynamic storage allocation begin­
ning with a character buffer of n bytes. (If necessary, the buffer is expanded.)
Dynamic buffers are useful for output.
Each of the constructors
strstreambuf( signed char* b,
in t le n ,
signed chair* s = 0 ) ;
strstreambuf( unsigned char* b,
in t len,
unsigned chair* s = 0 ) ;
creates an strstreambuf object that uses the static len-byte character
buffer b. Static buffers are useful for input.

Example 7.9.6. The following program modifies the file-copying program


of Example 7.9.3. This program uses strstreambuf objects to copy an array.

www.MathSchoolinternational.com
412 CHAPTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

The program prints the contents of the destination array outbuff to confirm
that the input array inbuff was copied.
#include <strstream.h>
#include <string.h>
#include <stdlib.h>

in t main()
{
char in b u ff[ ] = "Magical Mystery Tour",
ou tb u ff[ 25 ] ;

strstreambuf s in ( inbu ff, s trle n ( inbuff ) ) ;


strstreambuf sout( outbuff, s iz e o f ( outbuff ) ) ;
in t c;

c = sin .sgetcO ;

while ( c != EOF ) {
sout.sputc( c ) ;
c = sin. snextcO ;
}

fo r ( in t i = 0; i < s trle n ( inbuff ) ; i++ )


cout « ou tb u ff[ i ] ;
cout « endl;

return EXIT_SUCCESS;
>

The method
void fr e e z e ( in t fla g = 1 ) ;
with a nonzero argument, freezes the output— no more characters can be
written to the character buffer. With argument zero, the output is unfrozen.
The methods seekof f and s tr behave exactly like the methods with the
same names in streambuf.
The methods overflow and underflow take action appropriate to the
type of character buffers defined— dynamic or static.

www.MathSchoolinternational.com
COMMON PRO G R A M M IN G ERRORS 413

Exercises

1. Write statements that open the file balance.dat for input and output,
associate it with the file b u f object finout, and use a 256-byte user-
supplied character buffer.

2. Using class file b u f, write a complete program that copies files in the or­
der listed on the command line (the first file— the executable— is skipped)
to the standard output. Files that cannot be opened for reading are ig­
nored.

3. Rewrite Example 7.9.3. Change b u ffs ize to 8 and print the contents of
inbuff and outbuff at various points so that the process of reading and
writing the character buffers is clarified.

4. Use class strstreambuf to write a function h i_ to _ lo that receives a null-


terminated array of char and changes each uppercase letter to lowercase.
(Non-uppercase letters are unchanged.)

5. The C + + class library has separate classes istream and ostream for
high-level input and output. Why do you think that the C + + designers
declared a single buffer class streambuf for both input and output?

Com m on Program m ing Errors

1. It is an error to attempt to use any of the input/output classes

ios
streambuf
istream
ostream
iostream
istream_withassign
ostream_withassign
iostream_withassign

or any of the system manipulators with no parameters (e.g., endl) with­


out including at least one of the header files iostream.h, fstream.h, or
strstream.h.

2. It is an error to attempt to use any of the file input/output classes

www.MathSchoolinternational.com
414 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

file b u f
fstreambase
ifstream
ofstream
fstream

without including the header file fstream.h.

3. It is an error to attempt to use any of the character array input/output


classes

strstreambuf
strstreambase
istrstream
ostrstream
strstream

without including the header file strstream. h.

4. It is an error to attempt to use any of the system manipulators with


parameters (e.g., s e tf i l l ) without including the header file iomanip.h.

5. If the programmer writes a manipulator without parameters using the


method of Section 7.4, the header file iostream.h must be included.

6. If the programmer writes a manipulator with parameters using the method


of Section 7.4, the header file iomanip.h must be included.

7. The flag e o fb it in class ios is set by attempting to read beyond the end
of the file. Simply reading the last byte does not set e o fb it. For this
reason, the following code that attempts to echo integers in the standard
input to the standard output is incorrect; an extra line is printed:

in t i ;

// * * * * * ERROR: extra lin e is printed


while ( Ic in .e o fO ) {
cin » i ;
cout « i « endl;
>

A correct version is

in t i ;

do {
cin » i;

www.MathSchoolinternational.com
COM M ON PR O G R A M M IN G ERRORS 415

if
( Ic in .e o fO )
cout « i « endl;
} while ( Ic in .e o fO ) ;

8. The method fla g s with one argument in class ios sets the format flags
to the value passed, whereas method s e tf, also in class ios, sets specified
flags without changing the other flags. For example,

c o u t.fla g s ( i o s : : showpoint ) ;

sets the flag that causes subsequent floating-point numbers to be printed


with trailing zeros and clears all other format flags. The statement

cout. s e t f ( i o s : : showpoint ) ;

also sets the flag that causes subsequent floating-point numbers to be


printed with trailing zeros but does not modify any other format flag. In
many applications, s e tf is more useful than fla gs.

9. Do not assume that after an output statement is executed, all format


attributes revert to their default status. Except for the field width, which
can be changed using method width in class ios or the manipulator setw,
format attributes remain in effect until modified, even through multiple
statements. For example, the output from the following code

cout « hex « setw(8 ) « endl;


cout « 200 « endl;
cout « 200 « endl;

is

c8
c8

In the second line of output, the integer is still written in hexadecimal,


but the field width reverts to zero.

10. When the versions of methods seekg and seekp whose declarations are

istreamfe seekg( stream off, seek_dir ) ;


ostreamfe seekp( stream off, seek_dir ) ;

are used with files, the files should be opened as binary files. These
methods may not work properly if they are used with files which were
not opened in binary mode.
When the versions of methods seekg and seekp whose declarations are

istreamfe seekg( streampos ) ;


ostreamfe seekp( streampos ) ;

www.MathSchoolinternational.com
416 CHAPTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCHY

are used with files, the files can be opened as binary or non-binary files.
The argument should be a value returned by t e l l g (for seekg) or t e llp
(for seekp).

11. When the overloaded operator » is used to read a character, the default
action is to skip white space. Do not assume (as in C) that the next
character, white space or not, is read.

12. When the overloaded operator » is used to read a string and the field
width is set to n ^ 0, do not assume that the next n characters, white
space or not, are read and stored. The default action is to skip white
space and then read and store all non-white space characters up to the
next white space character, or n — 1 characters, whichever occurs first. A
null terminator is always added. As examples, if the standard input is

uuuPepper

the code

chair a [ 10 ] ;
cin » setw( 6 ) » a;

reads and stores

Peppe

followed by ’ \0' in the array a. If the standard input is

uuuDruPepper

the code

chax a [ 10 ] ;
cin » setw( 6 ) » a;

reads and stores

Dr

followed by ’ \0 ’ in the array a.

13. When overloading » for input, the first argument should be of type
istreamfe and the second argument should be the user-defined type. The
return type should be istreamfe. The type istreamfe is necessary to
correctly update the istream object passed.

14. When overloading « for output, the first argument should be of type
ostreamfe and the second argument should be the user-defined type. The
return type should be ostreamfe. The type ostreamfe is necessary to
correctly update the ostream object passed.

www.MathSchoolinternational.com
COM M ON PR O G R A M M IN G ERRORS 417

15. The code for a manipulator must return the modified stream. For this
reason,

void b e l l ( ostreamfe os )
{
// * * * * * ERROR: Must return the stream
os « "\a";
>

is an error. A correct version is

ostreamfe b e l l ( ostreamfe os )

return os « "\a";
>

16. It is an error to omit the mode when opening a file and associating it
with an fstream object

fstream f i o ;

// * * * * * ERROR: No mode
fio .o p e n ( "p a y ro ll.d a t" ) ;

since no default mode is supplied. The mode can be omitted for ifstream
and ofstream objects; the default mode for an ifstream object is input,
and the default mode for an ofstream object is output.

17. It is an error to fail to supply a character buffer when an istrstream


object is created. (The buffer supplied contains the input.) The pro­
grammer may or may not supply a character buffer when an ostrstream
object is created. If the programmer does not supply a character buffer
for the output, the system supplies a dynamic character buffer.

18. When a string is output by an ostrstream object using the « operator,


a null terminator is not added to the output. For this reason

ostrstream s;
s « "Wow";
char* p = s . s t r ( ) ;
I I * * * * * ERROR: No null terminator
cout « p;

The error can be corrected by replacing the line

s « "Wow";

by

www.MathSchoolinternational.com
418 CH APTER 7 THE C + + IN P U T / O U T P U T CLASS HIERARCH Y

s « "Wow" « ends;

19. The method sgetc in class streambuf returns the next character in the
character buffer but does not change the buffer position marker. For this
reason,

file b u f fin , fou t;

// *** * * ERROR: In fin it e loop


while ( ( c = fin .s g e t c () ) != EOF )
fou t.sp u tc( c ) ;

is an infinite loop. See Example 7.9.3 for a correct version.

20. When supplying a buffer for file input/output, set the buffer before open­
ing the file:

// Uses programmer-supplied b u ffer b


file b u f fou t;
char b [ 256 ] ;
fo u t.s e tb u f( b, 256 ) ;
fout.open( "winnings.dat", io s ::o u t ) ;

If the last two lines are reversed, the programmer’s buffer b is ignored:

// Uses system-supplied bu ffer


file b u f fou t;
char b [ 256 ] ;
fout.open( "winnings.dat", io s ::o u t ) ;
fo u t.s e tb u f( b, 256 ) ;

21. The output file character buffer is not flushed if the file is not closed.
The file may be closed by using the method close or passively by the
destructor.

22. The programmer must supply a character buffer for input to an s tr ­


streambuf object. The programmer may or may not supply a character
buffer for output from an strstreambuf object. If the programmer does
not supply a character buffer for output from an strstreambuf object,
the system supplies a dynamic character buffer.

Programming Exercises

7.1. Modify the random access file class of Section 7.6 in the following manner.
Add another field to the header, which flags the file as a random access

www.MathSchoolinternational.com
PRO G RAM M ING EXERCISES 419

file, by writing RA in the first two bytes of the header. When an existing
file is opened, this flag is checked. If the file is the wrong type, it is closed
and an error condition is set.

7.2. Provide implementations of fla g s, se tf, and unsetf (see Section 7.2).

7.3. Write an IMANIP template (see Section 7.4).

7.4. Derive a sequential file class from fstream (see Section 7.6).

7.5. Derive an indexed file class from fstream (see Section 7.6). The un­
derlying data structure should be a B-tree [see, e.g., L. Nyhoff and S.
Leestma, Data Structures and Program Design in Pascal, 2nd ed., (New
York: Macmillan, 1992)]. The indexed file class should allow the following
operations:

• Direct access to a record through the key.


• Sequential reads.
• Adding, deleting, or updating records.

7.6. Develop a class that serves as an interface to the frandom class of Section
7.6. The class should contain a pointer to another class that describes a
record. The record must contain a member of type char*, which serves
as the key.

7.7. Install assertions in the frandom class (see Section 7.6).

7.8. Write a high-level (in the sense of Section 7.8) encryption function based
on Huffman codes [see, e.g., L. Nyhoff and S. Leestma, Data Structures
and Program Design in Pascal, 2nd ed., (New York: Macmillan, 1992)].

7.9. Rewrite the frandom class (see Section 7.6) using buffer classes directly.

7.10. Derive a class scrn_out from ostream that provides output in specified
colors to specified parts of the screen.

www.MathSchoolinternational.com
www.MathSchoolinternational.com
Chapter 8

Advanced Topics

8.1 Exception H andling


8.2 R u n -T im e T yp e Identification
8.3 Nam espaces
8.4 Other O bject-O riented Languages
8.5 N e w Issues

421
www.MathSchoolinternational.com
422 CHAPTER 8 ADVANCED TOPICS

In this final chapter, we discuss several advanced topics. Exception handling


is a recent addition to C + + and lets the programmer deal with certain er­
ror conditions. Run-time type identification and namespaces are even more
recent additions to C ++. Run-time type identification allows type conver­
sions that are checked at run-time, provides an operator that describes the
run-time type of an object, and allows the user to extend run-time type
identification beyond that provided in the language. Namespaces provide a
way to distinguish among identical global names. Since these features have
been recently added to the language, they may not be generally supported
by existing C + + compilers.
We briefly introduce some other important object-oriented languages:
Smalltalk, Eiffel, and Objective C. We conclude by discussing several new
issues involving the object-oriented paradigm.

8.1 Exception Handling


An exception is a run-time error caused by some abnormal condition, for
example, an out-of-bounds array index. In C + + , it is possible for a function
f to define conditions that identify exceptions. Another function g that calls
f can, optionally, elect to test whether the exceptions defined by f occur
while the program executes. Further, g can provide its own handlers to deal
with these exceptions should they occur. We illustrate with an example.

Exam ple 8.1.1. The following code


class strin g {
char* s;
p u b lic :
enum { minSize = 1, maxSize = 1000 } ;
s tr in g ( ) ;
s tr in g ( int ) ;

>;

s t r i n g : : s tr in g ( int size )
{
// define "out of bounds" exception and throw i t
i f C size < minSize || size > maxSize )
throw( size ) ;
s = new chart size ] ;
// define "out of memory" exception and throw it
i f ( s == 0 )
throw( "Out of Memory" ) ;
>

www.MathSchoolinternational.com
E X C E PTIO N HANDLING 423

void f ( in t n )
{
try {
strin g s t r ( n ) ;
>

// handlers provided in case


// strin g s t r ( n ) ;
// f a i l s
catch( char* errMsg )
{
cerr « errMsg « endl;
abort ( ) ;
}

catch( in t k )
{
cerr « "Out of range error: " « k « endl;
f ( string::m axSize ) ;
>

// The code fo r f whichlo g ic a lly follow s the statement


// strin g s t r ( n ) ;
// goes here. This code w i l l be executed immediately
// a fte r
// strin g s t r ( n ) ;
// i f no exception is raised. I f anexception was
// raised, th is code w i l l be executed a fte r
// the exception that occurred has been handled.
// I f the exception was not handled, the program
// w ill terminate.
>
illustrates the C + + technique for defining and handling exceptions.
Code to detect an exception can be included in a function in which the
exception might occur. For example, the constructor

s t r in g :: s tr in g ( in t )

contains the line

if ( s ize < minSize I I s ize > maxSize )

that checks for an out-of-bounds argument to the constructor. If the out-of-


bounds exception occurs, it can be thrown by using the throw operator:

throw( size ) ;

www.MathSchoolinternational.com
424 CHAPTER 8 ADVANCED TOPICS

(throw is a keyword.) The throw operator requests action from a handler


to cope with the exception.
A function that uses the constructor
s t r in g : : s tr in g ( in t )
and is willing to handle exceptions defined by the constructor must indicate
this willingness by placing the code in which an out-of-bounds exception
might occur in a tr y block, (try is a keyword.) In our example, the function
f is willing to handle an out-of-bounds argument to the constructor so it
includes the tr y block
tr y {
strin g s t r ( n ) ;
>
When this tr y block is executed and no exception occurs, execution
continues with the code that follows the catch blocks, (catch is a keyword.)
If n is out of bounds, the exception is thrown because of the statement

throw( size ) ;
in the constructor. The exception is caught by the catch block whose argu­
ment type matches that of the thrower. Since size, the throw argument, is
of type int, the exception is caught by the catch block

catch( in t k )

cerr « "Out of range error: " « k « endl;


f ( s t r in g ::maxSize ) ;
>
whose argument is of type int. An error message is printed and then exe­
cution of f is restarted with the argument strin g: :maxSize.
When the tr y block is executed and the out-of-memory exception occurs,
the exception is thrown because of the statement

throw( "Out of Memory" ) ;


in the constructor. The exception is caught by the catch block whose argu­
ment type matches that of the thrower. Since the argument
"Out of Memory"
is of type char*, the exception is caught by the catch block
catch( char* errMsg )
■c
cerr « errMsg « endl;
a bort( ) ;
>

www.MathSchoolinternational.com
E X C E PTIO N HANDLING 425

whose argument is of type char*. An error message is printed and then the
library function abort, which terminates the program, is invoked. □

An exception may be rethrown by using the statement


throw;

Exam ple 8.1.2. In the code


void f ( )

tr y {

}
catch( in t i )

if ( ... )

else
throw;
}
}

void g ()

tr y {
f();
>
catch( in t k )

>

>
g calls f , which is in a try block. When f executes, if an in t exception is
raised in f ’s tr y block, f ’s handler
catch( in t i )

if ( ... )

e ls e
throw;
>
is invoked. If the condition in the i f statement is false, the exception is
rethrown. Since f now terminates and returns to g, the rethrown exception

www.MathSchoolinternational.com
426 CH APTER 8 ADVANCED TOPICS

is now handed off to g’s handler:


catch( in t k )
{

>

If a function throws an exception and no handler is defined, the function


terminate whose declaration is
void term inate( ) ;
is automatically invoked. The default action of terminate is to call abort,
but this default action can be changed by using the function set_terminate
whose declaration is
typedef v o id ( *PFV ) 0 ;
PFV set_term inate( PFV ) ;
In this case, terminate invokes the function specified in the most recent call
of set ..terminate.
A function may specify exceptions that it may throw. For example, the
declaration
void f ( ) throw( t l , t2 , t3 ) ;
states that f may throw exceptions of type t l , t2, and t3, exceptions derived
from these types if the type is a class, and no others. The function f does
not guarantee that it will throw these exceptions— only that it may throw
these exceptions. When no types are listed in the throw expression
void f ( ) throw( ) ;
f cannot throw any exception. When the throw statement is missing in the
declaration (as in Example 8.1.1), an exception of any type can be thrown.
An attempt to throw an exception not in the throw list (if present)
is an error and results in a call of the library function unexpected whose
declaration is
void unexpectedO ;
The default action of unexpected is to call terminate, but this default
action can be changed by using the function set_unexpected whose decla­
ration is
typedef v o id ( *PFV ) ( ) ;
PFV set_unexpected( PFV ) ;
In this case, unexpected invokes the function specified in the most recent
call of set .unexpected.

www.MathSchoolinternational.com
8.2 R U N -TIM E T Y P E ID E N TIFIC A TIO N 427

Assertions, discussed in Section 3.8, provide another means of dealing


with exceptions. Preconditions, postconditions, and class invariants are in­
troduced that must be satisfied in order for the code to be correct. If a
precondition, postcondition, or class invariant fails, the code is incorrect
and so the program terminates with a message. One problem with asser­
tions is that they do not permit the program to attempt to recover from the
violation and continue executing.
Standard C through the header file signal, h provides yet another method
of dealing with certain kinds of exceptions [see, e.g., R. Johnsonbaugh and M.
Kalin: Applications Programming in A N SI C, 3rd ed., (New York: Macmil­
lan, 1995), Appendix F]. Signals can be used to handle exceptions such as
keyboard interrupts that are external to the program (such exceptions are
called asynchronous exceptions). The C + + exception handling mechanism
handles only exceptions that result from executing the C + + code itself (such
exceptions are called synchronous exceptions).

Exercises

1. What will happen in Example 8.1.1 if the lines

catch( in t k )

cerr « "Out of range error: 11 « k « endl;


f ( string::m axSize ) ;
>

are removed from the function f and f is invoked with argument 2000?

2. What will happen in Example 8.1.1 if the constructor is changed to

s t r in g : : s trin g ( in t size ) throw( char* )

>

and the function f is invoked with argument 2000?

8.2 Run-Time Type Identification


Run-tim e type identification provides mechanisms to permit type con­
versions that are checked at run-time, to determine the type of an object at
run-time, and to allow the user to extend the run-time type identification
provided by C ++.

www.MathSchoolinternational.com
428 CH APTER 8 ADVANCED TO PIC S

T h e dynamic_cast O perator

C + + provides the dynamic_cast operator to perform safe type conversions


at run-time for classes with virtual functions. The operator is invoked as
dynamic_cast< T* >( p )
where p is a pointer and T is a type. If p points to an object of type T or
to an object in a class derived from T, the value of the expression is p and
the type is T*. If p does not point to an object of type T or to an object
in a class derived from T, the value of the expression is zero. The action of
the dynamic cast operator is twofold: it determines whether the conversion
of p to T* is valid and, if it is valid, it performs the conversion. Thus the
dynamic cast operator is a safe method of performing casts. Its principal
use is to a perform safe cast from a base class to a derived class.

Example 8.2.1. Given the declarations


class Book {
p u b lic:
v irtu a l void p r i n t _ t i t l e ( ) ;
};

class Textbook : public Book {


p u b lic:
void p r i n t _ t i t l e ( ) ;
v irtu a l void p r i n t _ l e v e l ( ) ;
>;

class Paperback : public Book {


p u b lic:
void p r i n t _ t i t l e ( ) ;
>;
the dynamic cast operator can be used to print appropriate information
about an arbitrary book:
void print_book_info( Book* book_ptr )
{
Textbook* p tr = dynamic_cast< Textbook* >( book_ptr ) ;

if ( p tr ) { // used i f book_ptr points to Textbook


p tr -> p r i n t _ t i t l e ( ) ;
p tr -> p r i n t _ l e v e l ( ) ;
>
else // used i f book_ptr does not point to a Textbook
book_ptr -> p r i n t _ t i t l e ( ) ;
}

www.MathSchoolinternational.com
R U N -TIM E T Y P E ID E N TIFIC A TIO N 429

In the function print_book_info, if at run-time the pointer book_ptr


points to a Textbook object, the dynamic cast operation
Textbook* p tr = dynamic_cast< Textbook* >( book_ptr ) ;
succeeds and the address of the Textbook object is stored in ptr. In this
case, the methods of class Textbook are used to print information about the
Textbook:
p tr -> p r i n t _ t i t l e ( ) ;
p tr -> p r in t _ le v e l();
If, on the other hand, at run-time the pointer book_ptr does not point to a
Textbook object, the dynamic cast operation fails and zero is stored in ptr.
In this case, the v irtu a l method p r in t _ t it le is used:
book_ptr -> p r i n t _ t i t l e ( ) ;

Exam ple 8.2.2. A dynamic cast expression can also be used as a condition.
For example, the function print_book_inf o can be rewritten as:
void print_book_info( Book* book_ptr )

if ( Textbook* ptr
= dynamic_cast< Textbook* >( book_ptr ) ) {
p tr -> p r i n t _ t i t l e ( ) ;
p tr -> p r i n t _ le v e l();
}
els e
book_ptr -> p r i n t _ t i t l e ( ) ;
>

An alternative to the technique of Example 8.2.1 is to change the decla­


ration of the classes and simply use polymorphism.

Exam ple 8.2.3. We revise the code of Example 8.2.1 to use polymorphism
to achieve the same effect:
class Book {
p u b lic :
v irtu a l void p r i n t _ t i t l e ( ) ;
v irtu a l void p r in t _ le v e l();
>;

class Textbook : public Book {


p u b lic:

www.MathSchoolinternational.com
430 CHAPTER 8 ADVANCED TO PICS

void p r i n t _ t i t l e ( ) ;
void p r i n t _ l e v e l ( ) ;
>;

class Paperback : public Book {


p u b lic:
void p r i n t _ t i t l e ( ) ;
void p r i n t _ l e v e l ( ) ;
>;

void print_book_info( Book* book_ptr )


{
book_ptr -> p r i n t _ t i t l e ( ) ;
book_ptr -> p r i n t _ l e v e l ( ) ;
}

The code of Example 8.2.3 is cleaner than that of Example 8.2.1 and is
preferable. In Example 8.2.3, the use of v ir tu a l functions automatically
takes care of the details of determining which versions of p r in t _ t it le and
p r in t_ le v e l to use. If, however, the programmer cannot modify the classes,
the technique of Example 8.2.1 may be the only safe way to obtain the desired
result.
T h e typeid O perator
The typeid operator returns a reference to an object in the library class
Type_info that describes the run-time type of an object. When using the
typeid operator, the header file TypeJnfo.h must be included.
The typeid operator is invoked as
type id ( typename )
or
type id ( expression )
If the operand of the typeid operator is the type typename, typ eid returns
a reference to a Type_inf o object that represents typename. If the operand
of the typeid operator is the expression expression, typeid returns a ref­
erence to a Type_info object that represents the expression’s type. The
programmer need not know the details of the return type to use the typeid
operator to compare types, as the following examples illustrate.

Exam ple 8.2.4. The typeid operator can be used with built-in types and
operators. Figure 8.2.1, which assumes the definitions
flo a t x;
long va l;

www.MathSchoolinternational.com
8.2 R U N -TIM E T Y P E ID E N TIFIC A TIO N 431

Expression Value
ty p eid ( x ) == ty p e id ( flo a t ) True
ty p eid ( x ) == ty p e id ( double ) False
typ eid ( x ) == typ eid ( flo a t * ) False
ty p eid ( v a l ) == ty p e id ( long ) True
typ eid ( v a l ) == ty p eid ( short ) False
typ eid ( 5280 ) == typ eid ( in t ) True
ty p e id ( 9.218836E-9L ) == typ eid ( long double ) True

Figure 8.2.1 Using the typeid operator to test run-time built-in types.

Expression Value
ty p e id ( book_ptr ) == ty p e id ( Book* ) True
typ eid ( book_ptr ) == typ eid ( Book ) False
ty p eid ( *book_ptr ) == ty p e id ( Book ) False
typ eid ( book_ptr ) == typ eid ( Textbook* ) False
ty p eid ( book_ptr ) == ty p e id ( Textbook ) False
ty p eid ( *book_ptr ) == ty p e id ( Textbook ) True

Figure 8.2.2 Using the typeid operator to test run-time class types.

shows the values of several expressions that use the typeid operator. □

Exam ple 8.2.5. Figure 8.2.2, which assumes the declarations of Example
8.2.1 and the definition

Book* book_ptr = new Textbook;

shows the values of several expressions that use the typeid operator.
The value of the expression

ty p e id ( book_ptr )

represents the type (Book*) declared for book_ptr, not the type of object
(Textbook) to which book_ptr points. For this reason, the first expression
is true, but the second, fourth, and fifth expressions are false.
The value of the expression

ty p e id ( *book_ptr )

represents the type of object (Textbook) to which book_ptr points. For this
reason, the third expression is false, but the last expression is true. □

The programmer can extend the run-time type identification provided


by the system by deriving classes from the standard class Type_info.

www.MathSchoolinternational.com
432 CHAPTER 8 ADVANCED TO PICS

Exercises

1. Explain how print_book_info in Example 8.2.1 executes when invoked


as

Book* bptr = new Book;


print_book_info( bptr ) ;

2. Explain how print_book_info in Example 8.2.1 executes when invoked


as

Book* bptr = new Textbook;


print_book_info( bptr ) ;

3. Explain how print_book_inf o in Example 8.2.1 executes when invoked


as

Book* bptr = new Paperback;


print_book_info( p tr ) ;

Give the value of each expression in Exercises 4-27. Assume the declara­
tions of Example 8.2.1 and the statements

double* dptr;
short i ;
char student[ 100 ] [ 80 ] ;
Book* bptr == new Book;

4. typ eid ( 2.0 ) == typ eid ( flo a t )

5. typ eid ( 2.0 ) == typ eid ( double )

6. typ eid ( 6972u ) == typ eid ( in t )

7. typ eid ( 6972u ) == ty p eid ( unsigned in t )

8. typ eid ( 6972u ) == typ eid ( unsigned )

9. typ eid ( dptr ) == ty p eid ( double )

10. typ eid ( dptr ) == ty p eid ( double* )

11. typ eid ( *dptr ) == typ eid ( double )

12. typ eid ( *dptr ) == typ eid ( double* )

13. ty p eid ( i ) ==: typ eid ( short )

14. typ eid ( ( int ) i ) == typ eid ( in t )

www.MathSchoolinternational.com
8.3 NAMESPACES 433

15. ty p e id ( i ) == typ eid ( in t )

16. ty p e id ( student ) == typ eid ( char )

17. ty p e id ( student ) == typ eid ( chair* )

18. ty p e id ( student ) == typ eid ( chair** )

19. ty p e id ( *student ) == typ eid ( chair )

20. ty p e id ( *student ) == typ eid ( char* )

21. ty p e id ( *student ) == typ eid ( chax** )

22. ty p e id ( bptr ) == typ eid ( Book* )

23. ty p e id ( bptr ) == typ eid ( Book )

24. ty p e id ( bptr ) == typ eid ( Textbook* )

25. ty p e id ( bptr ) == typ eid ( Textbook )

26. ty p e id ( *bptr ) == typ eid ( Book )

27. ty p e id ( *bptr ) == typ eid ( Textbook )

8.3 Namespaces
C + + uses namespaces to distinguish among identical global names. With­
out namespaces, a programmer could not directly use two libraries each of
which contains identical global names.

Exam ple 8.3.1. Suppose that two libraries each contain a toplevel func­
tion named clr_screen. To use namespaces to resolve the ambiguity, the
libraries, say l i b l and lib2, put the global declarations in namespaces: the
library l i b l contains the code
namespace l i b l {
void c lr _ s c r e e n ();

>
and the library lib 2 contains the code
namespace lib 2 {
void c lr _ s c r e e n ();

>

www.MathSchoolinternational.com
434 CHAPTER 8 ADVANCED TO PICS

(Any global declarations or definitions can be placed into a namespace.) □

The programmer may refer to namespace members in several ways.

Example 8.3.2. Given the namespaces of Example 8.3.1, the programmer


can reference l i b l ’s clr_screen as
/
l i b l :l^clr_screen()
and lib 2 ’s clr_screen as
l i b 2 : : clr_sc re e n ()
Since continually appending a namespace to an identifier can clutter a pro­
gram, the programmer can also write
using l i b l : : clr_screen;
which means that after this line, the reference
clr_screen ()
is to l i b l ’s clr_screen. □

The line
using l i b l : : clr_screen;
is equivalent to declaring a variable named clr_screen. Any reference there­
after to clr_screen within the scope of the declaration is to clr_screen in
l i b l ’s namespace. If the declaration is in a block, the identifier clr_screen
is known only within the block; otherwise, the identifier is known to the end
of the file.

Example 8.3.3. The code


namespace lib 3 {
char fname [ 100 ] ;
>

void printnameO

char fname[ 500 ] ;


using lib3::fnam e; // * * * * * ERROR: fname ambiguous

>
contains an error since printname contains two declarations of fname. □

The programmer can make all of the declarations in l i b l ’s namespace


available by writing
using l i b l ;

www.MathSchoolinternational.com
8.4 OTHER OBJECT-ORIENTED LANGUAGES 435

After this line, all references to names in l i b l ’s namespace are resolved in


favor of li b 1.
Namespaces were very recently added to C + + . Some of the details of
the semantics are yet to be resolved.

Exercises

1. Given

namespace kalin_software {
class strin g {
char item [ 100 ] ;
in t len;
p u b lic :
void s to re ( char* ) ;
char* g e t ( ) ;
>;
>

and

namespace jbaugh_software {
class strin g {
char item [ 100 ] ;
p u b lic :
void s to re ( char* ) ;
char* g e t ( ) ;
>;
>

write code that creates an object k of type strin g from kalin_softw are
and an object j of type strin g from jbaugh_software.

2. Given the code of Exercise 1, an object k of type strin g from ka-


lin_software, and an object j of type strin g from jbaugh_software,
write code that stores the string H i, Mom in k and in j .

8.4 Other Object-Oriented Languages


Many languages support object-oriented programming. Some such as Small­
talk and Eiffel were developed specifically as object-oriented languages. Oth­
ers such as C + + and Objective C are extensions of existing non-object-
oriented languages.

www.MathSchoolinternational.com
436 CHAPTER 8 ADVANCED TO PICS

Sm alltalk

Smalltalk, developed at Xerox PARC, is a “pure” object-oriented language


in the sense that everything is an object, all action is via message passing,
and all binding is dynamic. Implementations of Smalltalk vary and Small­
talk code is typically nonportable. For example, some versions of Smalltalk
support multiple inheritance whereas others support only single inheritance.
Variables are untyped and so may reference any object. (Internally, variables
are simply pointers.) As a result, there is no need for generics and there is
no type checking. Every Smalltalk system provides a rich class hierarchy.
Smalltalk usually includes a window-based, interactive development plat­
form that is tightly interwoven with the language. This platform usually has
an interactive debugger and a “browser,” which permits the programmer to
inspect code and examine existing classes. Smalltalk is typically translated
into an intermediate language, which is then interpreted. The disadvantage
of an interpreted, pure object-oriented language on a highly interactive plat­
form is that execution is often quite slow. We next give some examples of
Smalltalk code.
Smalltalk has unary, binary, and keyword messages. The statement
x := 9 sqrt.
furnishes an example of a unary message. The sqrt message is passed to the
object 9. (Everything in Smalltalk is an object— including numbers.) The
effect is to return the object 3, which is then copied into x. The assignment
operator is : =. A period marks the end of a statement, except that a period
at the end of the last statement is optional.
Binary messages are used for (binary) arithmetic operations as, for ex­
ample,
x := 2 + 5.
Although the syntax is the same as C + + , the meaning is quite different.
Here the + message, with argument object 5, is passed to the object 2. The
effect is to return the object 7, which is then copied into x.
The statement
a := b max: c.
is an example of a keyword message, in which a keyword followed by a colon
precedes each argument. Here the argument c, which is preceded by the
keyword max and a colon, is passed to object b. Object b computes the
maximum of itself and c and returns this maximum value, which is then
copied into a. Equivalent C + + statements are
a = max( b , c ) ;
or
a = b .max( c ) ;

www.MathSchoolinternational.com
8.4 OTHER OBJECT-ORIENTED LANGUAGES 437

Object

Set B ag IndexedCollection Character Date Number

t
Dictionary O rderedCollection FixedCollection

t / \
SortedCollection A rray String

Figure 8.4.1 Part of the Smalltalk class hierarchy.

if b is an object.
The syntax for keyword messages with more than one argument is quite
different from C + + . For example, the message between: and: takes two
arguments as indicated by the two colons. If the object has a value between
the two arguments, between: and: returns True; otherwise, it returns False.
For example, the statement
x := y between: a and: b.
sets x to True, if y is between a and b, and to False otherwise.Equivalent
C + + statements are
x = between( y, a, b ) ;
or
x = y.between( a, b ) ;
if y is an object.
Smalltalk has one base class Object from which all other classes are
directly or indirectly derived. A partial tree of a typical class library is
shown in Figure 8.4.1. Notice that characters, integers, and so on are indeed
classes, as are strings, sets, and arrays.
Since an array is an object, a method called a t : must be used to access
data in the array. The counterpart of the C + + statement
x = a[ i ] ;
in Smalltalk is
x := a a t : i .
The assignment operator simply copies a pointer. For example, the state­
ments
s t r l := ’ Cool, Beavis’ .
str2 := s t r l.
point s t r l and str2 to the same string object (see Figure 8.4.2). The
operator = tests whether two variables (pointers) reference equal objects.

www.MathSchoolinternational.com
438 CH APTER 8 ADVANCED TO PICS

str 1----- -------------------


Cool. Beavis
str 2'''*"
Figu re 8.4.2 The assignment operator s tr 1 := str2.

strl1 Cool, Beavis

strS Cool, Beavis

Figu re 8.4.3 Equal, but distinct, objects.

The operator == tests whether two variables (pointers) contain the same
address. Thus after the two previous statements execute, the conditions
s t r l = str2
s t r l == str2
are both true. For the situation in Figure 8.4.3, the condition
s t r l = str2
is true, but the condition
s t r l == str2
is false.
Since Smalltalk is typeless, the version of a method to be invoked is
determined by the kind of object that is being referenced. In other words,
all binding is done at run-time and polymorphism is automatic. For example,
the method a t : can be applied to an array object to obtain the item at a
specified index or to a string object to obtain the character at a specified
index.
Smalltalk has instance methods and class methods. An instance method,
which is like an ordinary method in C + + , applies to an object. In the
preceding examples, all methods are instance methods. A class method, on
the other hand, applies to a class. An example of a class method is the
method new, which is the counterpart of the C + + operator new. Method
new is defined in class Object (see Figure 8.4.1) and thus is inherited by
any class indirectly derived from Object. For example, if Complex is a class
indirectly derived from Object,
c := new Complex.
passes the message new to the class Complex. The effect is to create a
Complex object and return its address, which is then copied into c. No
d elete method is furnished since it is unnecessary; Smalltalk provides au­
tomatic garbage collection.
In Smalltalk, every class except Object is a subclass of some existing
class. When a new hierarchy is constructed, it is customary to make the top
class a subclass of the library class Object. The class declaration

www.MathSchoolinternational.com
8.4 OTHER OBJECT-ORIENTED LANGUAGES 439

Object subclass: #Complex


instanceVariableNames: ’ re a l imag’
classVariableNames: ’ ’
p oolD iction a ries: ’ ’
declares the class Complex as a subclass of Object. The pound sign signifies
that Complex is a symbol (identifier). Single quotes delimit the names in
the declaration; thus, Complex has two data members, re a l and imag, and
no class variable names and no pool dictionaries. (A discussion of the last
two items is beyond the scope of this brief introduction.) Data members are
always private.
Methods, which are always public, are tied to the class through the user
interface and do not appear directly as part of the class declaration. The
code for the method i n i t i a l i z e : i : to initialize a Complex object might be
written as
in i t i a l i z e : re i : im
re a l := re.
imag := im
Notice that i n i t i a l i z e : i : is the name of the method and re and im are
parameters. To obtain a Complex object C and initialize it with values 2 and
-4.86, we could write
C := Complex new.
C i n i t i a l i z e : 2 i : -4.86.
References on Smalltalk are: A. Goldberg and D. Robson, Smalltalk-80:
The Language, (Reading, Mass.: Addison-Wesley, 1989) and W. LaLonde
and J. Pugh, Inside Smalltalk, (Englewood Cliffs, N.J.: Prentice Hall, 1990).
Eiffel
Eiffel was developed by Bertrand Meyer [see B. Meyer, Object-oriented Soft­
ware Construction, (Englewood Cliffs, N.J.: Prentice Hall, 1988)]. Its de­
sign embodies Meyer’s view of proper principles of software design including
modularity, reusability, object-oriented techniques, and correctness. Our
comments apply to Eiffel, version 2.3.
Eiffel is a typed, object-oriented language. The types available are sim­
ple types— INTEGER, REAL, CHARACTER, and BOOLEAN— and classes. Arrays,
strings, sets, and the like, are classes. An INTEGER, REAL, or CHARACTER vari­
able is similar to its int, flo a t, or char counterpart in C++- A BOOLEAN
variable can take on only the values true or fa lse. A class variable is a
pointer. All action is via message passing. Every Eiffel system provides a
rich class hierarchy.
In Eiffel, : = is the assignment operator. When used with simple types,
the value is copied. When used with classes, the address is copied. We
illustrate the difference.
Variables i and j of type INTEGER are declared as

www.MathSchoolinternational.com
440 CHAPTER 8 ADVANCED TO PIC S

Figure 8.4.4 Copying values in Eiffel.

real = 0.0
imag = 0.0

Figure 8.4.5 Copying addresses in Eiffel.

i , j : INTEGER
Storage is reserved for two integers and i and j refer to this storage. The
instruction
i := 3
copies the value 3 to the storage named i. The instruction

j := 1
then copies i ’s value to j (see Figure 8.4.4).
Variables x and y of type COMPLEX, where COMPLEX is a class, are declared
as
x, y: COMPLEX
Storage is reserved for two pointers, but no COMPLEX objects are created. To
obtain a COMPLEX object and point x to it, we would write
x.Create
A Create method is provided by Eiffel although it can be overridden. The
instruction
y := x
then copies the address of the object so that both x and y point to the object
(see Figure 8.4.5).
The Clone method
y.C lone(x)
creates a copy of the object referenced by x and stores the address of the
object in y (see Figure 8.4.6).
The equality test (=) compares the values of simple types and the ad­
dresses of objects for class types. For the situations in Figures 8.4.4 and
8.4.5, the expression
x = y
is true, but for the situations in Figures 8.4.6 and 8.4.7, the expression

www.MathSchoolinternational.com
8.4 OTHER OBJECT-ORIENTED LANGUAGES 441

re al = 0.0
im ag = 0.0

re al = 0.0
im ag = 0.0

Figure 8.4.6 Cloning an object in Eiffel.

re al = 0.3
imag = 0.0

re al = 0.0
imag = 0.0

Figure 8.4.7 Unequal addresses and unequal objects.

x = y
is false.
The Equal method tests whether the data members of objects are equal.
For the situations in Figures 8.4.5 and 8.4.6, the expression
x.Equal(y)
is true, but for the situation in Figure 8.4.7, the expression
x.Equal(y)
is false.
As in C + + , the dot notation is used to pass a message to an object. After
declaring z to be type COMPLEX and associating z with a COMPLEX object, we
could invoke the method in it with arguments -4 and 26.9 for the object z
by writing
z . i n i t ( -4, 26.9 )
Class COMPLEX could be written as
class COMPLEX export
in it
feature
r e a l, imag: REAL;
i n i t ( re: REAL, im REAL ) is
— i n i t i a l i z e the re a l and imaginary parts
do
re a l := re;
imag := im
end — in it
end — class COMPLEX

www.MathSchoolinternational.com
442 CHAPTER 8 ADVANCED TO PICS

Two dashes introduce a comment, which extends to the end of the line. Semi­
colons serve as statement separators. The data members and methods listed
in the export section are available outside the class (export is like public in
C + + ). Data members and methods not listed in the export section are not
available outside the class hierarchy; they are like protected members in
C ++. All data members and methods are specified in the feature section.
Methods are distinguished from data members by the is-do-end syntax.
Eiffel includes preconditions, postconditions, and class invariants as part
of the language. A precondition is signaled by the keyword require and a
postcondition is signaled by the keyword ensure:
class C export

feature

f ( . . . ) is
require
— preconditions fo r f
do

ensure
— postconditions fo r f
end;

end
A class invariant is signaled by the keyword invariant:
class C export

feature

invariant
— class invariants
end
At compile time, the programmer can elect to check no assertions, to check
preconditions only, or to check all assertions. If an assertion to be checked is
false at run-time, the program is terminated and an error message is printed.
Eiffel also has automatic garbage collection, generic classes, exception
handling (much like that in C + + ), and multiple inheritance. Polymorphism
is the default in Eiffel, so there is no need for a “virtual” flag as in C + + .

O bjective C

Objective C, like C + + , is an extension of C that includes object-oriented fea­


tures. Brad Cox, Productivity Products International, developed Objective

www.MathSchoolinternational.com
OTHER OBJECT-ORIENTED LANGUAGES 443

C [see B. Cox, Object-Oriented Programming: An Evolutionary Approach,


(Reading, Mass.: Addison-Wesley, 1986)].
Objective C can be described as C combined with Smalltalk. The C
subset is essentially the same as standard C. We describe the Smalltalk
subset of Objective C.
As in Smalltalk itself, the Smalltalk subset of Objective C is typeless.
Variables used to reference objects are declared as generic type id. An id
variable can reference any object. Binding is done at run-time; thus, poly­
morphism is automatic and there is no need for a “virtual” flag as in C ++.
For id variables, the assignment operator copies addresses as in Smalltalk.
There is a copy method
x = [ y copy ] ;
which makes a copy of the object referenced by y. As shown, messages
passed to objects, such as the copy message passed to y, must be enclosed
in brackets [ ].
All classes are directly or indirectly derived from the class Object. All
data members are private; thus, outside a class, methods must be used to
access the data. The syntax for methods is that of Smalltalk. For example,
the statement
[ C i n i t i a l i z e : 2 i : -4.86 ] ;
passes a message to object C to invoke the method i n i t i a l i z e : i : with
arguments 2 and -4.86.
Objective C has instance methods (like Smalltalk’s instance methods),
which are preceded by a minus sign, and factory methods (like Smalltalk’s
class methods), which are preceded by a plus sign. The code
O interface Complex : Object
{
double re a l;
double imag;
>

- ( void ) i n it ia liz e : ( double ) re i : ( double ) im;


Send
declares the class Complex as a subclass of the class Object. As shown, a
class declaration is delimited by O interf ace and Qend and is said to belong
to the interface description part of the code. Data members, enclosed in
braces, are followed by declarations of the methods. Class Complex has one
instance method, i n i t i a l i z e : i, with parameters re and im each of type
double. The code to implement method i n i t i a l i z e : i could be written

www.MathSchoolinternational.com
444 CHAPTER 8 ADVANCED TO PICS

@implementation Complex
- ( void ) i n i t i a l i z e : ( double ) re i : ( double ) im
{
re a l = re;
imag = im;
>
Send
As shown, code to implement the methods is delimited by ^implementation
and Send and is said to belong to the implementation part of the code.
The new method is used to dynamically create an object
Complex z;
z = [ Complex new ] ;
and the fre e method is used to free a dynamically allocated object
[ Complex fre e ] ;
There is no automatic garbage collection in Objective C.

Other Languages
Other languages that include support for object-oriented programming are
• Object Pascal, QuickPascal, Turbo Pascal, Actor
— Each extends Pascal to include object-oriented features. Quick­
Pascal is an implementation of Object Pascal. L. Telser, “Ob­
ject Pascal Report,” (Santa Clara, Calif.: Apple Computer, 1985),
describes Object Pascal, and N. Shammas, Object-Oriented Pro­
gramming with QuickPascal, (New York: Wiley, 1990), describes
QuickPascal. Turbo Pascal is described in the programming guide
that accompanies the product from Borland International, Scotts
Valley, Calif. Actor is described in the Actor Language Manual,
(Evanston, 111.: The Whitewater Group, 1987).
• Simula
— An object-oriented extension of Algol 60 [see B. Kirkerud, Object-
Oriented Programming with Simula, (Reading, Mass.: Addison-
Wesley, 1989)], which is considered to be the first object-oriented
language. Simula is a general-purpose language, but, as its name
suggests, it includes support for simulations.
• CLOS (Common Lisp Object System)
— An object-oriented extension of Lisp [see S. E. Keene, Object-
Oriented Programming in Common Lisp, (Reading, Mass.: Addi­
son-Wesley, 1989)].

www.MathSchoolinternational.com
8.5 N E W ISSUES 445

Exercises

1. Compare the advantages and disadvantages of the object-oriented lan­


guages C + + , Smalltalk, Eiffel, and Objective C.

2. Report on another object-oriented language such as Simula or CLOS.

8.5 N ew Issues
In this section we discuss some current topics and future directions.

Concurrency

Concurrency refers to the concurrent execution of two or more processes.


A real-time system in a factory consisting of many processors monitoring
equipment provides an example of concurrency. Some languages (e.g., Ada)
provide support for concurrency directly within the language. Languages
(e.g., C and C + + ) that do not directly support concurrency rely on system
calls (e.g., fork and jo in in UNIX) to handle concurrent processes.
There is considerable interest and research into integrating support for
concurrency into object-oriented languages and using the object-oriented
paradigm to provide a coherent concurrency model [see, e.g., Proceedings of
the ACM SIGPLAN Workshop on Object-Based Concurrent Programming,
April 1989, SIG PLA N Notices, 24 (4)]. One idea is to consider a process
to be an object. Certain objects are active (executing) whereas others are
inactive (not executing). The objects communicate through message passing
for synchronization.

Persistence

An object is persistent if after creation it exists until destroyed even if


the program terminates. In C + + , objects are not persistent; after a C + +
program terminates, all objects are automatically destroyed either by de­
structors or by the system. Some object-oriented languages provide classes
that provide support for persistent objects. Eiffel, for example, provides the
class STORABLE with methods store and re trie v e to store permanently and
retrieve objects. Persistence is major issue in object-oriented databases.

O bject-O riented Databases

The major database model is the relational model. The basic structure
within a relational database is a table (see Figure 8.5.1). Records correspond
to rows, and columns describe attributes of the records. Operators on a
relational database manipulate the tables (e.g., store a table, retrieve a table,
combine tables).

www.MathSchoolinternational.com
446 CHAPTER 8 ADVANCED TO PICS

ID Number Name Position Age


22012 Johnsonbaugh c 22
93831 Glover of 24
90015 Kalin ss 33
06682 Krueger 3b 38
58199 Battey P 18
84341 Cage c 30
01180 Homer lb 37
26710 Score P 22
61049 Epp of 30
39826 Singleton 2b 31

Figure 8.5.1 A table in a relational database.

Figure 8.5.2 An object in an object-oriented database.

In an object-oriented database, tables are replaced by arbitrary ob­


jects (see Figure 8.5.2), which offer the user of the database considerably
more flexibility. Persistence is an issue in object-oriented databases since a
stored object must remain in existence even if the program is not executing.
Operations on tables in a relational database are well understood and
can be implemented efficiently. A challenge in developing object-oriented
databases is to optimize the operations on the objects so the performance
is acceptable. Optimization is more difficult when tables are replaced by
objects since tables, by definition, are of a very special type, whereas objects
are perfectly general.

Future Directions

We speculate that object will be ubiquitous in computer science. Writing


code in the traditional sense will be replaced by modifying and specializing
existing classes through the use of inheritance and generics. These modi­
fied classes will then be organized into the desired system. Programmers
will necessarily deal with more abstract, larger building blocks. The dis­
tinction among programs, files, databases, and so on, will be blurred as the
components become simply “objects.” Objects will also form the basis of
many theoretical models. (The example of objects modeling processes was
previously mentioned.)

www.MathSchoolinternational.com
N E W ISSUES 447

The trend toward the replacement of large mainframe computers with


networks of small but powerful desktop machines will continue. The highly
productive nature of these systems will result, in part, from the application
of object-oriented technology.
Even with these changes in the creation of systems, it will still be neces­
sary for programmers to have a detailed knowledge of the underlying object-
oriented language in order to deal with the available tools and to understand
and modify existing classes. Programming will be on a higher, more abstract
level; it will become more challenging and even more of an intellectual en­
deavor. It will involve not just “coding,” but also the use of advanced, formal
design techniques. A more sophisticated education of programmers will be
required. Efficient software production will become a reality even as fewer
programmers do the work that many did with older technologies.
Because of the impact of the object-oriented paradigm on the develop­
ment of software, object-oriented languages and object-oriented design meth­
ods will be found throughout the curriculum. (This is already beginning at
many institutions.) C + + will replace C. Object-oriented techniques will
start with the introductory course. “Data structures” will become “abstract
data types.” An exciting future awaits educators and software developers.

www.MathSchoolinternational.com
www.MathSchoolinternational.com
Appendix A

A S C II Table

Table A . l ASCII Codes


Decimal Hexadecimal Octal Standard Function
0 00 000 NUL (Null)
1 01 001 SOH (Start of heading)
2 02 002 STX (Start of text)
3 03 003 ETX (End of text)
4 04 004 EOT (End of transmission)
5 05 005 ENQ (Enquiry)
6 06 006 ACK (Acknowledge)
7 07 007 BEL (Ring bell)
8 08 010 BS (Backspace)
9 09 011 HT (Horizontal tab)
10 0A 012 LF (Line feed)
11 0B 013 V T (Vertical tab)
12 OC 014 FF (Form feed)
13 0D 015 CR (Carriage return)
14 0E 016 50 (Shift out)
15 OF 017 51 (Shift in)
16 10 020 DLE (Data link escape)
17 11 021 DC1 (Device control 1)
18 12 022 DC2 (Device control 2)
19 13 023 DC3 (Device control 3)
20 14 024 DC4 (Device control 4)

449

www.MathSchoolinternational.com
450 A P P E N D IX A

Table A . l ASCII Codes (Continued)


Decimal Hexadecimal Octal Standard Function
21 15 025 NAK (Negative acknowledge)
22 16 026 SYN (Synchronous idle)
23 17 027 ETB (End of transmission block)
24 18 030 CAN (Cancel)
25 19 031 EM (End of medium)
26 1A 032 SUB (Substitute)
27 IB 033 ESC (Escape)
28 1C 034 FS (File separator)
29 ID 035 GS (Group separator)
30 IE 036 RS (Record separator)
31 IF 037 US (Unit separator)
32 20 040 SP (Space)
33 21 041 !
34 22 042 n
35 23 043 #
36 24 044 $
37 25 045 %
38 26 046 &
39 27 047 ’ (Single quote)
40 28 050 (
41 29 051 )
42 2A 052 *
43 2B 053 +
44 2C 054 , (Comma)
45 2D 055 - (Hyphen)
46 2E 056
47 2F 057 /
48 30 060 0
49 31 061 1
50 32 062 2
51 33 063 3
52 34 064 4
53 35 065 5
54 36 066 6
55 37 067 7
56 38 070 8
57 39 071 9
58 3A 072
59 3B 073 >
60 3C 074 <
61 3D 075
62 3E 076 •

www.MathSchoolinternational.com
ASCII TABLE 451

Table A . l ASCII Codes (Continued)


Decimal Hexadecimal Octal Standard Function
63 3F 077 ?
64 40 100 @
65 41 101 A
66 42 102 B
67 43 103 C
68 44 104 D
69 45 105 E
70 46 106 F
71 47 107 G
72 48 no H
73 49 111 I
74 4A 112 J
75 4B 113 K
76 4C 114 L
77 4D 115 M
78 4E 116 N
79 4F 117 0
80 50 120 P
81 51 121 Q
82 52 122 R
83 53 123 S
84 54 124 T
85 55 125 U
86 56 126 V
87 57 127 W
88 58 130 X
89 59 131 Y
90 5A 132 Z
91 5B 133 [
92 5C 134 \
93 5D 135 ]
94 5E 136
95 5F 137 _ (Underscore)
96 60 140 ‘ (Grave accent)
97 61 141 a
98 62 142 b
99 63 143 c
100 64 144 d
101 65 145 e
102 66 146 f
103 67 147 g
104 68 150 h

www.MathSchoolinternational.com
452 A P P E N D IX A

Table A . l ASCII Codes (Continued)


Decimal Hexadecimal Octal Standard Function
105 69 151 i
106 6A 152 j
107 6B 153 k
108 6C 154 1
109 6D 155 m
no 6E 156 n
111 6F 157 o
112 70 160 P
113 71 161 q
114 72 162 r
115 73 163 s
116 74 164 t
117 75 165 u
118 76 166 V
119 77 167 w
120 78 170 X
121 79 171 y
122 7A 172 z
123 7B 173 {
124 7C 174 1
125 7D 175 }
126 7E 176
127 7F 177 DEL (Delete)

www.MathSchoolinternational.com
Appendix B

Selected C + + Functions

Before summarizing in detail several useful library functions and class meth­
ods, we shall briefly describe each. The following lists group the functions
by type:

Math Functions
abs Absolute value of an int floor Floor
acos Arccosine labs Absolute value of a long
asin Arcsine log logex
at an Arctangent loglO logio x
atof Convert string to double pow xy
atoi Convert string to int rand Generate a random integer
atol Convert string to long sin Sine
ce il Ceiling sinh Hyperbolic sine
cos Cosine sqrt Square root
cosh Hyperbolic cosine srand Seed the random number
generator
exp ex tan Tangent
f abs Absolute value of a double tanh. Hyperbolic tangent

453

www.MathSchoolinternational.com
454 APPENDIX В

ANSI С Input/Output Functions


fclose Close а Юе getc Read а character
fgetc Read а character getchar Read а character
fgets Read а string gets Read а string
fopen Open а file printf Write formatted output
fprintf Write formatted output putc Write а character
fputc Write а character putchar Write а character
fputs Write а string pnts Write а string
fread Read binary data rewind Моуе to beginrring of file
fscanf Read formatted input scanf Read formattcd input
fseek Моуе within а file sprintf Write formatted output
ftell Find position within а file sscanf Read formatted input
fwrite Write binary data ungetc Return а character to а buffer

Input/Output Class Methods


bad Signal whether badbi t is put Write character
set
clear Replace or clear stream putback Return character to
state stream
close Close а Юс rdbuf Return pointer to buffer
eof Signal end-of-file rdstate Return stream state
fail Signal wllether failbit read Read binary data
is set
fill Change the fill character seekg Моуе within input
stream
flags Change and/or return seekp Моуе within output
format flags stгеаш
flush Flush buffer setf Set specified format flags
gcount Return number of char- str Return character buffer
acters read
get Read characters and Synchronize С++ and С
strings input/output
good Signal ,vhether state is tellg Return position within
good input stгеаш
ignore Discard characters tellp Return position within
output stгеаш
open Open а file tie Tie an input stream to
an output stгеаш
pcount Return nnmber of char- unsetf Clear specified format
acters stored flags
peek Return next character width Change field width
precision Change the precision write Write binary data

www.MathSchoolinternational.com
SELECTED C + + FUNCTIO NS 455

Type and Conversion Functions


atof Convert string to double islower Lowercase character?
atoi Convert string to int isprint Printable character?
atol Convert string to long ispunct Punctuation character?
isalnum Alphanumeric? isspace Space character?
isalpha Alphabetic character? isupper Uppercase character?
iscntrl Control character? isxdigit Hexadecimal character?
isd ig it Decimal digit? tolower Convert from uppercase to
lowercase
isgraph Nonblank, printable toupper Convert from lowercase to
character? uppercase

String Functions
memchr Find leftmost character in strlen Length of string
object
memcmp Compare objects strncat Concatenate strings
memcpy Copy object strncmp Compare strings
memmove Copy object strncpy Copy string
strcat Concatenate strings strpbrk First break character
strchr Find leftmost character in strrchr Find rightmost character in
string string
strcmp Compare strings strspn Span
strcpy Copy string strstr Find substring
strcspn Complement of span

Miscellaneous Functions
abort Cause abnormal pro­ set_unexpected Specify function for
gram termination unexpected to call
bsearch Binary search signal Invoke a function to
handle a signal
clearerr Clear end-of-file and er­ system Execute a command
ror indicators
difftim e Compute difference be­ terminate End because of excep­
tween times tion handling error
exit Terminate program time Find time
qsort Quicksort unexpected Called when illegal
throw specification
set_terminate Specify function for
terminate to call

We now list the functions and class methods alphabetically. Class meth­
ods are designated as such and the class to which each belongs is specified.
Each description consists of the file to include (when the Working Paper
so specifies), the function’s declaration, and a few sentences that describe
what the function does. All character codes are given in ASCII. When we
write string, it is the address of (pointer to) a sequence of null-terminated,
contiguous chars.

www.MathSchoolinternational.com
456 A P P E N D IX B

a bort

#include <stdlib.h>
void abort( ) ;

Causes abnormal program termination. The status “unsuccessful termina­


tion” is returned to the invoking process.

abs

#include <stdlib.h>
in t abs( in t in teger ) ;

Returns the absolute value of integer. See also fabs and labs.

acos

#include <math.h>
double acos( double re a l ) ;

Returns the arccosine (in radians) of real. The value returned is between 0
and 7T.

as in

#include <math.h>
double asin ( double re a l ) ;

Returns the arcsine (in radians) of real. The value returned is between —^
and

a t an

#include <math.h>
double atan( double re a l ) ;

Returns the arctangent (in radians) of real. The value returned is between
- f a n d f-
at of

#include <stdlib.h>
double a t o f( const char *s trin g ) ;

Converts a real number, represented as string, to double. Returns the


converted number; strin g consists of optional tabs and spaces followed by
an optional sign followed by digits followed by an optional decimal point
followed by an optional exponent. The optional exponent is e or E followed
by an integer. See also a to i and atol.

www.MathSchoolinternational.com
SELECTED C + + FUNCTIONS 457

a to i

#include <stdlib.h>
in t a t o i( const char *strin g ) ;

Converts an integer, represented as string, to int. Returns the converted


number; strin g consists of optional tabs and spaces followed by an optional
sign followed by digits. See also a tof and atol.

a to l

#include <stdlib.h>
long a t o l( const char *s trin g ) ;

Converts an integer, represented as string, to long. Returns the converted


number; strin g consists of optional tabs and spaces followed by an optional
sign followed by digits. See also a tof and atoi.

bad

#include <iostream.h>
in t i o s : :b a d ();

Returns nonzero if badbit is set; otherwise, returns zero.

bsearch

#include <stdlib.h>
void *bsearch( const void *key,
void *s ta rt,
s iz e _ t n o_elts,
s iz e _ t s iz e _ e lt,
in t ( *cmp ) ( const void *, const void * ) );

Searches for *key in a sorted array of size no_elts whose initial cell is at
address start. The parameter s iz e _ e lt is the size in bytes of one cell
of the array. The parameter cmp is a pointer to a function that compares
*key and an element in the array and returns an integer to signal the result
of the comparison. The first argument to the comparison function *cmp
is key and the second is a pointer to an item in the array. The value of
the expression *cmp( * f i r s t , *second ) is negative if * f i r s t precedes
♦second in the sorted order; *cmp( * f i r s t , *second ) is zero if * f i r s t
is equal to *second; and *cmp( * f i r s t , *second ) is positive if * f i r s t
follows *second in the sorted order. If *key is in the array, bsearch returns
a pointer to a cell containing *key; if *key is not in the array, bsearch
returns NULL.

www.MathSchoolinternational.com
458 A P P E N D IX B

c e il

#include <math.h>
double c e i l ( double re a l ) ;

Returns the least integer (as a double) greater than or equal to real.

c le a r

#include <iostream.h>
void io s : : c le a r ( in t st = 0 ) ;

Changes stream state to st.

c le a r e r r

#include <stdio.h>
void c le a re rr( FILE * file _ p o in te r ) ;

Clears the end-of-file and error indicators in the file referenced by f ile _ p o in t-
er.

c lo s e

#include <fstream.h>
void fstream base:: c lo s e ( ) ;

Closes the file, if any, attached to the object.

cos

#include <math.h>
double cos( double re a l ) ;

Returns the cosine of real; re a l must be in radians.

cosh

#include <math.h>
double cosh( double re a l ) ;

Returns the hyperbolic cosine of real.

d ifft im e

#include <time.h>
double d ifftim e ( time_t end, time_t begin ) ;

Returns the difference (end —begin), in seconds, between the times end and
begin. See also time.

www.MathSchoolinternational.com
SELECTED C + + FUNCTIONS 459

eof

#include <iostream.h>
in t i o s : : e o f( ) ;

Returns nonzero if e o fb it is set; otherwise, returns zero.

e x it

#include <stdlib.h>
void e x it ( in t status_value ) ;

Terminates the program and sends the value status_value to the invoking
process (operating system, another program, etc.). The constants EXIT_SUC-
CESS and EXIT_FAILURE, defined in stdlib.h, may be used as arguments to
e x it to indicate successful or unsuccessful termination. The function e x it
flushes all buffers and closes all open files.

exp

#include <math.h>
double exp( double re a l ) ;

Returns ereal, where e (2.71828...) is the base of the natural logarithm. See
also pow.

f abs

#include <math.h>
double fa b s( double re a l ) ;

Returns the absolute value of real. See also abs and labs.

fa il

#include <iostream.h>
in t i o s : : f a i l ( ) ;

Returns nonzero if f a i l b i t is set; otherwise, returns zero.

fc lo s e

#include <stdio.h>
in t fc lo s e ( FILE * file _ p o in te r ) ;

Closes the file referenced by f ile_p oin ter. Flushes all buffers. If successful,
f close returns 0; otherwise, it returns EOF.

www.MathSchoolinternational.com
460 A P P E N D IX B

fg e t c

#include <stdio.h>
in t fg e t c ( FILE * file _ p o in te r ) ;

Returns the next character from the file referenced by f ile_p o in ter, or if
the end of the file is reached or an error occurs, it returns EOF. Equivalent
to the function getc. See also getc, getchar, and ungetc.

fgets

#include <stdio.h>
char * fg e t s ( char ^storage, in t max_line,
FILE * file _ p o in te r ) ;

Reads the next line from the file referenced by file _ p o in te r and stores it
at address storage. The “next line” consists of
The next max_line — 1 characters.
or
All characters up to and including the next newline character.
or
All characters up to the end of the file.
whichever is shortest. If at least one character is stored, fg e ts adds a
terminating null ’ \0’ to the end of the line. Notice that fg e ts stores the
newline character if it was read, and that fg e ts never stores more than
max_line characters (including newline and ’ NO’ ). If no characters are
stored or an error occurs, fg e ts returns NULL; otherwise, fg e ts returns the
address storage. See also gets.

fill

#include <iostream.h>
char i o s : : f i l l ( ) ;
char i o s : : f i l l ( char fill_ c h a r ) ;

The first version returns the current fill character. The second version
changes the fill character to fill_ c h a r and returns the old fill character.

flags
#include <iostream.h>
long i o s : : f l a g s ( ) ;
long i o s : : f la g s ( long new_flags ) ;

The first version returns the current format flags. The second version changes
the format flags to new_f lags and returns the old flags.

www.MathSchoolinternational.com
SELECTED C + + FUNCTIONS 461

flo o r

#include <math.h>
double f l o o r ( double re a l ) ;

Returns the greatest integer (as a double) less than or equal to real.

flu s h

#include <iostream.h>
ostreamfe ostream::f l u s h ();

Flushes the output buffer and returns the updated stream.

f open

#include <stdio.h>
FILE *fopen( const char *s trin g , const chair *mode ) ;

Opens the file whose name is pointed to by string. In addition,fopen


returns the address of a structure that allows access to the file, or in case
of error, it returns NULL. The pointer returned can be used in subsequent
input/output calls involving functions such as fread and fp r in tf.
If mode is "r" and the file exists, the file is opened as a text file for
reading. Reading commences at the beginning of the file. If the file does not
exist, fopen returns NULL.
If mode is "w", the file is opened as a text file for writing. A new (initially
empty) file is created whether the file exists or not.
If mode is "a", the file is opened as a text file for appending(writing at
the end of the file). If the file does not exist, it is created.
If mode is "r+" and the file exists, the file is opened as a text file for
reading and writing. Reading and writing commence at the beginning of the
file. If the file does not exist, fopen returns NULL.
If mode is "w+", thefile is opened as a text file for reading and writing.
A new (initially empty) file is created whether the file exists or not.
If mode is "a+", thefile is opened as a text file for reading and writing.
Reading and writing commence at the end of the file. If the file does not
exist, it is created.
The modes "ab", "rb", and "wb" have the same effect as "a", "r", and
"w" except that the file is opened as a binary file. The modes "ab+", "rb+",
and "wb+" have the same effect as "a+", "r+", and "w+" except that the file
is opened as a binary file. The mode "ab+", "rb+", or "wb+" may be written
"a+b", "r+b", or "w+b", respectively.

www.MathSchoolinternational.com
462 A P P E N D IX B

fp r in tf
#include <stdio.h>
in t f p r in t f ( FILE * file _ p o in t e r , const char *strin g, );

Writes formatted output to the file referenced by file _ p o in te r. The pa­


rameter strin g points to characters to be copied to the output, as well as
format specifications for the following arguments. The function fp r in t f
returns the number of characters written, or in case of error, it returns a
negative number. See also p r in tf and sprin tf.
fp u tc
#include <stdio.h>
in t fp u tc( in t character, FILE * file _ p o in te r ) ;

Writes character to the file referenced by file _ p o in te r. In addition,


fputc returns the character written, or in case of error, it returns EOF.
Equivalent to the function putc. See also putc and putchar.
fp u ts
#include <stdio.h>
in t fp u ts( const chair *s trin g , FILE * file _ p o in te r ) ;

Writes strin g to the file referenced by file _ p o in te r . The function fputs


does not add a newline or copy the null terminator to the output. If suc­
cessful, fputs returns a nonnegative value; in case of error, it returns EOF.
See also puts.
fre a d
#include <stdio.h>
s iz e _ t fre a d ( void *storage, s iz e _ t s iz e ,
s iz e _ t count, FILE * file _ p o in te r ) ;

Reads up to count items, each of s ize bytes, from the file referenced by
file _ p o in te r. The items are stored in memory, beginning at address
storage. The function fread returns the number of items ( not bytes) read.
fscanf

#include <stdio.h>
in t fs c a n f( FILE * file _ p o in t e r , const char * s t r in g ,. . . ) ;

Reads formatted input from the file referenced by f ile_p o in ter. The con­
verted data are stored at addresses given by the arguments that follow
string, which contains the format specifications for the data read. If the
end of the file is reached before any conversion, fscanf returns EOF; other­
wise, it returns the number of items read and stored. See also scanf and
sscanf.

www.MathSchoolinternational.com
SELECTED C + + FUNCTIO NS 463

f seek

#include <stdio.h>
in t fs e e k ( FILE * file _ p o in te r , long o ffs e t , in t base ) ;

Repositions the file position marker in the file referenced by f ile_p o in ter.
In a binary file, fseek repositions the file position marker o ffs e t bytes from
the beginning of the file (if base is equal to SEEK_SET), from the current
position of the file position marker (if base is equal to SEEK_CUR), or from
the end of the file (if base is equal to SEEK_END). In a text file, base must
be equal to SEEK_SET and o ffs e t must be either zero (in which case the file
position marker is moved to the beginning of the file) or a value returned
previously by f t e l l (in which case the file position marker is moved to
a previously saved position). If successful, fseek returns 0; otherwise, it
returns a nonzero value. See also f t e l l and rewind.

ft e ll

#include <stdio.h>
long f t e l l ( FILE * file _ p o in te r ) ;

Returns the location of the file position marker in the file referenced by
f ile _ p o in te r, or in case of error, it returns —1. If the file is a binary file,
the location is measured in bytes from the beginning of the file. If the file
is a text file, the value returned by f t e l l is useful only as an argument to
fseek. See also fseek.

fw r it e

#include <stdio.h>
s iz e _ t fw r it e ( const void ^storage, s iz e _ t s iz e ,
s ize _ t count, FILE * file _ p o in te r ) ;

Writes count items from address storage (unless an error occurs), each of
s iz e bytes, to the file referenced by file _ p o in te r. Returns the number of
items written.

gcount

#include <iostream.h>
in t istrea m ::gcount( ) ;

Returns the number of characters last read.

www.MathSchoolinternational.com
464 A P P E N D IX B

get
#include <iostream.h>
istreamfe istrea m ::g e t ( signed char* b u ff,
in t n,
char stop = ’ \n’ ) ;
istreamfe is tre a m ::g e t( unsigned char* b u ff,
in t n,
char stop = ’ \n’ ) ;
istreamfe istrea m ::g e t ( signed charfe c ) ;
istreamfe is tre a m ::g e t( unsigned charfe c ) ;
istreamfe is tre a m ::g e t( streambuffe sbu ff, char stop = ’ \n’ ) ;
in t is tr e a m ::g e t ();

In the first two versions, characters are read from the steam intothe array-
buff until the character stop, whose default value is ’ \n’ ,is encountered,
until end-of-stream, or until n - 1 characters have been read into buff,
whichever happens first. The character stop is not placed in the array buff,
nor is it removed from the stream. The method get adds a null terminator
’ \0’ .
In the third and fourth versions, the next character, white space or not,
is read into c.
In the fifth version, characters are read from the stream into the stream­
buf object sbuff until the character stop, whose default value is ’ \n’ , is
encountered. The character stop is not placed into sbuff, nor is it removed
from the stream.
In all but the last version, get returns the updated stream.
In the last version, the next character, white space or not, is returned,
or if no characters remain to be read, get returns EOF.
g e tc
#include <stdio.h>
in t g e tc ( FILE * file _ p o in te r ) ;

The function getc is equivalent to fg etc, except that getc is usually im­
plemented as a macro. The function getc returns the next character from
the file referenced by f ile _p o in te r, or if the end of the file is reached or an
error occurs, it returns EOF. See also fg etc, get char, and ungetc.
getch ar
#include <stdio.h>
in t getchar( void ) ;

Returns the next character from the standard input, or if the end of the
file is reached or an error occurs, it returns EOF. See also fgetc, getc, and
ungetc.

www.MathSchoolinternational.com
SELECTED C + + FUN C TIO N S 465

gets

#include <stdio.h>
char * g e ts ( char *storage ) ;

Reads the next line from the standard input. The “next line” consists of
all characters up to and including the next newline character or the end of
the file, whichever comes first. If at least one character is read, gets stores
at address storage all characters read except the newline that is discarded
and adds a terminating null to the end of the line. Notice that gets never
stores a newline character. If no characters are stored or an error occurs,
gets returns NULL; otherwise, gets returns the address storage. See also
fg ets.

good

#include <iostream.h>
in t io s ::g o o d ();

Returns nonzero if state is zero; otherwise, returns zero.

ignore

#include <iostream.h>
istreamft ign ore( in t count = 1, in t stop = EOF ) ;

Removes count characters from the stream, or all characters from the stream
up to stop, whichever comes first. The removed characters are not stored,
but discarded. Returns the updated stream.

isalnum

#include <ctype.h>
in t isalnum( in t character ) ;

Returns a nonzero integer if character is an alphanumeric character ( ’ a ’


through ’ z ’ , ’ A’ through ’ Z’ , or ’ O’ through ’ 9’ ); otherwise, it returns 0.

is a lp h a

#include <ctype.h>
in t isalpha( in t character ) ;

Returns a nonzero integer if character is an alphabetic character ( ’ a ’


through ’ z ’ or ’ A’ through ’ Z’ ); otherwise, it returns 0.

www.MathSchoolinternational.com
466 A P P E N D IX B

is c n t r l

#include <ctype.h>
in t is c n t r l( in t character ) ;

Returns a nonzero integer if character is a control character (integer value


decimal 127 or less than decimal 32); otherwise, it returns 0.

is d ig it

#include <ctype.h>
in t i s d i g i t ( in t character ) ;

Returns a nonzero integer if character is a decimal digit ( ’ O’ through ’ 9 ’ );


otherwise, it returns 0.

isgrap h

#include <ctype.h>
in t isgraph( in t character ) ;

Returns a nonzero integer if character is a nonblank printing character


(integer value greater than or equal to decimal 33 and less than or equal to
decimal 126); otherwise, it returns 0.

is lo w e r

#include <ctype.h>
in t islo w er( in t character ) ;

Returns a nonzero integer if character is a lowercase character (* a ’ through


’ z ’ ); otherwise, it returns 0.

is p r in t

#include <ctype.h>
in t is p r in t( in t character ) ;

Returns a nonzero integer if character is a printable character (integer value


greater than or equal to decimal 32 and less than or equal to decimal 126);
otherwise, it returns 0.

ispunct

#include <ctype.h>
in t ispunct( in t character ) ;

Returns a nonzero integer if character is a punctuation character (integer


value decimal 127 or integer value less than decimal 33); otherwise, it returns
0.

www.MathSchoolinternational.com
SELECTED C + + FUNCTIO NS 467

isspace

#include <ctype.h>
in t isspace( in t character ) ;

Returns a nonzero integer if character is a space character (space, tab,


carriage return, form feed, vertical tab, or newline— decimal 32 or greater
than decimal 8 and less than decimal 14); otherwise, it returns 0.

isupper

#include <ctype.h>
in t isupper( in t character ) ;

Returns a nonzero integer if character is an uppercase character ( ’ k ’


through ’ Z’ ); otherwise, it returns 0.

is x d i g i t

#include <ctype.h>
in t is x d i g i t ( in t character ) ;

Returns a nonzero integer if character is a hexadecimal digit ( ’ O’ through


’ 9 ’ , ’ a ’ through ’ f ’ , or ’ A’ through ’ F ’ ); otherwise, it returns 0.

labs

#include <stdlib.h>
long la b s( long in teger ) ;

Returns the absolute value of integer. See also abs and fabs.

lo g

#include <math.h>
double lo g ( double re a l ) ;

Returns the natural logarithm (log to the base e) of real.

loglO

#include <math.h>
double lo g lO ( double re a l ) ;

Returns the logarithm to the base 10 of real.

www.MathSchoolinternational.com
468 A P P E N D IX B

memchr

#include <string.h>
void *memchr( const void *block, in t character, s iz e _ t numb ) ;

Returns the address of the first occurrence of character in the first numb
bytes of the object at address block, or if character does not appear in
the first numb bytes of the object, it returns NULL. On some systems, memchr
may execute faster than strchr. See also strchr and strrchr.

memcmp

#include <string.h>
in t memcmp( const void *b lock l,
const void *block2,
s iz e _ t numb ) ;

Compares the first numb bytes of the object at address blockl with the first
numb bytes of the object at address block2. Returns a negative integer if the
item at blockl is less than the item at block2. Returns zero if the item at
blockl is equal to the item at block2. Returns a positive integer if the item
at blockl is greater than the item at block2. On some systems, memcmp
may execute faster than strncmp. See also strcmp and strncmp.

memcpy

#include <string.h>
void *memcpy( void *b lock l, const void *block2, s iz e _ t numb ) ;

Copies the first numb bytes of the object at address block2 into the object at
address blockl and returns blockl. The copy may not work if the objects
overlap. On some systems, memcpy may execute faster than memmove and
strncpy. See also memmove, strcpy, and strncpy.

memmove

#include <string.h>
void *memmove( void *b lock l, const void *block2, s iz e _ t numb ) ;

Copies the first numb bytes of the object at address block2 into the object at
address blockl and returns blockl. The objects are allowed to overlap. On
some systems, memmove may execute faster than strncpy. See also memcpy,
strcpy, and strncpy.

www.MathSchoolinternational.com
SELECTED C + + FUNCTIONS 469

open

#include <fstream.h>
void fstream base::open( const char* filename,
in t mode,
in t p tr = f i l e b u f : : openprot ) ;
void ofstream :: open( const char* filename,
in t mode = io s : : out,
in t p tr = f i l e b u f : : openprot ) ;
void ifs tre a m ::open( const char* filename,
in t mode = io s ::in ,
in t p tr = f i l e b u f : : openprot ) ;
void fstrea m ::open( const char* filename,
in t mode,
in t p tr = f i l e b u f : : openprot ) ;

Opens the file filename in mode mode with protection p tr and associates it
with an already existing object.
pcount

#include <strstream.h>
in t ostrstream ::pcount( ) ;

Returns the number of characters currently stored in the character array.

peek

#include <iostream.h>
in t is tre a m ::p e e k ();

Returns, but does not remove, the next character from the stream. If no
characters remain to be read, it returns EDF.

pow

#include <math.h>
double pow( double r e a ll, double real2 ) ;

Returns r e a llrea12. An error occurs if r e a ll is negative and real2 is not


an integer. See also exp.
p r e c is io n

#include <iostream.h>
in t i o s : :p r e c is io n ();
in t io s ::p r e c is io n ( in t new_prec ) ;

The first version returns the current precision. The second version changes
the precision to new_prec and returns the old precision.

www.MathSchoolinternational.com
470 A P P E N D IX B

p r in tf
#include <stdio.h>
in t p r in t f( const char * s t r in g ,. . . ) ;

Writes formatted output to the standard output. The parameter s trin g


points to characters to be copied to the output, as well as format specifica­
tions for the following arguments. The function p r in tf returns the number
of characters written, or in case of error, it returns a negative number. See
also fp r in t f and s p r in tf.
put
#include <iostream.h>
ostreamfe ostream ::put( char c ) ;

Writes c to the output stream and returns the updated stream.


putback
#include <iostream.h>
istreamfe istrea m ::putback( char c ) ;

Puts the character c back into the stream and returns the updated stream.
putc
#include <stdio.h>
in t putc( in t character, FILE * file _ p o in te r ) ;

The function putc is equivalent to fputc, except that putc is usually im­
plemented as a macro. The function putc writes character to the file ref­
erenced by f ile_p o in ter. In addition, putc returns the character written,
or in case of error, it returns EOF. See also fputc and putchar.
put chair
#include <stdio.h>
in t putchar( in t character ) ;

Writes character to the standard output. In addition, putchar returns the


character written, or in case of error, it returns EOF. See also fputc and
putc.
puts
#include <stdio.h>
in t puts( const char *s trin g ) ;

Writes strin g followed by a newline to the standard output. The function


puts does not copy the null terminator to the output. If successful, puts
returns a nonnegative value; in case of error, it returns EOF. See also fputs.

www.MathSchoolinternational.com
SELECTED C + + FUNCTIO NS 471

qsort

#include <stdlib.h>
void q s o rt( void *s ta rt, s iz e _ t n o_elts, s iz e _ t s iz e _ e lt ,
in t ( *cmp ) ( const void *, const void * ) ) ;

Sorts an array of size no_elts whose initial cell is at address start. The
parameter s iz e _ e lt is the size of one cell of the array in bytes. The pa­
rameter cmp is a pointer to a function that compares two elements whose
data type is the same as that of the array and returns an integer to signal
the result of the comparison. The arguments to the comparison function
♦cmp are pointers to the two items to be compared. The value of the ex­
pression * cmp ( * f i r s t , *second ) is negative if * f i r s t precedes * second
in the sorted order; *cmp( * f i r s t , *second ) is zero if * f i r s t is equal
to * second; and *cmp( * f i r s t , *second ) is positive if * f i r s t follows
♦second in the sorted order.

rand

#include <stdlib.h>
in t rand( void ) ;

Returns a pseudorandom integer in the range 0 to RAND_MAX (a constant


defined in stdlib.h). See also srand.

rdbuf

#include <strstream.h>
strstreambuf* strstreambase: : rdbuf( ) ;

Returns a pointer to the strstreambuf buffer object associated with the


strstreambase object.

rd s ta te

#include <iostream.h>
in t i o s : : rd s ta te ( ) ;

Returns the stream state (state).

read

#include <iostream.h>
istreamfe read( signed char* b u ff, in t n ) ;
istreamfe read( unsigned char* b u ff, in t n ) ;

Reads n characters into buff and returns the updated stream.

www.MathSchoolinternational.com
472 A P P E N D IX B

rewind
#include <stdio.h>
void rewind( FILE * file _ p o in te r ) ;

Repositions the file position marker in the file referenced by file _ p o in te r


to the beginning of the file. See also fseek.
scanf

#include <stdio.h>
in t scanf( const char * s t r in g ,. . . ) ;

Reads formatted input from the standard input. The converted data are
stored at addresses given by the arguments that follow string, which con­
tains the format specifications for the data read. If the end of the file is
reached before any conversion, scanf returns EOF; otherwise, it returns the
number of items read and stored. See also fscanf and sscanf.
seekg
#include <iostream.h>
istreamfe istrea m :: seekg( streamoff o f f , seek_dir d ir ) ;
istreamfe istrea m ::seekg( streampos pos ) ;

The first version moves the input stream position marker o f f bytes from d ir,
which must be one of: beg (from the beginning of the stream), cur (from the
current position), or end (from the end of the stream). The second version
sets the input stream position marker to location pos, which should be a
value returned by t e llg . Returns the updated stream.
seekp
#include <iostream.h>
ostreamfe ostream :: seekp( streamoff o f f , seek_dir d ir ) ;
ostreamfe ostream :: seekp( streampos pos ) ;

The first version moves the output stream position marker o f f bytes from
dir, which must be one of: beg (from the beginning of the stream), cur
(from the current position), or end (from the end of the stream). The second
version sets the output stream position marker to location pos, which should
be a value returned by t e llp . Returns the updated stream.
se tf
#include <iostream.h>
long i o s : : s e t f ( long spec_flags ) ;
long i o s : : s e t f ( long spec_flags, long f i e l d ) ;

The first version sets specified format flags spec_flags. In the second ver­
sion, f i e l d must be one of

www.MathSchoolinternational.com
SELECTED C + + FUNCTIO NS 473

io s ::b a s e fie ld , i o s : :a d ju stfield , f l o a t f i e l d

If f i e l d is i o s : :basef ie ld , spec_f lags must be io s : : dec (set integer base


to decimal), io s: :oct (set integer base to octal), or io s : :hex (set integer
base to hexadecimal). If f i e l d is io s :: adjustf ie ld , spec_flags must
be io s : : l e f t (set left justification), io s: :rig h t (set right justification),
or i o s :: in tern al (put fill character between sign and value). If f i e l d
is io s : :flo a t fie ld , spec_flags must be io s: :s c ie n t ific (set scientific
notation), io s: :fix e d (set fixed notation), or zero (set default notation).
In every case, s e tf returns the old format flags.

set_term in ate

typedef v o id ( *PFV ) ( ) ;
PFV set_term inate( PFV ) ;

A function for terminate to invoke may be specified by passing to set_ter-


minate a pointer to the function for terminate to invoke. In this case,
set_terminate returns a pointer to the function previously specified. If no
function is specified by set_terminate, terminate invokes abort.

set-unexpected

typedef v o id ( *PFV ) ( ) ;
PFV set_unexpected( PFV ) ;

A function for unexpected to invoke may be specified by passing to set_un-


expected a pointer to the function for unexpected to invoke. In this case,
set .unexpected returns a pointer to the function previously specified. If no
function is specified by set_unexpected, unexpected invokes terminate.

sig n a l

#include <signal.h>
void ( *s ig n a l( in t s ig , void ( ^handler ) ( in t ) ) ) ( in t ) ;

Catches a signal and invokes a function to handle the signal. If the request
can be handled, signal returns the value of handler for the previous call
to sign al for the given sig; otherwise, it returns SIG_ERR.

sm

#include <math.h>
double s in ( double re a l ) ;

Returns the sine of real, which must be in radians.

www.MathSchoolinternational.com
474 A P P E N D IX B

sinh

#include <math.h>
double sinh( double re a l ) ;

Returns the hyperbolic sine of real.

s p r in t f

#include <stdio.h>
in t sp rin tf ( char *storage, const chair * s t r in g , . . . ) ;

Writes formatted output to memory beginning at address storage, s p rin tf


adds a null terminator to the end of the output. The parameter s trin g
points to characters to be copied, as well as format specifications for the
following arguments. The function s p rin tf returns the number of characters
written (not counting the added null terminator), or in case of error, it
returns a negative number. See also fp r in t f and p rin tf.

s q rt

#include <math.h>
double s q r t( double re a l ) ;

Returns the square root of real.

srand

#include <stdlib.h>
void srand( unsigned in t seed ) ;

Seeds the random number generator. Calling srand with seed equal to 1 is
equivalent to calling the random number function rand without first invoicing
srand. See also rand.

sscan f

#include <stdio.h>
in t sscanf( const char * s tr in g l, const char * s tr in g 2 ,. . . ) ;

Reads formatted input from s trin g l. The converted data are stored at
addresses given by the arguments that follow string2, which contains the
format specifications for the data read. If the end of s tr in g l is reached
before any conversion, sscanf returns EOF; otherwise, it returns the number
of items read and stored. See also f scanf and scanf.

www.MathSchoolinternational.com
SELECTED C + + FUNCTIO NS 475

str

#include <strstream.h>
char* ostrstream :: s t r ( ) ;
char* s tr s tr e a m ::s tr ();

Returns a pointer to the character array buffer. If the buffer was allocated
by the system, the user is now responsible for deallocating it.

strcat

#include <string.h>
char * s tr c a t( char * s tr in g l, const char *string2 ) ;

Copies string2 to the end of s trin g l. Returns s trin g l (the address of the
first string). See also strncat.

strchr

#include <string.h>
char *s trc h r( const char *s trin g , in t character ) ;

Returns the address of the first occurrence of character in string, or if


character does not occur in string, it returns NULL. See also strrchr,
s trs tr, and memchr.

strcmp

#include <string.h>
in t strcmp( const char * s tr in g l, const char *strin g2 ) ;

Returns a negative integer if s trin g l is (lexicographically) less than string2.


Returns 0 if s tr in g l is equal to string2. Returns a positive integer if
s tr in g l is greater than string2. See also strncmp and memcmp.

strcpy

#include <string.h>
char *strcp y ( char * s tr in g l, const char *string2 ) ;

Copies string2 to s trin g l. Returns s trin g l (the address of the first


string). See also strncpy, memcpy, and memmove.

strcspn

#include <string.h>
s iz e _ t strcspn( const char * s tr in g l, const char *string2 ) ;

Returns the number of consecutive characters in s trin g l, beginning with


the first, that do not occur anywhere in string2. See also strspn.

www.MathSchoolinternational.com
476 A P P E N D IX B

s t r le n

#include <string.h>
s iz e _ t s tr le n ( const chair *s trin g ) ;

Returns the length of strin g (not counting the null terminator).

strn c a t

#include <string.h>
char *strn ca t( chau: * s tr in g l, const char *strin g2,
s iz e _ t max_len ) ;

Copies string2 or max_len characters from string2, whichever is shorter,


to the end of s trin g l. In either case, a terminating null is placed at the
end. Returns s tr in g l (the address of the first string). See also strcat.

strncmp

#include <string.h>
in t strncmp( const char * s tr in g l,
const char *strin g2 , s iz e _ t max_len ) ;

Let s denote the string obtained by choosing string2 or max_len characters


from string2, whichever is shorter. Returns a negative integer if s tr in g l is
(lexicographically) less than s. Returns 0 if s tr in g l is equal to s. Returns
a positive integer if s tr in g l is greater than s. See also strcmp and memcmp.

strncpy

#include <string.h>
char *strncpy( chair * s tr in g l, const char *strin g2 ,
s iz e _ t max_len ) ;

Copies exactly max_len characters (counting the null terminator ’ \0’ ) from
string2 to s trin g l. If the length of string2 is less than maLX_len, null
terminators are used to fill s trin g l. The resulting string is not not null
terminated if the length of string2 is greater than or equal to maix_len.
Returns s tr in g l (the address of the first string). See also strcpy, memcpy,
and memmove.

strp b rk

#include <string.h>
char *strpbrk( const chair * s tr in g l, const chair *strin g2 ) ;

Returns the address of the first character in s tr in g l that occurs anywhere


in string2, or if no character in s trin g l is also in string2, it returns NULL.

www.MathSchoolinternational.com
SELECTED C + + FUNCTIO NS 477

strrchr

#include <string.h>
char *s trrc h r( const char *strin g , int character ) ;

Returns the address of the last occurrence of character in string, or if


character does not occur in string, it returns NULL. See also strchr,
s trs tr, and memchr.

strspn

#include <string.h>
s iz e _t strspn( const char * s t r in g l, const char *strin g2 ) ;

Returns the number of consecutive characters in strin g l, beginning with


the first, that occur somewhere in string2. See also strcspn.

strstr

#include <string.h>
char * s t r s t r ( const char * s t r in g l, const char *strin g2 ) ;

Returns the address of the first occurrence in s trin g l of string2, or NULL


if string2 is not a substring of strin gl. See also strchr and strrchr.

sync_with_stdio

#include <iostream.h>
s ta tic void io s ::s y n c _w ith _s td io ();

Synchronizes the C + + input/output with the standard C input/output func­


tions. Anytime the C and C + + input/output libraries are intermixed, the
method sync_with_stdio should be invoked before doing any input or out­
put.

system

#include <stdlib.h >


int system( const char *strin g ) ;

Executes the command string. The value returned is implementation de­


pendent. (The value returned usually indicates the exit status of the com­
mand executed.)

tan

#include <math.h>
double tan( double re a l ) ;

Returns the tangent of real, which must be in radians.

www.MathSchoolinternational.com
478 A P P E N D IX B

tanh

#include <math.h>
double tanh( double re a l ) ;

Returns the hyperbolic tangent of real.


t e llg

#include <iostream.h>
streampos i s t r e a m : : t e llg ();

Returns the location of the input stream position marker.


t e llp

#include <iostream.h>
streampos o s tr e a m ::t e llp ();

Returns the location of the output stream position marker,


term in ate
void term inateO ;

The function terminate is called when there is an error in the exception


handling mechanism (e.g., a handler is missing for a thrown exception). The
function terminate, in turn, calls the function most recently specified by
set .terminate. If no function was specified by set_terminate, terminate
calls abort.
tie

#include <iostream.h>
ostream* i o s : : t i e ( ostream* out ) ;
ostream* i o s : : t i e ( ) ;

In the first version, out is tied to the (input) stream object, on which t i e is
invoked. If out is zero, t i e breaks the tie, if any. In either case, t i e returns
the output stream to which the (input) stream object was previously tied
or, if the stream object was not tied to an output stream, it returns zero.
In the second version, t i e returns the stream to which the (input) object
is tied, or zero, if it is not tied to an output stream.
time

#include <time.h>
tim e_t tim e( tim e_t *storage ) ;

Returns the time (typically measured in seconds elapsed since midnight,


January 1, 1970 GMT). If storage is not equal to NULL, time stores the
current time at address storage. See also d ifftim e.

www.MathSchoolinternational.com
SELECTED C + + FUNCTIONS 479

tolow er

#include <ctype.h>
in t tolow er( int character ) ;

Converts character from uppercase to lowercase and returns the converted


value. If character is not ’ A’ through ’ Z’ , tolower returns character.

toupper

#include <ctype.h>
int toupper( int character ) ;

Converts character from lowercase to uppercase and returns the converted


value. If character is not ’ a ’ through ’ z ’ , toupper returns character.

unexpected

void unexpectedO ;

When a function throws an exception not specified in its throw specification,


unexpected is called. The function unexpected, in turn, calls the function
most recently specified by set_unexpected. If no function was specified by
set_unexpected, unexpected calls terminate.

ungetc

#include <stdio.h>
int ungetc( int c, FILE *file _p o in te r ) ;

Writes c to the buffer of the file referenced by file _ p o in te r (opposite of


getc). If c is equal to EOF, the buffer is unchanged. If successful, ungetc
returns c; otherwise, it returns EOF. See also fgetc, getc, and getchar.

unsetf

#include <iostream.h>
long io s ::u n s e t f( long spec_flags ) ;

Clears specified flags spec_f lags and returns old flags.

width

#include <iostream.h>
int i o s : : w id t h ();
int io s ::w id th ( int new_width ) ;

The first version returns the field width. The second version changes the
field width to new_width and returns the old field width.

www.MathSchoolinternational.com
480 A P P E N D IX B

w rite

#include <iostream.h>
ostreamfe ostream ::w r ite ( const signed char* b u ff, in t n ) ;
ostreamfe ostream ::w r ite ( const unsigned char* b u ff, in t n ) ;

Writes n characters from the array buff to the output stream. Returns the
updated stream.

www.MathSchoolinternational.com
Appendix C

U N IX

UNIX provides utilities, such as make, to aid program development in C ++,


and C + + programs have straightforward access to the UNIX run-time li­
braries and system calls. This appendix serves as an introduction to UNIX,
but you should consult your user’s manual for your particular system.

On-line Help

man

UNIX systems usually furnish an on-line UNIX manual with clarification of


its commands. For example, the command man ( “manual” )

’/, man cp

asks the system to display to the standard output the manual pages on the
cp command. (We are using */, as the system prompt.) The man command
also may be used with benign self-reference. The command

V, man man

asks the system to display information about the man command itself. If you
want more information about the commands discussed in this appendix, you
may find it convenient to use the man command.

481

www.MathSchoolinternational.com
482 A P P E N D IX C

Producing Executable C + + Programs Under U N I X

When C + + source code is submitted to a C + + compiler, the preprocessor


first reads one or more source files, processes these files in accordance with
directives such as #def ine and #include, and produces one or more output
files that are then ready for the compilation. After the preprocessor produces
its files, the C + + compiler takes as input one or more preprocessed files and
produces as output the same number of object modules. (Some compilers
may produce additional intermediate files. For example the AT& T cfron t
compiler translates C + + source files to a C source files, which are then
compiled.) In the final stage, the linker takes as input one or more object
modules and produces as output a single load module. Only the load module
is executable. To run a C + + program under UNIX is to execute a load
module.
In this appendix, we describe the GNU project C + + compiler, which is
commonly available on UNIX systems. Other compilers, such as the A T& T
cfront compiler, are also supported under UNIX, and work similarly to the
GNU C + + compiler, although there are some differences. You should check
the specific details of the compiler on your system.
The GNU C + + compiler provides a single command g++ to accomplish
all of the stages of translation. Suppose that the program ROBOT has its com­
ponent functions spread among three files: main. C, sensors. C, and plans. C.
(The GNU C + + compiler requires that the C + + source code files have the
extension .C, .cc, or .cxx.) If we invoke the command g++ and reference the
files main. C, sensors. C, and plans. C,

*/, g++ main.C sensors.C plans.C

a single load module is produced. (The files can be listed in any order.) The
object modules produced along the way, main, o, sensors, o, and plans, o, are
deleted automatically once the load module has been produced. Each object
module receives a . 0 extension by default. The load module is named a. out
by default. To execute the load module, the user enters its name

'/, a.out

at the system prompt. This simple method of producing a load module


requires that the g++ command reference files that contain, among them,
the entire program. Of course, #include directives may be used to access
functions or macros from both standard and user-created files and libraries.
The second way of producing a load module from source code involves
separate compilation. Suppose that we want to compile each source file in
the ROBOT program after it is written. After completing the file plans. C, we
can compile it separately by issuing the command

% g++ -c plans.C

www.MathSchoolinternational.com
UNIX 483

The -c ( “compile” only) flag specifies that an object module, rather than a
load module, should be produced as output. In this case, the object module
is named plans, o. After separately compiling each of main. C, sensors. C, and
plans. C, we have the object modules main.o, sensors, o, and plans, o. These
object modules can then be linked by issuing the command
’/, g++ main.o plans.o sensors.o
The load module is named a. out. The g++ command can be invoked with
any mix of .C and .o files. For example, the command
’/, g++ main.C plans.o sensors.C
produces a load module from the source files main. C and sensors. C and the
object module plans, o. The load module is again named a. out.
The g++ command allows several options including:

Option Meaning
-c Compile separately, producing an object module rather
than a load module.
-lm Load referenced modules from the mathematics library.
-o name Name the resulting load module name instead of a. out,
the default.
-w Suppress warning messages.
-E Have the preprocessor, but not the compiler, pass over
the file and print the result to the standard output.
-0 Use the optimizer.

The flags may be used in combination. For example, the command


'/, g++ -o robot -w -0 main.C plans.o sensors.o
directs the compiler to produce a load module from the files main. C, plans, o,
and sensors.o; to give the load module the name robot; to suppress warning
messages; and to use the optimizer. The flags can occur in any order, but
the g++ command must come first, and the file names must follow the flags.

Directories and Paths


UNIX has a hierarchical directory structure. The root directory, whose iden­
tifier is the character /, has no parent directory but may have any number
of child directories or subdirectories. Any child directory has exactly one
parent directory and none or more child directories. Figure D.l depicts a
directory structure with the root directory and various subdirectories.
A file resides in a directory. Its full path name lists the names of
directories from the root down to the directory in which the file resides. For
example,
/users/fred/robotplans. C

www.MathSchoolinternational.com
484 A P P E N D IX C

users bin etc lib SYSDEBUG

projects jose betty fred helmut cpp libc.a cpp.ansi

h w k l.C games. C robotplans scratch hobbies robotplans.C

defs.h d ra ftl.C songs.old

Figure C . l UNIX directory structure.

is the full path name of the file robotplans. C, which resides in the child direc­
tory fred whose parent directory is users, which in turn is a child directory
of root (see Figure D .l). A working directory is the directory in which
you currently find yourself. For example, after you have just logged onto the
system, you may find that your working directory is /users/fred. We next
consider some UNIX commands for navigating its directory structure.

pwd, cd

The command pwd ( “print working directory” ) displays the working direc­
tory. For example, if the working directory is /users/'fred/robotplans, then
’/. pwd
displays
/users/fred/robotplans
The command cd ( “change directory” ) changes the working directory.
For example, if the working directory is /users/fred/robotplans, then
’/, cd roughdraft
changes the working directory to /users/fred/robotplans/roughdraft, assum­
ing that roughdraft is a child directory of /users/fred/robotplans. The com­
mand
'/, cd /users/fred/robotplans/roughdraft
has the same effect regardless of what the working directory is, as the com­
mand specifies the full path name.

m kdir, rmdir

Directories may be created with the mkdir ( “make directory” ) command and
destroyed with the rmdir ( “remove directory” ) command. If the working
directory is /users/fred, the command
*/, mkdir scratch

www.MathSchoolinternational.com
UNIX 485

creates a child directory whose full path name is /users/fred/scratch. The


command
*/, mkdir /users/fred/scratch
would accomplish the same thing regardless of the working directory, assum­
ing that the directory /users/fred/scratch did not already exist because it is
an error to create a directory that already exists. The command
’/, rmdir /users/fred/scratch
removes the directory /users/fred/scratch if the directory contains no files,
including subdirectories of its own. (Technically, a UNIX directory is a
special kind of file.)

rm

Files may be removed with the rm ( “remove” ) command. The command


’/, rm /users/fred/scratch/draftl.C
removes the file draft1. C from the directory /users/fred/scratch. The more
dangerous command
'/, rm /users/fred/scratch/*
removes all files, but not subdirectories, from /users/fred/scratch. The star
* is a wildcard character that matches any file name that is not itself a sub­
directory name. The extremely powerful and comparably dangerous com­
mand
’/, rm - r /users/fred
removes all files in /users/fred—including all files and subdirectories that
have /users/fred in their full path name. For example the command would
remove all files from /users/fred/scratch, /users/'fred/goodstuff, and
/users/fred/robotplans— and apply the rmdir command to these subdirecto­
ries in the process. The - r in the command stands for “recursive” to suggest
that the rm command works its way down the subdirectories starting at the
specified directory.

Is, c a t, p r, lp , more

UNIX provides various commands for displaying files, finding files, finding
contents of files, and copying or moving files. The command Is ( “list” ) lists
files in the working directory, except those such as .profile or .login whose
names begin with a period. The command
’/, Is -a
lists all such files, including those such as .profile whose names do begin with
a period. The Is command also can be used with the wildcard character
and with full path names. For example, the command

www.MathSchoolinternational.com
486 A P P E N D IX C

V, Is /users/fred/robotplans/*.h
lists all files with a .h extension in the subdirectory /users/fred/robotplans,
whatever the working directory may be.
The command cat ( “concatenate” )
*/, cat walk.C talk.C chew_gum.C
displays to the standard output the contents of files walk.C, talk.C, and
chew-gum. C if these files reside in the working directory. Again, cat may
specify the full path name, as in
’/, cat /users/fred/robotplans/defs.h
The pr ( “print” ) command behaves similarly to the cat command, ex­
cept that it does some formatting. For example, pr breaks the displayed
text into numbered pages, whereas cat does not. Either command may be
used with wildcard characters as, for example, in the command
'/, pr /users/fred/robotplans/*.h
which formats and displays the contents of all files with a .h extension in the
specified directory.
If we cat or pr a file that has too many lines to be displayed all at once,
UNIX simply displays the lines without pausing so that the top lines cannot
be seen when the bottom ones are finally displayed. A solution is to use the
more command, which stops after displaying as many lines as will fit on the
display. An example is
*/, more /users/f r ed/scr at ch/dr aiftl.C
After the screen fills, to advance one line, the user hits Return-, to advance
one full screen, the user hits the space bar.
The command lp ( “line printer” ) sends a designated file to the line
printer. For example, the command
*/, lp /users/fred/robotplans/defs.h
requests that the file /users/fred/robotplans/defs.h be printed. Later in this
appendix we explain how the pr and lp commands may be combined.
grep , fin d

The command grep ( “grab regular expression” ) can be used to search a file
for a pattern, and the command find ( “find” ) can be used to find a file.
Suppose, for example, that we want to find all occurrences of the pattern
misanthrope
in any file with a .txt extension in the working directory. The command
'/, grep misanthrope * .t x t
does the job by displaying to the standard output any line in the file that
contains the pattern misanthrope. If the pattern contains white space, it
should be enclosed in single quotation marks. For example, the command

www.MathSchoolinternational.com
UNIX 487

'/, grep ’ My Blue Heaven’ /users/fred/hobbies/songs.old


searches the file /users/fred/hobbies/songs, old for the pattern
My Blue Heaven
and displays to the standard output any lines containing this pattern.
The fin d command can be used to locate a file. Suppose, for example,
that we want to find the file plans. C. We are not sure in which directory it
resides but suspect that the directory begins /users. The command
*/, find /users -name plans.C -p rin t
asks UNIX to search for the file named plans. C, starting at the directory
/users, and to print the file’s full path name if it is found. (On some systems
the -p rin t option may not be required.) The search begins at directory
/users and descends through all subdirectories.

cp, mv

UNIX has commands to copy and move files. The command cp ( “copy” )
makes a copy of a specified file, whereas the command mv ( “move” ) moves a
specified file. For example, the command
*/, cp /users/fred/games.C /tmp/hide.C
copies the file /users/fred/games.C to the file /tmp/hide.C. If the latter file
does not already exists, UNIX creates it; if it does exist, UNIX overwrites
the previous contents with /users/fred/games.C. Accordingly, cp should be
used with caution.
The command
*/, mv /users/fred/games.C newgames.C
moves the file /users/fred/games.C to the file newgames.C in the working
directory. Again, UNIX creates newgames. C if it does not exist already and
overwrites it otherwise. After the mv command, the file /users/fred/games. C
ceases to exist; after the cp command, /users/fred/games.C continues to
exist.

The make Utility


Suppose that you are working on a large program that has many compo­
nent functions. You could put all the functions in a single file and simply
recompile the entire file every time you add a new function, correct an er­
ror, or make any other changes; but if you make only a minor change to
the file, it will become tedious to have to recompile the entire file. On the
other hand, if you divide the functions among various files and then make
changes to only a few files, you must remember exactly which files to recom­
pile; then you have to link all the object modules to produce a load module.

www.MathSchoolinternational.com
488 APPENDIX C

The UNIX make utility (also available in Turbo C + + and Borland C + + )


addresses just this problem. It allows you to divide a program’s component
functions among various files. Whenever you alter a file, it takes note of the
fact and recompiles only the altered file. It then links the resulting object
module with the others to produce an executable program. We clarify this
process with a short example.
Suppose that you have a program that computes your taxes and has the
following modules:

Module Sketch of Its Role


globals.h Definitions of macros; referenced in all functions.
defs.h Definitions used only in expenses. C.
taxes The load module (executable program).
main. C Invokes income and expenses.
income. C Computes income.
expenses. C Computes expenses.

To use the make utility, we first create a file called makefile, which looks like
taxes: main.o income.o expenses.o
g++ -o taxes main.o income.o expenses.o
main.o: glob als.h main.C
g++ -c main.C
income.o: glob als.h income.C
g++ -c income.C
expenses.o: glob als.h defs.h expenses.C
g++ -c expenses.C
The file has two types of commands. The unindented commands, such as
taxes: main.o income.o expenses.o
are dependency descriptions. A dependency description shows which mod­
ules depend on other modules in the sense that certain modules are needed to
produce another module. This particular dependency description shows that
the (load) module taxes depends on the object modules main.o, income.o,
and expenses.o. The indented commands, such as
g++ -o taxes main.o income.o expenses.o
are compile-link descriptions. A compile-link description shows what needs
to be compiled and/or linked to obtain the required module. This par­
ticular compile-link description shows the command that links main.o, in­
come, o, and expenses.o to obtain taxes. Our example makefile also shows
that main.o depends on globals.h and main.C; income.o depends on globals.h
and income. C] and expenses, o depends on globals.h, defs.h, and expenses. C.
Furthermore, we obtain main.o by compiling main.C', we obtain income.o
by compiling income. C; and we obtain expenses, o by compiling expenses. C.

www.MathSchoolinternational.com
UNIX 489

On most UNIX systems, a dependency description must begin in column


1 of makefile, and the entire description must occur on a single line. The
compile-link description occurs on its own line but on most systems must
begin with a tab character.
Once makefile has been built, we invoke the make utility by entering
'/, make
at the command level. If nothing needs to be done, the system responds
‘ taxes’ is up to date
Otherwise, the utility recompiles and links any modules that have been
changed since the last invocation of make. In our example, any change to
globals.h would result in the recompilation and relinking of all modules. By
contrast, a change to main. C would result only in the recompilation of the
module m ain. C and the relinking of m ain.o with the other object modules.
The make utility compiles all files with the -0 option for optimization. On
most UNIX systems, the make command itself can be invoked with various
options. For example, the command
'/, make - f spaceSystem
invokes the make utility, but the utility uses the file spaceSystem instead of
the file makefile.

Redirection and Pipes


In UNIX, the standard input may be redirected using < and the standard
output may be redirected using >. Suppose, for example, that we have an
executable program stock-tips that reads from the standard input, processes
the data read, and then writes to the standard output. The UNIX command
'/, stock_tips < market.dat
causes stock-tips to read from the file market, dat instead of from the standard
input. The standard input has been redirected. The command
% stock_tips > tip s .d a t
causes stock-tips to write to the file tips, dat instead of to the standard output.
The standard output has been redirected. The command
'/, stock_tips < market.dat > tip s .d a t
causes stock-tips to read from the file market.dat instead of from the standard
input and to write to the file tips, dat instead of to the standard output. Both
the standard input and the standard output have been redirected.
UNIX also supports the pipe, a utility through which one program’s
output becomes another program’s input. For example, suppose that we
have an executable program report-tips that can generate a report from

www.MathSchoolinternational.com
490 A P P E N D IX C

the output of stock-tips. We could combine the programs by executing the


following UNIX commands:

’/, stock_tips > temp.dat


*/, rep ort_tip s < temp.dat

stock-tips reads from the standard input and writes to temp.dat, after which
report-tips reads from temp.dat and writes to the standard output. A pipe
accomplishes the same result, but more conveniently:

'/, stock_tips I rep ort_tip s

The vertical bar I designates a pipe, stock-tips pipes its output into re-
port-tips, which then writes to the standard output.
A pipeline may be built out of individual pipes. Suppose that we want
the input to stock-tips to be sorted before it is formatted by report-tips and
written to the standard output and that we have a program sort-tips that
does the sorting. We could build a pipeline as follows:

*/, stock_tips I so rt_tip s I rep ort_tip s

The input to stock-tips is the standard input. Because of the first pipe, the
output of stock-tips is the input to sort-tips. Because of the second pipe, the
output of sort-tips is the input to report-tips, and report-tips writes to the
standard output.
Pipes can be combined with redirection. The following command is the
same as the preceding command except that the report is written to the file
report, dat:

*/, stock_tips I so rt_tip s I rep ort_tip s > report.d at

Pipes may be combined with various other commands in quite powerful


ways. Consider the pr, lp, more, and man commands discussed earlier. To
format the file /users/fred/robotplans/defs.h and print it, we pipe the output
of pr to lp:

'/, pr /users/fred/robotplans/defs.h I lp

Similarly, to format the file /users/project/large. C and display it a screen at


a time, we pipe the output of pr to more:

’/, pr /users/project/large.C I more

As a final example, suppose that we want a printed copy of the UNIX manual
pages on the command grep. The command

% man grep | lp

redirects the output of man to lp, which gives us a printed copy. UNIX
encourages its users to become pipers.

www.MathSchoolinternational.com
UNIX 491

Run-Tim e Libraries
System header files such as math.h usually contain function declarations
in addition to macros and typedefs. For example, the header file math.h
includes declarations such as
extern double pow( double, double ) ;
extern double f l o o r ( double ) ;
extern double c e i l ( double ) ;
because C + + requires that all functions have prototype declarations. The
system typically provides the functions themselves as object modules col­
lected in a run-time library, that is, a library of functions that an applications
program can access at run time but for which the source code is not avail­
able. For example, the functions declared in math.h are available in most
UNIX systems in the run-time library libm. a. To access the functions in this
library, an applications program typically must be linked explicitly with this
library. This can be done by using the -lm option in the g++ command. For
instance, if we wrote statistics functions that needed mathematics functions
such as pow, flo o r , and c e il, and placed these functions in the file stats.C,
our g++ command might look like
*/• g++ -c stats.C -lm
The -lm option in the command directs the linker to the library named
libm. a. If the g++ command succeeds, the object module produced, stats, o,
contains not only the functions defined in stats. C but also any functions in
libm. a, such as pow, invoked in stats. C.
In general, run-time libraries have names of the pattern libNAME.a,
where NAM E identifies the particular library. To take a second example,
most UNIX implementations provide a library named libcurses.a, which con­
tains functions for screen management and basic graphics. This library, too,
can be linked through the -1 option in the g++ command:
'/• g++ - c cusses_with_curses.C -lcurses
In the two preceding examples, the -1 option does not give the full path name
for the library because UNIX searches one or more default directories to find
a specified library. Many run-time libraries, such as libm.a and libcurses.a,
reside in the directory /usr/lib.
Programmers often find it useful to build run-time libraries of their own,
thereby allowing the component functions to be shared among many different
applications programs. It is common for each such library to have its own
header file that contains declarations for the functions implemented in the
library. The library and the header file could be located in any directory,
but it is convenient to place them in default directories so that their full
path names need not be given in #include directives or -1 options. The
C + + preprocessor, when encountering an #include directive with a header

www.MathSchoolinternational.com
A P P E N D IX C

file name in angle brackets, searches one or more default directories for the
header file. A familiar example is
#include <string.h>
Many system header files, such as string.h, reside in the directory
/usr/include. If our header files reside in a default directory, angle brack­
ets may be used in the corresponding #include directive. If our run-time
library’s name follows the pattern libNAME. a and resides in a default direc­
tory the -1 option in the g++ command need not give the library’s full path
name. We illustrate with a short example that assumes that /usr/include
is a default directory for header files and that /usr/lib is a default directory
for run-time libraries.
Suppose that we want to build a library of statistical functions for use
in a variety of applications programs. We create a header file

/usr/include/stats, h

which contains function declarations such as


// median of n doubles
extern double median( double nums[ ] , in t n ) ;

// mean of n doubles
extern double mean( double nums[ ] , in t n ) ;

// variance of n doubles
extern double va r( double nums[ ] , in t n, double mean ) ;
An applications program now can #include our header file with the direc­
tive
#include <stats.h>
Definitions for our statistical functions, which invoke mathematics functions
such as pow and flo o r, reside in the file stats. C. Accordingly, we compile
stats. C with the -lm option:
'/» g++ ~c stats.C -lm
The object module produced, stats, o, contains our functions and ones from
the mathematics library. Next, we use the object module to create a run­
time library named, say, libstats. a and we move libstats. a to a default library
directory such as /usr/lib. (On most UNIX systems, a run-time library is
created from one or more object modules by using the Id commands with
specified flags.) An applications program that needs to access our run-time
library should have the #include directive for stats.h in the appropriate
source files and should use the -1 option in the g++ command:
'/. g++ -o sm all_lies source.C -ls ta ts

www.MathSchoolinternational.com
UNIX 493

Note that the application sees no distinction between our run-time library
and one furnished by a UNIX system.

www.MathSchoolinternational.com
www.MathSchoolinternational.com
Appendix D

Borland C+-J

This appendix summarizes the commands for compiling, linking, and run­
ning a C + + program in Borland C + + . Borland C + + provides two different
ways to compile programs. The first way is to issue UNIX-like instructions
from the command line, and the second way is to use a windows-like envi­
ronment that includes a compiler, linker, editor, and debugger. We discuss
each method in turn.

T h e C om m and Line Com piler

We first discuss the situation in which the entire program resides in one file,
say convert.cpp. (Borland C + + files typically use the extension .cpp.) To
compile and link the program, we issue the command
C> bcc convert.cpp
(We assume that C> is the system prompt.) The executable file is named
convert.exe. To rim the program, we type
C> convert
To compile and link multiple source modules that make up a program,
we proceed as follows. Suppose that the program comprises the modules se­
ries. cpp, sum. cpp, and transform, cpp. To compile and link the three modules
from the command line, we issue the command
C> bcc series.cpp sum.cpp transform.cpp
The name of the load module produced, series.exe, is derived from the first
file listed. To run the program, we type

495

www.MathSchoolinternational.com
496 A PPE N D IX D

C> series
It is also possible to compile each file individually and then link the
resulting object files. In this case, we issue the commands
C> bcc -c series.cpp
C> bcc -c sum.cpp
C> bcc -c transform.cpp
At this point, we have the object modules series, obj, sum. obj, and trans­
form, obj. To link these object modules and produce the load module se­
ries. exe, we issue the command
C> bcc se rie s.o b j sum.obj transform.obj

T h e Integrated Environment

Borland C + + also provides an integrated environment, which is a menu-


driven system that includes a compiler, linker, editor, and debugger. From
DOS, the integrated environment is invoked by issuing the command
C> be
or from within windows, the user clicks on the Borland C + + icon. In either
case, the user is presented with a screen divided into two windows— a window
at the top to edit a file and a window at the bottom for messages (see Figure
D .l). In addition, a menu is given at the top of the screen and a reference
line is given at the bottom. A menu item can be selected by using a mouse
to click on the desired item. Alternatively, a main menu item can be selected
by pressing Alt and the identifying letter of the desired item simultaneously,
and pop-up menu items can be selected by pressing the indicated letter. For
example, to select the compile option on the main menu, press Alt-c, and to
select the make option on the compile pop-up menu, type m.
To create a program that resides in the file convert, cpp, select File on
the main menu, and then select Open on the file pop-up menu. In the box
provided, type convert.cpp, the name of the file to edit, and hit Return. At
this point, Borland C + + transfers control to the editor and the screen will
be that shown in Figure D.l. Type in the program. Figure D.2 summarizes
some of the most commonly used editor commands.
After typing in the program, to compile, link, and run it, select Run
on the main menu, and then select Run on the run pop-up menu. Borland
C + + tries to compile and link the program and, if successful, it then runs
the program. When the program is run, the screen clears, the program
executes, and control is returned to the Borland C + + screen. To view the
previous screen in which the program was run, hit AU-F5. To return from
the program screen to the Borland C + + screen, hit any key.
If an error is detected by the compiler or linker, a message will be dis­
played in a special window that gives information about the compilation and
linking process. To correct the errors, first leave the compiler by hitting any

www.MathSchoolinternational.com
BO RLAND C + + 497

= F ile Edit Search. Run Compile Debug Project Options W indow Help
- p [ > ] ----- COUVEB,T.CPP = ------- 8 = [ T ] =f]


1:1 = • < ■ I I I I I I I I I I I I I I I I I I I I I I I I I I I I I I I II .J
---------------------------------- M e s s a g e -------------------- 1-

F l Help F2 Save F3 Open A l t —F9 Compile F9 Make FIO Menu

Figure D .l Borland C + + integrated environment screen.

key. To correct the first error, hit Return. You will automatically be placed
into the editor, and the cursor will be at the position in the file that caused
the error message to be generated. You can now correct this error.
To correct the next error, click on Next Msg at the bottom of the screen
or hit Alt-F8; either moves the cursor to the line that caused the next error
message. Since you remain in the editor, the next error can immediately
be corrected. Clicking on Prev Msg or hitting Alt-F7 moves to the previous
error, and clicking on Next Msg or hitting Alt-F8 moves to the next error.
After correcting all the errors, the program can be rerun by selecting Run
on the main menu and then selecting Run on the run pop-up menu.
To save the editor file, select File on the main menu and then Save on
the file pop-up menu, or hit F2. To leave any pop-up menu, hit Esc. To
leave the integrated environment and return to windows or DOS, select File
on the main menu and then Quit on the file pop-up menu, or type Alt-x.
When the convert program is successfully compiled and linked, the ex­
ecutable file convert.exe is created. This file can be run from the MS-DOS
command line by typing
C> convert
To compile, link, and run a C + + program divided among two or more
source files in the integrated environment, we first use the editor to create the

www.MathSchoolinternational.com
498 A P P E N D IX D

Cursor Movement Insert/Delete


Movement Command Action Command
Character left Left arrow Insert mode on/off Ins
Character right Right arrow Delete character Del
Line up Up arrow Del char left Backspace
Line down Down arrow Del word right Ctrl-t
Word left Ctrl-a Delete line Ctrl-y
Word right Ctrl-f
Start of line Home
End of line End
Page up PgUp
Page down PgDn
Beginning of file Ctrl-qr
End of file Ctrl-qc

Find/Replace/Block Commands
Action Command
Find Ctrl-qf
Find/replace Ctrl-qa
Begin block Ctrl-kb
End block Ctrl-kk
Copy block Ctrl-kc
Delete block Ctrl-ky
Hide/display block Ctrl-kh
Move block Ctrl-kv

Figure D.2 Commands for the Borland C + + editor.

www.MathSchoolinternational.com
BORLAND C + + 499

files, say series, cpp, sum. cpp, and transform, cpp. We then create a “project”
that tells Borland C + + the names of the files that make up the program.
To open a project, select Project on the main menu and then select Open
on the project pop-up menu. In the space provided, type the name of the
project. In this example, we might select the name series. At this point,
the message window at the bottom of the screen is replaced by the project
window. To add files to the project, select Project on the main menu and
then select Add on the project pop-up menu. A window pops up into which
the names of the files that make up the project can be entered. Type in the
file names series, cpp, sum. cpp, and transform, cpp, and terminate each with
Return. After each file name is entered, it is also displayed in the project
window. After all the files are added, hit Esc to leave the add pop-up
window. To compile the program, select Compile on the main menu. Then
in the compile pop-up window, select Make. Borland C + + tries to compile
and link all files in the project into an executable file. Errors are flagged and
corrected, as described previously. After the files are successfully compiled
and linked, hit Return to leave the compile message window. To rim the
program, select Run on the main menu, then select the Run option on the
run pop-up window.
When the program is successfully compiled and linked, the executable file
series.exe, whose name is derived from the project name series, is created.
This file can be run from the MS-DOS command line by typing
C> series

www.MathSchoolinternational.com
i

www.MathSchoolinternational.com
Hints and Solutions to
Odd-Numbered Exercises

Section 1.1

1. Films, directors, stars.

3. The toplevel function main would invoke functions such as prompt.user,


geLresponse, and find-film. A function such as find-film would invoke
functions such as open.file, close-file, and display-films.

5. Useful objects include pages, sections, paragraphs, and headers. The


toplevel function main would invoke functions such as write-chap-opener,
puLpage.no, and writesec-opener.

7. When code that has been carefully designed and tested is reused, the
reused version will perform as reliably as the original. Since object-
oriented design provides a cleaner separation of the components, mainte­
nance— being easier— will also be more reliable.

Section 1.2

1. Other methods might support standard operations on strings such as


concatenation, substring extraction, copying, and string comparison.

3. Method store should copy the characters passed into the array data and
set len to the length of the string. Method length should simply return
len.

5. class stack {
in t data[ 100 ] ;
in t top;

501

www.MathSchoolinternational.com
502 HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES

p u b lic :
void i n i t ( ) ;
void push( in t ) ;
in t popO ;
>;
Method in it would be invoked to set up an empty stack by initializ­
ing top to —1. Method push would increment top and then store the
value passed in data[ top ]. Method pop would copy the value in
data[ top ], decrement top, and then return the saved value.

7. s.push( 10 ) ;
s_arr [ 8 ] .popO ;

Section 1.3

1. class book {
char t i t l e [ 100 ] ;
char author[ 100 ] ;
long id_number;
public:
void s t o r e _ t it le ( char* ) ;
void store_author( char* ) ;
void store_id_number( long ) ;
>;

3. class person {
p u b lic :
char name[ 100 ] ;
char address[ 100 ] ;
>;
Derive a class book_borrower from book and person and add a flag to
indicate whether the person has borrowed the book.

5. Multiple inheritance can simplify the construction of new classes. For


example, if there is a class to handle input and a class to handle output,
we can derive an input/output class from these two using multiple inher­
itance. If only single inheritance is available, we would have to derive
the input/output class from either the input class (and add the output
members) or the output class (and add the input members), which is awk­
ward and requires repeating code. A disadvantage of multiple inheritance
is that it adds considerable complexity to the language.

Section 1.4

1. book* book_array[ 50 ] ;

www.MathSchoolinternational.com
H INTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 503

fo r ( i = 0; i < 50; i++ )


book_array[ i ] -> p r in t ();

3. The following code shows one solution in C. The idea is to store pointers
to the various drawing functions in the array.

void d raw _circle( void ) ;


void draw_box( void ) ;

typedef void ( *DRAW_PTR ) ( void ) ;

DRAW_PTR com posite_fig[ 100 ] ;

/* I n it ia liz e composite_fig. For example,

com posite_fig[ 0 ] = draw_circle;


com posite_fig[ 1 ] = draw_box;

e tc .
*/

fo r ( i = 0; i < 100; i++ )


com posite_fig[ i ] 0 ;

Section 2.1

I. good_jobs jo b l, job2;

3. string s i, s 2 ;

5. This statement is erroneous since it is illegal to change the values in the


storage to which p points.

7. This statement is erroneous since it is illegal to change the value of p.

9. Legal

II. // This program reads up to 100 flo a t s from the


// standard input, computes the average of the numbers
// read, and then prin ts each number and it s absolute
// difference from the average.

// The statement
//
// return EXIT_SUCCESS;
//
// is explained in Section 2.2.

www.MathSchoolinternational.com
504 HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES

#include <stdio.h>
#include <math.h>
#include <stdlib.h >

// can handle at most max numbers


const int max = 100;

int main()

flo a t sum, x, a [ max ] ;


int count;

count = 0;
sum = 0.0;
while ( count < max )
if ( scanf( "%f", &x ) != EOF ) {
a [ count++ ] = x;
sum += x;
>
else
break;

if ( count == 0 ) {
p r in t f ( "No numbers read\n" ) ;
return EXIT_SUCCESS;
>

const flo a t average = sum / count;

p rin tf ( ""/.10s |x - average I\n", "x" ) ;


// fabs returns the absolute value as a
// double so needs a cast to flo a t
fo r ( int i = 0; i < count; i++ )
p rin tf ( "°/,10f '/.f\n", a [ i ] ,
( flo a t ) fa b s ( a [ i ] - average ) ) ;

return EXIT_SUCCESS;
>

Section 2.2

1. void move_arm( in t, flo a t , flo a t ) ;

3. Portable

www.MathSchoolinternational.com
HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 505

5. Portable

7. Portable

9. x = 5, y = -12

11. x = 4, y = -11

13. in t* new_index_C( const int a [ ] , const in t i )

return &a[ i - 1 ] ;
>
The first line would be rewritten as

val = *new_index_C( a, 8 ) ;

The second line would be rewritten as

*new_index_C( a, 8 ) = -16;

15. void p rin t( s trin g * s tr )

p r in t f( "*/,s\n", s t r .s ) ;
>

17. Function prin t promises not to modify the structure to which fp points;
however, print then passes fp to fp r in t f which does modify the struc­
ture to which fp points. The error can be corrected by omitting the
modifier const in p rin t’s header.

Section 2.3
1. 18 4

3. delete dbl_p tr;

5. d e le te [ ] s tr;

7. Add the statement

if ( current == 0 )
return 0;

after the statement

current = f i r s t = new elephant;

9. p tr = ftodometer: : miles;

www.MathSchoolinternational.com
506 HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES

11. od2.*ptr = 15004;

13. p = &odl;

15. void ( odometer::* f_ p tr ) ( long ) ;

17. ( o d l. *f_ p t r ) ( 69402 ) ;

Section 2.4
1. cin » i » x » s tr;

3. The program will go into an infinite loop since the newline will never be
read and discarded.

Section 3.1
1. Both data members and methods are private by default.

3. union Sample {
p riv a te :
int x;
flo a t y;
char c;
p u b lic :
void msg( char* ) ;
>;

5. class Dilemma {
enum Horn { Hornl, Horn2 >;
char h o rn l[ 100 ] ;
char horn2[ 200 ] ;
p u b lic :
int horn_crushed( Horn ) ;
void re s o lv e _p e a c e fu lly ();
>;

7. In the class created with the keyword struct, all datamembers and
methods are public by default. In the class created with thekeyword
class, all data members and methods are private by default.

9. Dilemma d l; // create a Dilemma object


... // manipulate it
// invoke d l ’ s horn_crushed method
dl.horn_crushed( Hornl ) ;

11. class Employee {


enum Gender { Female, Male } ;
char lname[ 25 ] ; // la s t name

www.MathSchoolinternational.com
HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 507

char fname[ 25 ] ; // f i r s t name


char addr[ 100 ] ; // address
char dept[ 4 ] ; // department
flo a t s a l; // base salary
flo a t bonus; // yearly
int age; // in years
int gender;
p u b lic :
void re a d _ b a s ic s ();
void w r it e _ b a s ic s ();
flo a t compute_taxes();
void compute_raise( flo a t ra is e ) ;
int y e a rs _ in _ s e rv ic e ();
void downsize( ) ;
>;

13. Yes.

15. Yes, but a class typically has methods.

17. A class declaration must end with a semicolon. The correct declaration
is:

class C {
p u b lic :
void w rite ( ) ;
>; // must end in semicolon

19. No.

Section 3.2
1. // return top item without popping
char Stack::view _top()
{
if ( empty( ) )
return EmptyFlag;
else
return items[ top ] ;
>

3. Reasons for: By having f u l l and empty as private methods, we practice


information hiding: the user can be spared details that are not essential
to Stack manipulation. Methods push and pop, which do belong to the
public interface, are sufficient for basic Stack manipulation. Reasons
against: By having f u l l and empty as public methods, we would give

www.MathSchoolinternational.com
508 HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES

the programmer greater flexibility. In certain applications, it may be


convenient for the user to test Stack status directly through calls to
f u l l and empty.

5. // substitute new fo r old on the Stack


void S tack:: stack_sub( char c, char s )

fo r ( in t i = 0; i <= top; i++ )


i f ( item s[ i ] == c )
item s[ i ] = s;
>

Section 3.3

I. No.

3. // declaration fo r Mystery destructor


M ystery:: "Mystery( ) ;

5. Constructors for the same class must differ in either the number or the
data type(s) of their arguments. C has two constructors that expect a
single char* argument.

7. Yes.

9. No, constructors should be public because they typically are invoked by


the compiler wherever a class object is created.

II. A C constructor cannot take an argument of type C. However, a C con­


structor can take a C reference as an argument:

class C {
in t x;

p u b lic :
C( Cft ) ; // ok

>;

13. No.

15. A constructor must not return a value, not even void.

17. The destructor automatically frees the storage to which p tr points when­
ever a C object is destroyed.

19. One.

www.MathSchoolinternational.com
HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES 509

21. A const data member must be initialized in a constructor’s header. The


correct version is:

// y must be in it ia liz e d in header


// x could be in it ia liz e d in header
// or in body
C( int a l, flo a t a2 ) : y ( a2 ) { x = a l; }

Section 3.4

1. void Z ip C :: shorten()
{
i f ( s t r le n ( p tr ) == MinZip )
return ;
char temp[ BigZip + 1 ] ;
// save o rig in a l
strcpy( temp, code ) ;
d e le te [ ] code;
// shorten
code = new char[ MinZip + 1 ] ;
strncpy( code, temp, MinZip ) ;
code[ MinZip ] = ’ \0’ ;
>

3. // extract 5 -d ig it ZipC and


// store in return_val, which invoker provides
void ZipC::getMainZip( char* return_val )

strncpy( return_val, code, MinZip ) ;


re tu rn _val[ MinZip ] = ’ \0’ ;
}

Section 3.5

1. The definition’s header should be

int D i e t :: operator> ( const Diet d ) const

instead of

int D ic t ::> ( const Diet d ) const

3. // sample use of D ie t: : operator> using


// method syntax, where d l and d2 are
// two dictionary objects
if ( d l.o perato r>( d2 ) )

www.MathSchoolinternational.com
HINTS AN D SOLUTIONS TO ODD-NUM BERED EXERCISES

Because the comma operator is binary, it could be overloaded to extend


arithmetic or relational operators to a class. For example, operator,
might be used as an exponentiation operator for an In teger class.

class C {
char* strin g;
flo a t flo a te r ;

p u b lic :
in t operator==( const C ) const;
>;

in t C::operator==( const C arg ) const

return flo a t e r == a r g .flo a te r &&


strcmp( strin g , a rg .s trin g ) ;
>

The member operator may not be overloaded.

.6

Complex Complex::operator+=( const Complex c )

return Complex( re a l += c .r e a l,
imag += c . img ) ;
>

Complex Complex::operator/=( const Complex c )

double abs_sq = c .r e a l * c .r e a l + c.imag + c.imag;

return Complex( re a l =
( re a l * c .r e a l + imag * c.imag ) / abs_sq,
imag =
( imag * c .re a l - re a l * c.imag ) / abs_sq ) ;
}

The const in the parameter list indicates that the the parameter is to be
read only. The const after the parameter list indicates that the object’s
data members (re a l and imag) are to be read only.

Complex c l ( 9.9, 8.8 ) ;


Complex c2( 3.3, 4.4 ) ;
Complex c3;

www.MathSchoolinternational.com
HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 511

c3 = c l.o p e ra to r-( c2 ) ; // method syntax


c3 = c l - c2; // operator syntax

Section 3.7

1. Any function, including main, may be a friend. Here is a sample,

class C {

frien d in t main( in t, char*[ ] ) ;


>;

3. The principle of information hiding encourages the programmer to hide


a class’s internal representation (i.e., its data members) from all func­
tions except the class’s own methods; and frie n d functions violate this
principle.

5. One obvious example is an overloaded operator such as operator+. A


code slice such as

Complex c l ( 1.1, 2.2 ) ;


Complex c2;

c2 = 99.9 + c l; // i l l e g a l i f operator+ is a method

is legal if operator+ is overloaded as a frie n d to Complex but not if it


is overloaded as a method. If operator+ is overloaded as a method, then
the first operand (in this case, 99.9) must be a Complex object because
its method is being invoked.

7. There are two errors:

c3 = c l . operator+( c2 ) ; // method syntax!


c3 = +( c2, c l ) ; // should be c3 = operator+( c2, c l )

Section 3.8

1. #include <stdio.h>
#include <iostream.h>
#include <string.h>
#include <assert.h>

const in t MinZip = 5;
const in t BigZip = 10;
const in t MaxZip = 32;
const in t InitChax = ;

www.MathSchoolinternational.com
HINTS AND SOLUTIONS TO ODD-NUMBERED EXERCISES

const int Hyphen = ;


const int SuffixLen = 4;

class ZipC {
char* code;
p u b lic :
ZipCO;
ZipC( const char* ) ;
ZipC( const unsigned long ) ;
"ZipC O ;
void w rite ( ) { cout « code « endl; }
void expand( const char* ) ;
>;

#define ZipC_inv ( s tr le n ( code ) <= MaxZip )

Z ip C ::ZipCO

code = new char[ MinZip + 1 ] ;


fo r ( int i = 0; i < MinZip; i++ )
code[ i ] = InitChar;
code[ i ] = ’ \0’ ;
a s s e rt( s tr le n ( code ) == MinZip && ZipC_inv ) ;

ZipC::ZipC ( const char* zip str )

int len = ( s tr le n ( zip str ) <= MaxZip )


? s tr le n ( zip str ) : MaxZip;
code = new char[ len + 1 ] ;
strncpy( code, z ip s tr, len ) ;
code[ len ] = ’ NO*;
a s s e rt( s trle n ( code ) <= MaxZip && s t r le n ( code ) <= len
&& ZipC_inv ) ;
>

ZipC::ZipC( const unsigned long zipnum )


■c
char b u ffe r [ BigZip + 1 ] ;
s p r in t f( b u ffer, M
,/,0*ld", MinZip, zipnum ) ;
b u f fe r [ MinZip ] = ’ NO’ ;
code = new chair[ s tr le n ( bu ffer ) + 1 ] ;
strcpy( code, bu ffer ) ;

www.MathSchoolinternational.com
HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES 513

a s s e rt( s tr le n ( code ) <= s tr le n ( b u ffe r )


&& s tr le n ( code ) == MinZip && ZipC_inv ) ;
>

ZipC: : "ZipCO
{
d e le te [ ] code;
>

void ZipC::expand( const char* s u ffix )


{
a s s e rt( s tr le n ( code ) == MinZip
&& s t r le n ( s u ffix ) == SuffixLen && ZipC_inv ) ;
char temp[ BigZip + 1 ] ;
char previous[ MinZip + 1 ] ;

strcpy( previous, code ) ;


d e le te [ ] code;
code = new char[ BigZip + 1 ] ;
sprin tf ( code, "%s'/,c,/,s", previous, Hyphen, s u ffix ) ;
a s s e rt( s tr le n ( code ) <= BigZip
&& strlen ( code ) == MinZip + Suff ixLen + 1
&& ZipC_inv ) ;

Section 3.9

1. The class is C< class Typ > not C. The correct code is

template< class Typ >


C< class Typ > : :C ()
{

>

3. Stack< String*, 2000 > s;

5. template< class Typ >


class Stack {
int FullStack;
enum { EmptyStack = -1 >;
Typ * items;
int top;
p u b lic :
Stack( int ) ;

www.MathSchoolinternational.com
514 HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES

"StackO ;
void push( Typ ) ;
Typ popO ;
int empty( ) ;
int f u l l O ;
>;

template< class Typ >


Stack< Typ > ::S tack ( int size = 100 )

items = new Typ[ FullStack = size ] ;


top = EmptyStack;
>

template< class Typ >


Stack< Typ > : : "StackO
{
d e le te [ ] items;
>

template< class Typ >


void Stack< Typ >::push( Typ c )

items[ ++top ] = c ;
>

template< class Typ >


Typ Stack< Typ > : :pop()

return items[ top— ] ;


>

template< class Typ >


in t Stack< Typ > : : f u l l ( )
{
return top + 1 == FullStack;
>

template< class Typ >


int Stack< Typ >::em pty()

return top == EmptyStack;


}

www.MathSchoolinternational.com
HINTS AND SOLUTIONS TO ODD-NUMBERED EXERCISES 515

7. Stack< Complex* > s ( 1000 ) ;

Section 4.1

1. String s i; // default
String s2( 1000 ) ; // convert
String s3( "fre d " ) ; //convert
String s4( s3 ) ; // copy

3. Consider the code slice

class C {
chax* name;
p u b lic:
C( chax* n )
■C
name = new chax[ s tr le n ( n ) + 1 ] ;
strcpy( name, n ) ;
>

>;

C c l ( "bar" ) ;
C c2( c l ) ;

The compiler’s copy constructor does a member-by-member assignment,


which means that cl.name and c2.name point to the same cells. What
we probably want is for the two pointers to point to different cells that
store the same chars.

5. const in t MaxStrLen = 80;

// w rite String to standard output


// with a terminating newline
in t S tr in g ::w r ite ()

puts( s tr ) ;
putc( ’ \n’ ) ;
return s tr le n ( s tr ) ;
}

// w rite String to sp e c ifie d f i l e


in t S tr in g ::w r ite ( FILE* o u t file )
i
i f ( !o u t file )
return W riteF ail;

www.MathSchoolinternational.com
516 HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES

fp u ts( s tr , o u tfile ) ;
fp utc( ’ W , o u t file ) ;
return s tr le n ( s tr ) ;
>

// read String from standard input


in t S t r in g ::read()
{
g e ts ( s tr ) ;

return len = s tr le n ( s tr ) ;
>

// read String from designated f i l e


in t S trin g ::re a d ( FILE* i n f i l e )

i f ( !i n f i l e )
return ReadFail;
else {
fg e t s ( s tr , MaxStrLen, i n f i l e ) ;
return len = s tr le n ( s tr ) ;
>
>

7. void S t r in g :s u b s t it u t e ( const char c )

fo r ( int i = 0; i < len; i++ )


s t r [ i ] = c;
>

9. in t S t r in g :‘.operator ! = ( const String s ) const

return strcmp( s tr , s .s t r ) != 0;
>

11. Variable compare_op is a pointer to a String function or function op­


erator that returns an int and expects a single const argument of type
String.

13. // take ErrorsIO out of class declaration


enum ErrorsIO { ReadFail, W riteFail } ;
class String -[

>;

www.MathSchoolinternational.com
HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 517

15. There is a function call. Recall that compare_op points to either


operator> or operatorc, which are overloaded as String methods. So

( a [ j ] . *compare_op ) ( a [ next ] ) ;

invokes one of a [ j ] ’s methods, either operator< or operator>, on


argument a [ next ]. Note that each of the overloaded operators expects
a single argument of type String.

17. Data member s tr points to a char vector, i.e., to a sequence of char cells
rather than to a single, standalone char cell. Therefore, the storage is
freed with d elete [ ] rather than with delete.

19. String*, i.e., pointer to String

21. th is cannot be the target in an assignment expression.

23. The const in the parameter list means that the String argument is read
only. The const to the right of the parameter list means that the object
whose operator+ method is invoked is read only.

Section 4.2

1. String s i; // defau lt
String s2 = s i; // copy
String s3; // defau lt
s3 = s i; // assignment
String s4( "g lo ry days by bs" ) ; // convert
String s5; // defau lt
s5 = s4; // assignment
String s6 = s5; // copy
String s7 = S trin g ( "judy blue eyes" ) ; // convert

3. A String vector (i.e., contiguous Strings allocated through the new op­
erator) cannot be given initial values. All Strings in the vector are
initialized through the default constructor.

5. Yes.

7. The compiler’s overload of the assignment operator uses member-by-


member assignments and the String class contains a pointer str. In
a code slice such as

S trin g s l ( "fo o " ) ;


String s2;
s2 = s i;

www.MathSchoolinternational.com
518 HINTS AND SOLUTIONS TO ODD-NUMBERED EXERCISES

we want s i . s tr and s2. s tr to point to different cells that hold the same
chars. The compiler’s assignment operator would result in the two s tr
data members pointing to the same vector.

9. The overloaded assignment operator first invokes the d elete [ ] operator


to free the storage to which s tr currently points. The operator next
allocates new storage sufficient to hold the chars to which parameter
strarg points. Finally, the operator uses strcpy— which expects a null-
terminated array of char— to copy strarg into str. If we allowed a
Strin g to be assigned to itself in a code slice such as

S trin g s l ( "jean" ) ;
s i = s i;

then s i . s t r would not point to a null-terminated array of char when


strcpy is invoked. The almost inevitable result is an access violation.

11. It is hard to imagine such a situation. The rule of thumb for writing your
own copy constructor and assignment operator is the same: You should
do so if the data members include a pointer.

13. No. Call by reference involves a pointer to an object, not a copy of the
object. Therefore, the copy constructor is not needed in call by reference.

15. Even if the compiler provided a default overload of op erator! =, the over­
loaded operator would use member-by-member tests for equality. In a
code slice such as

S trin g s l ( "fo o " ) ;


S trin g s2( "fo o " ) ;
i f ( s i != s2 )

we presumably want the i f test to fail even though s i . s t r and s2.s tr


point to different cells because the cells to which they point contain the
same chars.

Section 4.3

1. class Cl {

>;
class C2 ■[
frien d Cl;

www.MathSchoolinternational.com
HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES 519

class C3 {
frien d C2;

>;

3. No.

5. No. By making A a frie n d to B, we do not thereby make B a frie n d to


A.

Section 4.4

1. Yes.

3. void BST: :postorderO

postorderAux( root ) ;
}

void BST::postorderAux( Node* n )


{
// i f current subtree is empty,
// return as there are no nodes
/ / t o traverse in i t
i f ( n -> emptyO )
return;

postorderAux( n -> lc ) ; // traverse l e f t subtree


postorderAux( n -> rc ) ; // traverse rig h t subtree
n -> w r ite O ; // v i s i t Node
>

5. If a method is not part of a class’s public interface, then the method


should be private. For example, if method Ml is part of a functional
decomposition of method M, then it makes sense for Ml to be p riva te
because Ml clearly is not part of the class’s public interface. In different
terms, a method should be p riva te when the only functions that invoke
it are other methods or frie n d functions.

7. The default constructor has the assignment statement

root = tre e = new Node;

as its body.

9. The BST method addNodeAux accesses the Node data members val, lc,
and rc.

www.MathSchoolinternational.com
520 HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES

11. BST bst; // create a BST

Section 4.5

1. The only thing that needs to be changed is method stackNodesAux:

// preorder tra versa l


void IterB S T :: stackNodesAux( Node* n )

if ( n -> emptyO )
return;

nodeStack[ nextNode++ ] = n;
stackNodesAux( n -> lc ) ;
stackNodesAux( n -> rc ) ;
>

3. IterBST methods need to access root and count in BST and lc and rc
in Node.

5. Neither Node nor BST methods access any IterBST data members or meth­
ods.

7. Node* IterBST: :getNextNodeO


{
i f ( nextNode >= nodeCount )
return 0;
Node* r e tv a l = nodeStack[ nextNode ] ;
nextNode++;
return re tv a l;
>

Section 4.6

I. No, a s ta tic data member must be defined outside all blocks.

3. A s ta tic data member must not be defined inside a block. In this ex­
ample, X: :sX is defined inside main’s body.

5. A s ta tic data member must be defined without the keyword sta tic.

7. Yes.

9. A frie n d function has access to all data members in a class, whereas a


s ta tic method has access only to s ta tic data members in a class.

II. No. A destructor belongs to a specific object in a class, not to the class
as a whole.

www.MathSchoolinternational.com
HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 521

13. A s ta tic data member is tied to a particular class, whereas a global


variable is not. Further, a s ta tic data member is p rivate by default,
which promotes information hiding.

Section 5.1

1. B

Dl D2 D3

/
DD1

5. A superclass is called a base class in C + + . A subclass is called a derived


class in C + + .

Dl D2

DD 1
/
/
DDD1

/
DDDD1

DDDDD1
/
B has directly derived classes D l and D2. B has in d irectly derived classes
DD1, DDD1, DDDD1, and DDDDD1.

9. T h e error is

y = x;

because x is accessible only w ith in class A.

11. T h e error is

p tr -> f l = 3.14;

www.MathSchoolinternational.com
522 HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES

because f l is protected, not public, in A. Therefore, f l cannot be


accessed by a nonmethod or a nonfriend such as main.

13. class B {

class D : public B {

>;

15. »
t
Q
t
p

17. A protected data member or method is accessible only within a class hi­
erarchy, whereas a public data member or method is globally accessible.
Accordingly, a protected data member or method exemplifies informa­
tion hiding, a guiding principle of object-oriented programming.

19. The two references to b l . f l in main are illegal. Because inheritance


from A to B is private, the protected data member f l in A becomes
p riva te in B; and a p riva te data member is accessible only by methods
or friends— and main is neither. The error would remain even if the
inheritance from A to B were protected or public because an inherited
protected member cannot become globally accessible (i.e., public) in a
derived class.

21. The error is

b l.x = 8 ;

Since x is protected in B, it is not accessible outside B.

23. numl = n l;
num2 = n2;
num3 = n3;

25. The error is

a.x = 1;

in B: : f 2. The x inherited from A is accessible in B, which means that the


reference to x in method f 1 is a reference to B: : x and is therefore legal.

www.MathSchoolinternational.com
HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES 523

However, the data member a . x is not accessible in B because a is an A


object but not a B object. Therefore, the reference to a.x in method f2
is illegal.

Section 5.2

1. D’s constructor must explicitly invoke B’s parameterized constructor be­


cause B does not have a default constructor.

3. #include <iostream.h>
class A {
in t dummy;
pu b lic:
A () { cout « "A ::A " « endl; }
>;

class B : public A {
in t dummy;
p u b lic:
B() : A () { cout « "B::B" « endl; >
>;

class C : public B {
in t dummy;
p u b lic:
CO : BO { cout « "C::C" « endl; >
>;

A a l;
B b l, b2;
C c l, c2, c3;

5. No.

7. Yes. Here is an example:

class B { // base class


in t x;
>;

class D : public B { derived class


p u b lic :
DO { cout « "D::D" « endl; }
>;

D d; // D::D fir e s

www.MathSchoolinternational.com
524 HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES

A derived class is a specialization of a base class. The base class construc­


tor is used to take care of initialization, etc., for the part of the object
that comes from the base class.

S ection 5.3

1. Desktop is a derived class with respect to Computer but a base class with
respect to WS and PC.

3. // Desktop machines comprise workstations and


// personal computers,
class Desktop : public Computer {
enum { True = 1, False = 0 } ;
protected:
in t superscalar; // True or False
Desktop( flo a t a l, // a r ith -lo g ic
flo a t c, // control
flo a t m, // memory
flo a t io , // io
flo a t t , // ct
char* n, // name
flo a t 1, // lower bound
flo a t u ) // upper bound
: Computer( a l, c, m, io , t , n, 1, u ) { }
>;

5. A good place to get current data on PC technology is from magazines


such as Byte and P C Magazine. Vendor catalogs also contain extensive
technical information.

7. No, an improvement in throughput does not automatically bring an im­


provement in response time. Suppose that job J takes 10 seconds to run
on machine M. M ’s response time when running J is 10 and its through­
put is one J every 10 seconds. Now suppose that M ’s vendor upgrades
it by giving the machine parallel processing capability: M now can run
two Js concurrently, but its response time per job does not change. So
M ’s response time with respect to J is still 10. However, M ’s through­
put has doubled as it can complete two Js every 10 seconds. In sum,
M ’s throughput with respect to J has doubled but its response time is
unchanged.

9. When object workstation is created as a WS object, its constructor WS


is invoked. However, the WS constructor invokes the Desktop constructor
before executing its own body. The Desktop constructor, in turn, invokes
the Computer constructor before executing its own body. So the logical
order of constructor firing is Computer, then Desktop, then WS.

www.MathSchoolinternational.com
HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 525

11. class Test {


flo a t r t ; // response time in nanoseconds
flo a t mips;
void r e s u lts ( Computer c, BMark b ) ;
p u b lic:
T est( Computer c, BMark b ) ;
void setMips( flo a t m ) { mips = m; }
>;

Section 5.4

I. A!

3. Z!

5. Since no method is v irtu a l, compile-time binding is in effect. Because


p tr is of type A*, p tr binds to A: :f. The statement

p tr -> f ( ) ;

invokes A’s f . The statement

ptr -> Z : : f ( ) ;

therefore contains an error. For the statement to be correct, Z would have


to be a base class for A.

7. Compile-time binding.

9. A v irtu a l function must be a method.

II. An abstract class is a class to which objects cannot belong directly. An


object can belong only indirectly to an abstract class by belonging directly
to a class derived from the abstract class.

class Abstract {
v irtu a l void f ( ) = 0;
>;

class Derived : public Abstract {

>;

// * * * * * ERROR: can’ t belong d ir e c tly


// to Abstract
Abstract abs;
Derived der; / /ok

www.MathSchoolinternational.com
526 HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES

13. In C + + , we create an abstract class by declaring a pure v ir tu a l function


in the class declaration.

15. Yes.

17. The keyword v ir tu a l may not appear in the v ir tu a l function’s defini­


tion if the definition occurs outside the class declaration.

Section 5.5

I. BST still needs to access Node’s data members lc and rc.

3. BST method traverse is a pure v ir tu a l function.

5. No, the keyword occurs as part of the method’s declaration.

7. void PostBST::postorderAux( Node* n )

if ( n -> em ptyO )
re tu rn ;
p o s to rd erA u x ( n -> l c );
p osto rd erA u x ( n -> r c ) ;
n -> w r i t e ( );
>

9. The PreBST default and copy constructors simply invoke the BST default
and copy constructors to do their work. Therefore, the PreBST versions
have empty function bodies.

II. In the original version, the programmer determines the type of tree traver­
sal (e.g., inorder) by eocplicitly invoking the inorder method on a BST. In
the revised version, the object determines the sort of traversal appropri­
ate to it. For example, a preorder traversal is appropriate to a PreBST
object. The v irtu a l method traverse is invoked on any BST object
because the object itself then determines which version of the v irtu a l
function is appropriate to it. This is polymorphism at work. The original
version has no polymorphism.

Section 5.6

1. X::X
Y: :Y
X: :X
Y: :Y
Z: :Z
Z: :~Z
Y: :~Y
X: : ~X

www.MathSchoolinternational.com
H INTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 527

Y: : ~Y
X: : “ X

3. Z () and ~Z().

5.No, and a derived class destructor typically does not invoke a base class
destructor even if one exists.

7. In the class hierarchy

class A {
in t* ptrA;
p u b lic:
A () { ptrA = new i n t [ 1000 ] ; }
>;

class B : public A {
in t* ptrB;
p u b lic:
B () { ptrB = new i n t [ 1000 ] ; }
>;

there should be a v ir tu a l destructor because constructors for A and B


dynamically allocate separate storage. If a program using this hierarchy
should dynamically allocate, say, a B, then a v ir tu a l destructor would
prevent the creation of garbage.

9. If (1) constructors fora base and derived class dynamicallyallocate sep­


arate storage and (2) the program dynamically allocates a class object
from the hierarchy, then the hierarchy should have a v irtu a l destructor.

Section 5.7

1. Single inheritance is used to specialize or refine a class, whereas multiple


inheritance is used to combine classes.

3. R has four data members: x and y inherited from P, and a and b inherited
from Q.

5. void R ::a ssign ( in t xx, in t yy, in t aa, in t bb )


{
x = XX; y = yy;
a = aa; b = bb;
>

7. Two: one via Bl and one via B2.

www.MathSchoolinternational.com
528 HINTS AND SOLUTIONS TO ODD-NUMBERED EXERCISES

Section 6.1

I. The member operator . may not be overloaded.

3. The logical or operator I I must be overloaded as a binary operator, which


means that there should be a single argument instead of three arguments.

5. class C {
in t x;
in t y;
p u b lic :
// sample overload of && as frien d
frien d in t operator&&( C a l, C a2 )

i f ( a l <= 0 I I a2 <= 0 )
return 0;
return al == a2;
}
>;

7. class C {
in t x;
in t y;
p u b lic :
// sample overload of + as frien d
frien d in t operator+( C a l, C a2 )

return a l * a l + a2 * a2;
>

9. No, the ->* operator may be overloaded.

II. Yes, with the exception of the memory management operators new,
d elete, and d e le te [ ].

13. The s iz e o f operator already works on any data type, built-in or user-
defined. It is hard to imagine a need to overload such a basic operator in
the language.

15. class C {
in t x;
in t y;

www.MathSchoolinternational.com
HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 529

p u b lic:
// sample overload of ,
frien d in t o p era to r,( C a l, C a2 )
{
return a l . x ;
}
>;

Section 6.2

1. void Matrix::dump()
{
fo r ( in t i = 0; i < side * side; i++ )
cout « c e lls [ i ] « endl;
>

3. void M a trix :: copy( Matrixft m )


{
// same size?
i f ( m.side != side )
return;

// copy
fo r ( in t i = 0; i < side * side; i++ )
c e l l s [ i ] = m .c e lls [ i ] ;
}

5. If Matrix: :operator( ) returned an in t value rather than an in t refer­


ence, then a Matrix object could not be the target (i.e., left-hand side)
of an assignment operation.

7. No, because OverflowFlag itself can be the target of an assignment op­


eration and thus have its value reset.

9. Any other choice would be contrived or illegal. For example, we cannot


use operator [ ] because in an expression such as

Matrix m;
m[ 3, 4 ] ; // * * * * * ERROR: [ ] is unary

operator [ ] requires a single argument.

Section 6.3

1. We describe the changes. Entry no longer requires a default constructor


to initialize word and def to empty strings because the String class’s

www.MathSchoolinternational.com
530 HINTS AND SOLUTIONS TO ODD-NUMBERED EXERCISES

default constructor already handles this task. Methods add, write, and
match must be adjusted to handle String rather than char* data mem­
bers.

3. D ic tlte r references private Diet data members count and entries.

5. The first pair of parentheses identifies the operator, i.e., operator ( ) . The
second pair is the argument list, which is empty.

7. The function call operator ( ) may take zero or more arguments, which
makes it very flexible.

9. // append d to w’ s d efin itio n in dictionary


void D i e t ::append( char* w, char* d )
{
if ( !s tr le n ( d ) ) // nothing to append
return;

int i = 0;
while ( i < count )
i f ( e n trie s [ i ] .match( w ) )
// any room?
i f ( s tr le n ( e n trie s [ i ] .def +
s tr le n ( d ) < MaxDef ) ) {
s trc a t( e n trie s [ i ]. d e f , d ) ;
break;
>
else
i++;
>

Section 6.4

1. A type conversion operator must not have a return value in its definition.

3. The first const signals that code outside the String class should not
modify the storage to which s tr points. The second const signals that
the type conversion operator does not change this storage.

5. The compiler determines from context the type of value required for an
operation. For instance, the i f condition

String s ( " a lic ia " ) ;


i f ( s t r le n ( s ) )

www.MathSchoolinternational.com
HINTS A N D SOLUTIONS TO ODD-NUM BERED EXERCISES 531

invokes strlen, which expects a char* rather than a String argument.


Therefore, the compiler invokes the String to char* type conversion
operator, if one is defined.

7. The compiler rather than the programmer typically invokes a type conver­
sion operator; and, in writing a type conversion operator, the programmer
may not have anticipated all contexts in which compiler will invoke the
operator. This can lead to subtle bugs.
Section 6.5
1. F ile R e f’s overload of operator= and its type conversion operator (from
a F ileR ef to a char) both reference F ile ’s fp t r p riva te data member.

3. The overloaded operator= returns a F ileR ef reference (i.e., a F ileR ef &)


rather than a pointer to a F ileR ef (i.e., a F ile R e f*). Therefore, the
return value is *th is rather than this.

5. Implementation details are split between F ile and F ileR ef roughly as


follows: F ile handles the high-level details, whereas F ileR ef handles the
low-level details. In a code slice such as

F ile f ( "out.dat" ) ;
f [ 0 ] = ’Z’ ;

the subscript operator is implemented as a F ile method precisely to allow


the user to manipulate a F ile as if it were an array. The user needs to
know that the array syntax

f [ 0 ] = ...;

is now legal for Files. The user does not need to understand how the
assignment operator is working (e.g., that it does an fseek into a F ile ),
or even whether the assignment operator is built-in or overloaded. Ac­
cordingly, these details are hidden from the user within class FileR ef.

7. The overloaded assignment operator comes into play during writes but
not during reads. During reads, the type conversion operator (F ileR ef
to char) comes into play.

9. In a an expression such as

f [ 0 ] = >Z> // f is a F ile object

f ’s overloaded subscript operator is invoked. But f ’s overloaded assign­


ment operator is not at work. Instead, the subscript operator returns a
F ileR ef object so that this object’s assignment operator can be invoked.
We need a FileR ef object because the assignment operator is overloaded
as a FileR ef method, not as a F ile method.

www.MathSchoolinternational.com
532 HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES

Section 6.6

1. Cl* c l = new Cl; / / C l’ s new


C2* c2 = new C2; // b u ilt- in new
in t* i l = new in t; // b u ilt- in new
Cl* cs = new C l[ 10 ] ; // C l’ s new

3. If d elete is overloaded, its first argument must be of type void*.

5. No.

Section 7.1

3. Any data set can ultimately be encoded as a string of bits and so is a


stream.

5. If ofstream were derived from ostream and file b u f it would contain


both high-level and low-level input/output methods. The intent of the
original design was to separate the high-level and low-level methods into
different classes.

Section 7.2

I. io_obj .rd stateO & io s :: eof b it

3. The not operator ( ! ) is overloaded so that the value of the expression ! cin
is converted to a pointer. (If the pointer value is NULL the expression is
false; otherwise, the expression is true.)

5. fou t.seek p ( -10, ios::en d ) ;

7. cout.s e t f( io s : : showpoint );

9. cou t.u n setf( io s : : showpoint ) ;

II. c o u t.s e tf( io s ::o c t , io s : :b a sefield ) ;

13. c o u t.s e tf( 0, i o s : : f l o a t f i e l d ) ;

15. 0X42$$0X42$$0X420X42

Section 7.3

1. cin

3. 50

5. e

7. e

www.MathSchoolinternational.com
HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES 533

9. istreamfe o p e r a t o r » ( istreamfe in, ZipCodefe z )


■C
return in » z .re a d O ;
>

11. char a [ MAX_NO_STRINGS ] [ MAX_STR_SIZE ] ;

while ( cin.peekO != ’ \n’ ) {


c in .g e t( a [ i++ ] , MAX_STR_SIZE ) ;
c in .g e tO ; // discard newline
>
c in .g e tO ; // discard newline

13. ostreamfe o p e r a t o r « ( ostreamfe out, ZipCodefe z )


{
return out « z .w r it e O ;
>

Section 7.4

1. cout « r e s e t io s fla g s ( i o s : : showpoint ) ;

3. istreamfe ws( istreamfe is )

return i s . s e t f ( ios::skipw s ) ;
}

5. ostreamfe scien ( ostreamfe os )


■C
return o s .s e t f( i o s : : s c ie n t ific , i o s : :f lo a t f ie ld ) ;
>

7. ostreamfe s e t _ f i l l e r ( ostreamfe os, int c )


{
o s .f i ll ( c );
return os;
>
0MANIP< int > s e t f i l l ( int c )
{
return 0MANIP< int >( s e t _ f i l l e r , c ) ;
>

9. istreamfe seto( istreamfe is , streamoff n )


{
is .s e e k g ( n, i o s : :beg ) ;
return is ;
>

www.MathSchoolinternational.com
534 HINTS AN D SOLUTIONS TO ODD-NUMBERED EXERCISES

IMANIP< streamoff > s e t o ff( streamoff, n )


•C
return IMANIP< streamoff > ( seto, n ) ;
>

11. A manipulator is a toplevel function that “manipulates” an object in a


class using the overloaded input ( » ) or output ( « ) operator. A manip­
ulator can be added without modifying the class. A method is part of
a class. It too can manipulate an object using the overloaded input or
output operator in the class to which it belongs; however, adding such a
method means modifying the class, which may not be convenient or even
possible if the class is provided by a library.

Section 7.5

1. ifstream fin ;
fin .o p en ( "weather.in" ) ;

3. fstream fin o u t( "s ta rs .d a t", io s : :i n


I i o s : : out
I ios::nocreate ) ;

5. It is illegal to have two definitions of the same variable in the same scope.

7. #include <fstream.h>
#include <stdlib.h>

in t main( int c, char** argv )

char c;
ifstream fin ;

fo r ( int i = 1; i < c; i++ ) {


fin.open( argv[ i ] , io s ::b in a ry ) ;
i f ( fin )
while ( f i n .g e t ( c ) )
cout.put( c ) ;
fin .c lo s e O ;
>

return EXIT_SUCCESS;
>

www.MathSchoolinternational.com
HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 535

Section 7.7

1. char b u f f [ 100 ] ;
int i = 6;
ostrstream( b u ff, sizeof ( bu ff ) ) ;
sout « "Santa Claus Conquers " « i « " Martians" « ends;
cout « bu ff « endl;

3. // s is a character strin g that represents a long double


long double x;
istrstream s in ( s ) ;
sin » x;

Section 7.8

1. #include <fstream.h>
#include <strstream.h>
#include <std lib.h >

int mainO

char b u f f [ 10000 ] ;
ifstream f i n ( "data.in " ) ;
ostrstream sout( b u ff, sizeof ( bu ff ) ) ;

copy( f i n , sout ) ;

return EXIT_SUCCESS;
>

3. #include <fstream.h>
#include <std lib.h >

int mainO
{
ifstream f i n ( "d ata.in " ) ;

copy( fin , cerr ) ;

return EXIT_SUCCESS;
>

5. void copy( istreamfe in, ostreamfe out, int tabsize )


■c
char c;
long f l ;

www.MathSchoolinternational.com
536 HINTS AND SOLUTIONS TO ODD-NUMBERED EXERCISES

in t i ;

f l = in .u n setf( ios::skipw s ) ;
in t c, count = 0, i , numb;

while ( in » c )
i f ( c == J\ t ’ ) {
numb = tabsize - ( count */, tabsize ) ;
fo r ( i = 0; i < numb; i++ )
out « ’ ’ ;
count += numb;
>
else {
count++;
out « c;
i f ( c == ’ \n’ )
count = 0;
>

in . f la g s ( f l ) ;
>

Section 7.9

1. char b u ff[ 256 ] ;


file b u f fin o u t;
fin o u t( b u ff, s iz e o f ( buff ) ) ;
fin ou t.open ( "balance.dat", io s : : in | io s ::o u t ) ;

5. An abstract buffer is simply storage that can always be written to or read


from; the buffer classes reflect this abstract characterization. Also, the
buffer classes are low-level classes that service the high-level classes and
so are typically not accessed directly by the programmer. For this reason,
there is no overriding need to provide a logical separation of the input and
output methods. Finally, providing input/output buffer classes rather
than separate input buffer classes, output buffer classes, and input/output
buffer classes simplifies the declaration of the high-level input/output
classes. Class ios contains a pointer to a buffer class that can then be
used for either input or output or both by the various input classes, output
classes, and input/output classes derived from ios.

Section 8.1

1. Since no handler is defined, the system function terminate will be in­


voked.

www.MathSchoolinternational.com
HINTS A N D SOLUTIONS TO ODD-NUMBERED EXERCISES 537

Section 8.2

I. Since bptr points to a Book, the code

ptr -> p r i n t _ t i t l e ( ) ;
ptr -> p r i n t _ l e v e l ( ) ;

is executed.

3. Since bptr points to a Paperback, the code

ptr -> p r i n t _ t i t l e ( ) ;

is executed.

5. True

7. True

9. False

II. True

13. True

15. False

17. False

19. False

21. False

23. False

25. False

27. False

Section 8.3

1. kalin _softw are: : s trin g k;


jbaugh_software: : s trin g j ;

www.MathSchoolinternational.com
www.MathSchoolinternational.com
Index

abort, 425, 426, 457 bad, 341, 458


abs, 457 badb it, 340
Abstract class, 252, 260 Bag class, 139
Abstract data type, 6, 14, 74, Base class, 10, 204
82, 103, 108, 112, 142 b a s e fie ld , 346
Access declaration, 215 beg, 343, 353
Access specifier, 207 b e l l , 363, 364
acos, 457 Big integer class, 197, 287
Actor, 444 Binary, 343
a d ju s t fie ld , 346 Binary search tree class, 175
Anonymous union, 19 and virtual traversal, 260
iterator for, 181
app, 343
traversal, 179, 264
Argument
Binding
default, 31
compile-time, 250, 251
Arity
run-time, 243, 246, 251
o f operators, 292
Block scope, 80
as in, 457
Booch, G., 2
a s s e r t , 123
Borland C + + ObjectWindows,
Assertion, 121, 427, 442
334
and program correctness, 121
Bounds checking, 301
postconditions, 121
Browser, 436
preconditions, 121
bsearch, 458
Assignment operator (=) Buffer, 336
overloaded in S tr in g class, classes, 401
153 flush, 336
Associative array class, 306 Buffered input/output, 336
Asynchronous exception, 427
atan, 457 c a llo c , 45
ate, 343 Carroll, L., 71
a t o f , 457 Cast, 18, 428
a to i, 458 cat, 485
a to l, 458 Catch block, 424

539

www.MathSchoolinternational.com
IN D E X

cd, 484 Collision resolution policy, 376


c e il, 459 Comment, 18
Cell vector, 42 Complex number class, 112
cerr, 52, 336, 339 Concurrency, 445
Character array const,19
high-level input/output, 393 Constructor, 87
input/output classes, 392 and dynamic storage
cin, 52, 336, 339, 341 allocation, 95
class, 74, 77 as function with no return
Class, 6, 74 value, 96
abstract, 252 as public function, 96
and templates, 129 convert, 93
base, 10, 204 copy, 92, 93, 158
buffer, 401 default, 88
combination, 12 derived class rules, 225, 228
data member, 74 initialization, 90
declaration, 75 motivation for, 87
definition, 75 name, 88, 89
derived, 10, 75, 204 order of firing, 222, 225, 227,
file input/output, 366 240
function member, 74 under inheritance, 222
hierarchy, 12, 74 Convert constructor, 93
high-level input/output, 350 Coplien, J., 315
copy, 443
inherited, 10
Copy constructor, 92, 93, 158
input/output, 333
and assignment operator, 159
invariant, 121, 126, 427, 442
and object definition, 159
member, 74
and pass by value, 161
method, 74, 75, 76, 438
and return by value, 162
multiple inheritance, 12
problems with compiler’s
object, 3, 446
version, 148
Object, 437, 443
when programmer should
partial, 252
write, 148, 150
polymorphism, 15
cos, 459
public derivation, 11
cosh, 459
random access file, 373 cout, 52, 336, 339
specialization, 12 Cox, B., 442, 443
STORABLE, 445
cp, 487
subclass, 10, 204 Create, 440
superclass, 10, 204 cur, 343, 353
Type_inf°, 430 Cursor class, 181
without constructors, 95
clear, 342, 459
clearerr, 459 Data member, 74, 75, 76
Clock class, 297 Data member initialization, 87
Clone, 440 Database
CLOS, 444 object-oriented, 445
close, 368, 369, 371, 372, 406, relational, 445
459 table in, 445
Collision, 376 dec, 56, 339, 344, 362

www.MathSchoolinternational.com
IN D EX 541

Declaration, 18 fabs, 460


forward, 178 Factory method, 443
Default argument, 31 f a i l , 341, 460
Default constructor, 88 f a i l b i t , 340, 355
#define directive, 20 fclo se, 460
Definition, 18 feature, 442
variable, 21 Feigenbaum, M., 69
delete operator, 42, 320 fgetc, 461
delete [ ] operator, 42, 320 fge ts, 461
Derived class, 10, 204 File
constructors, 225 high-level input/output, 367
Design indexed, 419
object-oriented, 2 input/output classes, 366
top-down, 4 load factor, 376
Destructor, 87, 96, random access, 373
as function with no random access file class, 373
arguments, 99 relative, 375
as function with no return sequential, 419
value, 99 File scope, 80
motivation for, 97 File subscripts, 315
name, 97, 99, 100 file b u f , 336, 339, 367, 401,
under inheritance, 266 404
v irtu a l, 268 f i l l , 344, 345, 347, 461
Dictionary class, 306 find, 486
difftim e, 459 fixed , 344, 347
dynamic_cast operator, 428 Fixed notation for floating­
point output, 347
Eiffel, 439, 445 fla g s , 345, 346, 461
Encapsulation, 3, 7, 74, 83, 103, f lo a t f ie ld , 347
112, 142, 175, 181 flo o r, 462
Send, 443 Flush, 336
end, 343, 353 flu sh , 56, 339, 357, 362, 462
endl, 56, 339, 362, 363 fopen, 462
ends, 339, 362, 395 Forward class declaration,
ensure, 442 178
enum,18 fp r in t f, 463
Enumerated type, 18 fputc, 463
e o f , 341, 460 fputs, 463
e o fb it, 340, 341 frandom, 377, 386
Equal, 441 data members, 386
Exception, 50, 422 fread, 463
asynchronous, 427 free, 42, 45, 444
synchronous, 427 freeze, 412
Exception handling, 422 friend, 77, 185
exit, 460 classes, 172
EXIT_FAILURE, 26 functions, 116
EXIT_SUCCESS, 26 use in object-oriented
exp, 460 programming, 174
export, 442 frie n d classes, 172

www.MathSchoolinternational.com
IN D E X

friend functions, 116 High-level character array


and access to private input/output, 393
members, 116 High-level copy function, 398
in operator overloading, 118 High-level file input/output,
of two or more classes, 117 367
fscanf, 463 High-level input/output, 337
fseek, 464 High-level input/output classes,
fstream, 338, 339, 371, 386 350
fstream.h, 339, 367, 401 Huffman code, 419
fstreambase, 337, 339, 367
f t e ll, 464 id, 443
Function, 23 ifstream, 338, 339, 370
high-level copy, 398 ignore, 352, 466
inline, 76 IMANIP, 366
input/output, 453 ^implementation, 444
input/output class methods, in, 343
453 #include directive, 80
math, 453 Indexed file, 419
miscellaneous, 455 Information hiding, 6 , 74, 77,
overload, 33 83, 103, 112, 142, 175, 181
prototype, 23 Inheritance, 10, 204
string, 455 and access, 273
type and conversion, 455 and access declarations, 215
Function call operator, 301 and access specifiers, 207
Function-style parameter, 130 and class combination, 272
Functional decomposition, 4 and class specialization, 272
fwrite, 464 and member accessibility, 205
and virtual base classes,
Garbage, 97, 107 276
gcount, 352, 464 direct, 204
Generic classes, 129 indirect, 204, 214
get, 54, 351, 465 multiple, 12, 204, 271
getc, 465 private, 213
get char, 465 protected, 212
gets, 466 public, 208
Gillman, L., 71 single, 271
Goldberg, A., 439 types of, 205
good, 341, 466 Inheritance hierarchy, 204
goodbit, 340 inline, 30, 76
Graph class, 200, 286 contrasted with macro
grep, 486 expansion, 30
Input/output, 52, 333
Halsall, F., 288 buffered, 336
Handler, 50, 422 high-level, 337
hardfail, 340 high-level character array,
Hash function, 375 393
Hash table class, 198 high-level classes, 350
Hennessy, J., 238 high-level file, 367
hex, 55, 56, 339, 344, 361, 362 low-level, 336
Hierarchy, 12 stream, 52

www.MathSchoolinternational.com
1

INDEX 543

Input/output classes LaLonde, W., 439


character array, 392 Leestma, S., 419
file, 366 le ft, 344, 347
Instance method, 438, 443 Left-justify output, 346
©interface, 443 Left-shift operator ( « ) , 52, 334
Interface overload, 358
public, 6, 14 Linear probing, 376
Interface description, 443 List class, 198
internal, 344, 347 Load factor, 376
Internal representation, 74, 142 log, 468
Invariant, 427 loglO, 468
invariant, 442 Low-level input/output, 336
in_avail, 402 lp, 485
iomanip.h, 55, 361, 364 Is, 485
ios, 334, 339, 340, 401
conversion to void*, 342 Macro, 30
overload of !, 342 main, 25
iostream, 334, 359 make, 487
iostream.h, 52, 55, 338, 339, compile-link descriptions, 488
361, 393, 401 dependency descriptions, 488
iostream_withassign, 336, malloc, 42, 45
339, 360 man, 481
io_state, 340 Manipulator, 52, 55, 339, 361
isalnum, 466 bell, 363, 364
isalpha, 466 dec, 56, 339, 344, 362
iscntrl, 467 endl, 56, 339, 362, 363
isd igit, 467 ends, 339, 362, 395
isgraph, 467 flush, 56, 339, 357, 362
islower, 467 hex, 55, 56, 339, 344, 361, 362
isprint, 467 oct, 56, 339, 344, 362
ispunct, 467 resetiosflags, 362
isspace, 468 s e t fill, 56, 362
istream, 334, 339, 350 setiosflags, 362
istream_withassign, 336, 339, setprecision, 56, 362
359 setw, 56, 362
istrstream, 338, 339, 395 with arguments, 364
isupper, 468 with no arguments, 362
isxdigit, 468 ws, 56
is_open, 411 Matrix class, 301
Iterator class, 181 Measuring computer
performance, 231
Johnsonbaugh, R., 427 Member selector operators
Junk, 87 (.*, ->*), 45
memchr, 469
Kalin, M., 427 memcmp, 469
Keene, S. E., 444 memcpy, 469
Keyword message, 436 memmove, 469
Kirkerud, B., 444 Memory management operators,
320
labs, 468 Message, 3

www.MathSchoolinternational.com
544 IN D E X

Method, 3 new, 42, 320, 438, 444


class, 438 overload, 34
factory, 443 precedence of, 41
inline definition, 75 right-shift, 52, 334, 353, 356
instance, 438, 443 scope resolution, 40
syntax, 110, 155, 290 throw, 50, 423
Meyer, B., 122, 439 typeid, 430
mkdir, 484 value construction, 42
Monty Hall puzzle, 70 Operator arity, 292
more, 485 Operator function, 109, 155, 290
Motif, 14 Operator overloading, 108, 290
MS-DOS, 342 assignment operator (=), 164
Multiple inheritance, 12, 204, equality and inequality
212, 271 operators (==, !=), 167
as a graph, 272 memory management
mv, 487 operators, 320
of increment and decrement
operators, 295
Name hiding, 216 syntax, 110
Namespace, 433 type conversions, 311
NDBEBUG, 126 Operator precedence, 291
new operator, 42, 320, 438, 444 Operator syntax, 110, 155, 290
nocreate, 343 ostream, 334, 339, 343, 353,
n orep lace, 343 356, 363
Nyhoff, L., 419 ostream _w ithassign, 336, 339,
360
ostrstream , 338, 339, 393
Object, 3, 446 out, 342, 343
O bject, 437, 443 Output
Object initialization, 159 left-justify, 346
Object Pascal, 444 right-justify, 346
Object-oriented databases, 445 o u t_ a v a il, 402
Object-oriented design, 2 o verflow , 337, 406, 412
Object-oriented programming, Overload, 108
2, 265 function, 33
Objective C, 442 left-shift operator, 358
oct, 56, 339, 344, 362 operator, 34
ofstream , 338, 339, 342, 368 right-shift operator, 353, 356
OMANIP, 364
open, 368, 369, 370, 372, 404,
470 Parameter
openprot, 367 in templates, 130
Operations Parameterized types, 129
methods as, 75 Pass by reference, 27
Operator, 40 Pass by value, 161
d e le te , 42, 320 Patterson, D., 238
d e l e t e [ ] , 42, 320 Paulos, J. A., 69
dynamic_cast, 428 pcount, 395, 470
left-shift, 52, 334, 358 peek, 352, 470
member selector, 45 Persistence, 445, 446

www.MathSchoolinternational.com
IND EX 545

Polymorphism, 15, 74, 243, 429 resetiosflags, 362


and run-time binding, 243 retrieve, 445
and virtual methods, 243, Return by reference, 29
244 Return by value, 161
motivation for, 244, 251 rewind, 473
Postcondition, 121, 123, 427, 442 right, 344, 347
pow, 470 Right-justify output, 346
pr, 485 Right-shift operator ( » ) , 52,
Precedence of operators, 41, 291 334
precision, 345, 348, 470 overload, 353, 356
Precondition, 121, 123, 427, 442 rm, 485
printf, 471 rmdir, 484
private, 77, 78, 79 Robson, D., 439
derivation, 213 Rumbaugh, J., 2
inheritance, 213 Run-time binding, 243
Program correctness, 121 Run-time libraries, 491
protected, 205 Run-time type identification,
derivation, 212 427
inheritance, 212
Prototype, 23 sbumpc, 402
public, 11, 77, 78, 79 scanf, 473
derivation, 208 Schedule class, 199
inheritance, 208 scien tific, 344, 347
Public interface, 6, 14 Scientific notation for floating­
Pugh, J., 439 point output, 347
Pure virtual methods, 252 Scope resolution operator (::),
and abstract classes, 252 40
put, 357, 471 seekg, 353, 473
putback, 352, 471 seekoff, 403, 411, 412
putc, 471 seekp, 343, 353, 357, 473
put char, 471 seekpos, 403
puts, 471 seek_dir, 353
pwd, 484 Semaphore class, 138
Sequential file, 419
qsort, 472 Set class, 139
QuickPascal, 444 setbase, 362
setbuf, 402, 404
rand, 472 setf, 344, 345, 346, 473
Random access file, 373 setf i l l , 56, 362
rdbuf, 387, 393, 472 setiosflags, 362
rdstate, 341, 472 setprecision, 56, 362
read, 54, 352, 472 setw, 56, 362
Reference, 26 set_terminate, 426, 474
pass by, 27 set_unexpected, 426, 474
return by, 29 sgetc, 402
Regular expression class, 202 sgetn, 402
Relational database model, 445 Shammas, N., 444
Relative address, 375 showbase, 344, 346
Relative file, 375 showpoint, 344
require, 442 showpos, 344

www.MathSchoolinternational.com
546 IN D E X

Signal, 427 strlen, 477


signal, 474 strncat, 477
Simula, 444 strncmp, 477
Simulation, 231 strncpy, 477
sin, 474 Stroustrup, B., viii, 2, 306
Single inheritance strpbrk, 477
as a tree, 272 strrchr, 478
sinh, 475 strspn, 478
skipws, 344, 348, 354 strstr, 478
Smalltalk, 436, 443 strstream, 338, 339, 397
SMANIP, 365 strstream.h, 339, 393, 401
snextc, 403 strstreambase, 337, 338, 339,
Spreadsheet class, 200 393
sprintf, 393, 475 strstreambuf, 336, 339, 401,
sputbackc, 403 411
sputc, 403 struct, 74, 78
sputn, 403 Structure, 19
sqrt, 475 Subclass, 10, 204
srand, 475 Superclass, 10, 204
sscanf, 395, 475 Synchronous exception, 427
Stack class, 82, 87, 88 sync_with_stdio, 58, 348, 478
and class invariants, 122 system, 478
using templates, 129
Table, 445
Standard error, 52
tan, 478
Standard input, 52
tanh, 479
Standard output, 52
te llg , 353, 479
state, 340
tellp, 353, 357, 479
static, 186
Telser, L., 444
data members, 186
Templates, 129
methods, 190
terminate, 426, 479
stderr, 52
this, 154, 155
stdin, 52 throw operator, 50, 423
stdio, 344 tie, 348, 479
stdlib.h, 26 time, 479
stdout, 52 tolower, 480
STORABLE, 445 Top-down design, 4
store, 445 toupper, 480
stossc, 403 trunc, 343
str, 395, 398, 412, 476 try block, 424
strcat, 476 Turbo Pascal, 444
strchr, 476 Type conversion operators, 311
strcmp, 476 Type parameter, 130
strcpy, 476 typedef, 74
strcspn, 476 typeid operator, 430
Stream, 52, 334 Type_info class, 430
streambuf, 336, 339, 401, 402 Type.info.h, 430
streamoff, 353
streampos, 353 underflow, 337, 406, 412
String class, 142, 311 unexpected, 426, 480

www.MathSchoolinternational.com
IN D EX 547

ungetc, 480 virtual, 243


union, 79 base class, 276
Union destructor, 268
anonymous, 19 methods, 243
unitbuf, 344 methods in abstract classes,
UNIX, 342, 481 252
directories and paths, 483 Virtual tree traversal, 260
producing executable Visual C-|—K foundation classes,
programs under, 482 334
redirection and pipes, 489
run-time libraries, 491 width, 345, 347, 480
unsetf, 345, 348, 480 Windows, 14
uppercase, 344, 346 Working Paper, vii
using, 434 write, 357, 480
ws, 56, 339, 362
Value construction operator, 42
Variable definition, 21 X3J16 standards committee,
VAX/VMS, 342 vii
Vector
cell, 42 Zip code class, 103

www.MathSchoolinternational.com
www.MathSchoolinternational.com
PRENTICE HALL
Englewood Cliffs, NJ 07632 ISBN 0 -0 2 -3 6 0 6 8 2 -7

780023 606823

www.MathSchoolinternational.com

You might also like