SlideShare a Scribd company logo
C Plus Data Structures Subsequent Dale Nell B
download
https://ebookbell.com/product/c-plus-data-structures-subsequent-
dale-nell-b-55231722
Explore and download more ebooks at ebookbell.com
Here are some recommended products that we believe you will be
interested in. You can click the link to download.
C Plus Data Structures Nell B Dale
https://ebookbell.com/product/c-plus-data-structures-nell-b-
dale-4116476
C Plus Data Structures 6th Edition Dale Chip Weems Tim Richards
https://ebookbell.com/product/c-plus-data-structures-6th-edition-dale-
chip-weems-tim-richards-59050442
Ivor Hortons Beginning Visual C Plus Plus Imar Spaanjaars
https://ebookbell.com/product/ivor-hortons-beginning-visual-c-plus-
plus-imar-spaanjaars-2285074
Ordinary And Partial Differential Equation Routines C C Plus Plus
Fortran Java Maple Matlab
https://ebookbell.com/product/ordinary-and-partial-differential-
equation-routines-c-c-plus-plus-fortran-java-maple-matlab-2284398
C Primer Plus Developers Library 6th Edition Prata Stephen
https://ebookbell.com/product/c-primer-plus-developers-library-6th-
edition-prata-stephen-55235694
C Primer Plus Fifth Edition 5th Edition Prata Stephen
https://ebookbell.com/product/c-primer-plus-fifth-edition-5th-edition-
prata-stephen-22034856
C Primer Plus 5th Edition Stephen Prata
https://ebookbell.com/product/c-primer-plus-5th-edition-stephen-
prata-23341320
C Primer Plus 1st Edition Klaus Michelsen
https://ebookbell.com/product/c-primer-plus-1st-edition-klaus-
michelsen-2360792
C Primer Plus 5th Ed Stephen Prata
https://ebookbell.com/product/c-primer-plus-5th-ed-stephen-
prata-4098388
C Plus Data Structures Subsequent Dale Nell B
C++
T h i r d E d i t i o n
Nell Dale
J O N E S A N D B A R T L E T T C O M P U T E R S C I E N C E
PlusData
Structures
TEAM LinG - Live, Informative, Non-cost and Genuine!
C++
T h i r d E d i t i o n
Nell Dale
University of Texas, Austin
PlusData
Structures
TEAM LinG - Live, Informative, Non-cost and Genuine!
Copyright © 2003 by Jones and Bartlett Publishers, Inc.
Cover image © Douglas E. Walker / Masterfile
All rights reserved. No part of the material protected by this copyright notice may be reproduced or utilized
in any form, electronic or mechanical, including photocopying, recording, or any information storage or
retrieval system, without written permission from the copyright owner.
Chief Executive Officer: Clayton Jones
Chief Operating Officer: Don W. Jones, Jr.
Executive V.P. and Publisher: Robert Holland
V.P., Design and Production: Anne Spencer
V.P., Manufacturing and Inventory Control: Therese Bräuer
Editor-in-Chief, College: J. Michael Stranz
Production Manager: Amy Rose
Marketing Manager: Nathan Schultz
Associate Production Editor: Karen Ferreira
Editorial Assistant: Theresa DiDonato
Production Assistant: Jenny McIsaac
Cover Design: Night & Day Design
Composition: Northeast Compositors, Inc.
Text Design: Anne Spencer
Printing and Binding: Courier Westford
Cover Printing: Lehigh Press
Library of Congress Cataloging-in-Publication Data
Dale, Nell B.
C++ plus data structures / Nell Dale.—3rd ed.
p. cm.
ISBN 0-7637-0481-4
1. C++ (Computer program language) 2. Data structures (Computer science) I. Title.
QA76.73.C153 D334 2003
005.7’3—dc21
2002034168
This book was typeset in Quark 4.1 on a Macintosh G4. The font families used were Rotis Sans Serif, Rotis
Serif, and Prestige Elite. The first printing was printed on 45# Highland Book.
Printed in the United States of America
06 05 04 03 02 10 9 8 7 6 5 4 3 2 1
World Headquarters
Jones and Bartlett Publishers
40 Tall Pine Drive
Sudbury, MA 01776
978-443-5000
info@jbpub.com
www.jbpub.com
Jones and Bartlett Publishers
Canada
2406 Nikanna Road
Mississauga, ON L5C 2W6
CANADA
Jones and Bartlett Publishers
International
Barb House, Barb Mews
London W6 7PA
UK
TEAM LinG - Live, Informative, Non-cost and Genuine!
To Al, my husband and best friend, to our children and our
children's children, and to our dogs Maggie and Chrissie,
who round out our family.
N.D.
TEAM LinG - Live, Informative, Non-cost and Genuine!
TEAM LinG - Live, Informative, Non-cost and Genuine!
H
istorically, a course on data structures has been a mainstay of most computer
science departments. Over the last 18 years, however, the focus of this course
has broadened considerably. The topic of data structures has now been sub-
sumed under the broader topic of abstract data types (ADTs)—the study of classes of
objects whose logical behavior is defined by a set of values and a set of operations.
The term abstract data type describes a comprehensive collection of data values
and operations; the term data structures refers to the study of data and how to repre-
sent data objects within a program; that is, the implementation of structured rela-
tionships. The shift in emphasis is representative of the move towards more
abstraction in computer science education. We now are interested in the study of the
abstract properties of classes of data objects in addition to how the objects might be
represented in a program. Johannes J. Martin put it succinctly: “. . . depending on
the point of view, a data object is characterized by its type (for the user) or by its
structure (for the implementor).”1
Three Levels of Abstraction
The focus of this book is on abstract data types as viewed from three different per-
spectives: their specification, their application, and their implementation. The speci-
fication perspective describes the logical or abstract level of data types, and is
concerned with what the operations do. The application level, sometimes called the
user level, is concerned with how the data type might be used to solve a problem,
and is focused on why the operations do what they do. The implementation level is
where the operations are actually coded. This level is concerned with the how ques-
tions.
Within this focus, we stress computer science theory and software engineering
principles, including modularization, data encapsulation, information hiding, data
1Johannes J. Martin, Data Types and Data Structures, Prentice-Hall International Series in Computer Science,
C. A. R. Hoare, Series Editor, Prentice-Hall International, (UK), LTD, 1986, p. 1.
TEAM LinG - Live, Informative, Non-cost and Genuine!
abstraction, object-oriented decomposition, functional decomposition, the analysis of
algorithms, and life-cycle software verification methods. We feel strongly that these
principles should be introduced to computer science students early in their education so
that they learn to practice good software techniques from the beginning.
An understanding of theoretical concepts helps students put the new ideas they
encounter into place, and practical advice allows them to apply what they have learned.
To teach these concepts to students who may not have completed many college-level
mathematics courses, we consistently use intuitive explanations, even for topics that
have a basis in mathematics, like the analysis of algorithms. In all cases, our highest
goal has been to make our explanations as readable and as easily understandable as
possible.
Prerequisite Assumptions
In this book, we assume that students are familiar with the following C++ constructs:
• Built-in simple data types
• Stream I/O as provided in <iostream>
• Stream I/O as provided in <fstream>
• Control structures while, do-while, for, if, and switch
• User-defined functions with value and reference parameters
• Built-in array types
• Class construct
We have included sidebars within the text to refresh students’ memory concerning
some of the details of these topics.
Changes in the Third Edition
The third edition incorporates the following changes:
Object-oriented constructs moved forward: In the last five years, object-oriented pro-
gramming has become part of the first-year curriculum, as demonstrated by its inclu-
sion in all variations of the first year outlined in the Computing Curricula 2001
developed by the Joint Task Force of the IEEE Computer Society and the Association for
Computing Machinery. Accordingly, the class concept has moved into the first semes-
ter. Because of this, we assume that students have had experience using classes, and we
therefore moved much of the discussion of how to define and access classes to a side-
bar. We have kept a small discussion in the main text. Many students have already
seen inheritance and polymorphism, but the concepts are too important to move to a
sidebar, so we have moved them from Chapter 6 to Chapter 2.
More emphasis on object-oriented design: Object-oriented design is a hard topic for
most students, because people usually think procedurally in their lives. Because of this,
we introduce a methodology with four phases: brainstorming, during which the possible
vi | Preface
TEAM LinG - Live, Informative, Non-cost and Genuine!
objects in a problem are isolated; filtering, during which the set of possible objects are
reexamined to look for duplicates and/or missing objects; scenarios, during which hand
simulations of the processing take place asking “what if” questions and assigning
responsibilities to classes; and responsibility algorithms, during which the algorithms for
the classes are designed. We use CRC cards to capture the results of the four-phase
process. The output from the scenarios phase is a CRC card for each class. The CRC
card lists the responsibilities of the class and any other classes with which the class
must collaborate, hence the name CRC: class, responsibility, collaboration.
More practical emphasis on testing: The concept of a multipurpose test driver is intro-
duced in Chapter 1. After a test plan has been designed, it is implemented as input to
the test driver. Throughout the rest of the book, this technique is used to test the ADTs.
The drivers, the input data, and the output data are available on the book’s web site:
http://computerscience.jbpub.com/cppDataStructures
Reduced use of templates: The concept of generic data types, as implemented in C++
using templates, is very important. Making every ADT a class template after templates
are introduced in Chapter 4, however, inserts an unnecessary complexity into already
complex code. Thus, when introducing a new construct such as a linked list or a binary
search tree, we have chosen to use classes rather than class templates. Subsequent
implementations of a construct are often in the form of class templates, or the student is
asked to transform a class into a class template in the exercises.
Nonlinked binary tree representation covered with binary trees: The nonlinked represen-
tation of a binary tree is an important concept within its own right, not just as an
implementation for a heap. This implementation, therefore, is covered in Chapter 8 with
other tree implementation techniques.
Removal of material on binary expression trees: Although interesting applications of
trees, binary expression trees do not fit into the discussion of abstract data types. Thus,
we have moved this discussion to the web site.
Inclusion of the ADT set: The exclusion of the ADT set has been an omission from pre-
vious editions. Not only is a set an interesting mathematical object, but there are inter-
esting implementation issues. We propose two implementations, one explicit (bit
vector) and one implicit (list-based).
Content and Organization
Chapter 1 outlines the basic goals of high-quality software, and the basic principles of
software engineering for designing and implementing programs to meet these goals.
Abstraction, functional decomposition, and object-oriented design are discussed. This
chapter also addresses what we see as a critical need in software education: the ability
to design and implement correct programs and to verify that they are actually correct.
Topics covered include the concept of “life-cycle” verification; designing for correctness
using preconditions and postconditions; the use of deskchecking and design/code walk-
throughs and inspections to identify errors before testing; debugging techniques, data
coverage (black-box), and code coverage (clear- or white-box) approaches; test plans,
Preface | vii
TEAM LinG - Live, Informative, Non-cost and Genuine!
unit testing, and structured integration testing using stubs and drivers. The concept of a
generalized test driver is presented and executed in a Case Study that develops the ADT
Fraction.
Chapter 2 presents data abstraction and encapsulation, the software engineering
concepts that relate to the design of the data structures used in programs. Three per-
spectives of data are discussed: abstraction, implementation, and application. These
perspectives are illustrated using a real-world example, and then are applied to built-in
data structures that C++ supports: structs and arrays. The C++ class type is presented as
the way to represent the abstract data types we examine in subsequent chapters. The
principles of object-oriented programming—encapsulation, inheritance, and polymor-
phism—are introduced here along with the accompanying C++ implementation con-
structs. The Case Study at the end of this chapter reinforces the ideas of data abstraction
and encapsulation in designing and implementing a user-defined data type for general-
ized string input and output. This class is tested using a version of the generalized test
driver.
Chapter 2 ends with a discussion of two C++ constructs that help users write better
software: namespace and exception handling using the try/catch statement. Various
approaches to error handling are demonstrated in subsequent chapters.
We would like to think that the material in Chapters 1 and 2 is a review for most
students. The concepts in these two chapters, however, are so crucial to the future of
any and all students that we feel that we cannot rely on the assumption that they have
seen the material before.
Chapter 3 introduces the most fundamental abstract data type of all: the list. The
chapter begins with a general discussion of operations on abstract data types and then
presents the framework with which all of the other data types are examined: a presenta-
tion and discussion of the specification, a brief application using the operations, and the
design and coding of the operations. Both the unsorted and the sorted lists are pre-
sented with an array-based implementation. Overloading the relational operators is pre-
sented as a way to make the implementations more generic. The binary search is
introduced as a way to improve the performance of the search operation in the sorted
list. Because there is more than one way to solve a problem, we discuss how competing
solutions can be compared through the analysis of algorithms, using Big-O notation.
This notation is then used to compare the operations in the unsorted list and the sorted
list. The four-phase object-oriented methodology is presented and demonstrated in the
Case Study by using a simple real estate database.
Chapter 4 introduces the stack and the queue data types. Each data type is first
considered from its abstract perspective, and the idea of recording the logical abstrac-
tion in an ADT specification is stressed. Then the set of operations is implemented in
C++ using an array-based implementation. The concept of dynamic allocation is intro-
duced, along with the syntax for using C++ pointer variables, and then used to demon-
strate how arrays can be dynamically allocated to give the user more flexibility. With
the introduction of dynamic storage, the destructor must be introduced. Templates are
introduced as a way of implementing generic classes. A Case Study using stacks (post-
fix expression evaluator) and one using queues (simulation) are presented.
viii | Preface
TEAM LinG - Live, Informative, Non-cost and Genuine!
Chapter 5 reimplements the ADTs from Chapters 3 and 4 as linked structures. The
technique used to link the elements in dynamically allocated storage is described in
detail and illustrated with figures. The array-based implementations and the linked
implementations are then compared using Big-O notation.
Chapter 6 is a collection of advanced concepts and techniques. Circular linked lists
and doubly linked lists are discussed. The insertion, deletion, and list traversal algo-
rithms are developed and implemented for each variation. An alternative representation
of a linked structure, using static allocation (an array of structs), is designed. Class copy
constructors, assignment overloading, and dynamic binding are covered in detail. The
Case Study uses doubly linked lists to implement large integers.
Chapter 7 discusses recursion, giving the student an intuitive understanding of the
concept, and then shows how recursion can be used to solve programming problems.
Guidelines for writing recursive functions are illustrated with many examples. After
demonstrating that a by-hand simulation of a recursive routine can be very tedious, a
simple three-question technique is introduced for verifying the correctness of recursive
functions. Because many students are wary of recursion, the introduction to this mate-
rial is deliberately intuitive and nonmathematical. A more detailed discussion of how
recursion works leads to an understanding of how recursion can be replaced with itera-
tion and stacks. The Case Study develops and implements the Quick-Sort algorithm.
Chapter 8 introduces binary search trees as a way to arrange data, giving the flexi-
bility of a linked structure with O(log2N) insertion and deletion time. In order to build
on the previous chapter and exploit the inherent recursive nature of binary trees, the
algorithms first are presented recursively. After all the operations have been imple-
mented recursively, we code the insertion and deletion operations iteratively to show
the flexibility of binary search trees. A nonlinked array-based binary tree implementa-
tion is described. The Case Study discusses the process of building an index for a man-
uscript and implements the first phase.
Chapter 9 presents a collection of other branching structures: priority queues
(implemented with both lists and heaps), graphs, and sets. The graph algorithms make
use of stacks, queues, and priority queues, thus both reinforcing earlier material and
demonstrating how general these structures are. Two set implementations are discussed:
the bit-vector representation, in which each item in the base set is assigned a
present/absent flag and the operations are the built-in logic operations, and a list-based
representation, in which each item in a set is represented in a list of set items. If the
item is not in the list, it is not in the set.
Chapter 10 presents a number of sorting and searching algorithms and asks the
question: Which are better? The sorting algorithms that are illustrated, implemented,
and compared include straight selection sort, two versions of bubble sort, quick sort,
heap sort, and merge sort. The sorting algorithms are compared using Big-O nota-
tion. The discussion of algorithm analysis continues in the context of searching. Pre-
viously presented searching algorithms are reviewed and new ones are described.
Hashing techniques are discussed in some detail. Finally, radix sort is presented and
analyzed.
Preface | ix
TEAM LinG - Live, Informative, Non-cost and Genuine!
Additional Features
Chapter Goals A set of goals presented at the beginning of each chapter helps the
students assess what they will learn. These goals are tested in the exercises at the end
of each chapter.
Chapter Exercises Most chapters have more than 35 exercises. They vary in levels of
difficulty, including short programming problems, the analysis of algorithms, and
problems to test the student’s understanding of concepts. Approximately one-third of
the exercises are answered in the back of the book. The answer key for the remaining
exercises is in the Instructor’s Guide.
Case Studies There are seven case studies. Each includes a problem description, an
analysis of the problem input and required output, and a discussion of the appropriate
data types to use. Several of the case studies are completely coded and tested. Others
are left at various stages in their development, requiring the student to complete and
test the final version.
Program Disk The specification and implementation of each class representing an ADT
is available on a program disk that can be downloaded, free of charge, from the Jones
and Bartlett Student Diskette Page on the World Wide Web (www.jbpub.com/disks).
The source code for the completed case studies and the partial source code for the
others is also available.
Instructor Support Material Instructor teaching tools and resources are available on
the web at http://computerscience.jbpub.com/cppDataStructures. On this site you will
find:
• Goals
• Outlines
• Teaching Notes: suggestions for how to teach the material covered in each chap-
ter
• Workouts: suggestions for in-class activities, discussion questions, and short
exercises
• Exercise Key: answers to those questions that are not solved in the back of the
book
• Programming Assignments: a collection of a wide range of assignments carefully
chosen to illustrate the techniques described in the text
• Electronic TestBank: this computerized TestBank allows you to create cus-
tomized exams or quizzes from a collection of pre-made questions sorted by
chapter. Updated for this edition, the TestBank questions can be edited and
supplemented, and answers are provided for all pre-made questions. Each test
is developed using Brownstone Diploma Software and is available on the
book’s web site.
• PowerPoint Presentations: new PowerPoint slides developed specifically for the
third edition provide an excellent visual accompaniment to lectures. The Power-
x | Preface
TEAM LinG - Live, Informative, Non-cost and Genuine!
Point presentations for each chapter are designed to coordinate with the material
in the textbook, and can be downloaded from the book’s web site.
Acknowledgments
We would like to thank the following people who took the time to review the first edi-
tion of this manuscript: Donald Bagert, Texas Tech University; Susan Gauch, University
of Kansas; Pamela Lawhead, University of Mississippi; Pat Nettnin, Finger Lakes Com-
munity College; Bobbie Othmer, Westminster College of Salt Lake City; Suzanne
Pawlan-Levy, Allan Hancock College; Carol Roberts, University of Maine; and Robert
Strader, Stephen F. Austin State University. Thanks also to all of you who took the time
to answer our electronic survey concerning this third edition.
A special thanks to John McCormick, University of Northern Iowa, Mark Heading-
ton, University of Wisconsin—LaCrosse, and Dan Joyce. John and Dan graciously
allowed us to use some of their analogies from Ada Plus Data Structures and Object-
Oriented Data Structures Using Java, respectively. Mark’s ideas, suggestions, and sharp
eyes were invaluable. Thanks also to the students at Uppsala University in Sweden who
used the final draft of the manuscript of the second edition in a course in the fall of
1997. Because non-English readers see what is written, not what they expect to see,
their comments were invaluable in cleaning up ambiguous wording.
Thanks to my husband Al, our children and grandchildren too numerous to name,
and our dogs, Maggie, who keeps my feet warm, and Chrissie, whose role in life is to
keep the house in turmoil and mud.
A virtual bouquet of roses to the people who have worked on this book: Mike and
Sigrid Wile, along with our Jones and Bartlett family. Theresa DiDonato, a jack-of-all-
trades who helped with the survey; Jenny McIsaac, who jumped directly into the frying
pan on her first day; Nathan Schultz, whose “can do” attitude is a joy to work with; and
Michael Stranz and Amy Rose, whose team effort sustains all of us. Amy, thank heav-
ens this production schedule was a little more leisurely than the last—but not by much!
N. D.
Preface | xi
TEAM LinG - Live, Informative, Non-cost and Genuine!
TEAM LinG - Live, Informative, Non-cost and Genuine!
Preface v
1 Software Engineering Principles 1
1.1 The Software Process 2
1.2 Program Design 9
1.3 Verification of Software Correctness 19
Case Study: Fraction Class 50
Summary 58
Exercises 60
2 Data Design and Implementation 63
2.1 Different Views of Data 64
2.2 Abstraction and Built-In Types 72
2.3 Higher-Level Abstraction and the C++ Class Type 85
2.4 Object-Oriented Programming 91
2.5 Constructs for Program Verification 95
Case Study: User-Defined String I/O Class 100
Summary 116
Exercises 117
TEAM LinG - Live, Informative, Non-cost and Genuine!
3 ADTs Unsorted List and Sorted List 123
3.1 Lists 124
3.2 Abstract Data Type Unsorted List 125
3.3 Abstract Data Type Sorted List 146
3.4 Comparison of Algorithms 157
3.5 Comparison of Unsorted and Sorted List ADT Algorithms 164
3.6 Overloading Operators 167
3.7 Object-Oriented Design Methodology 170
Case Study: Real Estate Listings: An Object-Oriented Design 173
Summary 188
Exercises 189
4 ADTs Stack and Queue 195
4.1 Stacks 196
4.2 More about Generics: C++ Templates 210
4.3 Pointer Types 214
4.4 Dynamically Allocated Arrays 222
Case Study: Simulation 245
Summary 261
Exercises 262
5 Linked Structures 279
5.1 Implementing a Stack as a Linked Structure 280
5.2 Implementing a Queue as a Linked Structure 296
5.3 Implementing the Unsorted List as a Linked Structure 307
5.4 Implementing the Sorted List as a Linked Structure 318
Summary 327
Exercises 327
6 Lists Plus 333
6.1 Circular Linked Lists 334
6.2 Doubly Linked Lists 344
6.3 Linked Lists with Headers and Trailers 348
6.4 Copy Structures 350
xiv | Contents
TEAM LinG - Live, Informative, Non-cost and Genuine!
6.5 A Linked List as an Array of Records 358
6.6 Polymorphism with Virtual Functions 368
6.7 A Specialized List ADT 373
Case Study: Implementing a Large Integer ADT 379
Summary 392
Exercises 392
7 Programming with Recursion 399
7.1 What is Recursion? 400
7.2 The Classic Example of Recursion 401
7.3 Programming Recursively 404
7.4 Verifying Recursive Functions 407
7.5 Writing Recursive Functions 408
7.6 Using Recursion to Simplify Solutions 411
7.7 Recursive Linked List Processing 412
7.8 A Recursive Version of Binary Search 416
7.9 Recursive Versions of InsertItem and DeleteItem 418
7.10 How Recursion Works 420
7.11 Tracing the Execution of Recursive Function Insert 429
7.12 Debugging Recursive Routines 432
7.13 Removing Recursion 432
7.14 Deciding Whether to Use a Recursive Solution 436
Case Study: QuickSort 438
Summary 446
Exercises 447
8 Binary Search Trees 455
8.1 Trees 456
8.2 Logical Level 460
8.3 Application Level 463
8.4 Implementation Level 463
8.5 Recursive Binary Search Tree Operations 464
8.6 Iterative Insertion and Deletion 496
8.7 Comparing Binary Search Trees and Linear Lists 504
8.8 A Nonlinked Representation of Binary Trees 506
Case Study: Building an Index 510
Contents | xv
TEAM LinG - Live, Informative, Non-cost and Genuine!
Summary 517
Exercises 517
9 Priority Queues, Heaps, Graphs, and Sets 529
9.1 ADT Priority Queue 530
9.2 Heaps 533
9.3 Graphs 546
9.4 Sets 571
Summary 579
Exercises 579
10 Sorting and Searching Algorithms 588
10.1 Sorting 588
10.2 Searching 619
10.3 Hashing 622
10.4 Radix Sort 637
Summary 642
Exercises 644
Answer to Selected Exercises 653
Appendix A Reserved Words 713
Appendix B Operator Precedents 713
Appendix C A Selection of Standard Library Routines 715
Appendix D Character Sets 724
Appendix E The Standard Template Library 726
Glossary 771
Index 789
xvi | Contents
TEAM LinG - Live, Informative, Non-cost and Genuine!
After studying this chapter, you should be able to
Describe the general activities in the software life cycle
Describe the goals for “quality” software
Explain the following terms: software requirements, software specifica-
tions, algorithm, information hiding, abstraction, stepwise refinement
Explain and apply the fundamental ideas of top-down design
Explain and apply the fundamental ideas of object-oriented design
Identify several sources of program errors
Describe strategies to avoid software errors
Specify the preconditions and postconditions of a program segment or function
Show how deskchecking, code walk-throughs, and design and code inspections
can improve software quality and reduce the software development effort
Explain the following terms: acceptance tests, regression testing, verification,
validation, functional domain, black-box testing, white-box testing
State several testing goals and indicate when each would be appropriate
Describe several integration-testing strategies and indicate when each would
be appropriate
Explain how program verification techniques can be applied throughout the
software development process
Create a C++ test driver program to test a simple class
Goals
Software Engineering
Principles
TEAM LinG - Live, Informative, Non-cost and Genuine!
2 | Chapter 1: Software Engineering Principles
At this point in your computing career, you have completed at least one semester of
computer science course work. You can take a problem of medium complexity, write an
algorithm to solve the problem, code the algorithm in C++, and demonstrate the correct-
ness of your solution. At least, that’s what the syllabus for your introductory class said
you should be able to do when you complete the course. Now that you are starting your
second (or third?) semester, it is time to stop and review those principles that, if adhered
to, guarantee that you can indeed do what your previous syllabus claimed.
In this chapter, we review the software design process and the verification of soft-
ware correctness. In Chapter 2, we review data design and implementation.
1.1 The Software Process
When we consider computer programming, we immediately think of writing a program
for a computer to execute—the generation of code in some computer language. As a
beginning student of computer science, you wrote programs that solved relatively sim-
ple problems. Much of your initial effort went into learning the syntax of a program-
ming language such as C++: the language’s reserved words, its data types, its constructs
for selection (if-else and switch) and looping (while, do while, and for), and its
input/output mechanisms (cin and cout).
You may have learned a programming methodology that took you from the problem
description that your instructor handed out all the way through the delivery of a good
software solution. Programmers have created many design techniques, coding standards,
and testing methods to help develop high-quality software. But why bother with all that
methodology? Why not just sit down at a computer and write programs? Aren’t we
wasting a lot of time and effort, when we could just get started on the “real” job?
If the degree of our programming sophistication never had to rise above the level of
trivial programs (like summing a list of prices or averaging grades), we might get away
with such a code-first technique (or, rather, lack of technique). Some new programmers
work this way, hacking away at the code until the program works more or less cor-
rectly—usually less.
As your programs grow larger and more complex, however, you must pay attention
to other software issues in addition to coding. If you become a software professional,
someday you may work as part of a team that develops a system containing tens of
thousands, or even millions, of lines of code. The activities involved in such a software
project’s whole “life cycle” clearly go beyond just sitting down at a computer and writ-
ing programs. These activities include
• Problem analysis Understanding the nature of the problem to be solved
• Requirements elicitation Determining exactly what the program must do
• Requirements definition Specifying what the program must do (functional
requirements) and any constraints on the solution approach (nonfunctional
requirements such as what language to use)
• High- and low-level design Recording how the program meets the requirements,
from the “big picture” overview to the detailed design
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.1 The Software Process | 3
• Implementation of the design Coding a program in a computer language
• Testing and verification Detecting and fixing errors and demonstrating the cor-
rectness of the program
• Delivery Turning over the tested program to the customer or user (or instructor!)
• Operation Actually using the program
• Maintenance Making changes to fix operational errors and to add or modify
the program’s function
Software development is not simply a matter of going through these steps sequen-
tially. Rather, many activities take place concurrently. We may code one part of the
solution while we design another part, or define requirements for a new version of a
program while we continue testing the current version. Often a number of people may
work on different parts of the same program simultaneously. Keeping track of all these
activities is not an easy task.
We use the term software engineering to
refer to the discipline concerned with all
aspects of the development of high quality
software systems. It encompasses all varia-
tions of techniques used during the software
life cycle plus supporting activities such as
documentation and teamwork. A software
process is a specific set of interrelated soft-
ware engineering techniques, used by a per-
son or organization to create a system.
What makes our jobs as programmers or
software engineers challenging is the ten-
dency of software to grow in size and complexity and to change at every stage of its
development. A good software process uses tools to manage this size and complexity
effectively. Usually a programmer takes advantage of several toolboxes, each containing
tools that help to build and shape a software product.
Hardware One toolbox contains the hardware itself: the computers and their
peripheral devices (such as monitors, terminals, storage devices, and printers), on which
and for which we develop software.
Software A second toolbox contains various software tools: operating systems to
control the computer’s resources, text editors to help us enter programs, compilers to
translate high-level languages like C++ into something that the computer can execute,
interactive debugging programs, test-data generators, and so on. You’ve used some of
these tools already.
Ideaware A third toolbox is filled with the shared body of knowledge that
programmers have collected over time. This box contains the algorithms that we use to
solve common programming problems as well as data structures for modeling the
Software engineering The discipline devoted to the
design, production, and maintenance of computer pro-
grams that are developed on time and within cost esti-
mates, using tools that help to manage the size and
complexity of the resulting software products
Software process A standard, integrated set of software
engineering tools and techniques used on a project or by
an organization
TEAM LinG - Live, Informative, Non-cost and Genuine!
4 | Chapter 1: Software Engineering Principles
information processed by our programs. Recall that an
algorithm is a step-by-step description of the solution
to a problem. How we choose between two algorithms
that carry out the same task often depends on the
requirements of a particular application. If no relevant
requirements exist, the choice may be based on the
programmer’s own style.
Ideaware contains programming methodologies such as top-down and object-ori-
ented design and software concepts, including information hiding, data encapsulation,
and abstraction. It includes aids for creating designs such as CRC (Classes, Responsibili-
ties, and Collaborations) cards and methods for describing designs such as the UML
(Unified Modeling Language). It also contains some tools for measuring, evaluating, and
proving the correctness of our programs. We devote most this book to exploring the
contents of this third toolbox.
Some might argue that using these tools takes the creativity out of programming,
but we don’t believe that to be true. Artists and composers are creative, yet their inno-
vations are grounded in the basic principles of their crafts. Similarly, the most creative
programmers build high-quality software through the disciplined use of basic program-
ming tools.
Goals of Quality Software
Quality software entails much more than a program that somehow accomplishes the
task at hand. A good program achieves the following goals:
1. It works.
2. It can be modified without excessive time and effort.
3. It is reusable.
4. It is completed on time and within budget.
It’s not easy to meet these goals, but they are all important.
Goal 1: Quality Software Works The program must do the task it was designed to
perform, and it must do it correctly and completely. Thus the first step in
the development process is to determine exactly
what the program is required to do. To write a
program that works, you first need to have a
definition of the program’s requirements. For students,
the requirements often are included in the instruc-
tor’s problem description: “Write a program that
calculates....” For programmers working on a govern-
ment contract, the requirements document may be
hundreds of pages long.
We develop programs that meet the user’s requirements using software specifica-
tions. The specifications indicate the format of the input and the expected output,
Algorithm A logical sequence of discrete steps that
describes a complete solution to a given problem, com-
putable in a finite amount of time
Requirements A statement of what is to be provided by
a computer system or software product
Software specification A detailed description of the
function, inputs, processing, outputs, and special require-
ments of a software product; it provides the information
needed to design and implement the program
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.1 The Software Process | 5
details about processing, performance measures (how fast? how big? how accurate?),
what to do in case of errors, and so on. The specifications tell exactly what the program
does, but not how it is done. Sometimes your instructor will provide detailed specifica-
tions; other times you may have to write them yourself, based on the requirements defi-
nition, conversations with your instructor, or guesswork. (We discuss this issue in more
detail later in this chapter.)
How do you know when the program is right? A program must be complete (it
should “do everything” specified) and correct (it should “do it right”) to meet its require-
ments. In addition, it should be usable. For instance, if the program needs to receive
data from a person sitting at a terminal, it must indicate when it expects input. The pro-
gram’s outputs should be readable and understandable to users. Indeed, creating a good
user interface is an important subject in software engineering today.
Finally, Goal 1 means that the program should be as efficient as it needs to be. We
would never deliberately write programs that waste time or space in memory, but not all
programs demand great efficiency. When they do, however, we must meet these
demands or else the programs will not satisfy the requirements. A space-launch control
program, for instance, must execute in “real time”; that is, the software must process
commands, perform calculations, and display results in coordination with the activities
it is supposed to control. Closer to home, if a desktop-publishing program cannot
update the screen as rapidly as the user can type, the program is not as efficient as it
needs to be. In such a case, if the software isn’t efficient enough, it doesn’t meet its
requirements; thus, according to our definition, it doesn’t work correctly.
Goal 2: Quality Software Can Be Modified When does software need to be modified?
Changes occur in every phase of its existence.
Software gets changed in the design phase. When your instructor or employer gives
you a programming assignment, you begin to think of how to solve the problem. The
next time you meet, however, you may be notified of a small change in the program
description.
Software gets changed in the coding phase. You make changes in your program as
a result of compilation errors. Sometimes you suddenly see a better solution to a part of
the problem after the program has been coded, so you make changes.
Software gets changed in the testing phase. If the program crashes or yields wrong
results, you must make corrections.
In an academic environment, the life of the software typically ends when a cor-
rected program is turned in to be graded. When software is developed for real-world
use, however, most of the changes take place during the “maintenance” phase. Someone
may discover an error that wasn’t uncovered in testing, someone else may want to
include additional functions, a third party may want to change the input format, and a
fourth person may want to run the program on another system.
As you see, software changes often and in all phases of its life cycle. Knowing this
fact, software engineers try to develop programs that are modified easily. If you think it
is a simple matter to change a program, try to make a “small change” in the last pro-
gram you wrote. It’s difficult to remember all the details of a program after some time
has passed, isn’t it? Modifications to programs often are not even made by the original
TEAM LinG - Live, Informative, Non-cost and Genuine!
6 | Chapter 1: Software Engineering Principles
authors but rather by subsequent maintenance programmers. (Someday you may be the
one making the modifications to someone else’s program.)
What makes a program easy to modify? First, it should be readable and understand-
able to humans. Before it can be changed, it must be understood. A well-designed,
clearly written, well-documented program is certainly easier for human readers to
understand. The number of pages of documentation required for “real-world” programs
usually exceeds the number of pages of code. Almost every organization has its own
policy for documentation. Reading a well-written program can teach you techniques
that help you write good programs. In fact, it’s difficult to imagine how anyone could
become a good programmer without reading good programs.
Second, the program should readily be able to withstand small changes. The key
idea is to partition your programs into manageable pieces that work together to solve
the problem, yet remain relatively independent. The design methodologies reviewed
later in this chapter should help you write programs that meet this goal.
Goal 3: Quality Software Is Reusable It takes time and effort to create quality software.
Therefore, it is important to realize as much value from the software as possible.
One way to save time and effort when building a software solution is to reuse pro-
grams, classes, functions, and other components from previous projects. By using previ-
ously designed and tested code, you arrive at your solution sooner and with less effort.
Alternatively, when you create software to solve a problem, it is sometimes possible to
structure that software so it can help solve future, related problems. By doing so, you
gain more value from the software created.
Creating reusable software does not happen automatically. It requires extra effort
during the specification and design phases. To be reusable, software must be well docu-
mented and easy to read, so that a programmer can quickly determine whether it can be
used for a new project. It usually has a simple interface so that it can easily be plugged
into another system. It is also modifiable (Goal 2), in case a small change is needed to
adapt it to the new system.
When creating software to fulfill a narrow, specific function, you can sometimes
make the software more generally usable with a minimal amount of extra effort. In this
way, you increase the chances that you can reuse the software later. For example, if
you are creating a routine that sorts a list of integers into increasing order, you might
generalize the routine so that it can also sort other types of data. Furthermore, you
could design the routine to accept the desired sort order, increasing or decreasing, as a
parameter.
Goal 4: Quality Software Is Completed on Time and Within Budget You know what
happens in school when you turn in your program late. You probably have grieved over an
otherwise perfect program that received only half credit—or no credit at all—because you
turned it in one day late. “But the network was down five hours last night!” you protest.
Although the consequences of tardiness may seem arbitrary in the academic world,
they are significant in the business world. The software for controlling a space launch
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.1 The Software Process | 7
must be developed and tested before the launch can take place. A patient database sys-
tem for a new hospital must be installed before the hospital can open. In such cases, the
program doesn’t meet its requirements if it isn’t ready when needed.
“Time is money” may sound trite but failure to meet deadlines is expensive. A com-
pany generally budgets a certain amount of time and money for the development of a
piece of software. As a programmer, you are paid a salary or an hourly wage. If your
part of the project is only 80% complete when the deadline arrives, the company must
pay you—or another programmer—to finish the work. The extra expenditure in salary is
not the only cost, however. Other workers may be waiting to integrate your part of the
program into the system for testing. If the program is part of a contract with a cus-
tomer, monetary penalties may be assessed for missed deadlines. If it is being developed
for commercial sales, the company may be beaten to the market by a competitor and
eventually forced out of business.
Once you have identified your goals, what can you do to meet them? Where should
you start? Software engineers use many tools and techniques. In the next few sections
of this chapter, we review some of these techniques to help you understand, design, and
code programs.
Specification: Understanding the Problem
No matter which programming design technique you use, the first steps are always the
same. Imagine the following all-too-familiar situation. On the third day of class, you are
given a 12-page description of Programming Assignment 1, which must be running per-
fectly and turned in by noon, one week from yesterday. You read the assignment and
realize that this program is three times larger than any program you have ever written.
What is your first step?
The responses listed here are typical of those given by a class of computer science
students in such a situation:
1. Panic 39%
2. Sit down at the computer and begin typing 30%
3. Drop the course 27%
4. Stop and think 4%
Response 1 is a predictable reaction from students who have not learned good pro-
gramming techniques. Students who adopt Response 3 will find their education pro-
gressing rather slowly. Response 2 may seem to be a good idea, especially considering
the deadline looming ahead. Resist the temptation, though—the first step is to think.
Before you can come up with a program solution, you must understand the problem.
Read the assignment, and then read it again. Ask questions of your instructor (or man-
ager, or client). Starting early affords you many opportunities to ask questions; starting
the night before the program is due leaves you no opportunity at all.
The problem with writing first is that it tends to lock you into the first solution you
think of, which may not be the best approach. We have a natural tendency to believe
TEAM LinG - Live, Informative, Non-cost and Genuine!
8 | Chapter 1: Software Engineering Principles
that once we’ve put something in writing, we have invested too much in the idea to toss
it out and start over.
On the other hand, don’t agonize about all the possibilities until the day before your
deadline. (Chances are that a disk drive, network, or printer will fail that day!) When
you think you understand the problem, you should begin writing your design.
Writing Detailed Specifications
Many writers experience a moment of terror when faced with a blank piece of paper—
where to begin? As a programmer, however, you don’t have to wonder about where to
begin. Using the assignment description (your “requirements”), first write a complete
definition of the problem, including the details of the expected inputs and outputs, the
necessary processing and error handling, and all assumptions about the problem. When
you finish this task, you have a detailed specification—a formal definition of the prob-
lem your program must solve, which tells you exactly what the program should do. In
addition, the process of writing the specifications brings to light any holes in the
requirements. For instance, are embedded blanks in the input significant or can they be
ignored? Do you need to check for errors in the input? On which computer system(s)
will your program run? If you get the answers to these questions at this stage, you can
design and code your program correctly from the start.
Many software engineers work with user/operational scenarios to understand the
requirements. In software design, a scenario is a sequence of events for one execution of
the program. For example, a designer might consider the following scenario when
developing the software for a bank’s automated teller machine (ATM):
1. The customer inserts a bank card.
2. The ATM reads the account number on the card.
3. The ATM requests a PIN (personal identification number) from the customer.
4. The customer enters 5683.
5. The ATM successfully verifies the account number PIN combination.
6. The ATM asks the customer to select a transaction type (deposit, show balance,
withdrawal, or quit).
7. The customer selects the show balance option.
8. The ATM obtains the current account balance ($1,204.35) and displays it.
9. The ATM asks the customer to select a transaction type (deposit, show balance,
withdrawal, or quit).
10. The customer selects quit.
11. The ATM returns the customer’s bank card.
Scenarios allow us to get a feel for the behavior expected from the system. Of
course, a single scenario cannot show all possible behaviors. For this reason, software
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.2 Program Design | 9
engineers typically prepare many different scenarios to gain a full understanding of the
system’s requirements.
You must know some details to write and run the program. Other details, if not
explicitly stated in the program’s requirements, may be handled according to the pro-
grammer’s preference. Assumptions about unstated or ambiguous specifications should
always be written explicitly in the program’s documentation.
The detailed specification clarifies the problem to be solved. But it does more than
that: It also serves as an important piece of written documentation about the program.
There are many ways in which specifications may be expressed and a number of differ-
ent sections that may be included, depending on the nature of the problem. Our recom-
mended program specification includes the following sections:
• Processing requirements
• Sample inputs with expected outputs
• Assumptions
If special processing is needed for unusual or error conditions, it should be specified
as well. Sometimes it is helpful to include a section containing definitions of terms used.
Likewise, it may prove useful to list any testing requirements so that verifying the pro-
gram is considered early in the development process.
1.2 Program Design
Remember, the specification of the program tells what the program must do, but not
how it does it. Once you have fully clarified the goals of the program, you can begin to
develop and record a strategy for meeting them; in other words, you can begin the
design phase of the software life cycle.
Tools
In this section, we review some ideaware tools that are used for software design, includ-
ing abstraction, information hiding, stepwise refinement, and visual tools.
Abstraction The universe is filled with complex systems. We learn about such systems
through models. A model may be mathematical, like equations describing the motion of
satellites around the earth. A physical object such as a model airplane used in wind-
tunnel tests is another form of model. In this approach to understanding complex
systems, the important concept is that we consider only the essential characteristics of
the system; we ignore minor or irrelevant details. For example, although the earth is an
oblate ellipsoid, globes (models of the earth) are spheres. The small difference between
the earth’s equatorial diameter and polar diameter is not important to us in studying the
political divisions and physical landmarks on the earth. Similarly, the model airplanes
used to study aerodynamics do not include in-flight movies.
TEAM LinG - Live, Informative, Non-cost and Genuine!
10 | Chapter 1: Software Engineering Principles
Figure 1.1 An abstraction includes the essential details relative to the perspective of the viewer.
f=ma
An abstraction is a model of a complex system
that includes only the essential details. Abstractions
are the fundamental way that we manage complexity.
Different viewers use different abstractions of a partic-
ular system. Thus, while we may see a car as a means
to transport us and our friends, the automotive brake
engineer may see it as a large mass with a small con-
tact area between it and the road (Figure 1.1).
What does abstraction have to do with software
development? The programs we write are abstractions. A spreadsheet program that is
used by an accountant models the books used to record debits and credits. An educa-
tional computer game about wildlife models an ecosystem. Writing software is difficult
because both the systems we model and the processes we use to develop the software
are complex. One of our major goals is to convince you to use abstractions to manage
the complexity of developing software. In nearly every chapter, we make use of abstrac-
tion to simplify our work.
Information Hiding Many design methods are based on decomposing a problem’s
solution into modules. A module is a cohesive system subunit that performs a share of
the work. Decomposing a system into modules helps us manage complexity. Additionally,
the modules can form the basis of assignments for different programming teams working
separately on a large system. One important feature of any design method is that the
details that are specified in lower levels of the program design remain hidden from the
higher levels. The programmer sees only the details that are relevant at a particular level
Abstraction A model of a complex system that includes
only the details essential to the perspective of the viewer
of the system
Module A cohesive system subunit that performs a
share of the work
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.2 Program Design | 11
of the design. This information hiding makes
certain details inaccessible to the programmer
at higher levels.
Modules act as an abstraction tool.
Because the complexity of its internal struc-
ture can be hidden from the rest of the sys-
tem, the details involved in implementing a module remain isolated from the details of
the rest of the system.
Why is hiding the details desirable? Shouldn’t the programmer know everything?
No! In this situation, a certain amount of ignorance truly is advantageous. Information
hiding prevents the higher levels of the design from becoming dependent on low-level
design details that are more likely to be changed. For example, you can stop a car with-
out knowing whether it has disc brakes or drum brakes. You don’t need to know these
lower-level details of the car’s brake subsystem to stop it.
Furthermore, you don’t want to require a complete understanding of the complicated
details of low-level routines for the design of higher-level routines. Such a requirement
would introduce a greater risk of confusion and error throughout the whole program. For
example, it would be disastrous if every time we wanted to stop our car, we had to think,
“The brake pedal is a lever with a mechanical advantage of 10.6 coupled to a hydraulic
system with a mechanical advantage of 7.3 that presses a semi-metallic pad against a steel
disc. The coefficient of friction of the pad/disc contact is....”
Information hiding is not limited to driving cars and programming computers. Try to
list all the operations and information required to make a peanut butter and jelly sand-
wich. We normally don’t consider the details of planting, growing, and harvesting peanuts,
grapes, and wheat as part of making a sandwich. Information hiding lets us deal with only
those operations and information needed at a particular level in the solution of a problem.
The concepts of abstraction and information hiding are fundamental principles of soft-
ware engineering. We will come back to them again and again throughout this book.
Besides helping us manage the complexity of a large system, abstraction and information
hiding support our quality-related goals of modifiability and reusability. In a well-designed
system, most modifications can be localized to just a few modules. Such changes are much
easier to make than changes that permeate the entire system. Additionally, a good system
design results in the creation of generic modules that can be used in other systems.
To achieve these goals, modules should be good abstractions with strong cohesion;
that is, each module should have a single purpose or identity and the module should
stick together well. A cohesive module can usually be described by a simple sentence. If
you have to use several sentences or one very convoluted sentence to describe your
module, it is probably not cohesive. Each module should also exhibit information hiding
so that changes within it do not result in changes in the modules that use it. This inde-
pendent quality of modules is known as loose coupling. If your module depends on the
internal details of other modules, it is not loosely coupled.
Stepwise Refinement In addition to concepts such as abstraction and information
hiding, software developers need practical approaches to conquer complexity. Stepwise
Information hiding The practice of hiding the details of
a function or data structure with the goal of controlling
access to the details of a module or structure
TEAM LinG - Live, Informative, Non-cost and Genuine!
12 | Chapter 1: Software Engineering Principles
1Grady Booch, Object Oriented Design with Applications (Benjamin Cummings, 1991).
refinement is a widely applicable approach. Many variations of it exist, such as top-
down, bottom-up, functional decomposition, and even “round-trip gestalt design.”
Undoubtedly you have learned a variation of stepwise refinement in your studies, as it
is a standard method for organizing and writing essays, term papers, and books. For
example, to write a book an author first determines the main theme and the major
subthemes. Next, the chapter topics can be identified, followed by section and
subsection topics. Outlines can be produced and further refined for each subsection. At
some point the author is ready to add detail—to actually begin writing sentences.
In general, with stepwise refinement, a problem is approached in stages. Similar
steps are followed during each stage, with the only difference reflecting the level of
detail involved. The completion of each stage brings us closer to solving our problem.
Let’s look at some variations of stepwise refinement:
• Top-down With this approach, the problem is first broken into several large
parts. Each of these parts is, in turn, divided into sections, the sections are subdi-
vided, and so on. The important feature is that details are deferred as long as
possible as we move from a general to a specific solution. The outline approach
to writing a book involves a form of top-down stepwise refinement.
• Bottom-up As you might guess, with this approach the details come first. Bot-
tom-up development is the opposite of the top-down approach. After the detailed
components are identified and designed, they are brought together into increas-
ingly higher-level components. This technique could be used, for example, by the
author of a cookbook who first writes all the recipes and then decides how to
organize them into sections and chapters.
• Functional decomposition This program design approach encourages program-
ming in logical action units, called functions. The main module of the design
becomes the main program (also called the main function), and subsections
develop into functions. This hierarchy of tasks forms the basis for functional
decomposition, with the main program or function controlling the processing.
The general function of the method is continually divided into subfunctions until
the level of detail is considered fine enough to code. Functional decomposition is
top-down stepwise refinement with an emphasis on functionality.
• Round-trip gestalt design This confusing term is used to define the stepwise
refinement approach to object-oriented design suggested by Grady Booch,1 one
of the leaders of the “object” movement. First, the tangible items and events in
the problem domain are identified and assigned to candidate classes and objects.
Next, the external properties and relationships of these classes and objects are
defined. Finally, the internal details are addressed; unless these are trivial, the
designer must return to the first step for another round of design. This approach
entails top-down stepwise refinement with an emphasis on objects and data.
Good software designers typically use a combination of the stepwise refinement
techniques described here.
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.2 Program Design | 13
2K. B. Beck and W. Cunningham, http://c2.com/doc/oopsla89/paper.html.
Visual Tools Abstraction, information hiding, and stepwise refinement are interrelated
methods for controlling complexity during the design of a system. We now look at
some tools that can help us visualize our designs. Diagrams are used in many
professions. For example, architects use blueprints, investors use market trend graphs,
and truck drivers use maps.
Software engineers use different types of diagrams and tables, such as the Unified Mod-
eling Language (UML) and Class, Responsibility, and Collaboration (CRC) cards. The
UML is used to specify, visualize, construct, and document the components of a soft-
ware system. It combines the best practices that have evolved over the past several
decades for modeling systems, and it is particularly well suited to modeling object-ori-
ented designs. UML diagrams represent another form of abstraction. They hide imple-
mentation details and allow systems designers to concentrate on only the major design
components. UML includes a large variety of interrelated diagram types, each with its
own set of icons and connectors. A very powerful development and modeling tool, it is
helpful for modeling designs after they have been developed.
In contrast, CRC cards help us determine our initial designs. CRC cards were first
described by Beck and Cunningham,2 in 1989, as a means to allow object-oriented pro-
grammers to identify a set of cooperating classes to solve a problem.
A programmer uses a physical 4  6 index card to represent each class that had
been identified as part of a problem solution. Figure 1.2 shows a blank CRC card. It con-
tains room for the following information about a class:
1. Class name
2. Responsibilities of the class—usually represented by verbs and implemented by pub-
lic functions (called methods in object-oriented terminology)
3. Collaborations—other classes or objects that are used in fulfilling the responsibilities
TEAM LinG - Live, Informative, Non-cost and Genuine!
14 | Chapter 1: Software Engineering Principles
Figure 1.2 A blank CRC card
Class Name: Superclass: Subclasses:
Responsibilities Collaborations
CRC cards are great tools for refining an object-oriented design, especially in a
team programming environment. They provide a physical manifestation of the building
blocks of a system that allows programmers to walk through user scenarios, identifying
and assigning responsibilities and collaborations. We discuss a problem-solving
methodology using CRC cards in Chapter 3.
UML is beyond the scope of this text, but we will use CRC cards throughout.
Design Approaches
We have defined the concept of a module, described the characteristics of a good mod-
ule, and presented the concept of stepwise refinement as a strategy for defining mod-
ules. But what should these modules be? How do we define them? One approach is to
break the problem into functional subproblems (do this, then do this, then do that).
Another approach is to divide the problem into the “things” or objects that interact to
solve the problem. We explore both of these approaches in this section.
Top-Down Design One method for designing software is based on the functional
decomposition and top-down strategies. You may have learned this method in your
introductory class. First the problem is broken into several large tasks. Each of these
tasks is, in turn, divided into sections, the sections are subdivided, and so on. As we
said previously, the key feature is that details are deferred as long as possible as we
move from a general to a specific solution.
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.2 Program Design | 15
Make Cake
Get ingredients
Mix cake ingredients
Bake
Cool
Apply icing
To develop a computer program by this method, we begin with a “big picture”
solution to the problem defined in the specification. We then devise a general strat-
egy for solving the problem by dividing it into manageable functional modules.
Next, each of the large functional modules is subdivided into several tasks. We do
not need to write the top level of the functional design in source code (such as C++);
rather, we can write it in English or “pseudocode.” (Some software development proj-
ects even use special design languages that can be compiled.) This divide-and-con-
quer activity continues until we reach a level that can be easily translated into lines
of code.
Once it has been divided into modules, the problem is simpler to code into a well-
structured program. The functional decomposition approach encourages programming
in logical units, using functions. The main module of the design becomes the main pro-
gram (also called the main function), and subsections develop into functions. This hier-
archy of tasks forms the basis for functional decomposition, with the main program or
function controlling the processing.
As an example, let’s start the functional design for making a cake.
The problem now is divided into five logical units, each of which might be further
decomposed into more detailed functional modules. Figure 1.3 illustrates the hierarchy
of such a functional decomposition.
Object-Oriented Design Another approach to designing programs is called object-
oriented design (OOD). This methodology originated with the development of programs
to simulate physical objects and processes in the real world. For example, to simulate an
electronic circuit, you could develop a module for simulating each type of component in
the circuit and then “wire up” the simulation by having the modules pass information
among themselves along the same pattern in which wires connect the electronic
components.
In a simulation, the top-down decomposition of the problem has already taken
place. An engineer has designed a circuit or a mechanical device, a physicist has devel-
oped a model of a physical system, a biologist has developed an experimental model, an
TEAM LinG - Live, Informative, Non-cost and Genuine!
16 | Chapter 1: Software Engineering Principles
Figure 1.3 A portion of a functional design for baking a cake
Make cake
Get
ingredients
Mix cake
ingredients
Bake Cool
Apply
icing
Mix liquid
ingredients
Mix dry
ingredients
Combine
liquid and dry
ingredients
.
.
.
.
.
.
.
.
economist has designed an economic model, and so on. As a programmer, your job is to
take this problem decomposition and implement it.
In object-oriented design, the first steps are to identify the simplest and most widely
used objects and processes in the decomposition and to implement them faithfully. Once
you have completed this stage, you often can reuse these objects and processes to
implement more complex objects and processes. This hierarchy of objects forms the
basis for object-oriented design.
Object-oriented design, like top-down design, takes a divide-and-conquer approach.
However, instead of decomposing the problem into functional modules, we divide it into
entities or things that make sense in the context of the problem being solved. These
entities, called objects, collaborate and interact to solve the problem. The code that
allows these objects to interact is called a driver program.
Let’s list some of the objects in our baking problem. There are, of course, all of the
various ingredients: eggs, milk, flour, butter, and so on. We also need certain pieces of
equipment, such as pans, bowls, measuring spoons, and an oven. The baker is another
important entity. All of these entities must collaborate
to create a cake. For example, a spoon measures indi-
vidual ingredients and a bowl holds a mixture of
ingredients.
Groups of objects with similar properties and
behaviors are described by an object class (usually
Object class (class) The description of a group of
objects with similar properties and behaviors; a pattern
for creating individual objects
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.2 Program Design | 17
shortened to class). Each oven in the world is a unique object. We cannot hope to
describe every oven, but we can group oven objects together into a class called oven
that has certain properties and behaviors.
An object class is similar to a C++ class (see the sidebar on page 18 on class syntax
and the discussion in Chapter 2). C++ types are templates for variables; classes are tem-
plates for objects. Like types, object classes have attributes and operations associated with
them. For example, an oven class might have an attribute to specify whether it is gas or
electric and operations to turn it on or off and to set it to maintain a desired temperature.
With object-oriented design, we determine the classes from the things in the prob-
lem as described in the problem statement. We record each object class using a CRC
card. From this work, we determine a set of properties (attributes) and a set of responsi-
bilities (operations) to associate with each class. With object-oriented design, the func-
tionality of the program is distributed among a set of collaborating objects. Table 1.1
illustrates some of the object classes that participate in baking a cake.
Once we have defined an oven class, we can reuse it in other cooking problems,
such as roasting a turkey. Reuse of classes is an important aspect of modern software
development. One major goal of this text is to introduce you to a number of classes that
are particularly important in the development of software—abstract data types. We dis-
cuss the concept of an abstract data type in detail in Chapter 2. Throughout the book,
we fully develop many abstract data types, and we describe others leaving you to
develop them yourself. As these classes are fundamental to computer science, we can
often obtain the C++ code for them from a public or private repository or purchase it
from vendors who market C++ components. In fact, the new C++ language standard
includes components in the Standard Template Library (STL). You may wonder why, if
they are already available, we spend so much time on their development. Our goal is to
teach you how to develop software. As with any skill, you need to practice the funda-
mentals before you can become a virtuoso.
To summarize, top-down design methods focus on the process of transforming the
input into the output, resulting in a hierarchy of tasks. Object-oriented design focuses
on the data objects that are to be transformed, resulting in a hierarchy of objects. Grady
Table 1.1 Example of object classes that participate in baking a cake
Class Attributes Responsibilities (Operations)
Oven Energy source Turn on
Size Turn off
Temperature Set desired temperature
Number of racks
Bowl Capacity Add to
Current amount Dump
Egg Size Crack
Separate (white from yolk)
TEAM LinG - Live, Informative, Non-cost and Genuine!
18 | Chapter 1: Software Engineering Principles
Booch puts it this way: “Read the specification of the software you want to build.
Underline the verbs if you are after procedural code, the nouns if you aim for an object-
oriented program.”3
We propose that you circle the nouns and underline the verbs. The nouns become
objects; the verbs become operations. In a functional design, the verbs are the primary
focus; in an object-oriented design, the nouns are the primary focus.
3Grady Booch, “What Is and Isn’t Object Oriented Design.” American Programmer, special issue on object ori-
entation, vol. 2, no. 7–8, Summer 1989.
Class Syntax
A C++ class contains both data and functions that operate on the data. A class is declared in two
parts: the specification of the class and the implementation of the class functions.
class MoneyType
{
public:
void Initialize(long, long);
// Initializes dollars and cents.
long DollarsAre() const;
// Returns dollars.
long CentsAre() const;
// Returns cents.
private:
long dollars;
long cents;
};
A member function is defined like any function with one exception: The name of the class type
within which the member is declared precedes the member function name with a double colon in
between (::). The double colon operator is called the scope resolution operator.
void MoneyType::Initialize(long newDollars, long newCents)
// Post: dollars is set to newDollars; cents is set to
// newCents.
{
dollars = newDollars;
cents = newCents;
}
C++
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.3 Verification of Software Correctness | 19
long MoneyType::DollarsAre() const
// Post: Class member dollars is returned.
{
return dollars;
}
long MoneyType::CentsAre() const
// Post: Class member cents is returned.
{
return cents;
}
If money is a variable of type MoneyType, the following statement prints the data fields of money:
cout  $  money.DollarsAre()
 .  money.CentsAre();
1.3 Verification of Software Correctness
At the beginning of this chapter, we discussed some characteristics of good programs.
The first of these was that a good program works—it accomplishes its intended function.
How do you know when your program meets that goal? The simple answer is, test it.
Let’s look at testing as it relates to the rest
of the software development process. As pro-
grammers, we first make sure that we under-
stand the requirements. We then come up with
a general solution. Next, we design the solu-
tion in terms of a computer program, using
good design principles. Finally, we implement
the solution, using good structured coding,
with classes, functions, self-documenting code,
and so on.
Once we have the program coded, we compile it repeatedly until no syntax errors
appear. Then we run the program, using carefully selected test data. If the program
doesn’t work, we say that it has a “bug” in it. We try to pinpoint the error and fix it, a
process called debugging. Notice the distinction between testing and debugging. Testing
is running the program with data sets designed to discover any errors; debugging is
removing errors once they are discovered.
When the debugging is completed, the software is put into use. Before final deliv-
ery, software is sometimes installed on one or more customer sites so that it can be
tested in a real environment with real data. After passing this acceptance test phase, the
Testing The process of executing a program with data
sets designed to discover errors
Debugging The process of removing known errors
Acceptance test The process of testing the system in its
real environment with real data
TEAM LinG - Live, Informative, Non-cost and Genuine!
20 | Chapter 1: Software Engineering Principles
4B. W. Boehm, Software Engineering Economics (Englewood Cliffs, N.J.: Prentice-Hall, 1981).
software can be installed at all customer sites. Is the verification process now finished?
Hardly! More than half of the total life-cycle costs and effort generally occur after the
program becomes operational, in the maintenance phase. Some changes correct errors in
the original program; other changes add new capabilities to the software system. In
either case, testing must occur after any program
modification. This phase is called regression testing.
Testing is useful in revealing the presence of bugs
in a program, but it doesn’t prove their absence. We
can only say for sure that the program worked cor-
rectly for the cases we tested. This approach seems
somewhat haphazard. How do we know which tests or
how many of them to run? Debugging a whole pro-
gram at once isn’t easy. Also, fixing the errors found
during such testing can sometimes be a messy task.
Too bad we couldn’t have detected the errors earlier—
while we were designing the program, for instance.
They would have been much easier to fix then.
We know how program design can be improved by using a good design methodol-
ogy. Can we use something similar to improve our program verification activities? Yes,
we can. Program verification activities don’t need to start when the program is com-
pletely coded; they can be incorporated into the entire software development process,
from the requirements phase on. Program verification is more than just testing.
In addition to program verification, which involves fulfilling the requirement speci-
fications, the software engineer has another important task—making sure the specified
requirements actually solve the underlying problem. Countless times a programmer has
finished a large project and delivered the verified software, only to be told, “Well, that’s
what I asked for but it’s not what I need.”
The process of determining that software accomplishes its intended task is called
program validation. Program verification asks, “Are we doing the job right?”; program
validation asks, “Are we doing the right job?”4
Can we really “debug” a program before it has ever been run—or even before it has
been written? In this section we review a number of topics related to satisfying the cri-
terion “quality software works.” The topics include
• Designing for correctness
• Performing code and design walk-throughs and inspections
• Using debugging methods
• Choosing test goals and data
• Writing test plans
• Structured integration testing
Regression testing Reexecution of program tests after
modifications have been made to ensure that the program
still works correctly
Program verification The process of determining the
degree to which a software product fulfills its specifica-
tions
Program validation The process of determining the
degree to which software fulfills its intended purpose
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.3 Verification of Software Correctness | 21
Origin of Bugs
When Sherlock Holmes goes off to solve a case, he doesn’t start from scratch every
time; he knows from experience all kinds of things that help him find solutions. Sup-
pose Holmes finds a victim in a muddy field. He immediately looks for footprints in the
mud, for he can tell from a footprint what kind of shoe made it. The first print he finds
matches the shoes of the victim, so he keeps looking. Now he finds another print, and
from his vast knowledge of footprints he can tell that it was made by a certain type of
boot. He deduces that such a boot would be worn by a particular type of laborer, and
from the size and depth of the print he guesses the suspect’s height and weight. Now,
knowing something about the habits of laborers in this town, he guesses that at 6:30
P.M. the suspect might be found in Clancy’s Pub.
In software verification we are often expected to play detective. Given certain clues,
we have to find the bugs in programs. If we know what kinds of situations produce pro-
gram errors, we are more likely to be able to detect and correct problems. We may even
be able to step in and prevent many errors entirely, just as Sherlock Holmes sometimes
intervenes in time to prevent a crime from taking place.
Let’s look at some types of software errors that show up at various points in pro-
gram development and testing and see how they might be avoided.
Specifications and Design Errors What would happen if, shortly before you were
supposed to turn in a major class assignment, you discovered that some details in the
professor’s program description were incorrect? To make matters worse, you also found
out that the corrections were discussed at the beginning of class on the day you got
there late, and somehow you never knew about the problem until your tests of the class
data set came up with the wrong answers. What do you do now?
Writing a program to the wrong specifications is probably the worst kind of soft-
ware error. How bad can it be? Let’s look at a true story. Some time ago, a computer
company contracted to replace a government agency’s obsolete system with new hard-
TEAM LinG - Live, Informative, Non-cost and Genuine!
22 | Chapter 1: Software Engineering Principles
ware and software. A large and complicated program was written, based on specifica-
tions and algorithms provided by the customer. The new system was checked out at
every point in its development to ensure that its functions matched the requirements in
the specifications document. When the system was complete and the new software was
executed, users discovered that the results of its calculations did not match those of the
old system. A careful comparison of the two systems showed that the specifications of
the new software were erroneous because they were based on algorithms taken from the
old system’s inaccurate documentation. The new program was “correct” in that it
accomplished its specified functions, but the program was useless to the customer
because it didn’t accomplish its intended functions—it didn’t work. The cost of correct-
ing the errors measured in the millions of dollars.
How could correcting the error be so expensive? First, much of the conceptual and
design effort, as well as the coding, was wasted. It took a great deal of time to pinpoint
which parts of the specification were in error and then to correct this document before
the program could be redesigned. Then much of the software development activity
(design, coding, and testing) had to be repeated. This case is an extreme one, but it illus-
trates how critical specifications are to the software process. In general, programmers
are more expert in software development techniques than in the “application” areas of
their programs, such as banking, city planning, satellite control, or medical research.
Thus correct program specifications are crucial to the success of program development.
Most studies indicate that it costs 100 times as much to correct an error discovered
after software delivery than it does if the problem is discovered early in the software life
cycle. Figure 1.4 shows how fast the costs rise in subsequent phases of software devel-
opment. The vertical axis represents the relative cost of fixing an error; this cost might
be measured in units of hours, hundreds of dollars, or “programmer months” (the
amount of work one programmer can do in one month). The horizontal axis represents
the stages in the development of a software product. As you can see, an error that
would have taken one unit to fix when you first started designing might take 100 units
to correct when the product is actually in operation!
Good communication between the programmers (you) and the party who originated
the problem (the professor, manager, or customer) can prevent many specification
errors. In general, it pays to ask questions when you don’t understand something in the
program specifications. And the earlier you ask, the better.
A number of questions should come to mind as you first read a programming assign-
ment. What error checking is necessary? What algorithm or data structure should be used
in the solution? What assumptions are reasonable? If you obtain answers to these ques-
tions when you first begin working on an assignment, you can incorporate them into your
design and implementation of the program. Later in the program’s development, unex-
pected answers to these questions can cost you time and effort. In short, to write a pro-
gram that is correct, you must understand precisely what your program is supposed to do.
Sometimes specifications change during the design or implementation of a pro-
gram. In such cases, a good design helps you to pinpoint which sections of the program
must be redone. For instance, if a program defines and uses type StringType to imple-
ment strings, changing the implementation of StringType does not require rewriting
the entire program. We should be able to see from the design—either functional or
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.3 Verification of Software Correctness | 23
Figure 1.4 This graph demonstrates the importance of early detection of software errors.
Preliminary
design
Detailed
design
Code/
Debug
Integrate Validate Operation
Phase in which error is detected
100
50
20
10
5
2
1
Relative
cost
to
correct
error
Sources
• IBM
• TRW
• GTE
• Bell Labs
object-oriented—that the offending code is restricted to the module where StringType
is defined. The parts of the program that require changes can usually be located more
easily from the design than from the code itself.
Compile-Time Errors In the process of learning your first programming language, you
probably made a number of syntax errors. These mistakes resulted in error messages
(for example, “TYPE MISMATCH,” “ILLEGAL ASSIGNMENT,” “SEMICOLON
EXPECTED,” and so on) when you tried to compile the program. Now that you are more
familiar with the programming language, you can save your debugging skills for
tracking down really important logical errors. Try to get the syntax right the first time.
Having your program compile cleanly on the first attempt is not an unreasonable goal.
A syntax error wastes computing time and money, as well as programmer time, and it
is preventable. Some programmers argue that looking for syntax errors is a waste of
their time, that it is faster to let the compiler catch all the typos and syntax errors.
Don’t believe them! Sometimes a coding error turns out to be a legal statement,
TEAM LinG - Live, Informative, Non-cost and Genuine!
24 | Chapter 1: Software Engineering Principles
syntactically correct but semantically wrong. This situation may cause very obscure,
hard-to-locate errors.
As you progress in your college career or move into a professional computing job,
learning a new programming language is often the easiest part of a new software assign-
ment. This does not mean, however, that the language is the least important part. In this
book we discuss abstract data types and algorithms that we believe are language inde-
pendent. That is, they can be implemented in almost any general-purpose programming
language. In reality, the success of the implementation depends on a thorough under-
standing of the features of the programming language. What is considered acceptable
programming practice in one language may be inadequate in another, and similar syntac-
tic constructs may be just different enough to cause serious trouble.
For this reason, it is worthwhile to develop an expert knowledge of both the control
and data structures and the syntax of the language in which you are programming. In
general, if you have a good knowledge of your programming language—and are care-
ful—you can avoid syntax errors. The ones you might miss are relatively easy to locate
and correct. Most are flagged by the compiler with an error message. Once you have a
“clean” compilation, you can execute your program.
Run-Time Errors Errors that occur during the execution of a program are usually more
difficult to detect than syntax errors. Some run-time errors stop execution of the
program. When this situation happens, we say that the program “crashed” or
“terminated abnormally.”
Run-time errors often occur when the programmer makes too many assumptions.
For instance,
result = dividend / divisor;
is a legitimate assignment statement, if we can assume that divisor is never zero. If
divisor is zero, however, a run-time error results.
Sometimes run-time errors occur because the programmer does not fully under-
stand the programming language. For example, in C++ the assignment operator is =,
and the equality test operator is ==. Because they look so much alike, they often are
miskeyed one for the other. You might think that this would be a syntax error that the
compiler would catch, but it is actually a logic error. Technically, an assignment in
C++ consists of an expression with two parts: The expression on the right of the
assignment operator (=) is evaluated and the result is returned and stored in the place
named on the left. The key word here is returned; the result of evaluating the right-
hand side is the result of the expression. Therefore, if the assignment operator is
miskeyed for the equality test operator, or vice versa, the code executes with surprising
results.
Let’s look at an example. Consider the following two statements:
count == count + 1;
if (count = 10)
.
.
.
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.3 Verification of Software Correctness | 25
The first statement returns false; count can never be equal to count + 1. The semi-
colon ends the statement, so nothing happens to the value returned; count has not
changed. In the next statement, the expression (count = 10) is evaluated, and 10 is
returned and stored in count. Because a nonzero value (10) is returned, the if expres-
sion always evaluates to true.
Run-time errors also occur because of unanticipated user errors. For instance, if
newValue is declared to be of type int, the statement
cin  newValue;
causes a stream failure if the user inputs a nonnumeric character. An invalid file-
name can cause a stream failure. In some languages, the system reports a run-time
error and halts. In C++, the program doesn't halt; the program simply continues with
erroneous data. Well-written programs should not stop unexpectedly (crash) or con-
tinue with bad data. They should catch such errors and stay in control until the user
is ready to quit.
Stream Input and Output
In C++, input and output are considered streams of characters. The keyboard input stream is cin;
the screen output stream is cout. Important declarations relating to these streams are supplied in
the library file iostream. If you plan to use the standard input and output streams, you must
include this file in your program. You must also provide for access to the namespace with the using
directive,
#include iostream
int main()
{
using namespace std;
int intValue;
float realValue;
cout  Enter an integer number followed by return.
 endl;
cin  intValue;
cout  Enter a real number followed by return.
 endl;
cin  realValue;
cout  You entered   intValue   and 
 realValue  endl;
return 0;
}
C++
TEAM LinG - Live, Informative, Non-cost and Genuine!
26 | Chapter 1: Software Engineering Principles
 is called the insertion operator: The expressions on the right describe what is inserted into
the output stream.  is called the extraction operator: Values are extracted from the input stream
and stored in the places named on the right. endl is a special language feature called a
manipulator; it terminates the current output line.
If you are reading or writing to a file, you include fstream. You then have access to the data
types ifstream (for input) and ofstream (for output). Declare variables of these types, use the
open function to associate each with the external file name, and use the variable names in place of
cin and cout, respectively.
#include fstream
int main()
{
using namespace std;
int intValue;
float realValue;
ifstream inData;
ofstream outData;
inData.open(input.dat);
outData.open(output.dat);
inData  intValue;
inData  realValue;
outData  The input values are 
 intValue   and 
 realValue  endl;
return 0;
}
On input, whether from the keyboard or from a file, the  operator skips leading whitespace
characters (blank, tab, line feed, form feed, carriage return) before extracting the input value. To
avoid skipping whitespace characters, you can use the get function. You invoke it by giving the
name of the input stream, a dot, and then the function name and parameter list:
cin.get(inputChar);
The get function inputs the next character waiting in the input stream, even if it is a white-
space character.
Stream Failure
The key to reading data in correctly (from either the keyboard or a file) is to ensure that the order
and the form in which the data are keyed are consistent with the order and type of the identifiers on
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.3 Verification of Software Correctness | 27
the input statement. If an error occurs while accessing an I/O stream, the stream enters the fail state,
and any further references to the stream will be ignored. For example, if you misspell the name of
the file that is the parameter to the function open (In.dat instead of Data.In, for example), the
file input stream will enter the fail state. Alternatively, if you try to input a value when the stream is
at the end of the file, the stream will enter the fail state. Your program may continue to execute
while the stream remains in the fail state, but all further references to the stream will be ignored.
C++ gives you a way to test the state of a stream: The stream name used in an expression
returns a value that is converted to true if the state is good and to false if the stream is in the fail
state. For example, the following code segment prints an error message and halts execution if the
proper input file is not found:
#include fstream
#include iostream
int main()
{
using namespace std;
ifstream inData;
inData.open(myData.dat);
if (!inData)
{
cout  File myData.dat was not found.  endl;
return 1;
}
.
.
.
return 0;
}
By convention, the main function returns an exit status of 0 if execution completed normally,
whereas it returns a nonzero value (above, we used 1) otherwise.
The ability of a program to recover
when an error occurs is called robustness. If
a commercial program is not robust, people
do not buy it. Who wants a word processor
that crashes if the user says “SAVE” when
there is no disk in the drive? We want the
Robustness The ability of a program to recover follow-
ing an error; the ability of a program to continue to oper-
ate within its environment
TEAM LinG - Live, Informative, Non-cost and Genuine!
28 | Chapter 1: Software Engineering Principles
program to tell us, “Put your disk in the drive, and press Enter.” For some types of
software, robustness is a critical requirement. An airplane’s automatic pilot system or
an intensive care unit’s patient-monitoring program cannot afford to just crash. In
such situations, a defensive posture produces good results.
In general, you should actively check for error-creating conditions rather than let
them abort your program. For instance, it is generally unwise to make too many
assumptions about the correctness of input, especially “interactive” input from a key-
board. A better approach is to check explicitly for the correct type and bounds of such
input. The programmer can then decide how to handle an error (request new input, print
a message, or go on to the next data) rather than leave the decision to the system. Even
the decision to quit should be made by a program that controls its own execution. If
worse comes to worst, let your program die gracefully.
Of course, not everything that the program inputs must be checked for errors.
Sometimes inputs are known to be correct—for instance, input from a file that has been
verified. The decision to include error checking must be based upon the requirements of
the program.
Some run-time errors do not stop execution but do produce the wrong results.
You may have incorrectly implemented an algorithm or used a variable before it was
assigned a value. You may have inadvertently swapped two parameters of the same
type on a function call or forgotten to designate a function’s output data as a refer-
ence parameter. (See the Parameter Passing sidebar, page 74.) These “logical” errors
are often the hardest to prevent and locate. Later we will talk about debugging tech-
niques to help pinpoint run-time errors. We will also discuss structured testing meth-
ods that isolate the part of the program being tested. But knowing that the earlier we
find an error, the easier it is to fix, we turn now to ways of catching run-time errors
before run time.
Designing for Correctness
It would be nice if there were some tool that would locate the errors in our design or
code without our even having to run the program. That sounds unlikely, but consider an
analogy from geometry. We wouldn’t try to prove the Pythagorean Theorem by proving
that it worked on every triangle; that result would merely demonstrate that the theorem
works for every triangle we tried. We prove theorems in geometry mathematically. Why
can’t we do the same for computer programs?
The verification of program correctness, independent of data testing, is an impor-
tant area of theoretical computer science research. Such research seeks to establish a
method for proving programs that is analogous to the method for proving theorems
in geometry. The necessary techniques exist, but the proofs are often more compli-
cated than the programs themselves. Therefore a major focus of verification research
is the attempt to build automated program provers—verifiable programs that verify
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.3 Verification of Software Correctness | 29
5We do not go into this subject in detail here. For students who are interested in this topic, see David Gries,
The Science of Programming (New York: Springer-Verlag, 1981).
other programs. In the meantime, the formal verification techniques can be carried
out by hand.5
Assertions An assertion is a logical pro-
position that can be true or false. We can
make assertions about the state of the
program. For instance, with the assignment
statement
sum = part + 1 ; // sum and part are integers.
we might assert the following: “The value of sum is greater than the value of part.”
That assertion might not be very useful or interesting by itself, but let’s see what we can
do with it. We can demonstrate that the assertion is true by making a logical argument:
No matter what value part has (negative, zero, or positive), when it is increased by 1,
the result is a larger value. Now note what we didn’t do. We didn’t have to run a pro-
gram containing this assignment statement to verify that the assertion was correct.
The general concept behind formal program verification is that we can make asser-
tions about what the program is intended to do, based on its specifications, and then
prove through a logical argument (rather than through execution of the program) that a
design or implementation satisfies the assertions. Thus the process can be broken down
into two steps:
1. Correctly assert the intended function of the part of the program to be verified.
2. Prove that the actual design or implementation does what is asserted.
The first step, making assertions, sounds as if it might be useful to us in the process
of designing correct programs. After all, we already know that we cannot write correct
programs unless we know what they are supposed to do.
Preconditions and Postconditions Let’s take the idea of making assertions down a level
in the design process. Suppose we want to design a module (a logical chunk of the
program) to perform a specific operation. To ensure that this module fits into the
program as a whole, we must clarify what happens at its boundaries—that is, what must
be true when we enter the module and what must be true when we exit.
To make the task more concrete, picture the design module as it is eventually coded,
as a function that is called within a program. To call the function, we must know its
Assertion A logical proposition that can be true or false
TEAM LinG - Live, Informative, Non-cost and Genuine!
30 | Chapter 1: Software Engineering Principles
exact interface: the name and the parameter list,
which indicates its inputs and outputs. But this infor-
mation isn’t enough: We must also know any assump-
tions that must be true for the operation to function
correctly. We call the assertions that must be true on
entry into the function preconditions. The precondi-
tions act like a product disclaimer:
For instance, when we said that following the execution of
sum = part + 1;
we can assert that sum is greater than part, we made
an assumption—a precondition—that part is not
INT_MAX. If this precondition were violated, our asser-
tion would not be true.
We must also know what conditions are true
when the operation is complete. The postconditions
are assertions that describe the results of the opera-
tion. The postconditions do not tell us how these results are accomplished; rather, they
merely tell us what the results should be.
Let’s consider the preconditions and postconditions for a simple operation, one that
deletes the last element from a list and returns its value as an output. (We are using
“list” in an intuitive sense here; we formally define it in Chapter 3.) The specification for
GetLast is as follows:
GetLast(ListType list, ValueType lastValue)
Function: Remove the last element in the list and return its
value in lastValue.
Precondition: The list is not empty.
Postconditions: lastValue is the value of the last element in the
list, the last element has been removed, and the
list length has been decremented.
WARNING
If you try to execute this operation
when the preconditions are not true,
the results are not guaranteed.
Preconditions Assertions that must be true on entry
into an operation or function for the postconditions to be
guaranteed
Postconditions Assertions that state what results are
expected at the exit of an operation or function, assuming
that the preconditions are true
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.3 Verification of Software Correctness | 31
What do these preconditions and postconditions have to do with program verifica-
tion? By making explicit assertions about what is expected at the interfaces between
modules, we can avoid making logical errors based on misunderstandings. For instance,
from the precondition we know that we must check outside of this operation for the
empty condition; this module assumes that at least one element is present in the list.
The postcondition tells us that when the value of the last list element is retrieved, that
element is deleted from the list. This fact is an important one for the list user to know. If
we just want to take a peek at the last value without affecting the list, we cannot use
GetLast.
Experienced software developers know that misunderstandings about interfaces to
someone else’s modules are one of the main sources of program problems. We use pre-
conditions and postconditions at the module or function level in this book, because the
information they provide helps us to design programs in a truly modular fashion. We
can then use the modules we’ve designed in our programs, confident that we are not
introducing errors by making mistakes about assumptions and about what the modules
actually do.
Design Review Activities When an individual programmer is designing and
implementing a program, he or she can find many software errors with pencil and
paper. Deskchecking the design solution is a
very common method of manually verifying
a program. The programmer writes down
essential data (variables, input values,
parameters of subprograms, and so on) and
walks through the design, marking changes
in the data on the paper. Known trouble
spots in the design or code should be
double-checked. A checklist of typical errors
(such as loops that do not terminate,
variables that are used before they are
initialized, and incorrect order of parameters on function calls) can be used to make
the deskcheck more effective. A sample checklist for deskchecking a C++ program
appears in Figure 1.5.
Have you ever been really stuck trying to debug a program and showed it to a
classmate or colleague who detected the bug right away? It is generally acknowledged
that someone else can detect errors in a program better than the original author can. In
an extension of deskchecking, two programmers can trade code listings and check each
other’s programs. Universities, however, frequently discourage students from examining
each other’s programs for fear that this exchange will lead to cheating. Thus many stu-
dents become experienced in writing programs but don’t have much opportunity to
practice reading them.
Teams of programmers develop most sizable computer programs. Two extensions of
deskchecking that are effectively used by programming teams are design or code walk-
throughs and inspections. The intention of these formal team activities is to move the
responsibility for uncovering bugs from the individual programmer to the group.
Deskchecking Tracing an execution of a design or pro-
gram on paper
Walk-through A verification method in which a team
performs a manual simulation of the program or design
Inspection A verification method in which one member
of a team reads the program or design line by line and the
other members point out errors
TEAM LinG - Live, Informative, Non-cost and Genuine!
32 | Chapter 1: Software Engineering Principles
The Design
1. Does each module in the design have a clear function or purpose?
2. Can large modules be broken down into smaller pieces? (A common rule of thumb is that a C++
function should fit on one page.)
3. Are all the assumptions valid? Are they well documented?
4. Are the preconditions and postconditions accurate assertions about what should be happening in
the module they specify?
5. Is the design correct and complete as measured against the program specification? Are there any
missing cases? Is there faulty logic?
6. Is the program designed well for understandability and maintainability?
The Code
7. Has the design been clearly and correctly implemented in the programming language? Are fea-
tures of the programming language used appropriately?
8. Are all output parameters of functions assigned values?
9. Are parameters that return values marked as reference parameters (have  to the right of the type
if the parameter is not an array)?
10. Are functions coded to be consistent with the interfaces shown in the design?
11. Are the actual parameters on function calls consistent with the parameters declared in the func-
tion prototype and definition?
12. Is each data object to be initialized set correctly at the proper time? Is each data object set before
its value is used?
13. Do all loops terminate?
14. Is the design free of “magic” numbers? (A “magic” number is one whose meaning is not immedi-
ately evident to the reader.)
15. Does each constant, type, variable, and function have a meaningful name? Are comments
included with the declarations to clarify the use of the data objects?
Figure 1.5 Checklist for deskchecking a C++ program
Because testing is time consuming and errors cost more the later they are discovered,
the goal is to identify errors before testing begins.
In a walk-through, the team performs a manual simulation of the design or program
with sample test inputs, keeping track of the program’s data by hand on paper or on a
blackboard. Unlike thorough program testing, the walk-through is not intended to simu-
late all possible test cases. Instead, its purpose is to stimulate discussion about the way
the programmer chose to design or implement the program’s requirements.
At an inspection, a reader (not the program’s author) goes through the design or
code line by line. Inspection participants point out errors, which are recorded on an
TEAM LinG - Live, Informative, Non-cost and Genuine!
1.3 Verification of Software Correctness | 33
inspection report. Some errors are uncovered just by the process of reading aloud. Oth-
ers may have been noted by team members during their preinspection preparation. As
with the walk-through, the chief benefit of the team meeting is the discussion that takes
place among team members. This interaction among programmers, testers, and other
team members can uncover many program errors long before the testing stage begins.
At the high-level design stage, the design should be compared to the program
requirements to make sure that all required functions have been included and that this
program or module correctly “interfaces” with other software in the system. At the low-
level design stage, when the design has been filled out with more details, it should be
reinspected before it is implemented. When the coding has been completed, the com-
piled listings should be inspected again. This inspection (or walk-through) ensures that
the implementation is consistent with both the requirements and the design. Successful
completion of this inspection means that testing of the program can begin.
For the last 20 years, the Software Engineering Institute at Carnegie Mellon Univer-
sity has played a major role in supporting research into formalizing the inspection
process in large software projects, including sponsoring workshops and conferences. A
paper presented at the SEI Software Engineering Process Group (SEPG) Conference
reported on a project that was able to reduce the number of product defects by 86.6%
by using a two-tiered inspection process of group walk-throughs and formal inspec-
tions. The process was applied to packets of requirements, design, or code at every stage
of the life cycle. Table 1.2 shows the defects per 1,000 source lines of code (KSLOC) that
were found in the various phases of the software life cycle in a maintenance project.
This project added 40,000 lines of source code to a software program of half a million
lines of code. The formal inspection process was used in all of the phases except testing
activities.
Looking back at Figure 1.4, you can see that the cost of fixing an error is relatively
cheap until you reach the coding phase. After that stage, the cost of fixing an error
increases dramatically. Using the formal inspection process clearly benefited this project.
These design-review activities should be carried out in as nonthreatening a manner
as possible. The goal is not to criticize the design or the designer, but rather to remove
defects in the product. Sometimes it is difficult to eliminate the natural human emotion
of pride from this process, but the best teams adopt a policy of egoless programming.
Table 1.2 Defects Found in Different Phases*
Stage KSLOC
System Design 2
Software Requirements 8
Design 12
Code Inspection 34
Testing Activities 3
*Dennis Beeson, Manager, Naval Air Warfare Center, Weapons Division, F-18 Software Development Team.
TEAM LinG - Live, Informative, Non-cost and Genuine!
Random documents with unrelated
content Scribd suggests to you:
In gauntlet and in thew:
He rides the highway of his cause
To die or do.
His purpose leads him, like a flame,
Through forest and through fen,
To castle walls of wrong and shame
And blood-stained men.
Hope's are the lips that wind the horn
Before the gates of lust:
Though fifty dragons hiss him scorn,
Still will he trust.
Strength's is the hand that thunders at
The entrances of night:
Though ten-score demons crush him flat
Still will he fight.
Love's is the heart that finds a way
To dungeons vast of sin:
A thousand deaths may rise to slay,
Still will he win.
THE FORESTER
I met him here at Ammendorf one spring.
It was the end of April and the Harz,
Treed to their ruin-crested summits, seemed
One pulse of tender green and delicate gold,
Beneath a heaven that was like the face
Of girlhood waking into motherhood.
Along the furrowed meadow, freshly ploughed,
The patient oxen, loamy to the knees,
Plodded or lowed or snuffed the fragrant soil;
And in each thorn-tree hedge the wild bird sang
A song to spring, full of its own wild self
And soul, that heard the blossom-laden May's
Heart beating like a star at break of day,
As, kissing red the roses, she drew near,
Her mouth's ripe rose all dewdrops and perfume.
Here at this inn and underneath this tree
We took our wine, the morning prismed in its
Flame-crystalled gold.—A goodly vintage that!
Tang with the ripeness of full twenty years.
Rare! I remember! wine that spurred the blood,
That brought the heart glad to the songful lip,
And made the eyes unlatticed casements whence
A man's true soul smiled, breezy as the blue.
As royal a Rhenish, I will vouch to say,
As that, old legends tell, which Necromance
And Magic keep, gnome-guarded, in huge casks
Of antique make deep in the Kyffhäuser,
Webbed, frosty gray, with salt-petre and mold,
The Cellar of the Knights near Sittendorf.—
So solaced by that wine we sat an hour
He told me his intent in coming here.
His name was Rudolf; and his native place,
Franconia; but no word of parentage:
Only his mind to don the buff and green
A d li f t ith d b
And live a forester with us and be
Enfellowed in the Duke of Brunswick's train,
And for the Duke's estate even now was bound.
Tall was he for his age and strong and brown,
And lithe of limb; and with a face that seemed
Hope's counterpart—but with the eyes of doubt:
Deep stealthy disks, instinct with starless night,
That seemed to say, We're sure of Earth—at least
For some short while, my friend; but afterward—
Nay! ransack not to-morrow till to-day
Lest it engulf thy joy before it is!—
And when he spoke, the fire in his eyes
Worked restless as a hunted animal's;
Or like the Count von Hackelnburg's,—the eyes
Of the Wild Huntsman,—his that turn and turn
Feeling the unseen presence of a fiend.
And then his smile! a thrust-like thing that curled
His lips with heresy and incredible lore
When Christ's or th' Virgin's holy name was said,
Exclaimed in reverence or admonishment:
And once he sneered,—What is this God you mouth,
Employ whose name to bless yourselves or damn?
A curse or blessing?—It hath passed my skill
T' interpret what He is. And then your faith—
What is this faith that helps you unto Him?
Distinguishment unseen, design unlawed.
Why, earth, air, fire, and water, heat and cold,
Hint not at Him: and man alone it is
Who needs must worship something. And for me—
No God like that whom man hath kinged and crowned!
Rather your Satan cramped in Hell—the Fiend!
God-countenanced as he is, and tricked with horns.
No God for me, bearded as Charlemagne,
Throned on a tinsel throne of gold and jade,
Earth's pygmy monarchs imitate in mien
Earth s pygmy monarchs imitate in mien
And mind and tyranny and majesty,
Aping a God in a sonorous Heaven.
Give me the Devil in all mercy then,
Bad as he is! for I will none of such!
And laughed an oily laugh of easy jest
To bow out God and let the Devil in.
And grasped of both wild hands, swung trenchant. Page 285
Accolon of Gaul
[See larger version]
Then, as it chanced, old Kurt had come that morn
With some six of his jerkined foresters
From the Thuringian forest; wet with dew,
And fresh as morn with early travel; bound
For Brunswick, Dummburg and the Hakel passed.
Chief huntsman he then to our lord the Duke,
And father of the loveliest maiden here
In Ammendorf, the sunny Ilsabe:
Her mother dead, the gray-haired father prized
His daughter more than all that men hold dear;
His only happiness, who was beloved
Of all as Lora of Thuringia was,
For gentle ways that spoke a noble soul,
Winning all hearts to love her and to praise,
As might a great and beautiful thought that holds
Us by the simplest words.—Blue were her eyes
As the high glory of a summer day.
Her hair,—serene and braided over brows
White as a Harz dove's wing,—an auburn brown,
And deep as mists the sun has drenched with gold:
And her young presence, like embodied song,
Filled every heart she smiled on with sweet calm,
Like some Tyrolean melody of love,
Heard on an Alpine path at close of day
When homing shepherds pipe to tinkling flocks:
Being with you a while, so, when she left,—
How shall I say it?—'twas as when one hath
Beheld an Undine on the moonlit Rhine,
Who, ere the mind adjusts a thought, is gone,
And to the soul it seems it was a dream.
Some thirty years ago it was;—and I,
Commissioner of the Duke—(no sinecure
I can assure you)—had scarce reached the age
Of thirty,—that we sat here at our wine;
A d 't th h th t R d lf h t fi t
And 'twas through me that Rudolf,—whom at first,
From some rash words dropped then in argument,
The foresterhood was like to be denied,—
Was then enfellowed. Yes, said I, he's young.
Kurt, he is young: but look you! what a man!
What arms! what muscles! what a face—for deeds!
An eye—that likes me not; too quick to turn!—
But that may be the restless soul within:
A soul perhaps with virtues that have been
Severely tried and could not stand the test;
These be thy care, Kurt: and if not too deep
In vices of the flesh, discover them,
As divers bring lost riches up from ooze.—
Thou hast a daughter; let him be thy son.
A year thereafter was it that I heard
Of Rudolf's passion for Kurt's Ilsabe;
Then their betrothal. And it was from this,—
(How her fair memory haunts my old heart still!—
Sweet Ilsabe! whose higher womanhood,
True as the touchstone which philosophers feign
Transmutes to gold base metals it may touch,
Had turned to good all evil in this man,)—
Surmised I of the excellency which
Refinement of her purer company,
And contact with her innocence, had resolved
His fiery nature to, conditioning slave.
And so I came from Brunswick—as, you know,
Is custom of the Duke or, by his seal
Commissioned proxy, his commissioner—
To test the marksmanship of Rudolf, who
Succeeded Kurt with marriage of his child,
An heir of Kuno.—He?—Great-grandfather
To Kurt; and of this forest-keepership
The first possessor; thus established here—
Or this the tale they tell on winter nights:—
Kuno, once in the Knight of Wippach's train,
Rode on a grand hunt with the Duke, who came,—
Grandfather of the father of our Duke,—
With much magnificence of knights and squires,
Great velvet-vestured nobles, cloaked and plumed,
To hunt Thuringian deer. Then morn,—so rathe
To bid good-morrow to the husbandman
Heavy with slumber,—was too slow for these,
And on the wind-trod hills recumbent yawned
Aroused an hour too soon: ashamed, disrobed,
Rubbed the stiff sleep from eyes that still would close;
Like some young milkmaid whom the cock hath waked,
Who sits within her loft and, half asleep,
Stretches and hears the house below her stir,
Yet sits and yawns, reluctant still to rise.—
Horns sang and deer-hounds tugged a whimpering leash,
Or, loosened, bounded through the baying glens:
And ere the mountain mists, compact of white,
Broke wild before the azure spears of day,
The far-off hunt, that woke the woods to life,
Seemed but the heart-beat of the ancient hills.
And then, near noon, within a forest brake,
The ban-dogs roused a red gigantic stag,
Lashed to whose back with gnarly-knotted cords,
And borne along like some pale parasite,
A man shrieked: tangle-bearded, and his hair
A mane of forest-burrs. The man himself,
Emaciated and half-naked from
The stag's mad flight through headlong rocks and trees,
One bleeding bruise, his eyes two holes of fire.
For such the law then: when the peasant chased
Or slew the dun deer of his tyrant lords,
If caught, as punishment the withes and spine
Of some strong stag, a gift to him of game
Enough till death—death in the antlered herd,
oug t deat deat t e a t e ed e d,
Or slow starvation in the haggard hills.
Then was the great Duke glad, and forthwith cried
To all his hunting-train a rich reward
For him who slew the stag and saved the man,
But death for him who slew both man and beast.
So plunged the hunt after the hurrying slot,
A shout and glimmer through the sounding woods,—
Like some wild torrent that the hills have loosed,
Death for its goal.—'Twas late; and none had yet
Risked that hard shot,—too desperate the risk
Beside the poor life and a little gold,—
When this young Kuno, with hot eyes, wherein
Hunt and impatience kindled reckless flame,
Cried, Has the dew made wet each powder-pan?
Or have we left our marksmanship at home?
Here's for its heart! the Fiend direct my ball!—
And fired into a covert packed with briers,
An intertangled wall of matted night,
Wherein the eye might vainly strive and strive
To pierce one fathom, gaze one foot beyond:
But, ha! the huge stag staggered from the brake,
Heart-hit, and fell: and that wan wretch, unbound,
Rescued, was cared for. Then his grace, the Duke,
Charmed with the eagle aim, called Kuno up,
And there to him and his forever gave
The forest-keepership.
But envious tongues
Were soon at wag; and whispered went the tale
Of how the shot was free; and how the balls
Used by young Kuno were free bullets—which
To say is: Lead by magic molded, in
The presence and directed of the Fiend.
Of some effect these tales, and of some force
Even with the Duke, who lent an ear so far
As to ordain Kuno's descendants all
To proof of skill ere their succession to
The father's office. Kurt himself hath shot
The silver ring out o' the popinjay's beak—
A good shot he, you see, who would succeed.
The Devil guards his secrets close as God.
For who can say what elementaries,
Demonic, lurk in desolate dells and hills
And shadowy woods? malignant forces who,
Malicious vassals of satanic power,
Are agents to that Evil none may name,
Who signs himself, through these, a slave to those,
Those mortals who call in the aid of Hell,
And for some earthly, transitory gift,
Barter their souls and all their hopes of Heaven.
Of these enchanted bullets let me speak:
There may be such: our earth hath things as strange,
Perhaps, and stranger, that we doubt not of,
While we behold,—not only 'neath the thatch
Of Ignorance's hovel,—but within
The stately halls of Wisdom's palaces,
How Superstition sits an honored guest.
A cross-way, so they say, among the hills;
A cross-way in a solitude of pines;
And on the lonely cross-way you must draw
A bloody circle with a bloody sword;
And round the circle, runic characters,
Weird and symbolic: here a skull, and there
A scythe, and cross-bones, and an hour-glass here:
And in the centre, fed with coffin-wood,
Stolen from the grave of—say a murderer,
A fitful fire. Eleven of the clock
The first ball leaves the mold—the sullen lead
Mixed with three bullets that have hit their mark,
And blood the wounded Sacramental Host
And blood the wounded Sacramental Host,
Stolen, and hence unhallowed, oozed when shot
Fixed to a riven pine. Ere midnight strike,
With never a word until that hour sound,
Must all the balls be cast; and these must be
In number three and sixty; three of which
The Fiend's dark agent, demon Sammael,
Claims for his master and stamps for his own
To hit aside their mark, askew for harm.
The other sixty shall not miss their mark.
No cry, no word, no whisper, even though
Vague, gesturing shapes, that loom like moonlit mists,
Their faces human but of animal form,
Whinnying and whining lusts, faun-faced, goat-formed,
Rise thick around and threaten to destroy.
No cry, no word, no whisper should there come,
Weeping, a wandering shadow like the girl
You love, or loved, now lost to you, her eyes
Hollow with tears; sad, palely beckoning
With beautiful arms, or censuring; her face
Wild with despondent love: who, if you speak
Or waver from that circle—hideous change!—
Shrinks to a wrinkled hag, whose harpy hands
Shall tear you limb from limb with horrible mirth.
Nor be deceived if some far midnight bell
Strike that anticipated hour; nor leave
By one short inch the circle, for, unseen
Though now they be, Hell's minions still are there,
Watching with flaming eyes to seize your soul.
But when the hour of midnight sounds, will come
A noise of galloping hoofs and outriders,
Shouting: six midnight steeds,—their nostrils, pits
Of burning blood,—postilioned, roll a stage,
Black and with groaning wheels of spinning fire:
Room there!—What, ho!—Who bars the mountain way?—
On over him!—But fear not nor fare forth;
On over him! But fear not, nor fare forth;
'Tis but the last trick of your bounden slave.
And ere the red moon rushes from the clouds
And dives again, high the huge leaders leap,
Their fore-hoofs flashing and their eyeballs flame,
And, spun a spiral spark into the night,
Hissing the phantasm flies and fades away.
Some say there comes no stage; that Hackelnburg,
Wild-Huntsman of the Harz, comes dark as storm,
With rain and wind and demon dogs of Hell;
The terror of his hunting-horn, an owl,
And the dim deer he hunts, rush on before:
The forests crash, and whirlwinds are the leaves,
And all the skies a-thunder, as he hurls,
Straight on the circle, horse and hounds and stag.
And at the last, plutonian-cloaked, there comes,—
Infernal fire streaming from his eyes,—
Upon a stallion gaunt and lurid black,
The minister of Satan, Sammael,
Who greets you, and informs you, and assures.
Enough! these wives' tales told, to what I've seen:
To Ammendorf I came; and Rudolf here
With Kurt and his assembled men in buff
And woodland green were gathered at this inn.
The abundant Year—like some sweet wife,—a-smile
At her brown baby, Autumn, in her arms,
Stood 'mid the garnered harvests of her fields
Dreaming of days that pass like almoners
Scattering their alms in minted gold of flowers;
Of nights, that forest all the skies with stars,
Wherethrough the moon—bare-bosomed huntress—rides,
One cloud before her like a flying fawn.
Then I proposed the season's hunt; till eve
The test of Rudolf's skill postponed; at which
He seemed embarrassed. And 'twas then I heard
How he an execrable marksman was;
o e a e ec ab e a s a as;
And tales that told of close, incredible shots,
That missed their mark; or how the flint-lock oft
Flamed harmless powder, while the curious deer
Stood staring, as in pity of such aim,
Or as inviting him to try once more.
Howbeit, he that day acquitted him
Of all this gossip; in that day's long hunt
Missing no shot, however rashly made
Or distant through the intercepting trees.
And the piled, various game brought down of all
Good marksmen of Kurt's train had not sufficed,
Doubled, nay, trebled, there to match his heap.
And marvelling the hunters saw, nor knew
How to excuse them. My indulgence giv'n,
Some told me that but yesterday old Kurt
Had made his daughter weep and Rudolf frown,
By vowing end to their betrothéd love,
Unless that love developed better skill
Against the morrow's test; his ancestors'
High fame should not be tarnished. So he railed;
Then bowed his gray head and sat moodily:
But, looking up, forgave all when he saw
Tears in his daughter's eyes and Rudolf gone
Out in the night, black with approaching storm.
Before this inn, crowding the green, they stood,
The holiday village come to view the trial:
Fair maidens and their comely mothers with
Their sweethearts and their husbands. And I marked
Kurt and his daughter here; his florid face
All creased with smiles at Rudolf's great success;
Hers, radiant with happiness; for this
Her marriage eve—so had her father said—
Should Rudolf come successful from the hunt.
So pleased was I with what I'd seen him do,
The trial of skill superfluous seemed; and so
Was on the bare brink of announcing, when
Out of the western heaven's deepening red,—
Like a white message dropped of scarlet lips,—
A wild dove clove the luminous winds and there,
Upon that limb, a peaceful moment sat.
Then I, Thy rifle, Rudolf! pierce its head!
Cried pointing, and chief-forester art thou!
Why did he falter with a face as strange
And strained as terror's? did his soul divine
What was to be, with tragic prescience?—
What a bad dream it all seems now!—Again
I see him aim. Again I hear her cry,
My dove! O Rudolf, do not kill my dove!
And from the crowd, like some sweet dove herself,
A fluttering whiteness, rushed our Ilsabe—
Too late! the rifle cracked.... The unhurt dove
Rose, beating frightened wings—but Ilsabe!...
My God! the sight!... fell smitten; sudden red,
Sullying the whiteness of her bridal bodice,
Showed where the ball had pierced her innocent heart.
And Rudolf?—Ah, of him you still would know?
—When he beheld this thing which he had done,
Why, he went mad—I say—but others not.
An hour he raved of how her life had paid
For the unholy missiles he had used,
And how his soul was three times lost and damned.
I say that he went mad and fled forthwith
Into the haunted Harz.—Some say, to die
The prey of demons of the Dummburg ruin.
I,—one of those less superstitious,—say,
He in the Bodé—from that blackened rock,—
Whereon were found his hunting-cap and horn,—
The Devil's Dancing Place, did leap and die.
THE MOATED MANSE
I
And now once more we stood within the walls
Of that old manor near the riverside;
Dead leaves lay rotting in its empty halls,
And here and there the ivy could not hide
The year-old scars, made by the Royalists' balls,
Around the doorway, where so many died
In that last effort to defend the stair,
When Rupert, like a demon, entered there.
II
The basest Cavalier who e'er wore spurs
Or drew a sword, I count him; with his grave
Eyes 'neath his plumed hat like a wolf's whom curs
Rouse, to their harm, within a forest cave;
And hair like harvest; and a voice like verse
For smoothness. Ay, a handsome man and—brave!—
Brave?—who would question it! yea! tho' 'tis true
He warred with one weak woman and her few.
III
Lady Isolda of the Moated Manse,
Whom here, that very noon, it happened me
To meet near her old home. A single glance
Showed me 'twas she. I marveled much to see
How lovely still she was! as fair, perchance,
As when Red Rupert thrust her brutally,—
Her long hair loosened,—down the shattered stair,
And cast her, shrieking, 'mid his followers there.
IV
She is for you! Take her! I promised it!
Take her, my bullies!—shouting so, he flung
Her in their midst. Then, on her poor hands (split,
And beaten by his dagger when she clung
Resisting him) and knees, she crept a bit
Nearer his feet and begged for death. No tongue
Can tell the way he turned from her and cursed,
Then bade his men draw lots for which were first.
V
I saw it all from that low parapet,
Where, bullet-wounded in the hip and head,
I lay face-upward in the whispering wet,
Exhausted 'mid the dead and left for dead.
We had held out two days without a let
Against these bandits. You could trace with red
From room to room how we resisted hard
Since the great door crashed in to their petard.
VI
The rain revived me, and I leaned with pain
And saw her lying there, pale, soiled and splashed
And miserable; on her cheek a stain,
A dull red bruise, made when his mad hand dashed
And struck her to the stones; the wretched rain
Dripped from her dark hair; and her hands were gashed.—
Oh f k t t l
Oh, for a musket or a petronel
With which to send his devil's soul to hell!
VII
But helpless there I lay, no weapon near,
Only the useless sword I could not reach
His traitor's heart with, while I chafed to hear
The laugh, the insult and the villain speech
Of him to her.—Oh, God! could I but clear
The height between and, hanging like a leech,
My fingers at his throat, tear out his base
Vile tongue! yea, tear, and lash it in his face!
VIII
But, badly wounded, what could I but weep
With rage and pity of my helplessness
And her misfortune! Could I only creep
A little nearer so that she might guess
I was not dead; that I my life would keep,
Dedicate to revenge!—Oh, the distress
Of that last moment when, half-dead, I saw
Them mount and bear her swooning through the shaw.
IX
Long time I lay unconscious. It befell
Some woodsmen found me, having heard the sound
Of fighting cease that, for two days, made hell
Of that wild region; ventured on the ground
For plunder: and it had not then gone well
With me I fear had not their leader found
With me, I fear, had not their leader found
That in some way I would repay his care;
So bore me to his hut and nursed me there.
X
How roughly kind he was! For weeks I hung
'Twixt life and death; health, like a varying, sick
And fluttering pendulum, now this way swung,
Now that, until at last its querulous tick
Beat out life's usual time, and slowly rung
The long, loud hours, that exclaimed, Be quick!—
Arise!—Go forth!—Hear how her black wrongs call!—
Make them the salve to cure thy wounds withal!—
XI
They were my balsam: for, ere autumn came,
Weak still, but over eager to be gone,
I took my leave of him. A little lame
From that hip wound, and somewhat thin and wan,
I sought the village. Here I heard her name
And shame's made one. How Rupert passed one dawn;
How she among his troopers rode—astride
Like any man—pale-faced and feverish-eyed.
XII
Which way these took they pointed, and I went
Like fire after. Oh, the thought was good
That they were on before! And much it meant
To know she lived still; she, whose image stood
Like flame before me making turbulent
Like flame before me, making turbulent
Each heart-beat with her wrongs, that were fierce food
Unto my hate that, Courage! cried, Rest not!
Think of her there, and let thy haste be hot!
XIII
But months went by and still I had not found:
Yet, here and there, as wearily I sought,
I caught some news: how he had held his ground
Against the Roundhead troops; or how he'd fought
Then fled—returned and conquered. Like a hound
Questing a boar, I followed; but was brought
No nearer to my quarry. Day by day
It seemed that Satan kept him from my way.
XIV
A woman rode beside him, so they said,
A fair-faced wanton, mounted like a man—
Isolda!—my Isolda!—Better dead,
Yea, dead and damned! than thus—the courtezan
Bold, unreluctant, to such men! A dread,
That such should be, unmanned me. Doubt began
To whisper at my heart.—But I was mad,
To insult her with such thoughts, whose love I had.
XV
At last one day I rested in a glade
Near that same woodland which I lay in when
Sore wounded: and, while sitting in the shade
Of an old beech—what! did I dream? or men
O a o d beec at d d d ea o e
Like Rupert's own ride near me? and a maid—
Isolda or her double!—Wildly then
I rose and, shouting, leapt upon my horse;
Unsheathed my sword and rode across their course.
XVI
Mainly I looked for Rupert, and by name
Challenged him forth:—Dog! dost thou hide behind?—
Insulter of women! Coward! save where shame
And rapine call thee! God at last is kind,
And my sword waits!—Like an upbeating flame,
My voice rose to a windy shout; and blind
I seemed to sit, till, with an outstretched hand,
Isolda rode before me from that band.
XVII
Gerald! she cried; not as a soul surprised
With gladness that the loved, deemed dead, still lives;
But like the soul that long hath realized
Only misfortune and to fortune gives
No confidence, though it be recognized
As good. She spoke: Lo, we are fugitives.
Rupert is slain. And I am going home.
Then like a child asked simply, Wilt thou come?...
XVIII
Oh, I have suffered, Gerald! Oh, my God!
What shame! What torture! Once my soul was clean—
Stained and defiled behold it!—I have trod
Sad ways of hell and horror. I have seen
And lived all depths of lust. Yet, oh, my God!
Blameless I hold myself of what hath been,
Though through it all, yea,—this thou too must know,—
I loved him, my betrayer and thy foe!
XIX
Sobbing she spoke as if but half awake,
Her eyes far-fixed beyond me, far beyond
All hope of mine.—So! it was for his sake,
His love, that she had suffered!... Blind and fond,
For what return!... And I—to nurse a snake,
And never dream its nature would respond
With some such fang of venom! 'Twas for this
That I had ventured all—to find her his!
XX
At first half-stunned I stood; then blood and brain,
Like two stern judges, who had slept, awoke,
Rose up and thundered, Slay her! Every vein
And nerve responded, Slay her at a stroke!—
And I had done it, but my heart again,
Like a strong captain in a tumult, spoke,
And the fierce discord fell. And quietly
I sheathed my sword and said, I'll go with thee.
XXI
But this was my reward for all I'd borne,
My loyalty and love! To see her eyes
ll f f h h h h k
Hollow from tears for him; her thin cheeks worn
With grief for him; to know them all for lies,
Her vows of faith to me; to come forlorn,
Where I had hoped to come on Paradise,
On Hell's black gulf; and, as if not enough,
Soiled as she was and outcast, still to love!
XXII
Then rode one ruffian from the rest, clay-flecked
From spur to plume with hurry; seized my rein,
And—What art thou, demanded, who hast checked
Our way and challenged?—Then, with some disdain,
Isolda, Sir, my kinsman did expect
Your captain here. What honor may remain
To me I pledge for him. Hold off thy hands!
He but attends me to the Moated Manse.
XXIII
We rode in silence. And at evening came
Unto the Moated Manse.—Great clouds had grown
Up in the west, on which the sunset's flame
Lay like the hand of slaughter.—Very lone
Its rooms and halls: a splintered door that, lame,
Swung on one hinge; a cabinet o'erthrown;
Or arras torn; or blood-stain turning wan,
Showed us the way the battle once had gone.
XXIV
We reached the tower-chamber towards the west,
In hich on that da k da she tho ght to hide
In which on that dark day she thought to hide
From Rupert when, at last, 'twas manifest
We could not hold the Manse. There was no pride
In her deep eyes now; nor did scorn invest
Her with such dignity as once defied
Him bursting in to find her standing here
Prepared to die like some dog-hunted deer.
XXV
She took my hand, and, as if naught of love
Had ever been between us, said,—All know
The madness of that hour when with his glove
He struck, then slew my brother, and brought woe
On all our house: and thou, incensed above
The rest, came here, and made my foe thy foe.
But he had left. 'Twas then I promised thee
My hand, but, ah! my heart was gone from me.
XXVI
Yea, he had won me, this same Rupert, when
He was our guest.—Thou know'st how gallantry
And recklessness make heroes of most men
To us weak women!—And so secretly
I vowed to be his wife. It happened then
My brother found him in some villainy;
The insult followed: Guy was killed ... and thou
Dost still remember how I made a vow.—
XXVII
But still this man pursued me and I held
But still this man pursued me, and I held
Firm to my vow, albeit I loved him still,
Unknown to all, with all the love unquelled
Of first impressions, and against my will.
At last despair of winning me compelled
Him to the oath he swore: He would not kill,
But take me living and would make my life
A living death. No man should make me wife.
XXVIII
The war, that now consumes us, did, indeed,
Give him occasion.—I had not been warned,
When down he came against me in the lead
Of his marauders. With thy help I scorned
His mad attacks two days. I would not plead
Nor parley with him, who came hoofed and horned,
Like Satan's self in soul, and, with Hell's aid,
Took this strong house and kept the oath he made.
XXIX
Months passed. Alas! it needs not here to tell
What often thou hast heard: Of how he led
His ruffians here now there; or what befell
Me of dishonor. Oft I wished me dead,
Loathing my life,—than which the nether Hell
Hath less of horror!—So we fought or fled
From place to place until a year had passed,
And Parliament forces hemmed us in at last.
XXX
Yea, I had only lived for this—to right
With death my wrongs sometime. And love and hate
Contended in my bosom when, that night
Before the fight that should decide our fate,
I entered where he slept. There was no light
Save of the stars to see by. Long and late
I leaned above him there, yet could not kill—
Hate raised the dagger but love held it still.
XXXI
The woman in me conquered. What a slave
To our emotions are we! To relent
At this long-waited moment!—Wave on wave
Of pitying weakness swept me, and I bent—
And kissed his face. Then prayed to God; and gave
My trust to God; and left to God th' event.—
I never looked on Rupert's face again,
For in the morning's combat—he was slain.
XXXII
Out of defeat escaped some scant three score
Of all his followers. And night and day
We fled; and while the Roundheads pressed us sore,
And in our road, good as a fortress, lay
The Moated Manse,—where our three-score or more
Might well hold out,—I pointed them the way.
And we are come, amid its wrecks to end
The crime begun here.—Thou must go, my friend!
XXXIII
XXXIII
Go quickly! For the time approaches when
Destruction must arrive.—Oh, well I know
All thou wouldst say to me.—What boots it then?—
I tell thee thou must go! that thou must go!—
Yea, dost thou think I'd have thee die 'mid men
Like these, for such an one as I?—No! no!—
Thy life is clean. Thou shalt not cast away
Thy clean life for my soiled one! ... I will stay!
XXXIV
I said.—Then spoke ... I know not what it was.
And seized her hand and kissed it and then said,—
Thou art my promised wife. Thou hast no cause
That is not mine. I love thee. We will wed.
Isolda, come!—A moment did she pause,
Then shook her head and sighed, My heart is dead.
This can not be. Behold, that way is thine.
I will not let thee share the way that's mine.
XXXV
Then turning from me ere I could prevent
Passed like a shadow from the shadowy room,
Leaving my soul in shadow.... Naught was meant
By my sweet flower of love then! bloom by bloom
I'd watched it wither; then its fragrance went,
And dust it was now.... It was dark as doom,
And bells seemed ringing far off in the rain,
When from that house I turned my face again.
XXXVI
Then in the night a trumpet; and the dull
Close thud of horse and clash of spurs and arms;
And glimmering helms swept by me.—Sorrowful
I stood and waited till against the storm's
Black breast, the Manse,—a burning carbuncle,—
Blazed like a battle-beacon, and alarms
Of onslaught clanged around it.—Then, like one,
Who bears with him God's curse, I galloped on.
AN OLD TALE RETOLD
From the terrace here, where the hills indent,
You can see the uttermost battlement
Of the castle there: the Clifford's home
Where the seasons go and the seasons come
And never a footstep else doth fall
Save the prowling fox's; the ancient hall
Echoes no voice save the owlet's call:
Its turret chambers are homes for the bat;
And its courts are tangled and wild to see;
And where in the cellar was once the rat,
The viper and toad move stealthily.
Long years have passed since the place was burned,
And he sailed to the wars in France and earned
The name that he bears of the bold and true
On his tomb.—Long years, since my lord, Sir Hugh,
Lived, and I was his favorite page,
And the thing then happened; and he of an age
When a man will love and be loved again,
Or off to the wars or a monastery;
Or toil till he deaden his heart's hard pain;
Or drink and forget it and finally bury.
I was his page. And often we fared
Through the Clare demesne, in autumn, hawking—
If the Baron had known, how they would have glared,
'Neath their bushy brows, those eyes of mocking!—
That last of the Strongbows, Richard, I mean—
And growling some six of his henchmen lean
To mount and after this Clifford and hang
With his crop-eared page to the nearest oak,
How he would have cursed us while he spoke!
For Clare and Clifford had ever a fang
In the other's side.... And I hear the clang
Of his rage in the hall when the hawker told—
If he told!—how we met on the autumn wold
Hi d ht t Cl f Cl th d
His daughter, sweet Clara of Clare, the day
Her hooded tiercel its brails did burst,
And trailing its jesses, came flying our way—
An untrained haggard the falconer cursed
While he tried to secure:—as the eyas flew
Slant, low and heavily over us, Hugh,—
Who saw it coming, and had just then cast
His peregrine hawk at a heron quarry,—
In his saddle rising thus, as it passed
By the jesses caught, and to her did carry,
Where she stood near the wood. Her face flushed rose
With the glad of the meeting.—No two foes
Her eyes and my lord's, I swear, who saw
'Twas love from the start.—And I heard him speak;
Dismount, then kneel—and the sombre shaw,
With the sad of the autumn waste and bleak,
Grew spring with her smile, as the hawk she took
On her slender wrist, where it pruned and shook
Its callowness. Then I saw him seize
The hand that she reached to him, long and white,
As she smilingly bade him rise from his knees—
When he kissed her fingers her eyes grew bright.
But her cheeks were pallid when, lashing through
The thicket there, his face a-flare
With the sting of the wind, and his gipsy hair
Flying, the falconer came, and two
Or three of the people of Castle Clare.
And the leaves of the autumn made a frame
For the picture there in the morning's flame.
What was said in that moment I do not know,
That moment of meeting between those lovers:
Whatever it was, 'twas whispered low,
Soft as a leaf that swings and hovers,
A twinkling gold, when the woods are yellow.
And her face with the joy was still aglow
When out of the wood that burly fellow
When out of the wood that burly fellow
Came with his frown, and made a pause
In the pulse of their words.—My lord, Sir Hugh,
Stood with the soil on his knee. No cause
Had he, but his hanger he partly drew,
Then clapped it sharp in its sheath again,
And bowed to my lady, and strode away;
And vaulting his horse, with a loosened rein
Rode with a song in his heart all day.
He loved and was loved, I knew; for, look!
All other sports for the chase he forsook.
And strange that he never went to hawk,
Or hunt, but Clara would meet him there
In the Strongbow forest!—I know the rock,
With its ferns and its moss, by the bramble lair,
Where oft and often he met—by chance,
Shall I say?—the daughter of Clare; as fair
Of face as a queen in an old romance,
Who waits expectant and pale; her hair
Night-deep; and eyes dove-gray with dreams;—
By the fountain-side where the statue gleams
And the moonbeam lolls in the lily white,—
For her knightly lover who comes at night.
Heigh-ho! they ceased, those meetings. I wot,
Betrayed to the Baron by some of his crew
Of menials who followed and saw and knew.
For she loved too well to have once forgot
The time and the place of their trysting true.
Why and when? would ask Sir Hugh
In the labored letters he used to lock
—The lovers' post—in a coigne of that rock.
She used to answer, but now did not.
But, nearing Yule, love gat them again
A twilight tryst—through frowardness sure!—
They met. And the day was gray with rain,
Welcome to our website – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.
More than just a book-buying platform, we strive to be a bridge
connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.
Join us on a journey of knowledge exploration, passion nurturing, and
personal growth every day!
ebookbell.com

More Related Content

PDF
C++ plus data structures, 3rd edition (2003)
 
PDF
Data Structures and Other Objects Using C 4th Edition Main
DOCX
Algorithms and Data Structures~hmftj
PDF
C Programming Program design including data structures 5ed. Edition Malik D.S.
PPTX
INTRODUCTION TO DATA STRUCTURE & ABSTRACT DATA TYPE.pptx
PPT
01_intro-cpp.ppt
PPT
01_intro-cpp.ppt
PPT
01_intro-cpjgknkhjgjv hugbbf vjouhghp.ppt
C++ plus data structures, 3rd edition (2003)
 
Data Structures and Other Objects Using C 4th Edition Main
Algorithms and Data Structures~hmftj
C Programming Program design including data structures 5ed. Edition Malik D.S.
INTRODUCTION TO DATA STRUCTURE & ABSTRACT DATA TYPE.pptx
01_intro-cpp.ppt
01_intro-cpp.ppt
01_intro-cpjgknkhjgjv hugbbf vjouhghp.ppt

Similar to C Plus Data Structures Subsequent Dale Nell B (20)

PDF
(eBook PDF) Starting Out with C++: From Control Structures through Objects 8t...
PDF
C Programming Program Design Including Data Structures 5th Edition D. S. Malik
PPT
Introduction to data structure and algorithm
PDF
Starting out with C from control structures through objects Eighth Edition To...
PDF
PDF
Data_Structure_and_Algorithms_Using_C++ _ Nho Vĩnh Share.pdf
PDF
C Programming Program Design Including Data Structures 5th Edition D. S. Malik
PDF
C Programming Program Design Including Data Structures 5th Edition D. S. Malik
PDF
Data Structure and its Fundamentals
PPTX
Introduction to datastructures presentation
PDF
Data Structures And Applications A Simple And Systematic Approach Padma Reddy
PDF
Iare ds lecture_notes_2
PDF
C Programming Program design including data structures 5ed. Edition Malik D.S.
PPTX
1-Introduction to Data Structures beginner.pptx
PDF
PPTX
data structures and its importance
PDF
Object Oriented Programming With C Sharma A K
PDF
(eBook PDF) Starting Out with C++: From Control Structures through Objects, B...
PDF
Starting Out with C++: From Control Structures through Objects 8th Edition, (...
PDF
(eBook PDF) Starting Out with C++: From Control Structures through Objects 8t...
(eBook PDF) Starting Out with C++: From Control Structures through Objects 8t...
C Programming Program Design Including Data Structures 5th Edition D. S. Malik
Introduction to data structure and algorithm
Starting out with C from control structures through objects Eighth Edition To...
Data_Structure_and_Algorithms_Using_C++ _ Nho Vĩnh Share.pdf
C Programming Program Design Including Data Structures 5th Edition D. S. Malik
C Programming Program Design Including Data Structures 5th Edition D. S. Malik
Data Structure and its Fundamentals
Introduction to datastructures presentation
Data Structures And Applications A Simple And Systematic Approach Padma Reddy
Iare ds lecture_notes_2
C Programming Program design including data structures 5ed. Edition Malik D.S.
1-Introduction to Data Structures beginner.pptx
data structures and its importance
Object Oriented Programming With C Sharma A K
(eBook PDF) Starting Out with C++: From Control Structures through Objects, B...
Starting Out with C++: From Control Structures through Objects 8th Edition, (...
(eBook PDF) Starting Out with C++: From Control Structures through Objects 8t...
Ad

Recently uploaded (20)

PPTX
PPH.pptx obstetrics and gynecology in nursing
PDF
2.FourierTransform-ShortQuestionswithAnswers.pdf
PDF
Pre independence Education in Inndia.pdf
PDF
Abdominal Access Techniques with Prof. Dr. R K Mishra
PPTX
Microbial diseases, their pathogenesis and prophylaxis
PDF
Complications of Minimal Access Surgery at WLH
PDF
O5-L3 Freight Transport Ops (International) V1.pdf
PPTX
human mycosis Human fungal infections are called human mycosis..pptx
PDF
Physiotherapy_for_Respiratory_and_Cardiac_Problems WEBBER.pdf
PDF
VCE English Exam - Section C Student Revision Booklet
PDF
Sports Quiz easy sports quiz sports quiz
PDF
RMMM.pdf make it easy to upload and study
PPTX
Pharmacology of Heart Failure /Pharmacotherapy of CHF
PPTX
PPT- ENG7_QUARTER1_LESSON1_WEEK1. IMAGERY -DESCRIPTIONS pptx.pptx
PPTX
Renaissance Architecture: A Journey from Faith to Humanism
PDF
Black Hat USA 2025 - Micro ICS Summit - ICS/OT Threat Landscape
PDF
102 student loan defaulters named and shamed – Is someone you know on the list?
PDF
Chapter 2 Heredity, Prenatal Development, and Birth.pdf
PDF
Module 4: Burden of Disease Tutorial Slides S2 2025
PDF
Basic Mud Logging Guide for educational purpose
PPH.pptx obstetrics and gynecology in nursing
2.FourierTransform-ShortQuestionswithAnswers.pdf
Pre independence Education in Inndia.pdf
Abdominal Access Techniques with Prof. Dr. R K Mishra
Microbial diseases, their pathogenesis and prophylaxis
Complications of Minimal Access Surgery at WLH
O5-L3 Freight Transport Ops (International) V1.pdf
human mycosis Human fungal infections are called human mycosis..pptx
Physiotherapy_for_Respiratory_and_Cardiac_Problems WEBBER.pdf
VCE English Exam - Section C Student Revision Booklet
Sports Quiz easy sports quiz sports quiz
RMMM.pdf make it easy to upload and study
Pharmacology of Heart Failure /Pharmacotherapy of CHF
PPT- ENG7_QUARTER1_LESSON1_WEEK1. IMAGERY -DESCRIPTIONS pptx.pptx
Renaissance Architecture: A Journey from Faith to Humanism
Black Hat USA 2025 - Micro ICS Summit - ICS/OT Threat Landscape
102 student loan defaulters named and shamed – Is someone you know on the list?
Chapter 2 Heredity, Prenatal Development, and Birth.pdf
Module 4: Burden of Disease Tutorial Slides S2 2025
Basic Mud Logging Guide for educational purpose
Ad

C Plus Data Structures Subsequent Dale Nell B

  • 1. C Plus Data Structures Subsequent Dale Nell B download https://ebookbell.com/product/c-plus-data-structures-subsequent- dale-nell-b-55231722 Explore and download more ebooks at ebookbell.com
  • 2. Here are some recommended products that we believe you will be interested in. You can click the link to download. C Plus Data Structures Nell B Dale https://ebookbell.com/product/c-plus-data-structures-nell-b- dale-4116476 C Plus Data Structures 6th Edition Dale Chip Weems Tim Richards https://ebookbell.com/product/c-plus-data-structures-6th-edition-dale- chip-weems-tim-richards-59050442 Ivor Hortons Beginning Visual C Plus Plus Imar Spaanjaars https://ebookbell.com/product/ivor-hortons-beginning-visual-c-plus- plus-imar-spaanjaars-2285074 Ordinary And Partial Differential Equation Routines C C Plus Plus Fortran Java Maple Matlab https://ebookbell.com/product/ordinary-and-partial-differential- equation-routines-c-c-plus-plus-fortran-java-maple-matlab-2284398
  • 3. C Primer Plus Developers Library 6th Edition Prata Stephen https://ebookbell.com/product/c-primer-plus-developers-library-6th- edition-prata-stephen-55235694 C Primer Plus Fifth Edition 5th Edition Prata Stephen https://ebookbell.com/product/c-primer-plus-fifth-edition-5th-edition- prata-stephen-22034856 C Primer Plus 5th Edition Stephen Prata https://ebookbell.com/product/c-primer-plus-5th-edition-stephen- prata-23341320 C Primer Plus 1st Edition Klaus Michelsen https://ebookbell.com/product/c-primer-plus-1st-edition-klaus- michelsen-2360792 C Primer Plus 5th Ed Stephen Prata https://ebookbell.com/product/c-primer-plus-5th-ed-stephen- prata-4098388
  • 5. C++ T h i r d E d i t i o n Nell Dale J O N E S A N D B A R T L E T T C O M P U T E R S C I E N C E PlusData Structures TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 6. C++ T h i r d E d i t i o n Nell Dale University of Texas, Austin PlusData Structures TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 7. Copyright © 2003 by Jones and Bartlett Publishers, Inc. Cover image © Douglas E. Walker / Masterfile All rights reserved. No part of the material protected by this copyright notice may be reproduced or utilized in any form, electronic or mechanical, including photocopying, recording, or any information storage or retrieval system, without written permission from the copyright owner. Chief Executive Officer: Clayton Jones Chief Operating Officer: Don W. Jones, Jr. Executive V.P. and Publisher: Robert Holland V.P., Design and Production: Anne Spencer V.P., Manufacturing and Inventory Control: Therese Bräuer Editor-in-Chief, College: J. Michael Stranz Production Manager: Amy Rose Marketing Manager: Nathan Schultz Associate Production Editor: Karen Ferreira Editorial Assistant: Theresa DiDonato Production Assistant: Jenny McIsaac Cover Design: Night & Day Design Composition: Northeast Compositors, Inc. Text Design: Anne Spencer Printing and Binding: Courier Westford Cover Printing: Lehigh Press Library of Congress Cataloging-in-Publication Data Dale, Nell B. C++ plus data structures / Nell Dale.—3rd ed. p. cm. ISBN 0-7637-0481-4 1. C++ (Computer program language) 2. Data structures (Computer science) I. Title. QA76.73.C153 D334 2003 005.7’3—dc21 2002034168 This book was typeset in Quark 4.1 on a Macintosh G4. The font families used were Rotis Sans Serif, Rotis Serif, and Prestige Elite. The first printing was printed on 45# Highland Book. Printed in the United States of America 06 05 04 03 02 10 9 8 7 6 5 4 3 2 1 World Headquarters Jones and Bartlett Publishers 40 Tall Pine Drive Sudbury, MA 01776 978-443-5000 info@jbpub.com www.jbpub.com Jones and Bartlett Publishers Canada 2406 Nikanna Road Mississauga, ON L5C 2W6 CANADA Jones and Bartlett Publishers International Barb House, Barb Mews London W6 7PA UK TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 8. To Al, my husband and best friend, to our children and our children's children, and to our dogs Maggie and Chrissie, who round out our family. N.D. TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 9. TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 10. H istorically, a course on data structures has been a mainstay of most computer science departments. Over the last 18 years, however, the focus of this course has broadened considerably. The topic of data structures has now been sub- sumed under the broader topic of abstract data types (ADTs)—the study of classes of objects whose logical behavior is defined by a set of values and a set of operations. The term abstract data type describes a comprehensive collection of data values and operations; the term data structures refers to the study of data and how to repre- sent data objects within a program; that is, the implementation of structured rela- tionships. The shift in emphasis is representative of the move towards more abstraction in computer science education. We now are interested in the study of the abstract properties of classes of data objects in addition to how the objects might be represented in a program. Johannes J. Martin put it succinctly: “. . . depending on the point of view, a data object is characterized by its type (for the user) or by its structure (for the implementor).”1 Three Levels of Abstraction The focus of this book is on abstract data types as viewed from three different per- spectives: their specification, their application, and their implementation. The speci- fication perspective describes the logical or abstract level of data types, and is concerned with what the operations do. The application level, sometimes called the user level, is concerned with how the data type might be used to solve a problem, and is focused on why the operations do what they do. The implementation level is where the operations are actually coded. This level is concerned with the how ques- tions. Within this focus, we stress computer science theory and software engineering principles, including modularization, data encapsulation, information hiding, data 1Johannes J. Martin, Data Types and Data Structures, Prentice-Hall International Series in Computer Science, C. A. R. Hoare, Series Editor, Prentice-Hall International, (UK), LTD, 1986, p. 1. TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 11. abstraction, object-oriented decomposition, functional decomposition, the analysis of algorithms, and life-cycle software verification methods. We feel strongly that these principles should be introduced to computer science students early in their education so that they learn to practice good software techniques from the beginning. An understanding of theoretical concepts helps students put the new ideas they encounter into place, and practical advice allows them to apply what they have learned. To teach these concepts to students who may not have completed many college-level mathematics courses, we consistently use intuitive explanations, even for topics that have a basis in mathematics, like the analysis of algorithms. In all cases, our highest goal has been to make our explanations as readable and as easily understandable as possible. Prerequisite Assumptions In this book, we assume that students are familiar with the following C++ constructs: • Built-in simple data types • Stream I/O as provided in <iostream> • Stream I/O as provided in <fstream> • Control structures while, do-while, for, if, and switch • User-defined functions with value and reference parameters • Built-in array types • Class construct We have included sidebars within the text to refresh students’ memory concerning some of the details of these topics. Changes in the Third Edition The third edition incorporates the following changes: Object-oriented constructs moved forward: In the last five years, object-oriented pro- gramming has become part of the first-year curriculum, as demonstrated by its inclu- sion in all variations of the first year outlined in the Computing Curricula 2001 developed by the Joint Task Force of the IEEE Computer Society and the Association for Computing Machinery. Accordingly, the class concept has moved into the first semes- ter. Because of this, we assume that students have had experience using classes, and we therefore moved much of the discussion of how to define and access classes to a side- bar. We have kept a small discussion in the main text. Many students have already seen inheritance and polymorphism, but the concepts are too important to move to a sidebar, so we have moved them from Chapter 6 to Chapter 2. More emphasis on object-oriented design: Object-oriented design is a hard topic for most students, because people usually think procedurally in their lives. Because of this, we introduce a methodology with four phases: brainstorming, during which the possible vi | Preface TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 12. objects in a problem are isolated; filtering, during which the set of possible objects are reexamined to look for duplicates and/or missing objects; scenarios, during which hand simulations of the processing take place asking “what if” questions and assigning responsibilities to classes; and responsibility algorithms, during which the algorithms for the classes are designed. We use CRC cards to capture the results of the four-phase process. The output from the scenarios phase is a CRC card for each class. The CRC card lists the responsibilities of the class and any other classes with which the class must collaborate, hence the name CRC: class, responsibility, collaboration. More practical emphasis on testing: The concept of a multipurpose test driver is intro- duced in Chapter 1. After a test plan has been designed, it is implemented as input to the test driver. Throughout the rest of the book, this technique is used to test the ADTs. The drivers, the input data, and the output data are available on the book’s web site: http://computerscience.jbpub.com/cppDataStructures Reduced use of templates: The concept of generic data types, as implemented in C++ using templates, is very important. Making every ADT a class template after templates are introduced in Chapter 4, however, inserts an unnecessary complexity into already complex code. Thus, when introducing a new construct such as a linked list or a binary search tree, we have chosen to use classes rather than class templates. Subsequent implementations of a construct are often in the form of class templates, or the student is asked to transform a class into a class template in the exercises. Nonlinked binary tree representation covered with binary trees: The nonlinked represen- tation of a binary tree is an important concept within its own right, not just as an implementation for a heap. This implementation, therefore, is covered in Chapter 8 with other tree implementation techniques. Removal of material on binary expression trees: Although interesting applications of trees, binary expression trees do not fit into the discussion of abstract data types. Thus, we have moved this discussion to the web site. Inclusion of the ADT set: The exclusion of the ADT set has been an omission from pre- vious editions. Not only is a set an interesting mathematical object, but there are inter- esting implementation issues. We propose two implementations, one explicit (bit vector) and one implicit (list-based). Content and Organization Chapter 1 outlines the basic goals of high-quality software, and the basic principles of software engineering for designing and implementing programs to meet these goals. Abstraction, functional decomposition, and object-oriented design are discussed. This chapter also addresses what we see as a critical need in software education: the ability to design and implement correct programs and to verify that they are actually correct. Topics covered include the concept of “life-cycle” verification; designing for correctness using preconditions and postconditions; the use of deskchecking and design/code walk- throughs and inspections to identify errors before testing; debugging techniques, data coverage (black-box), and code coverage (clear- or white-box) approaches; test plans, Preface | vii TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 13. unit testing, and structured integration testing using stubs and drivers. The concept of a generalized test driver is presented and executed in a Case Study that develops the ADT Fraction. Chapter 2 presents data abstraction and encapsulation, the software engineering concepts that relate to the design of the data structures used in programs. Three per- spectives of data are discussed: abstraction, implementation, and application. These perspectives are illustrated using a real-world example, and then are applied to built-in data structures that C++ supports: structs and arrays. The C++ class type is presented as the way to represent the abstract data types we examine in subsequent chapters. The principles of object-oriented programming—encapsulation, inheritance, and polymor- phism—are introduced here along with the accompanying C++ implementation con- structs. The Case Study at the end of this chapter reinforces the ideas of data abstraction and encapsulation in designing and implementing a user-defined data type for general- ized string input and output. This class is tested using a version of the generalized test driver. Chapter 2 ends with a discussion of two C++ constructs that help users write better software: namespace and exception handling using the try/catch statement. Various approaches to error handling are demonstrated in subsequent chapters. We would like to think that the material in Chapters 1 and 2 is a review for most students. The concepts in these two chapters, however, are so crucial to the future of any and all students that we feel that we cannot rely on the assumption that they have seen the material before. Chapter 3 introduces the most fundamental abstract data type of all: the list. The chapter begins with a general discussion of operations on abstract data types and then presents the framework with which all of the other data types are examined: a presenta- tion and discussion of the specification, a brief application using the operations, and the design and coding of the operations. Both the unsorted and the sorted lists are pre- sented with an array-based implementation. Overloading the relational operators is pre- sented as a way to make the implementations more generic. The binary search is introduced as a way to improve the performance of the search operation in the sorted list. Because there is more than one way to solve a problem, we discuss how competing solutions can be compared through the analysis of algorithms, using Big-O notation. This notation is then used to compare the operations in the unsorted list and the sorted list. The four-phase object-oriented methodology is presented and demonstrated in the Case Study by using a simple real estate database. Chapter 4 introduces the stack and the queue data types. Each data type is first considered from its abstract perspective, and the idea of recording the logical abstrac- tion in an ADT specification is stressed. Then the set of operations is implemented in C++ using an array-based implementation. The concept of dynamic allocation is intro- duced, along with the syntax for using C++ pointer variables, and then used to demon- strate how arrays can be dynamically allocated to give the user more flexibility. With the introduction of dynamic storage, the destructor must be introduced. Templates are introduced as a way of implementing generic classes. A Case Study using stacks (post- fix expression evaluator) and one using queues (simulation) are presented. viii | Preface TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 14. Chapter 5 reimplements the ADTs from Chapters 3 and 4 as linked structures. The technique used to link the elements in dynamically allocated storage is described in detail and illustrated with figures. The array-based implementations and the linked implementations are then compared using Big-O notation. Chapter 6 is a collection of advanced concepts and techniques. Circular linked lists and doubly linked lists are discussed. The insertion, deletion, and list traversal algo- rithms are developed and implemented for each variation. An alternative representation of a linked structure, using static allocation (an array of structs), is designed. Class copy constructors, assignment overloading, and dynamic binding are covered in detail. The Case Study uses doubly linked lists to implement large integers. Chapter 7 discusses recursion, giving the student an intuitive understanding of the concept, and then shows how recursion can be used to solve programming problems. Guidelines for writing recursive functions are illustrated with many examples. After demonstrating that a by-hand simulation of a recursive routine can be very tedious, a simple three-question technique is introduced for verifying the correctness of recursive functions. Because many students are wary of recursion, the introduction to this mate- rial is deliberately intuitive and nonmathematical. A more detailed discussion of how recursion works leads to an understanding of how recursion can be replaced with itera- tion and stacks. The Case Study develops and implements the Quick-Sort algorithm. Chapter 8 introduces binary search trees as a way to arrange data, giving the flexi- bility of a linked structure with O(log2N) insertion and deletion time. In order to build on the previous chapter and exploit the inherent recursive nature of binary trees, the algorithms first are presented recursively. After all the operations have been imple- mented recursively, we code the insertion and deletion operations iteratively to show the flexibility of binary search trees. A nonlinked array-based binary tree implementa- tion is described. The Case Study discusses the process of building an index for a man- uscript and implements the first phase. Chapter 9 presents a collection of other branching structures: priority queues (implemented with both lists and heaps), graphs, and sets. The graph algorithms make use of stacks, queues, and priority queues, thus both reinforcing earlier material and demonstrating how general these structures are. Two set implementations are discussed: the bit-vector representation, in which each item in the base set is assigned a present/absent flag and the operations are the built-in logic operations, and a list-based representation, in which each item in a set is represented in a list of set items. If the item is not in the list, it is not in the set. Chapter 10 presents a number of sorting and searching algorithms and asks the question: Which are better? The sorting algorithms that are illustrated, implemented, and compared include straight selection sort, two versions of bubble sort, quick sort, heap sort, and merge sort. The sorting algorithms are compared using Big-O nota- tion. The discussion of algorithm analysis continues in the context of searching. Pre- viously presented searching algorithms are reviewed and new ones are described. Hashing techniques are discussed in some detail. Finally, radix sort is presented and analyzed. Preface | ix TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 15. Additional Features Chapter Goals A set of goals presented at the beginning of each chapter helps the students assess what they will learn. These goals are tested in the exercises at the end of each chapter. Chapter Exercises Most chapters have more than 35 exercises. They vary in levels of difficulty, including short programming problems, the analysis of algorithms, and problems to test the student’s understanding of concepts. Approximately one-third of the exercises are answered in the back of the book. The answer key for the remaining exercises is in the Instructor’s Guide. Case Studies There are seven case studies. Each includes a problem description, an analysis of the problem input and required output, and a discussion of the appropriate data types to use. Several of the case studies are completely coded and tested. Others are left at various stages in their development, requiring the student to complete and test the final version. Program Disk The specification and implementation of each class representing an ADT is available on a program disk that can be downloaded, free of charge, from the Jones and Bartlett Student Diskette Page on the World Wide Web (www.jbpub.com/disks). The source code for the completed case studies and the partial source code for the others is also available. Instructor Support Material Instructor teaching tools and resources are available on the web at http://computerscience.jbpub.com/cppDataStructures. On this site you will find: • Goals • Outlines • Teaching Notes: suggestions for how to teach the material covered in each chap- ter • Workouts: suggestions for in-class activities, discussion questions, and short exercises • Exercise Key: answers to those questions that are not solved in the back of the book • Programming Assignments: a collection of a wide range of assignments carefully chosen to illustrate the techniques described in the text • Electronic TestBank: this computerized TestBank allows you to create cus- tomized exams or quizzes from a collection of pre-made questions sorted by chapter. Updated for this edition, the TestBank questions can be edited and supplemented, and answers are provided for all pre-made questions. Each test is developed using Brownstone Diploma Software and is available on the book’s web site. • PowerPoint Presentations: new PowerPoint slides developed specifically for the third edition provide an excellent visual accompaniment to lectures. The Power- x | Preface TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 16. Point presentations for each chapter are designed to coordinate with the material in the textbook, and can be downloaded from the book’s web site. Acknowledgments We would like to thank the following people who took the time to review the first edi- tion of this manuscript: Donald Bagert, Texas Tech University; Susan Gauch, University of Kansas; Pamela Lawhead, University of Mississippi; Pat Nettnin, Finger Lakes Com- munity College; Bobbie Othmer, Westminster College of Salt Lake City; Suzanne Pawlan-Levy, Allan Hancock College; Carol Roberts, University of Maine; and Robert Strader, Stephen F. Austin State University. Thanks also to all of you who took the time to answer our electronic survey concerning this third edition. A special thanks to John McCormick, University of Northern Iowa, Mark Heading- ton, University of Wisconsin—LaCrosse, and Dan Joyce. John and Dan graciously allowed us to use some of their analogies from Ada Plus Data Structures and Object- Oriented Data Structures Using Java, respectively. Mark’s ideas, suggestions, and sharp eyes were invaluable. Thanks also to the students at Uppsala University in Sweden who used the final draft of the manuscript of the second edition in a course in the fall of 1997. Because non-English readers see what is written, not what they expect to see, their comments were invaluable in cleaning up ambiguous wording. Thanks to my husband Al, our children and grandchildren too numerous to name, and our dogs, Maggie, who keeps my feet warm, and Chrissie, whose role in life is to keep the house in turmoil and mud. A virtual bouquet of roses to the people who have worked on this book: Mike and Sigrid Wile, along with our Jones and Bartlett family. Theresa DiDonato, a jack-of-all- trades who helped with the survey; Jenny McIsaac, who jumped directly into the frying pan on her first day; Nathan Schultz, whose “can do” attitude is a joy to work with; and Michael Stranz and Amy Rose, whose team effort sustains all of us. Amy, thank heav- ens this production schedule was a little more leisurely than the last—but not by much! N. D. Preface | xi TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 17. TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 18. Preface v 1 Software Engineering Principles 1 1.1 The Software Process 2 1.2 Program Design 9 1.3 Verification of Software Correctness 19 Case Study: Fraction Class 50 Summary 58 Exercises 60 2 Data Design and Implementation 63 2.1 Different Views of Data 64 2.2 Abstraction and Built-In Types 72 2.3 Higher-Level Abstraction and the C++ Class Type 85 2.4 Object-Oriented Programming 91 2.5 Constructs for Program Verification 95 Case Study: User-Defined String I/O Class 100 Summary 116 Exercises 117 TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 19. 3 ADTs Unsorted List and Sorted List 123 3.1 Lists 124 3.2 Abstract Data Type Unsorted List 125 3.3 Abstract Data Type Sorted List 146 3.4 Comparison of Algorithms 157 3.5 Comparison of Unsorted and Sorted List ADT Algorithms 164 3.6 Overloading Operators 167 3.7 Object-Oriented Design Methodology 170 Case Study: Real Estate Listings: An Object-Oriented Design 173 Summary 188 Exercises 189 4 ADTs Stack and Queue 195 4.1 Stacks 196 4.2 More about Generics: C++ Templates 210 4.3 Pointer Types 214 4.4 Dynamically Allocated Arrays 222 Case Study: Simulation 245 Summary 261 Exercises 262 5 Linked Structures 279 5.1 Implementing a Stack as a Linked Structure 280 5.2 Implementing a Queue as a Linked Structure 296 5.3 Implementing the Unsorted List as a Linked Structure 307 5.4 Implementing the Sorted List as a Linked Structure 318 Summary 327 Exercises 327 6 Lists Plus 333 6.1 Circular Linked Lists 334 6.2 Doubly Linked Lists 344 6.3 Linked Lists with Headers and Trailers 348 6.4 Copy Structures 350 xiv | Contents TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 20. 6.5 A Linked List as an Array of Records 358 6.6 Polymorphism with Virtual Functions 368 6.7 A Specialized List ADT 373 Case Study: Implementing a Large Integer ADT 379 Summary 392 Exercises 392 7 Programming with Recursion 399 7.1 What is Recursion? 400 7.2 The Classic Example of Recursion 401 7.3 Programming Recursively 404 7.4 Verifying Recursive Functions 407 7.5 Writing Recursive Functions 408 7.6 Using Recursion to Simplify Solutions 411 7.7 Recursive Linked List Processing 412 7.8 A Recursive Version of Binary Search 416 7.9 Recursive Versions of InsertItem and DeleteItem 418 7.10 How Recursion Works 420 7.11 Tracing the Execution of Recursive Function Insert 429 7.12 Debugging Recursive Routines 432 7.13 Removing Recursion 432 7.14 Deciding Whether to Use a Recursive Solution 436 Case Study: QuickSort 438 Summary 446 Exercises 447 8 Binary Search Trees 455 8.1 Trees 456 8.2 Logical Level 460 8.3 Application Level 463 8.4 Implementation Level 463 8.5 Recursive Binary Search Tree Operations 464 8.6 Iterative Insertion and Deletion 496 8.7 Comparing Binary Search Trees and Linear Lists 504 8.8 A Nonlinked Representation of Binary Trees 506 Case Study: Building an Index 510 Contents | xv TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 21. Summary 517 Exercises 517 9 Priority Queues, Heaps, Graphs, and Sets 529 9.1 ADT Priority Queue 530 9.2 Heaps 533 9.3 Graphs 546 9.4 Sets 571 Summary 579 Exercises 579 10 Sorting and Searching Algorithms 588 10.1 Sorting 588 10.2 Searching 619 10.3 Hashing 622 10.4 Radix Sort 637 Summary 642 Exercises 644 Answer to Selected Exercises 653 Appendix A Reserved Words 713 Appendix B Operator Precedents 713 Appendix C A Selection of Standard Library Routines 715 Appendix D Character Sets 724 Appendix E The Standard Template Library 726 Glossary 771 Index 789 xvi | Contents TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 22. After studying this chapter, you should be able to Describe the general activities in the software life cycle Describe the goals for “quality” software Explain the following terms: software requirements, software specifica- tions, algorithm, information hiding, abstraction, stepwise refinement Explain and apply the fundamental ideas of top-down design Explain and apply the fundamental ideas of object-oriented design Identify several sources of program errors Describe strategies to avoid software errors Specify the preconditions and postconditions of a program segment or function Show how deskchecking, code walk-throughs, and design and code inspections can improve software quality and reduce the software development effort Explain the following terms: acceptance tests, regression testing, verification, validation, functional domain, black-box testing, white-box testing State several testing goals and indicate when each would be appropriate Describe several integration-testing strategies and indicate when each would be appropriate Explain how program verification techniques can be applied throughout the software development process Create a C++ test driver program to test a simple class Goals Software Engineering Principles TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 23. 2 | Chapter 1: Software Engineering Principles At this point in your computing career, you have completed at least one semester of computer science course work. You can take a problem of medium complexity, write an algorithm to solve the problem, code the algorithm in C++, and demonstrate the correct- ness of your solution. At least, that’s what the syllabus for your introductory class said you should be able to do when you complete the course. Now that you are starting your second (or third?) semester, it is time to stop and review those principles that, if adhered to, guarantee that you can indeed do what your previous syllabus claimed. In this chapter, we review the software design process and the verification of soft- ware correctness. In Chapter 2, we review data design and implementation. 1.1 The Software Process When we consider computer programming, we immediately think of writing a program for a computer to execute—the generation of code in some computer language. As a beginning student of computer science, you wrote programs that solved relatively sim- ple problems. Much of your initial effort went into learning the syntax of a program- ming language such as C++: the language’s reserved words, its data types, its constructs for selection (if-else and switch) and looping (while, do while, and for), and its input/output mechanisms (cin and cout). You may have learned a programming methodology that took you from the problem description that your instructor handed out all the way through the delivery of a good software solution. Programmers have created many design techniques, coding standards, and testing methods to help develop high-quality software. But why bother with all that methodology? Why not just sit down at a computer and write programs? Aren’t we wasting a lot of time and effort, when we could just get started on the “real” job? If the degree of our programming sophistication never had to rise above the level of trivial programs (like summing a list of prices or averaging grades), we might get away with such a code-first technique (or, rather, lack of technique). Some new programmers work this way, hacking away at the code until the program works more or less cor- rectly—usually less. As your programs grow larger and more complex, however, you must pay attention to other software issues in addition to coding. If you become a software professional, someday you may work as part of a team that develops a system containing tens of thousands, or even millions, of lines of code. The activities involved in such a software project’s whole “life cycle” clearly go beyond just sitting down at a computer and writ- ing programs. These activities include • Problem analysis Understanding the nature of the problem to be solved • Requirements elicitation Determining exactly what the program must do • Requirements definition Specifying what the program must do (functional requirements) and any constraints on the solution approach (nonfunctional requirements such as what language to use) • High- and low-level design Recording how the program meets the requirements, from the “big picture” overview to the detailed design TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 24. 1.1 The Software Process | 3 • Implementation of the design Coding a program in a computer language • Testing and verification Detecting and fixing errors and demonstrating the cor- rectness of the program • Delivery Turning over the tested program to the customer or user (or instructor!) • Operation Actually using the program • Maintenance Making changes to fix operational errors and to add or modify the program’s function Software development is not simply a matter of going through these steps sequen- tially. Rather, many activities take place concurrently. We may code one part of the solution while we design another part, or define requirements for a new version of a program while we continue testing the current version. Often a number of people may work on different parts of the same program simultaneously. Keeping track of all these activities is not an easy task. We use the term software engineering to refer to the discipline concerned with all aspects of the development of high quality software systems. It encompasses all varia- tions of techniques used during the software life cycle plus supporting activities such as documentation and teamwork. A software process is a specific set of interrelated soft- ware engineering techniques, used by a per- son or organization to create a system. What makes our jobs as programmers or software engineers challenging is the ten- dency of software to grow in size and complexity and to change at every stage of its development. A good software process uses tools to manage this size and complexity effectively. Usually a programmer takes advantage of several toolboxes, each containing tools that help to build and shape a software product. Hardware One toolbox contains the hardware itself: the computers and their peripheral devices (such as monitors, terminals, storage devices, and printers), on which and for which we develop software. Software A second toolbox contains various software tools: operating systems to control the computer’s resources, text editors to help us enter programs, compilers to translate high-level languages like C++ into something that the computer can execute, interactive debugging programs, test-data generators, and so on. You’ve used some of these tools already. Ideaware A third toolbox is filled with the shared body of knowledge that programmers have collected over time. This box contains the algorithms that we use to solve common programming problems as well as data structures for modeling the Software engineering The discipline devoted to the design, production, and maintenance of computer pro- grams that are developed on time and within cost esti- mates, using tools that help to manage the size and complexity of the resulting software products Software process A standard, integrated set of software engineering tools and techniques used on a project or by an organization TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 25. 4 | Chapter 1: Software Engineering Principles information processed by our programs. Recall that an algorithm is a step-by-step description of the solution to a problem. How we choose between two algorithms that carry out the same task often depends on the requirements of a particular application. If no relevant requirements exist, the choice may be based on the programmer’s own style. Ideaware contains programming methodologies such as top-down and object-ori- ented design and software concepts, including information hiding, data encapsulation, and abstraction. It includes aids for creating designs such as CRC (Classes, Responsibili- ties, and Collaborations) cards and methods for describing designs such as the UML (Unified Modeling Language). It also contains some tools for measuring, evaluating, and proving the correctness of our programs. We devote most this book to exploring the contents of this third toolbox. Some might argue that using these tools takes the creativity out of programming, but we don’t believe that to be true. Artists and composers are creative, yet their inno- vations are grounded in the basic principles of their crafts. Similarly, the most creative programmers build high-quality software through the disciplined use of basic program- ming tools. Goals of Quality Software Quality software entails much more than a program that somehow accomplishes the task at hand. A good program achieves the following goals: 1. It works. 2. It can be modified without excessive time and effort. 3. It is reusable. 4. It is completed on time and within budget. It’s not easy to meet these goals, but they are all important. Goal 1: Quality Software Works The program must do the task it was designed to perform, and it must do it correctly and completely. Thus the first step in the development process is to determine exactly what the program is required to do. To write a program that works, you first need to have a definition of the program’s requirements. For students, the requirements often are included in the instruc- tor’s problem description: “Write a program that calculates....” For programmers working on a govern- ment contract, the requirements document may be hundreds of pages long. We develop programs that meet the user’s requirements using software specifica- tions. The specifications indicate the format of the input and the expected output, Algorithm A logical sequence of discrete steps that describes a complete solution to a given problem, com- putable in a finite amount of time Requirements A statement of what is to be provided by a computer system or software product Software specification A detailed description of the function, inputs, processing, outputs, and special require- ments of a software product; it provides the information needed to design and implement the program TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 26. 1.1 The Software Process | 5 details about processing, performance measures (how fast? how big? how accurate?), what to do in case of errors, and so on. The specifications tell exactly what the program does, but not how it is done. Sometimes your instructor will provide detailed specifica- tions; other times you may have to write them yourself, based on the requirements defi- nition, conversations with your instructor, or guesswork. (We discuss this issue in more detail later in this chapter.) How do you know when the program is right? A program must be complete (it should “do everything” specified) and correct (it should “do it right”) to meet its require- ments. In addition, it should be usable. For instance, if the program needs to receive data from a person sitting at a terminal, it must indicate when it expects input. The pro- gram’s outputs should be readable and understandable to users. Indeed, creating a good user interface is an important subject in software engineering today. Finally, Goal 1 means that the program should be as efficient as it needs to be. We would never deliberately write programs that waste time or space in memory, but not all programs demand great efficiency. When they do, however, we must meet these demands or else the programs will not satisfy the requirements. A space-launch control program, for instance, must execute in “real time”; that is, the software must process commands, perform calculations, and display results in coordination with the activities it is supposed to control. Closer to home, if a desktop-publishing program cannot update the screen as rapidly as the user can type, the program is not as efficient as it needs to be. In such a case, if the software isn’t efficient enough, it doesn’t meet its requirements; thus, according to our definition, it doesn’t work correctly. Goal 2: Quality Software Can Be Modified When does software need to be modified? Changes occur in every phase of its existence. Software gets changed in the design phase. When your instructor or employer gives you a programming assignment, you begin to think of how to solve the problem. The next time you meet, however, you may be notified of a small change in the program description. Software gets changed in the coding phase. You make changes in your program as a result of compilation errors. Sometimes you suddenly see a better solution to a part of the problem after the program has been coded, so you make changes. Software gets changed in the testing phase. If the program crashes or yields wrong results, you must make corrections. In an academic environment, the life of the software typically ends when a cor- rected program is turned in to be graded. When software is developed for real-world use, however, most of the changes take place during the “maintenance” phase. Someone may discover an error that wasn’t uncovered in testing, someone else may want to include additional functions, a third party may want to change the input format, and a fourth person may want to run the program on another system. As you see, software changes often and in all phases of its life cycle. Knowing this fact, software engineers try to develop programs that are modified easily. If you think it is a simple matter to change a program, try to make a “small change” in the last pro- gram you wrote. It’s difficult to remember all the details of a program after some time has passed, isn’t it? Modifications to programs often are not even made by the original TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 27. 6 | Chapter 1: Software Engineering Principles authors but rather by subsequent maintenance programmers. (Someday you may be the one making the modifications to someone else’s program.) What makes a program easy to modify? First, it should be readable and understand- able to humans. Before it can be changed, it must be understood. A well-designed, clearly written, well-documented program is certainly easier for human readers to understand. The number of pages of documentation required for “real-world” programs usually exceeds the number of pages of code. Almost every organization has its own policy for documentation. Reading a well-written program can teach you techniques that help you write good programs. In fact, it’s difficult to imagine how anyone could become a good programmer without reading good programs. Second, the program should readily be able to withstand small changes. The key idea is to partition your programs into manageable pieces that work together to solve the problem, yet remain relatively independent. The design methodologies reviewed later in this chapter should help you write programs that meet this goal. Goal 3: Quality Software Is Reusable It takes time and effort to create quality software. Therefore, it is important to realize as much value from the software as possible. One way to save time and effort when building a software solution is to reuse pro- grams, classes, functions, and other components from previous projects. By using previ- ously designed and tested code, you arrive at your solution sooner and with less effort. Alternatively, when you create software to solve a problem, it is sometimes possible to structure that software so it can help solve future, related problems. By doing so, you gain more value from the software created. Creating reusable software does not happen automatically. It requires extra effort during the specification and design phases. To be reusable, software must be well docu- mented and easy to read, so that a programmer can quickly determine whether it can be used for a new project. It usually has a simple interface so that it can easily be plugged into another system. It is also modifiable (Goal 2), in case a small change is needed to adapt it to the new system. When creating software to fulfill a narrow, specific function, you can sometimes make the software more generally usable with a minimal amount of extra effort. In this way, you increase the chances that you can reuse the software later. For example, if you are creating a routine that sorts a list of integers into increasing order, you might generalize the routine so that it can also sort other types of data. Furthermore, you could design the routine to accept the desired sort order, increasing or decreasing, as a parameter. Goal 4: Quality Software Is Completed on Time and Within Budget You know what happens in school when you turn in your program late. You probably have grieved over an otherwise perfect program that received only half credit—or no credit at all—because you turned it in one day late. “But the network was down five hours last night!” you protest. Although the consequences of tardiness may seem arbitrary in the academic world, they are significant in the business world. The software for controlling a space launch TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 28. 1.1 The Software Process | 7 must be developed and tested before the launch can take place. A patient database sys- tem for a new hospital must be installed before the hospital can open. In such cases, the program doesn’t meet its requirements if it isn’t ready when needed. “Time is money” may sound trite but failure to meet deadlines is expensive. A com- pany generally budgets a certain amount of time and money for the development of a piece of software. As a programmer, you are paid a salary or an hourly wage. If your part of the project is only 80% complete when the deadline arrives, the company must pay you—or another programmer—to finish the work. The extra expenditure in salary is not the only cost, however. Other workers may be waiting to integrate your part of the program into the system for testing. If the program is part of a contract with a cus- tomer, monetary penalties may be assessed for missed deadlines. If it is being developed for commercial sales, the company may be beaten to the market by a competitor and eventually forced out of business. Once you have identified your goals, what can you do to meet them? Where should you start? Software engineers use many tools and techniques. In the next few sections of this chapter, we review some of these techniques to help you understand, design, and code programs. Specification: Understanding the Problem No matter which programming design technique you use, the first steps are always the same. Imagine the following all-too-familiar situation. On the third day of class, you are given a 12-page description of Programming Assignment 1, which must be running per- fectly and turned in by noon, one week from yesterday. You read the assignment and realize that this program is three times larger than any program you have ever written. What is your first step? The responses listed here are typical of those given by a class of computer science students in such a situation: 1. Panic 39% 2. Sit down at the computer and begin typing 30% 3. Drop the course 27% 4. Stop and think 4% Response 1 is a predictable reaction from students who have not learned good pro- gramming techniques. Students who adopt Response 3 will find their education pro- gressing rather slowly. Response 2 may seem to be a good idea, especially considering the deadline looming ahead. Resist the temptation, though—the first step is to think. Before you can come up with a program solution, you must understand the problem. Read the assignment, and then read it again. Ask questions of your instructor (or man- ager, or client). Starting early affords you many opportunities to ask questions; starting the night before the program is due leaves you no opportunity at all. The problem with writing first is that it tends to lock you into the first solution you think of, which may not be the best approach. We have a natural tendency to believe TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 29. 8 | Chapter 1: Software Engineering Principles that once we’ve put something in writing, we have invested too much in the idea to toss it out and start over. On the other hand, don’t agonize about all the possibilities until the day before your deadline. (Chances are that a disk drive, network, or printer will fail that day!) When you think you understand the problem, you should begin writing your design. Writing Detailed Specifications Many writers experience a moment of terror when faced with a blank piece of paper— where to begin? As a programmer, however, you don’t have to wonder about where to begin. Using the assignment description (your “requirements”), first write a complete definition of the problem, including the details of the expected inputs and outputs, the necessary processing and error handling, and all assumptions about the problem. When you finish this task, you have a detailed specification—a formal definition of the prob- lem your program must solve, which tells you exactly what the program should do. In addition, the process of writing the specifications brings to light any holes in the requirements. For instance, are embedded blanks in the input significant or can they be ignored? Do you need to check for errors in the input? On which computer system(s) will your program run? If you get the answers to these questions at this stage, you can design and code your program correctly from the start. Many software engineers work with user/operational scenarios to understand the requirements. In software design, a scenario is a sequence of events for one execution of the program. For example, a designer might consider the following scenario when developing the software for a bank’s automated teller machine (ATM): 1. The customer inserts a bank card. 2. The ATM reads the account number on the card. 3. The ATM requests a PIN (personal identification number) from the customer. 4. The customer enters 5683. 5. The ATM successfully verifies the account number PIN combination. 6. The ATM asks the customer to select a transaction type (deposit, show balance, withdrawal, or quit). 7. The customer selects the show balance option. 8. The ATM obtains the current account balance ($1,204.35) and displays it. 9. The ATM asks the customer to select a transaction type (deposit, show balance, withdrawal, or quit). 10. The customer selects quit. 11. The ATM returns the customer’s bank card. Scenarios allow us to get a feel for the behavior expected from the system. Of course, a single scenario cannot show all possible behaviors. For this reason, software TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 30. 1.2 Program Design | 9 engineers typically prepare many different scenarios to gain a full understanding of the system’s requirements. You must know some details to write and run the program. Other details, if not explicitly stated in the program’s requirements, may be handled according to the pro- grammer’s preference. Assumptions about unstated or ambiguous specifications should always be written explicitly in the program’s documentation. The detailed specification clarifies the problem to be solved. But it does more than that: It also serves as an important piece of written documentation about the program. There are many ways in which specifications may be expressed and a number of differ- ent sections that may be included, depending on the nature of the problem. Our recom- mended program specification includes the following sections: • Processing requirements • Sample inputs with expected outputs • Assumptions If special processing is needed for unusual or error conditions, it should be specified as well. Sometimes it is helpful to include a section containing definitions of terms used. Likewise, it may prove useful to list any testing requirements so that verifying the pro- gram is considered early in the development process. 1.2 Program Design Remember, the specification of the program tells what the program must do, but not how it does it. Once you have fully clarified the goals of the program, you can begin to develop and record a strategy for meeting them; in other words, you can begin the design phase of the software life cycle. Tools In this section, we review some ideaware tools that are used for software design, includ- ing abstraction, information hiding, stepwise refinement, and visual tools. Abstraction The universe is filled with complex systems. We learn about such systems through models. A model may be mathematical, like equations describing the motion of satellites around the earth. A physical object such as a model airplane used in wind- tunnel tests is another form of model. In this approach to understanding complex systems, the important concept is that we consider only the essential characteristics of the system; we ignore minor or irrelevant details. For example, although the earth is an oblate ellipsoid, globes (models of the earth) are spheres. The small difference between the earth’s equatorial diameter and polar diameter is not important to us in studying the political divisions and physical landmarks on the earth. Similarly, the model airplanes used to study aerodynamics do not include in-flight movies. TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 31. 10 | Chapter 1: Software Engineering Principles Figure 1.1 An abstraction includes the essential details relative to the perspective of the viewer. f=ma An abstraction is a model of a complex system that includes only the essential details. Abstractions are the fundamental way that we manage complexity. Different viewers use different abstractions of a partic- ular system. Thus, while we may see a car as a means to transport us and our friends, the automotive brake engineer may see it as a large mass with a small con- tact area between it and the road (Figure 1.1). What does abstraction have to do with software development? The programs we write are abstractions. A spreadsheet program that is used by an accountant models the books used to record debits and credits. An educa- tional computer game about wildlife models an ecosystem. Writing software is difficult because both the systems we model and the processes we use to develop the software are complex. One of our major goals is to convince you to use abstractions to manage the complexity of developing software. In nearly every chapter, we make use of abstrac- tion to simplify our work. Information Hiding Many design methods are based on decomposing a problem’s solution into modules. A module is a cohesive system subunit that performs a share of the work. Decomposing a system into modules helps us manage complexity. Additionally, the modules can form the basis of assignments for different programming teams working separately on a large system. One important feature of any design method is that the details that are specified in lower levels of the program design remain hidden from the higher levels. The programmer sees only the details that are relevant at a particular level Abstraction A model of a complex system that includes only the details essential to the perspective of the viewer of the system Module A cohesive system subunit that performs a share of the work TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 32. 1.2 Program Design | 11 of the design. This information hiding makes certain details inaccessible to the programmer at higher levels. Modules act as an abstraction tool. Because the complexity of its internal struc- ture can be hidden from the rest of the sys- tem, the details involved in implementing a module remain isolated from the details of the rest of the system. Why is hiding the details desirable? Shouldn’t the programmer know everything? No! In this situation, a certain amount of ignorance truly is advantageous. Information hiding prevents the higher levels of the design from becoming dependent on low-level design details that are more likely to be changed. For example, you can stop a car with- out knowing whether it has disc brakes or drum brakes. You don’t need to know these lower-level details of the car’s brake subsystem to stop it. Furthermore, you don’t want to require a complete understanding of the complicated details of low-level routines for the design of higher-level routines. Such a requirement would introduce a greater risk of confusion and error throughout the whole program. For example, it would be disastrous if every time we wanted to stop our car, we had to think, “The brake pedal is a lever with a mechanical advantage of 10.6 coupled to a hydraulic system with a mechanical advantage of 7.3 that presses a semi-metallic pad against a steel disc. The coefficient of friction of the pad/disc contact is....” Information hiding is not limited to driving cars and programming computers. Try to list all the operations and information required to make a peanut butter and jelly sand- wich. We normally don’t consider the details of planting, growing, and harvesting peanuts, grapes, and wheat as part of making a sandwich. Information hiding lets us deal with only those operations and information needed at a particular level in the solution of a problem. The concepts of abstraction and information hiding are fundamental principles of soft- ware engineering. We will come back to them again and again throughout this book. Besides helping us manage the complexity of a large system, abstraction and information hiding support our quality-related goals of modifiability and reusability. In a well-designed system, most modifications can be localized to just a few modules. Such changes are much easier to make than changes that permeate the entire system. Additionally, a good system design results in the creation of generic modules that can be used in other systems. To achieve these goals, modules should be good abstractions with strong cohesion; that is, each module should have a single purpose or identity and the module should stick together well. A cohesive module can usually be described by a simple sentence. If you have to use several sentences or one very convoluted sentence to describe your module, it is probably not cohesive. Each module should also exhibit information hiding so that changes within it do not result in changes in the modules that use it. This inde- pendent quality of modules is known as loose coupling. If your module depends on the internal details of other modules, it is not loosely coupled. Stepwise Refinement In addition to concepts such as abstraction and information hiding, software developers need practical approaches to conquer complexity. Stepwise Information hiding The practice of hiding the details of a function or data structure with the goal of controlling access to the details of a module or structure TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 33. 12 | Chapter 1: Software Engineering Principles 1Grady Booch, Object Oriented Design with Applications (Benjamin Cummings, 1991). refinement is a widely applicable approach. Many variations of it exist, such as top- down, bottom-up, functional decomposition, and even “round-trip gestalt design.” Undoubtedly you have learned a variation of stepwise refinement in your studies, as it is a standard method for organizing and writing essays, term papers, and books. For example, to write a book an author first determines the main theme and the major subthemes. Next, the chapter topics can be identified, followed by section and subsection topics. Outlines can be produced and further refined for each subsection. At some point the author is ready to add detail—to actually begin writing sentences. In general, with stepwise refinement, a problem is approached in stages. Similar steps are followed during each stage, with the only difference reflecting the level of detail involved. The completion of each stage brings us closer to solving our problem. Let’s look at some variations of stepwise refinement: • Top-down With this approach, the problem is first broken into several large parts. Each of these parts is, in turn, divided into sections, the sections are subdi- vided, and so on. The important feature is that details are deferred as long as possible as we move from a general to a specific solution. The outline approach to writing a book involves a form of top-down stepwise refinement. • Bottom-up As you might guess, with this approach the details come first. Bot- tom-up development is the opposite of the top-down approach. After the detailed components are identified and designed, they are brought together into increas- ingly higher-level components. This technique could be used, for example, by the author of a cookbook who first writes all the recipes and then decides how to organize them into sections and chapters. • Functional decomposition This program design approach encourages program- ming in logical action units, called functions. The main module of the design becomes the main program (also called the main function), and subsections develop into functions. This hierarchy of tasks forms the basis for functional decomposition, with the main program or function controlling the processing. The general function of the method is continually divided into subfunctions until the level of detail is considered fine enough to code. Functional decomposition is top-down stepwise refinement with an emphasis on functionality. • Round-trip gestalt design This confusing term is used to define the stepwise refinement approach to object-oriented design suggested by Grady Booch,1 one of the leaders of the “object” movement. First, the tangible items and events in the problem domain are identified and assigned to candidate classes and objects. Next, the external properties and relationships of these classes and objects are defined. Finally, the internal details are addressed; unless these are trivial, the designer must return to the first step for another round of design. This approach entails top-down stepwise refinement with an emphasis on objects and data. Good software designers typically use a combination of the stepwise refinement techniques described here. TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 34. 1.2 Program Design | 13 2K. B. Beck and W. Cunningham, http://c2.com/doc/oopsla89/paper.html. Visual Tools Abstraction, information hiding, and stepwise refinement are interrelated methods for controlling complexity during the design of a system. We now look at some tools that can help us visualize our designs. Diagrams are used in many professions. For example, architects use blueprints, investors use market trend graphs, and truck drivers use maps. Software engineers use different types of diagrams and tables, such as the Unified Mod- eling Language (UML) and Class, Responsibility, and Collaboration (CRC) cards. The UML is used to specify, visualize, construct, and document the components of a soft- ware system. It combines the best practices that have evolved over the past several decades for modeling systems, and it is particularly well suited to modeling object-ori- ented designs. UML diagrams represent another form of abstraction. They hide imple- mentation details and allow systems designers to concentrate on only the major design components. UML includes a large variety of interrelated diagram types, each with its own set of icons and connectors. A very powerful development and modeling tool, it is helpful for modeling designs after they have been developed. In contrast, CRC cards help us determine our initial designs. CRC cards were first described by Beck and Cunningham,2 in 1989, as a means to allow object-oriented pro- grammers to identify a set of cooperating classes to solve a problem. A programmer uses a physical 4 6 index card to represent each class that had been identified as part of a problem solution. Figure 1.2 shows a blank CRC card. It con- tains room for the following information about a class: 1. Class name 2. Responsibilities of the class—usually represented by verbs and implemented by pub- lic functions (called methods in object-oriented terminology) 3. Collaborations—other classes or objects that are used in fulfilling the responsibilities TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 35. 14 | Chapter 1: Software Engineering Principles Figure 1.2 A blank CRC card Class Name: Superclass: Subclasses: Responsibilities Collaborations CRC cards are great tools for refining an object-oriented design, especially in a team programming environment. They provide a physical manifestation of the building blocks of a system that allows programmers to walk through user scenarios, identifying and assigning responsibilities and collaborations. We discuss a problem-solving methodology using CRC cards in Chapter 3. UML is beyond the scope of this text, but we will use CRC cards throughout. Design Approaches We have defined the concept of a module, described the characteristics of a good mod- ule, and presented the concept of stepwise refinement as a strategy for defining mod- ules. But what should these modules be? How do we define them? One approach is to break the problem into functional subproblems (do this, then do this, then do that). Another approach is to divide the problem into the “things” or objects that interact to solve the problem. We explore both of these approaches in this section. Top-Down Design One method for designing software is based on the functional decomposition and top-down strategies. You may have learned this method in your introductory class. First the problem is broken into several large tasks. Each of these tasks is, in turn, divided into sections, the sections are subdivided, and so on. As we said previously, the key feature is that details are deferred as long as possible as we move from a general to a specific solution. TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 36. 1.2 Program Design | 15 Make Cake Get ingredients Mix cake ingredients Bake Cool Apply icing To develop a computer program by this method, we begin with a “big picture” solution to the problem defined in the specification. We then devise a general strat- egy for solving the problem by dividing it into manageable functional modules. Next, each of the large functional modules is subdivided into several tasks. We do not need to write the top level of the functional design in source code (such as C++); rather, we can write it in English or “pseudocode.” (Some software development proj- ects even use special design languages that can be compiled.) This divide-and-con- quer activity continues until we reach a level that can be easily translated into lines of code. Once it has been divided into modules, the problem is simpler to code into a well- structured program. The functional decomposition approach encourages programming in logical units, using functions. The main module of the design becomes the main pro- gram (also called the main function), and subsections develop into functions. This hier- archy of tasks forms the basis for functional decomposition, with the main program or function controlling the processing. As an example, let’s start the functional design for making a cake. The problem now is divided into five logical units, each of which might be further decomposed into more detailed functional modules. Figure 1.3 illustrates the hierarchy of such a functional decomposition. Object-Oriented Design Another approach to designing programs is called object- oriented design (OOD). This methodology originated with the development of programs to simulate physical objects and processes in the real world. For example, to simulate an electronic circuit, you could develop a module for simulating each type of component in the circuit and then “wire up” the simulation by having the modules pass information among themselves along the same pattern in which wires connect the electronic components. In a simulation, the top-down decomposition of the problem has already taken place. An engineer has designed a circuit or a mechanical device, a physicist has devel- oped a model of a physical system, a biologist has developed an experimental model, an TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 37. 16 | Chapter 1: Software Engineering Principles Figure 1.3 A portion of a functional design for baking a cake Make cake Get ingredients Mix cake ingredients Bake Cool Apply icing Mix liquid ingredients Mix dry ingredients Combine liquid and dry ingredients . . . . . . . . economist has designed an economic model, and so on. As a programmer, your job is to take this problem decomposition and implement it. In object-oriented design, the first steps are to identify the simplest and most widely used objects and processes in the decomposition and to implement them faithfully. Once you have completed this stage, you often can reuse these objects and processes to implement more complex objects and processes. This hierarchy of objects forms the basis for object-oriented design. Object-oriented design, like top-down design, takes a divide-and-conquer approach. However, instead of decomposing the problem into functional modules, we divide it into entities or things that make sense in the context of the problem being solved. These entities, called objects, collaborate and interact to solve the problem. The code that allows these objects to interact is called a driver program. Let’s list some of the objects in our baking problem. There are, of course, all of the various ingredients: eggs, milk, flour, butter, and so on. We also need certain pieces of equipment, such as pans, bowls, measuring spoons, and an oven. The baker is another important entity. All of these entities must collaborate to create a cake. For example, a spoon measures indi- vidual ingredients and a bowl holds a mixture of ingredients. Groups of objects with similar properties and behaviors are described by an object class (usually Object class (class) The description of a group of objects with similar properties and behaviors; a pattern for creating individual objects TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 38. 1.2 Program Design | 17 shortened to class). Each oven in the world is a unique object. We cannot hope to describe every oven, but we can group oven objects together into a class called oven that has certain properties and behaviors. An object class is similar to a C++ class (see the sidebar on page 18 on class syntax and the discussion in Chapter 2). C++ types are templates for variables; classes are tem- plates for objects. Like types, object classes have attributes and operations associated with them. For example, an oven class might have an attribute to specify whether it is gas or electric and operations to turn it on or off and to set it to maintain a desired temperature. With object-oriented design, we determine the classes from the things in the prob- lem as described in the problem statement. We record each object class using a CRC card. From this work, we determine a set of properties (attributes) and a set of responsi- bilities (operations) to associate with each class. With object-oriented design, the func- tionality of the program is distributed among a set of collaborating objects. Table 1.1 illustrates some of the object classes that participate in baking a cake. Once we have defined an oven class, we can reuse it in other cooking problems, such as roasting a turkey. Reuse of classes is an important aspect of modern software development. One major goal of this text is to introduce you to a number of classes that are particularly important in the development of software—abstract data types. We dis- cuss the concept of an abstract data type in detail in Chapter 2. Throughout the book, we fully develop many abstract data types, and we describe others leaving you to develop them yourself. As these classes are fundamental to computer science, we can often obtain the C++ code for them from a public or private repository or purchase it from vendors who market C++ components. In fact, the new C++ language standard includes components in the Standard Template Library (STL). You may wonder why, if they are already available, we spend so much time on their development. Our goal is to teach you how to develop software. As with any skill, you need to practice the funda- mentals before you can become a virtuoso. To summarize, top-down design methods focus on the process of transforming the input into the output, resulting in a hierarchy of tasks. Object-oriented design focuses on the data objects that are to be transformed, resulting in a hierarchy of objects. Grady Table 1.1 Example of object classes that participate in baking a cake Class Attributes Responsibilities (Operations) Oven Energy source Turn on Size Turn off Temperature Set desired temperature Number of racks Bowl Capacity Add to Current amount Dump Egg Size Crack Separate (white from yolk) TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 39. 18 | Chapter 1: Software Engineering Principles Booch puts it this way: “Read the specification of the software you want to build. Underline the verbs if you are after procedural code, the nouns if you aim for an object- oriented program.”3 We propose that you circle the nouns and underline the verbs. The nouns become objects; the verbs become operations. In a functional design, the verbs are the primary focus; in an object-oriented design, the nouns are the primary focus. 3Grady Booch, “What Is and Isn’t Object Oriented Design.” American Programmer, special issue on object ori- entation, vol. 2, no. 7–8, Summer 1989. Class Syntax A C++ class contains both data and functions that operate on the data. A class is declared in two parts: the specification of the class and the implementation of the class functions. class MoneyType { public: void Initialize(long, long); // Initializes dollars and cents. long DollarsAre() const; // Returns dollars. long CentsAre() const; // Returns cents. private: long dollars; long cents; }; A member function is defined like any function with one exception: The name of the class type within which the member is declared precedes the member function name with a double colon in between (::). The double colon operator is called the scope resolution operator. void MoneyType::Initialize(long newDollars, long newCents) // Post: dollars is set to newDollars; cents is set to // newCents. { dollars = newDollars; cents = newCents; } C++ TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 40. 1.3 Verification of Software Correctness | 19 long MoneyType::DollarsAre() const // Post: Class member dollars is returned. { return dollars; } long MoneyType::CentsAre() const // Post: Class member cents is returned. { return cents; } If money is a variable of type MoneyType, the following statement prints the data fields of money: cout $ money.DollarsAre() . money.CentsAre(); 1.3 Verification of Software Correctness At the beginning of this chapter, we discussed some characteristics of good programs. The first of these was that a good program works—it accomplishes its intended function. How do you know when your program meets that goal? The simple answer is, test it. Let’s look at testing as it relates to the rest of the software development process. As pro- grammers, we first make sure that we under- stand the requirements. We then come up with a general solution. Next, we design the solu- tion in terms of a computer program, using good design principles. Finally, we implement the solution, using good structured coding, with classes, functions, self-documenting code, and so on. Once we have the program coded, we compile it repeatedly until no syntax errors appear. Then we run the program, using carefully selected test data. If the program doesn’t work, we say that it has a “bug” in it. We try to pinpoint the error and fix it, a process called debugging. Notice the distinction between testing and debugging. Testing is running the program with data sets designed to discover any errors; debugging is removing errors once they are discovered. When the debugging is completed, the software is put into use. Before final deliv- ery, software is sometimes installed on one or more customer sites so that it can be tested in a real environment with real data. After passing this acceptance test phase, the Testing The process of executing a program with data sets designed to discover errors Debugging The process of removing known errors Acceptance test The process of testing the system in its real environment with real data TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 41. 20 | Chapter 1: Software Engineering Principles 4B. W. Boehm, Software Engineering Economics (Englewood Cliffs, N.J.: Prentice-Hall, 1981). software can be installed at all customer sites. Is the verification process now finished? Hardly! More than half of the total life-cycle costs and effort generally occur after the program becomes operational, in the maintenance phase. Some changes correct errors in the original program; other changes add new capabilities to the software system. In either case, testing must occur after any program modification. This phase is called regression testing. Testing is useful in revealing the presence of bugs in a program, but it doesn’t prove their absence. We can only say for sure that the program worked cor- rectly for the cases we tested. This approach seems somewhat haphazard. How do we know which tests or how many of them to run? Debugging a whole pro- gram at once isn’t easy. Also, fixing the errors found during such testing can sometimes be a messy task. Too bad we couldn’t have detected the errors earlier— while we were designing the program, for instance. They would have been much easier to fix then. We know how program design can be improved by using a good design methodol- ogy. Can we use something similar to improve our program verification activities? Yes, we can. Program verification activities don’t need to start when the program is com- pletely coded; they can be incorporated into the entire software development process, from the requirements phase on. Program verification is more than just testing. In addition to program verification, which involves fulfilling the requirement speci- fications, the software engineer has another important task—making sure the specified requirements actually solve the underlying problem. Countless times a programmer has finished a large project and delivered the verified software, only to be told, “Well, that’s what I asked for but it’s not what I need.” The process of determining that software accomplishes its intended task is called program validation. Program verification asks, “Are we doing the job right?”; program validation asks, “Are we doing the right job?”4 Can we really “debug” a program before it has ever been run—or even before it has been written? In this section we review a number of topics related to satisfying the cri- terion “quality software works.” The topics include • Designing for correctness • Performing code and design walk-throughs and inspections • Using debugging methods • Choosing test goals and data • Writing test plans • Structured integration testing Regression testing Reexecution of program tests after modifications have been made to ensure that the program still works correctly Program verification The process of determining the degree to which a software product fulfills its specifica- tions Program validation The process of determining the degree to which software fulfills its intended purpose TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 42. 1.3 Verification of Software Correctness | 21 Origin of Bugs When Sherlock Holmes goes off to solve a case, he doesn’t start from scratch every time; he knows from experience all kinds of things that help him find solutions. Sup- pose Holmes finds a victim in a muddy field. He immediately looks for footprints in the mud, for he can tell from a footprint what kind of shoe made it. The first print he finds matches the shoes of the victim, so he keeps looking. Now he finds another print, and from his vast knowledge of footprints he can tell that it was made by a certain type of boot. He deduces that such a boot would be worn by a particular type of laborer, and from the size and depth of the print he guesses the suspect’s height and weight. Now, knowing something about the habits of laborers in this town, he guesses that at 6:30 P.M. the suspect might be found in Clancy’s Pub. In software verification we are often expected to play detective. Given certain clues, we have to find the bugs in programs. If we know what kinds of situations produce pro- gram errors, we are more likely to be able to detect and correct problems. We may even be able to step in and prevent many errors entirely, just as Sherlock Holmes sometimes intervenes in time to prevent a crime from taking place. Let’s look at some types of software errors that show up at various points in pro- gram development and testing and see how they might be avoided. Specifications and Design Errors What would happen if, shortly before you were supposed to turn in a major class assignment, you discovered that some details in the professor’s program description were incorrect? To make matters worse, you also found out that the corrections were discussed at the beginning of class on the day you got there late, and somehow you never knew about the problem until your tests of the class data set came up with the wrong answers. What do you do now? Writing a program to the wrong specifications is probably the worst kind of soft- ware error. How bad can it be? Let’s look at a true story. Some time ago, a computer company contracted to replace a government agency’s obsolete system with new hard- TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 43. 22 | Chapter 1: Software Engineering Principles ware and software. A large and complicated program was written, based on specifica- tions and algorithms provided by the customer. The new system was checked out at every point in its development to ensure that its functions matched the requirements in the specifications document. When the system was complete and the new software was executed, users discovered that the results of its calculations did not match those of the old system. A careful comparison of the two systems showed that the specifications of the new software were erroneous because they were based on algorithms taken from the old system’s inaccurate documentation. The new program was “correct” in that it accomplished its specified functions, but the program was useless to the customer because it didn’t accomplish its intended functions—it didn’t work. The cost of correct- ing the errors measured in the millions of dollars. How could correcting the error be so expensive? First, much of the conceptual and design effort, as well as the coding, was wasted. It took a great deal of time to pinpoint which parts of the specification were in error and then to correct this document before the program could be redesigned. Then much of the software development activity (design, coding, and testing) had to be repeated. This case is an extreme one, but it illus- trates how critical specifications are to the software process. In general, programmers are more expert in software development techniques than in the “application” areas of their programs, such as banking, city planning, satellite control, or medical research. Thus correct program specifications are crucial to the success of program development. Most studies indicate that it costs 100 times as much to correct an error discovered after software delivery than it does if the problem is discovered early in the software life cycle. Figure 1.4 shows how fast the costs rise in subsequent phases of software devel- opment. The vertical axis represents the relative cost of fixing an error; this cost might be measured in units of hours, hundreds of dollars, or “programmer months” (the amount of work one programmer can do in one month). The horizontal axis represents the stages in the development of a software product. As you can see, an error that would have taken one unit to fix when you first started designing might take 100 units to correct when the product is actually in operation! Good communication between the programmers (you) and the party who originated the problem (the professor, manager, or customer) can prevent many specification errors. In general, it pays to ask questions when you don’t understand something in the program specifications. And the earlier you ask, the better. A number of questions should come to mind as you first read a programming assign- ment. What error checking is necessary? What algorithm or data structure should be used in the solution? What assumptions are reasonable? If you obtain answers to these ques- tions when you first begin working on an assignment, you can incorporate them into your design and implementation of the program. Later in the program’s development, unex- pected answers to these questions can cost you time and effort. In short, to write a pro- gram that is correct, you must understand precisely what your program is supposed to do. Sometimes specifications change during the design or implementation of a pro- gram. In such cases, a good design helps you to pinpoint which sections of the program must be redone. For instance, if a program defines and uses type StringType to imple- ment strings, changing the implementation of StringType does not require rewriting the entire program. We should be able to see from the design—either functional or TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 44. 1.3 Verification of Software Correctness | 23 Figure 1.4 This graph demonstrates the importance of early detection of software errors. Preliminary design Detailed design Code/ Debug Integrate Validate Operation Phase in which error is detected 100 50 20 10 5 2 1 Relative cost to correct error Sources • IBM • TRW • GTE • Bell Labs object-oriented—that the offending code is restricted to the module where StringType is defined. The parts of the program that require changes can usually be located more easily from the design than from the code itself. Compile-Time Errors In the process of learning your first programming language, you probably made a number of syntax errors. These mistakes resulted in error messages (for example, “TYPE MISMATCH,” “ILLEGAL ASSIGNMENT,” “SEMICOLON EXPECTED,” and so on) when you tried to compile the program. Now that you are more familiar with the programming language, you can save your debugging skills for tracking down really important logical errors. Try to get the syntax right the first time. Having your program compile cleanly on the first attempt is not an unreasonable goal. A syntax error wastes computing time and money, as well as programmer time, and it is preventable. Some programmers argue that looking for syntax errors is a waste of their time, that it is faster to let the compiler catch all the typos and syntax errors. Don’t believe them! Sometimes a coding error turns out to be a legal statement, TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 45. 24 | Chapter 1: Software Engineering Principles syntactically correct but semantically wrong. This situation may cause very obscure, hard-to-locate errors. As you progress in your college career or move into a professional computing job, learning a new programming language is often the easiest part of a new software assign- ment. This does not mean, however, that the language is the least important part. In this book we discuss abstract data types and algorithms that we believe are language inde- pendent. That is, they can be implemented in almost any general-purpose programming language. In reality, the success of the implementation depends on a thorough under- standing of the features of the programming language. What is considered acceptable programming practice in one language may be inadequate in another, and similar syntac- tic constructs may be just different enough to cause serious trouble. For this reason, it is worthwhile to develop an expert knowledge of both the control and data structures and the syntax of the language in which you are programming. In general, if you have a good knowledge of your programming language—and are care- ful—you can avoid syntax errors. The ones you might miss are relatively easy to locate and correct. Most are flagged by the compiler with an error message. Once you have a “clean” compilation, you can execute your program. Run-Time Errors Errors that occur during the execution of a program are usually more difficult to detect than syntax errors. Some run-time errors stop execution of the program. When this situation happens, we say that the program “crashed” or “terminated abnormally.” Run-time errors often occur when the programmer makes too many assumptions. For instance, result = dividend / divisor; is a legitimate assignment statement, if we can assume that divisor is never zero. If divisor is zero, however, a run-time error results. Sometimes run-time errors occur because the programmer does not fully under- stand the programming language. For example, in C++ the assignment operator is =, and the equality test operator is ==. Because they look so much alike, they often are miskeyed one for the other. You might think that this would be a syntax error that the compiler would catch, but it is actually a logic error. Technically, an assignment in C++ consists of an expression with two parts: The expression on the right of the assignment operator (=) is evaluated and the result is returned and stored in the place named on the left. The key word here is returned; the result of evaluating the right- hand side is the result of the expression. Therefore, if the assignment operator is miskeyed for the equality test operator, or vice versa, the code executes with surprising results. Let’s look at an example. Consider the following two statements: count == count + 1; if (count = 10) . . . TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 46. 1.3 Verification of Software Correctness | 25 The first statement returns false; count can never be equal to count + 1. The semi- colon ends the statement, so nothing happens to the value returned; count has not changed. In the next statement, the expression (count = 10) is evaluated, and 10 is returned and stored in count. Because a nonzero value (10) is returned, the if expres- sion always evaluates to true. Run-time errors also occur because of unanticipated user errors. For instance, if newValue is declared to be of type int, the statement cin newValue; causes a stream failure if the user inputs a nonnumeric character. An invalid file- name can cause a stream failure. In some languages, the system reports a run-time error and halts. In C++, the program doesn't halt; the program simply continues with erroneous data. Well-written programs should not stop unexpectedly (crash) or con- tinue with bad data. They should catch such errors and stay in control until the user is ready to quit. Stream Input and Output In C++, input and output are considered streams of characters. The keyboard input stream is cin; the screen output stream is cout. Important declarations relating to these streams are supplied in the library file iostream. If you plan to use the standard input and output streams, you must include this file in your program. You must also provide for access to the namespace with the using directive, #include iostream int main() { using namespace std; int intValue; float realValue; cout Enter an integer number followed by return. endl; cin intValue; cout Enter a real number followed by return. endl; cin realValue; cout You entered intValue and realValue endl; return 0; } C++ TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 47. 26 | Chapter 1: Software Engineering Principles is called the insertion operator: The expressions on the right describe what is inserted into the output stream. is called the extraction operator: Values are extracted from the input stream and stored in the places named on the right. endl is a special language feature called a manipulator; it terminates the current output line. If you are reading or writing to a file, you include fstream. You then have access to the data types ifstream (for input) and ofstream (for output). Declare variables of these types, use the open function to associate each with the external file name, and use the variable names in place of cin and cout, respectively. #include fstream int main() { using namespace std; int intValue; float realValue; ifstream inData; ofstream outData; inData.open(input.dat); outData.open(output.dat); inData intValue; inData realValue; outData The input values are intValue and realValue endl; return 0; } On input, whether from the keyboard or from a file, the operator skips leading whitespace characters (blank, tab, line feed, form feed, carriage return) before extracting the input value. To avoid skipping whitespace characters, you can use the get function. You invoke it by giving the name of the input stream, a dot, and then the function name and parameter list: cin.get(inputChar); The get function inputs the next character waiting in the input stream, even if it is a white- space character. Stream Failure The key to reading data in correctly (from either the keyboard or a file) is to ensure that the order and the form in which the data are keyed are consistent with the order and type of the identifiers on TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 48. 1.3 Verification of Software Correctness | 27 the input statement. If an error occurs while accessing an I/O stream, the stream enters the fail state, and any further references to the stream will be ignored. For example, if you misspell the name of the file that is the parameter to the function open (In.dat instead of Data.In, for example), the file input stream will enter the fail state. Alternatively, if you try to input a value when the stream is at the end of the file, the stream will enter the fail state. Your program may continue to execute while the stream remains in the fail state, but all further references to the stream will be ignored. C++ gives you a way to test the state of a stream: The stream name used in an expression returns a value that is converted to true if the state is good and to false if the stream is in the fail state. For example, the following code segment prints an error message and halts execution if the proper input file is not found: #include fstream #include iostream int main() { using namespace std; ifstream inData; inData.open(myData.dat); if (!inData) { cout File myData.dat was not found. endl; return 1; } . . . return 0; } By convention, the main function returns an exit status of 0 if execution completed normally, whereas it returns a nonzero value (above, we used 1) otherwise. The ability of a program to recover when an error occurs is called robustness. If a commercial program is not robust, people do not buy it. Who wants a word processor that crashes if the user says “SAVE” when there is no disk in the drive? We want the Robustness The ability of a program to recover follow- ing an error; the ability of a program to continue to oper- ate within its environment TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 49. 28 | Chapter 1: Software Engineering Principles program to tell us, “Put your disk in the drive, and press Enter.” For some types of software, robustness is a critical requirement. An airplane’s automatic pilot system or an intensive care unit’s patient-monitoring program cannot afford to just crash. In such situations, a defensive posture produces good results. In general, you should actively check for error-creating conditions rather than let them abort your program. For instance, it is generally unwise to make too many assumptions about the correctness of input, especially “interactive” input from a key- board. A better approach is to check explicitly for the correct type and bounds of such input. The programmer can then decide how to handle an error (request new input, print a message, or go on to the next data) rather than leave the decision to the system. Even the decision to quit should be made by a program that controls its own execution. If worse comes to worst, let your program die gracefully. Of course, not everything that the program inputs must be checked for errors. Sometimes inputs are known to be correct—for instance, input from a file that has been verified. The decision to include error checking must be based upon the requirements of the program. Some run-time errors do not stop execution but do produce the wrong results. You may have incorrectly implemented an algorithm or used a variable before it was assigned a value. You may have inadvertently swapped two parameters of the same type on a function call or forgotten to designate a function’s output data as a refer- ence parameter. (See the Parameter Passing sidebar, page 74.) These “logical” errors are often the hardest to prevent and locate. Later we will talk about debugging tech- niques to help pinpoint run-time errors. We will also discuss structured testing meth- ods that isolate the part of the program being tested. But knowing that the earlier we find an error, the easier it is to fix, we turn now to ways of catching run-time errors before run time. Designing for Correctness It would be nice if there were some tool that would locate the errors in our design or code without our even having to run the program. That sounds unlikely, but consider an analogy from geometry. We wouldn’t try to prove the Pythagorean Theorem by proving that it worked on every triangle; that result would merely demonstrate that the theorem works for every triangle we tried. We prove theorems in geometry mathematically. Why can’t we do the same for computer programs? The verification of program correctness, independent of data testing, is an impor- tant area of theoretical computer science research. Such research seeks to establish a method for proving programs that is analogous to the method for proving theorems in geometry. The necessary techniques exist, but the proofs are often more compli- cated than the programs themselves. Therefore a major focus of verification research is the attempt to build automated program provers—verifiable programs that verify TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 50. 1.3 Verification of Software Correctness | 29 5We do not go into this subject in detail here. For students who are interested in this topic, see David Gries, The Science of Programming (New York: Springer-Verlag, 1981). other programs. In the meantime, the formal verification techniques can be carried out by hand.5 Assertions An assertion is a logical pro- position that can be true or false. We can make assertions about the state of the program. For instance, with the assignment statement sum = part + 1 ; // sum and part are integers. we might assert the following: “The value of sum is greater than the value of part.” That assertion might not be very useful or interesting by itself, but let’s see what we can do with it. We can demonstrate that the assertion is true by making a logical argument: No matter what value part has (negative, zero, or positive), when it is increased by 1, the result is a larger value. Now note what we didn’t do. We didn’t have to run a pro- gram containing this assignment statement to verify that the assertion was correct. The general concept behind formal program verification is that we can make asser- tions about what the program is intended to do, based on its specifications, and then prove through a logical argument (rather than through execution of the program) that a design or implementation satisfies the assertions. Thus the process can be broken down into two steps: 1. Correctly assert the intended function of the part of the program to be verified. 2. Prove that the actual design or implementation does what is asserted. The first step, making assertions, sounds as if it might be useful to us in the process of designing correct programs. After all, we already know that we cannot write correct programs unless we know what they are supposed to do. Preconditions and Postconditions Let’s take the idea of making assertions down a level in the design process. Suppose we want to design a module (a logical chunk of the program) to perform a specific operation. To ensure that this module fits into the program as a whole, we must clarify what happens at its boundaries—that is, what must be true when we enter the module and what must be true when we exit. To make the task more concrete, picture the design module as it is eventually coded, as a function that is called within a program. To call the function, we must know its Assertion A logical proposition that can be true or false TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 51. 30 | Chapter 1: Software Engineering Principles exact interface: the name and the parameter list, which indicates its inputs and outputs. But this infor- mation isn’t enough: We must also know any assump- tions that must be true for the operation to function correctly. We call the assertions that must be true on entry into the function preconditions. The precondi- tions act like a product disclaimer: For instance, when we said that following the execution of sum = part + 1; we can assert that sum is greater than part, we made an assumption—a precondition—that part is not INT_MAX. If this precondition were violated, our asser- tion would not be true. We must also know what conditions are true when the operation is complete. The postconditions are assertions that describe the results of the opera- tion. The postconditions do not tell us how these results are accomplished; rather, they merely tell us what the results should be. Let’s consider the preconditions and postconditions for a simple operation, one that deletes the last element from a list and returns its value as an output. (We are using “list” in an intuitive sense here; we formally define it in Chapter 3.) The specification for GetLast is as follows: GetLast(ListType list, ValueType lastValue) Function: Remove the last element in the list and return its value in lastValue. Precondition: The list is not empty. Postconditions: lastValue is the value of the last element in the list, the last element has been removed, and the list length has been decremented. WARNING If you try to execute this operation when the preconditions are not true, the results are not guaranteed. Preconditions Assertions that must be true on entry into an operation or function for the postconditions to be guaranteed Postconditions Assertions that state what results are expected at the exit of an operation or function, assuming that the preconditions are true TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 52. 1.3 Verification of Software Correctness | 31 What do these preconditions and postconditions have to do with program verifica- tion? By making explicit assertions about what is expected at the interfaces between modules, we can avoid making logical errors based on misunderstandings. For instance, from the precondition we know that we must check outside of this operation for the empty condition; this module assumes that at least one element is present in the list. The postcondition tells us that when the value of the last list element is retrieved, that element is deleted from the list. This fact is an important one for the list user to know. If we just want to take a peek at the last value without affecting the list, we cannot use GetLast. Experienced software developers know that misunderstandings about interfaces to someone else’s modules are one of the main sources of program problems. We use pre- conditions and postconditions at the module or function level in this book, because the information they provide helps us to design programs in a truly modular fashion. We can then use the modules we’ve designed in our programs, confident that we are not introducing errors by making mistakes about assumptions and about what the modules actually do. Design Review Activities When an individual programmer is designing and implementing a program, he or she can find many software errors with pencil and paper. Deskchecking the design solution is a very common method of manually verifying a program. The programmer writes down essential data (variables, input values, parameters of subprograms, and so on) and walks through the design, marking changes in the data on the paper. Known trouble spots in the design or code should be double-checked. A checklist of typical errors (such as loops that do not terminate, variables that are used before they are initialized, and incorrect order of parameters on function calls) can be used to make the deskcheck more effective. A sample checklist for deskchecking a C++ program appears in Figure 1.5. Have you ever been really stuck trying to debug a program and showed it to a classmate or colleague who detected the bug right away? It is generally acknowledged that someone else can detect errors in a program better than the original author can. In an extension of deskchecking, two programmers can trade code listings and check each other’s programs. Universities, however, frequently discourage students from examining each other’s programs for fear that this exchange will lead to cheating. Thus many stu- dents become experienced in writing programs but don’t have much opportunity to practice reading them. Teams of programmers develop most sizable computer programs. Two extensions of deskchecking that are effectively used by programming teams are design or code walk- throughs and inspections. The intention of these formal team activities is to move the responsibility for uncovering bugs from the individual programmer to the group. Deskchecking Tracing an execution of a design or pro- gram on paper Walk-through A verification method in which a team performs a manual simulation of the program or design Inspection A verification method in which one member of a team reads the program or design line by line and the other members point out errors TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 53. 32 | Chapter 1: Software Engineering Principles The Design 1. Does each module in the design have a clear function or purpose? 2. Can large modules be broken down into smaller pieces? (A common rule of thumb is that a C++ function should fit on one page.) 3. Are all the assumptions valid? Are they well documented? 4. Are the preconditions and postconditions accurate assertions about what should be happening in the module they specify? 5. Is the design correct and complete as measured against the program specification? Are there any missing cases? Is there faulty logic? 6. Is the program designed well for understandability and maintainability? The Code 7. Has the design been clearly and correctly implemented in the programming language? Are fea- tures of the programming language used appropriately? 8. Are all output parameters of functions assigned values? 9. Are parameters that return values marked as reference parameters (have to the right of the type if the parameter is not an array)? 10. Are functions coded to be consistent with the interfaces shown in the design? 11. Are the actual parameters on function calls consistent with the parameters declared in the func- tion prototype and definition? 12. Is each data object to be initialized set correctly at the proper time? Is each data object set before its value is used? 13. Do all loops terminate? 14. Is the design free of “magic” numbers? (A “magic” number is one whose meaning is not immedi- ately evident to the reader.) 15. Does each constant, type, variable, and function have a meaningful name? Are comments included with the declarations to clarify the use of the data objects? Figure 1.5 Checklist for deskchecking a C++ program Because testing is time consuming and errors cost more the later they are discovered, the goal is to identify errors before testing begins. In a walk-through, the team performs a manual simulation of the design or program with sample test inputs, keeping track of the program’s data by hand on paper or on a blackboard. Unlike thorough program testing, the walk-through is not intended to simu- late all possible test cases. Instead, its purpose is to stimulate discussion about the way the programmer chose to design or implement the program’s requirements. At an inspection, a reader (not the program’s author) goes through the design or code line by line. Inspection participants point out errors, which are recorded on an TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 54. 1.3 Verification of Software Correctness | 33 inspection report. Some errors are uncovered just by the process of reading aloud. Oth- ers may have been noted by team members during their preinspection preparation. As with the walk-through, the chief benefit of the team meeting is the discussion that takes place among team members. This interaction among programmers, testers, and other team members can uncover many program errors long before the testing stage begins. At the high-level design stage, the design should be compared to the program requirements to make sure that all required functions have been included and that this program or module correctly “interfaces” with other software in the system. At the low- level design stage, when the design has been filled out with more details, it should be reinspected before it is implemented. When the coding has been completed, the com- piled listings should be inspected again. This inspection (or walk-through) ensures that the implementation is consistent with both the requirements and the design. Successful completion of this inspection means that testing of the program can begin. For the last 20 years, the Software Engineering Institute at Carnegie Mellon Univer- sity has played a major role in supporting research into formalizing the inspection process in large software projects, including sponsoring workshops and conferences. A paper presented at the SEI Software Engineering Process Group (SEPG) Conference reported on a project that was able to reduce the number of product defects by 86.6% by using a two-tiered inspection process of group walk-throughs and formal inspec- tions. The process was applied to packets of requirements, design, or code at every stage of the life cycle. Table 1.2 shows the defects per 1,000 source lines of code (KSLOC) that were found in the various phases of the software life cycle in a maintenance project. This project added 40,000 lines of source code to a software program of half a million lines of code. The formal inspection process was used in all of the phases except testing activities. Looking back at Figure 1.4, you can see that the cost of fixing an error is relatively cheap until you reach the coding phase. After that stage, the cost of fixing an error increases dramatically. Using the formal inspection process clearly benefited this project. These design-review activities should be carried out in as nonthreatening a manner as possible. The goal is not to criticize the design or the designer, but rather to remove defects in the product. Sometimes it is difficult to eliminate the natural human emotion of pride from this process, but the best teams adopt a policy of egoless programming. Table 1.2 Defects Found in Different Phases* Stage KSLOC System Design 2 Software Requirements 8 Design 12 Code Inspection 34 Testing Activities 3 *Dennis Beeson, Manager, Naval Air Warfare Center, Weapons Division, F-18 Software Development Team. TEAM LinG - Live, Informative, Non-cost and Genuine!
  • 55. Random documents with unrelated content Scribd suggests to you:
  • 56. In gauntlet and in thew: He rides the highway of his cause To die or do. His purpose leads him, like a flame, Through forest and through fen, To castle walls of wrong and shame And blood-stained men. Hope's are the lips that wind the horn Before the gates of lust: Though fifty dragons hiss him scorn, Still will he trust. Strength's is the hand that thunders at The entrances of night: Though ten-score demons crush him flat Still will he fight. Love's is the heart that finds a way To dungeons vast of sin: A thousand deaths may rise to slay, Still will he win.
  • 58. I met him here at Ammendorf one spring. It was the end of April and the Harz, Treed to their ruin-crested summits, seemed One pulse of tender green and delicate gold, Beneath a heaven that was like the face Of girlhood waking into motherhood. Along the furrowed meadow, freshly ploughed, The patient oxen, loamy to the knees, Plodded or lowed or snuffed the fragrant soil; And in each thorn-tree hedge the wild bird sang A song to spring, full of its own wild self And soul, that heard the blossom-laden May's Heart beating like a star at break of day, As, kissing red the roses, she drew near, Her mouth's ripe rose all dewdrops and perfume. Here at this inn and underneath this tree We took our wine, the morning prismed in its Flame-crystalled gold.—A goodly vintage that! Tang with the ripeness of full twenty years. Rare! I remember! wine that spurred the blood, That brought the heart glad to the songful lip, And made the eyes unlatticed casements whence A man's true soul smiled, breezy as the blue. As royal a Rhenish, I will vouch to say, As that, old legends tell, which Necromance And Magic keep, gnome-guarded, in huge casks Of antique make deep in the Kyffhäuser, Webbed, frosty gray, with salt-petre and mold, The Cellar of the Knights near Sittendorf.— So solaced by that wine we sat an hour He told me his intent in coming here. His name was Rudolf; and his native place, Franconia; but no word of parentage: Only his mind to don the buff and green A d li f t ith d b
  • 59. And live a forester with us and be Enfellowed in the Duke of Brunswick's train, And for the Duke's estate even now was bound. Tall was he for his age and strong and brown, And lithe of limb; and with a face that seemed Hope's counterpart—but with the eyes of doubt: Deep stealthy disks, instinct with starless night, That seemed to say, We're sure of Earth—at least For some short while, my friend; but afterward— Nay! ransack not to-morrow till to-day Lest it engulf thy joy before it is!— And when he spoke, the fire in his eyes Worked restless as a hunted animal's; Or like the Count von Hackelnburg's,—the eyes Of the Wild Huntsman,—his that turn and turn Feeling the unseen presence of a fiend. And then his smile! a thrust-like thing that curled His lips with heresy and incredible lore When Christ's or th' Virgin's holy name was said, Exclaimed in reverence or admonishment: And once he sneered,—What is this God you mouth, Employ whose name to bless yourselves or damn? A curse or blessing?—It hath passed my skill T' interpret what He is. And then your faith— What is this faith that helps you unto Him? Distinguishment unseen, design unlawed. Why, earth, air, fire, and water, heat and cold, Hint not at Him: and man alone it is Who needs must worship something. And for me— No God like that whom man hath kinged and crowned! Rather your Satan cramped in Hell—the Fiend! God-countenanced as he is, and tricked with horns. No God for me, bearded as Charlemagne, Throned on a tinsel throne of gold and jade, Earth's pygmy monarchs imitate in mien
  • 60. Earth s pygmy monarchs imitate in mien And mind and tyranny and majesty, Aping a God in a sonorous Heaven. Give me the Devil in all mercy then, Bad as he is! for I will none of such! And laughed an oily laugh of easy jest To bow out God and let the Devil in. And grasped of both wild hands, swung trenchant. Page 285 Accolon of Gaul [See larger version]
  • 61. Then, as it chanced, old Kurt had come that morn With some six of his jerkined foresters From the Thuringian forest; wet with dew, And fresh as morn with early travel; bound For Brunswick, Dummburg and the Hakel passed. Chief huntsman he then to our lord the Duke, And father of the loveliest maiden here In Ammendorf, the sunny Ilsabe: Her mother dead, the gray-haired father prized His daughter more than all that men hold dear; His only happiness, who was beloved Of all as Lora of Thuringia was, For gentle ways that spoke a noble soul, Winning all hearts to love her and to praise, As might a great and beautiful thought that holds Us by the simplest words.—Blue were her eyes As the high glory of a summer day. Her hair,—serene and braided over brows White as a Harz dove's wing,—an auburn brown, And deep as mists the sun has drenched with gold: And her young presence, like embodied song, Filled every heart she smiled on with sweet calm, Like some Tyrolean melody of love, Heard on an Alpine path at close of day When homing shepherds pipe to tinkling flocks: Being with you a while, so, when she left,— How shall I say it?—'twas as when one hath Beheld an Undine on the moonlit Rhine, Who, ere the mind adjusts a thought, is gone, And to the soul it seems it was a dream. Some thirty years ago it was;—and I, Commissioner of the Duke—(no sinecure I can assure you)—had scarce reached the age Of thirty,—that we sat here at our wine; A d 't th h th t R d lf h t fi t
  • 62. And 'twas through me that Rudolf,—whom at first, From some rash words dropped then in argument, The foresterhood was like to be denied,— Was then enfellowed. Yes, said I, he's young. Kurt, he is young: but look you! what a man! What arms! what muscles! what a face—for deeds! An eye—that likes me not; too quick to turn!— But that may be the restless soul within: A soul perhaps with virtues that have been Severely tried and could not stand the test; These be thy care, Kurt: and if not too deep In vices of the flesh, discover them, As divers bring lost riches up from ooze.— Thou hast a daughter; let him be thy son. A year thereafter was it that I heard Of Rudolf's passion for Kurt's Ilsabe; Then their betrothal. And it was from this,— (How her fair memory haunts my old heart still!— Sweet Ilsabe! whose higher womanhood, True as the touchstone which philosophers feign Transmutes to gold base metals it may touch, Had turned to good all evil in this man,)— Surmised I of the excellency which Refinement of her purer company, And contact with her innocence, had resolved His fiery nature to, conditioning slave. And so I came from Brunswick—as, you know, Is custom of the Duke or, by his seal Commissioned proxy, his commissioner— To test the marksmanship of Rudolf, who Succeeded Kurt with marriage of his child, An heir of Kuno.—He?—Great-grandfather To Kurt; and of this forest-keepership The first possessor; thus established here— Or this the tale they tell on winter nights:—
  • 63. Kuno, once in the Knight of Wippach's train, Rode on a grand hunt with the Duke, who came,— Grandfather of the father of our Duke,— With much magnificence of knights and squires, Great velvet-vestured nobles, cloaked and plumed, To hunt Thuringian deer. Then morn,—so rathe To bid good-morrow to the husbandman Heavy with slumber,—was too slow for these, And on the wind-trod hills recumbent yawned Aroused an hour too soon: ashamed, disrobed, Rubbed the stiff sleep from eyes that still would close; Like some young milkmaid whom the cock hath waked, Who sits within her loft and, half asleep, Stretches and hears the house below her stir, Yet sits and yawns, reluctant still to rise.— Horns sang and deer-hounds tugged a whimpering leash, Or, loosened, bounded through the baying glens: And ere the mountain mists, compact of white, Broke wild before the azure spears of day, The far-off hunt, that woke the woods to life, Seemed but the heart-beat of the ancient hills. And then, near noon, within a forest brake, The ban-dogs roused a red gigantic stag, Lashed to whose back with gnarly-knotted cords, And borne along like some pale parasite, A man shrieked: tangle-bearded, and his hair A mane of forest-burrs. The man himself, Emaciated and half-naked from The stag's mad flight through headlong rocks and trees, One bleeding bruise, his eyes two holes of fire. For such the law then: when the peasant chased Or slew the dun deer of his tyrant lords, If caught, as punishment the withes and spine Of some strong stag, a gift to him of game Enough till death—death in the antlered herd,
  • 64. oug t deat deat t e a t e ed e d, Or slow starvation in the haggard hills. Then was the great Duke glad, and forthwith cried To all his hunting-train a rich reward For him who slew the stag and saved the man, But death for him who slew both man and beast. So plunged the hunt after the hurrying slot, A shout and glimmer through the sounding woods,— Like some wild torrent that the hills have loosed, Death for its goal.—'Twas late; and none had yet Risked that hard shot,—too desperate the risk Beside the poor life and a little gold,— When this young Kuno, with hot eyes, wherein Hunt and impatience kindled reckless flame, Cried, Has the dew made wet each powder-pan? Or have we left our marksmanship at home? Here's for its heart! the Fiend direct my ball!— And fired into a covert packed with briers, An intertangled wall of matted night, Wherein the eye might vainly strive and strive To pierce one fathom, gaze one foot beyond: But, ha! the huge stag staggered from the brake, Heart-hit, and fell: and that wan wretch, unbound, Rescued, was cared for. Then his grace, the Duke, Charmed with the eagle aim, called Kuno up, And there to him and his forever gave The forest-keepership. But envious tongues Were soon at wag; and whispered went the tale Of how the shot was free; and how the balls Used by young Kuno were free bullets—which To say is: Lead by magic molded, in The presence and directed of the Fiend. Of some effect these tales, and of some force Even with the Duke, who lent an ear so far As to ordain Kuno's descendants all
  • 65. To proof of skill ere their succession to The father's office. Kurt himself hath shot The silver ring out o' the popinjay's beak— A good shot he, you see, who would succeed. The Devil guards his secrets close as God. For who can say what elementaries, Demonic, lurk in desolate dells and hills And shadowy woods? malignant forces who, Malicious vassals of satanic power, Are agents to that Evil none may name, Who signs himself, through these, a slave to those, Those mortals who call in the aid of Hell, And for some earthly, transitory gift, Barter their souls and all their hopes of Heaven. Of these enchanted bullets let me speak: There may be such: our earth hath things as strange, Perhaps, and stranger, that we doubt not of, While we behold,—not only 'neath the thatch Of Ignorance's hovel,—but within The stately halls of Wisdom's palaces, How Superstition sits an honored guest. A cross-way, so they say, among the hills; A cross-way in a solitude of pines; And on the lonely cross-way you must draw A bloody circle with a bloody sword; And round the circle, runic characters, Weird and symbolic: here a skull, and there A scythe, and cross-bones, and an hour-glass here: And in the centre, fed with coffin-wood, Stolen from the grave of—say a murderer, A fitful fire. Eleven of the clock The first ball leaves the mold—the sullen lead Mixed with three bullets that have hit their mark, And blood the wounded Sacramental Host
  • 66. And blood the wounded Sacramental Host, Stolen, and hence unhallowed, oozed when shot Fixed to a riven pine. Ere midnight strike, With never a word until that hour sound, Must all the balls be cast; and these must be In number three and sixty; three of which The Fiend's dark agent, demon Sammael, Claims for his master and stamps for his own To hit aside their mark, askew for harm. The other sixty shall not miss their mark. No cry, no word, no whisper, even though Vague, gesturing shapes, that loom like moonlit mists, Their faces human but of animal form, Whinnying and whining lusts, faun-faced, goat-formed, Rise thick around and threaten to destroy. No cry, no word, no whisper should there come, Weeping, a wandering shadow like the girl You love, or loved, now lost to you, her eyes Hollow with tears; sad, palely beckoning With beautiful arms, or censuring; her face Wild with despondent love: who, if you speak Or waver from that circle—hideous change!— Shrinks to a wrinkled hag, whose harpy hands Shall tear you limb from limb with horrible mirth. Nor be deceived if some far midnight bell Strike that anticipated hour; nor leave By one short inch the circle, for, unseen Though now they be, Hell's minions still are there, Watching with flaming eyes to seize your soul. But when the hour of midnight sounds, will come A noise of galloping hoofs and outriders, Shouting: six midnight steeds,—their nostrils, pits Of burning blood,—postilioned, roll a stage, Black and with groaning wheels of spinning fire: Room there!—What, ho!—Who bars the mountain way?— On over him!—But fear not nor fare forth;
  • 67. On over him! But fear not, nor fare forth; 'Tis but the last trick of your bounden slave. And ere the red moon rushes from the clouds And dives again, high the huge leaders leap, Their fore-hoofs flashing and their eyeballs flame, And, spun a spiral spark into the night, Hissing the phantasm flies and fades away. Some say there comes no stage; that Hackelnburg, Wild-Huntsman of the Harz, comes dark as storm, With rain and wind and demon dogs of Hell; The terror of his hunting-horn, an owl, And the dim deer he hunts, rush on before: The forests crash, and whirlwinds are the leaves, And all the skies a-thunder, as he hurls, Straight on the circle, horse and hounds and stag. And at the last, plutonian-cloaked, there comes,— Infernal fire streaming from his eyes,— Upon a stallion gaunt and lurid black, The minister of Satan, Sammael, Who greets you, and informs you, and assures. Enough! these wives' tales told, to what I've seen: To Ammendorf I came; and Rudolf here With Kurt and his assembled men in buff And woodland green were gathered at this inn. The abundant Year—like some sweet wife,—a-smile At her brown baby, Autumn, in her arms, Stood 'mid the garnered harvests of her fields Dreaming of days that pass like almoners Scattering their alms in minted gold of flowers; Of nights, that forest all the skies with stars, Wherethrough the moon—bare-bosomed huntress—rides, One cloud before her like a flying fawn. Then I proposed the season's hunt; till eve The test of Rudolf's skill postponed; at which He seemed embarrassed. And 'twas then I heard How he an execrable marksman was;
  • 68. o e a e ec ab e a s a as; And tales that told of close, incredible shots, That missed their mark; or how the flint-lock oft Flamed harmless powder, while the curious deer Stood staring, as in pity of such aim, Or as inviting him to try once more. Howbeit, he that day acquitted him Of all this gossip; in that day's long hunt Missing no shot, however rashly made Or distant through the intercepting trees. And the piled, various game brought down of all Good marksmen of Kurt's train had not sufficed, Doubled, nay, trebled, there to match his heap. And marvelling the hunters saw, nor knew How to excuse them. My indulgence giv'n, Some told me that but yesterday old Kurt Had made his daughter weep and Rudolf frown, By vowing end to their betrothéd love, Unless that love developed better skill Against the morrow's test; his ancestors' High fame should not be tarnished. So he railed; Then bowed his gray head and sat moodily: But, looking up, forgave all when he saw Tears in his daughter's eyes and Rudolf gone Out in the night, black with approaching storm. Before this inn, crowding the green, they stood, The holiday village come to view the trial: Fair maidens and their comely mothers with Their sweethearts and their husbands. And I marked Kurt and his daughter here; his florid face All creased with smiles at Rudolf's great success; Hers, radiant with happiness; for this Her marriage eve—so had her father said— Should Rudolf come successful from the hunt. So pleased was I with what I'd seen him do, The trial of skill superfluous seemed; and so
  • 69. Was on the bare brink of announcing, when Out of the western heaven's deepening red,— Like a white message dropped of scarlet lips,— A wild dove clove the luminous winds and there, Upon that limb, a peaceful moment sat. Then I, Thy rifle, Rudolf! pierce its head! Cried pointing, and chief-forester art thou! Why did he falter with a face as strange And strained as terror's? did his soul divine What was to be, with tragic prescience?— What a bad dream it all seems now!—Again I see him aim. Again I hear her cry, My dove! O Rudolf, do not kill my dove! And from the crowd, like some sweet dove herself, A fluttering whiteness, rushed our Ilsabe— Too late! the rifle cracked.... The unhurt dove Rose, beating frightened wings—but Ilsabe!... My God! the sight!... fell smitten; sudden red, Sullying the whiteness of her bridal bodice, Showed where the ball had pierced her innocent heart. And Rudolf?—Ah, of him you still would know? —When he beheld this thing which he had done, Why, he went mad—I say—but others not. An hour he raved of how her life had paid For the unholy missiles he had used, And how his soul was three times lost and damned. I say that he went mad and fled forthwith Into the haunted Harz.—Some say, to die The prey of demons of the Dummburg ruin. I,—one of those less superstitious,—say, He in the Bodé—from that blackened rock,— Whereon were found his hunting-cap and horn,— The Devil's Dancing Place, did leap and die.
  • 71. I And now once more we stood within the walls Of that old manor near the riverside; Dead leaves lay rotting in its empty halls, And here and there the ivy could not hide The year-old scars, made by the Royalists' balls, Around the doorway, where so many died In that last effort to defend the stair, When Rupert, like a demon, entered there. II The basest Cavalier who e'er wore spurs Or drew a sword, I count him; with his grave Eyes 'neath his plumed hat like a wolf's whom curs Rouse, to their harm, within a forest cave; And hair like harvest; and a voice like verse For smoothness. Ay, a handsome man and—brave!— Brave?—who would question it! yea! tho' 'tis true He warred with one weak woman and her few. III Lady Isolda of the Moated Manse, Whom here, that very noon, it happened me To meet near her old home. A single glance Showed me 'twas she. I marveled much to see How lovely still she was! as fair, perchance, As when Red Rupert thrust her brutally,— Her long hair loosened,—down the shattered stair,
  • 72. And cast her, shrieking, 'mid his followers there. IV She is for you! Take her! I promised it! Take her, my bullies!—shouting so, he flung Her in their midst. Then, on her poor hands (split, And beaten by his dagger when she clung Resisting him) and knees, she crept a bit Nearer his feet and begged for death. No tongue Can tell the way he turned from her and cursed, Then bade his men draw lots for which were first. V I saw it all from that low parapet, Where, bullet-wounded in the hip and head, I lay face-upward in the whispering wet, Exhausted 'mid the dead and left for dead. We had held out two days without a let Against these bandits. You could trace with red From room to room how we resisted hard Since the great door crashed in to their petard. VI The rain revived me, and I leaned with pain And saw her lying there, pale, soiled and splashed And miserable; on her cheek a stain, A dull red bruise, made when his mad hand dashed And struck her to the stones; the wretched rain Dripped from her dark hair; and her hands were gashed.— Oh f k t t l
  • 73. Oh, for a musket or a petronel With which to send his devil's soul to hell! VII But helpless there I lay, no weapon near, Only the useless sword I could not reach His traitor's heart with, while I chafed to hear The laugh, the insult and the villain speech Of him to her.—Oh, God! could I but clear The height between and, hanging like a leech, My fingers at his throat, tear out his base Vile tongue! yea, tear, and lash it in his face! VIII But, badly wounded, what could I but weep With rage and pity of my helplessness And her misfortune! Could I only creep A little nearer so that she might guess I was not dead; that I my life would keep, Dedicate to revenge!—Oh, the distress Of that last moment when, half-dead, I saw Them mount and bear her swooning through the shaw. IX Long time I lay unconscious. It befell Some woodsmen found me, having heard the sound Of fighting cease that, for two days, made hell Of that wild region; ventured on the ground For plunder: and it had not then gone well With me I fear had not their leader found
  • 74. With me, I fear, had not their leader found That in some way I would repay his care; So bore me to his hut and nursed me there. X How roughly kind he was! For weeks I hung 'Twixt life and death; health, like a varying, sick And fluttering pendulum, now this way swung, Now that, until at last its querulous tick Beat out life's usual time, and slowly rung The long, loud hours, that exclaimed, Be quick!— Arise!—Go forth!—Hear how her black wrongs call!— Make them the salve to cure thy wounds withal!— XI They were my balsam: for, ere autumn came, Weak still, but over eager to be gone, I took my leave of him. A little lame From that hip wound, and somewhat thin and wan, I sought the village. Here I heard her name And shame's made one. How Rupert passed one dawn; How she among his troopers rode—astride Like any man—pale-faced and feverish-eyed. XII Which way these took they pointed, and I went Like fire after. Oh, the thought was good That they were on before! And much it meant To know she lived still; she, whose image stood Like flame before me making turbulent
  • 75. Like flame before me, making turbulent Each heart-beat with her wrongs, that were fierce food Unto my hate that, Courage! cried, Rest not! Think of her there, and let thy haste be hot! XIII But months went by and still I had not found: Yet, here and there, as wearily I sought, I caught some news: how he had held his ground Against the Roundhead troops; or how he'd fought Then fled—returned and conquered. Like a hound Questing a boar, I followed; but was brought No nearer to my quarry. Day by day It seemed that Satan kept him from my way. XIV A woman rode beside him, so they said, A fair-faced wanton, mounted like a man— Isolda!—my Isolda!—Better dead, Yea, dead and damned! than thus—the courtezan Bold, unreluctant, to such men! A dread, That such should be, unmanned me. Doubt began To whisper at my heart.—But I was mad, To insult her with such thoughts, whose love I had. XV At last one day I rested in a glade Near that same woodland which I lay in when Sore wounded: and, while sitting in the shade Of an old beech—what! did I dream? or men
  • 76. O a o d beec at d d d ea o e Like Rupert's own ride near me? and a maid— Isolda or her double!—Wildly then I rose and, shouting, leapt upon my horse; Unsheathed my sword and rode across their course. XVI Mainly I looked for Rupert, and by name Challenged him forth:—Dog! dost thou hide behind?— Insulter of women! Coward! save where shame And rapine call thee! God at last is kind, And my sword waits!—Like an upbeating flame, My voice rose to a windy shout; and blind I seemed to sit, till, with an outstretched hand, Isolda rode before me from that band. XVII Gerald! she cried; not as a soul surprised With gladness that the loved, deemed dead, still lives; But like the soul that long hath realized Only misfortune and to fortune gives No confidence, though it be recognized As good. She spoke: Lo, we are fugitives. Rupert is slain. And I am going home. Then like a child asked simply, Wilt thou come?... XVIII Oh, I have suffered, Gerald! Oh, my God! What shame! What torture! Once my soul was clean— Stained and defiled behold it!—I have trod
  • 77. Sad ways of hell and horror. I have seen And lived all depths of lust. Yet, oh, my God! Blameless I hold myself of what hath been, Though through it all, yea,—this thou too must know,— I loved him, my betrayer and thy foe! XIX Sobbing she spoke as if but half awake, Her eyes far-fixed beyond me, far beyond All hope of mine.—So! it was for his sake, His love, that she had suffered!... Blind and fond, For what return!... And I—to nurse a snake, And never dream its nature would respond With some such fang of venom! 'Twas for this That I had ventured all—to find her his! XX At first half-stunned I stood; then blood and brain, Like two stern judges, who had slept, awoke, Rose up and thundered, Slay her! Every vein And nerve responded, Slay her at a stroke!— And I had done it, but my heart again, Like a strong captain in a tumult, spoke, And the fierce discord fell. And quietly I sheathed my sword and said, I'll go with thee. XXI But this was my reward for all I'd borne, My loyalty and love! To see her eyes ll f f h h h h k
  • 78. Hollow from tears for him; her thin cheeks worn With grief for him; to know them all for lies, Her vows of faith to me; to come forlorn, Where I had hoped to come on Paradise, On Hell's black gulf; and, as if not enough, Soiled as she was and outcast, still to love! XXII Then rode one ruffian from the rest, clay-flecked From spur to plume with hurry; seized my rein, And—What art thou, demanded, who hast checked Our way and challenged?—Then, with some disdain, Isolda, Sir, my kinsman did expect Your captain here. What honor may remain To me I pledge for him. Hold off thy hands! He but attends me to the Moated Manse. XXIII We rode in silence. And at evening came Unto the Moated Manse.—Great clouds had grown Up in the west, on which the sunset's flame Lay like the hand of slaughter.—Very lone Its rooms and halls: a splintered door that, lame, Swung on one hinge; a cabinet o'erthrown; Or arras torn; or blood-stain turning wan, Showed us the way the battle once had gone. XXIV We reached the tower-chamber towards the west, In hich on that da k da she tho ght to hide
  • 79. In which on that dark day she thought to hide From Rupert when, at last, 'twas manifest We could not hold the Manse. There was no pride In her deep eyes now; nor did scorn invest Her with such dignity as once defied Him bursting in to find her standing here Prepared to die like some dog-hunted deer. XXV She took my hand, and, as if naught of love Had ever been between us, said,—All know The madness of that hour when with his glove He struck, then slew my brother, and brought woe On all our house: and thou, incensed above The rest, came here, and made my foe thy foe. But he had left. 'Twas then I promised thee My hand, but, ah! my heart was gone from me. XXVI Yea, he had won me, this same Rupert, when He was our guest.—Thou know'st how gallantry And recklessness make heroes of most men To us weak women!—And so secretly I vowed to be his wife. It happened then My brother found him in some villainy; The insult followed: Guy was killed ... and thou Dost still remember how I made a vow.— XXVII But still this man pursued me and I held
  • 80. But still this man pursued me, and I held Firm to my vow, albeit I loved him still, Unknown to all, with all the love unquelled Of first impressions, and against my will. At last despair of winning me compelled Him to the oath he swore: He would not kill, But take me living and would make my life A living death. No man should make me wife. XXVIII The war, that now consumes us, did, indeed, Give him occasion.—I had not been warned, When down he came against me in the lead Of his marauders. With thy help I scorned His mad attacks two days. I would not plead Nor parley with him, who came hoofed and horned, Like Satan's self in soul, and, with Hell's aid, Took this strong house and kept the oath he made. XXIX Months passed. Alas! it needs not here to tell What often thou hast heard: Of how he led His ruffians here now there; or what befell Me of dishonor. Oft I wished me dead, Loathing my life,—than which the nether Hell Hath less of horror!—So we fought or fled From place to place until a year had passed, And Parliament forces hemmed us in at last. XXX
  • 81. Yea, I had only lived for this—to right With death my wrongs sometime. And love and hate Contended in my bosom when, that night Before the fight that should decide our fate, I entered where he slept. There was no light Save of the stars to see by. Long and late I leaned above him there, yet could not kill— Hate raised the dagger but love held it still. XXXI The woman in me conquered. What a slave To our emotions are we! To relent At this long-waited moment!—Wave on wave Of pitying weakness swept me, and I bent— And kissed his face. Then prayed to God; and gave My trust to God; and left to God th' event.— I never looked on Rupert's face again, For in the morning's combat—he was slain. XXXII Out of defeat escaped some scant three score Of all his followers. And night and day We fled; and while the Roundheads pressed us sore, And in our road, good as a fortress, lay The Moated Manse,—where our three-score or more Might well hold out,—I pointed them the way. And we are come, amid its wrecks to end The crime begun here.—Thou must go, my friend! XXXIII
  • 82. XXXIII Go quickly! For the time approaches when Destruction must arrive.—Oh, well I know All thou wouldst say to me.—What boots it then?— I tell thee thou must go! that thou must go!— Yea, dost thou think I'd have thee die 'mid men Like these, for such an one as I?—No! no!— Thy life is clean. Thou shalt not cast away Thy clean life for my soiled one! ... I will stay! XXXIV I said.—Then spoke ... I know not what it was. And seized her hand and kissed it and then said,— Thou art my promised wife. Thou hast no cause That is not mine. I love thee. We will wed. Isolda, come!—A moment did she pause, Then shook her head and sighed, My heart is dead. This can not be. Behold, that way is thine. I will not let thee share the way that's mine. XXXV Then turning from me ere I could prevent Passed like a shadow from the shadowy room, Leaving my soul in shadow.... Naught was meant By my sweet flower of love then! bloom by bloom I'd watched it wither; then its fragrance went, And dust it was now.... It was dark as doom, And bells seemed ringing far off in the rain, When from that house I turned my face again.
  • 83. XXXVI Then in the night a trumpet; and the dull Close thud of horse and clash of spurs and arms; And glimmering helms swept by me.—Sorrowful I stood and waited till against the storm's Black breast, the Manse,—a burning carbuncle,— Blazed like a battle-beacon, and alarms Of onslaught clanged around it.—Then, like one, Who bears with him God's curse, I galloped on.
  • 84. AN OLD TALE RETOLD
  • 85. From the terrace here, where the hills indent, You can see the uttermost battlement Of the castle there: the Clifford's home Where the seasons go and the seasons come And never a footstep else doth fall Save the prowling fox's; the ancient hall Echoes no voice save the owlet's call: Its turret chambers are homes for the bat; And its courts are tangled and wild to see; And where in the cellar was once the rat, The viper and toad move stealthily. Long years have passed since the place was burned, And he sailed to the wars in France and earned The name that he bears of the bold and true On his tomb.—Long years, since my lord, Sir Hugh, Lived, and I was his favorite page, And the thing then happened; and he of an age When a man will love and be loved again, Or off to the wars or a monastery; Or toil till he deaden his heart's hard pain; Or drink and forget it and finally bury. I was his page. And often we fared Through the Clare demesne, in autumn, hawking— If the Baron had known, how they would have glared, 'Neath their bushy brows, those eyes of mocking!— That last of the Strongbows, Richard, I mean— And growling some six of his henchmen lean To mount and after this Clifford and hang With his crop-eared page to the nearest oak, How he would have cursed us while he spoke! For Clare and Clifford had ever a fang In the other's side.... And I hear the clang Of his rage in the hall when the hawker told— If he told!—how we met on the autumn wold Hi d ht t Cl f Cl th d
  • 86. His daughter, sweet Clara of Clare, the day Her hooded tiercel its brails did burst, And trailing its jesses, came flying our way— An untrained haggard the falconer cursed While he tried to secure:—as the eyas flew Slant, low and heavily over us, Hugh,— Who saw it coming, and had just then cast His peregrine hawk at a heron quarry,— In his saddle rising thus, as it passed By the jesses caught, and to her did carry, Where she stood near the wood. Her face flushed rose With the glad of the meeting.—No two foes Her eyes and my lord's, I swear, who saw 'Twas love from the start.—And I heard him speak; Dismount, then kneel—and the sombre shaw, With the sad of the autumn waste and bleak, Grew spring with her smile, as the hawk she took On her slender wrist, where it pruned and shook Its callowness. Then I saw him seize The hand that she reached to him, long and white, As she smilingly bade him rise from his knees— When he kissed her fingers her eyes grew bright. But her cheeks were pallid when, lashing through The thicket there, his face a-flare With the sting of the wind, and his gipsy hair Flying, the falconer came, and two Or three of the people of Castle Clare. And the leaves of the autumn made a frame For the picture there in the morning's flame. What was said in that moment I do not know, That moment of meeting between those lovers: Whatever it was, 'twas whispered low, Soft as a leaf that swings and hovers, A twinkling gold, when the woods are yellow. And her face with the joy was still aglow When out of the wood that burly fellow
  • 87. When out of the wood that burly fellow Came with his frown, and made a pause In the pulse of their words.—My lord, Sir Hugh, Stood with the soil on his knee. No cause Had he, but his hanger he partly drew, Then clapped it sharp in its sheath again, And bowed to my lady, and strode away; And vaulting his horse, with a loosened rein Rode with a song in his heart all day. He loved and was loved, I knew; for, look! All other sports for the chase he forsook. And strange that he never went to hawk, Or hunt, but Clara would meet him there In the Strongbow forest!—I know the rock, With its ferns and its moss, by the bramble lair, Where oft and often he met—by chance, Shall I say?—the daughter of Clare; as fair Of face as a queen in an old romance, Who waits expectant and pale; her hair Night-deep; and eyes dove-gray with dreams;— By the fountain-side where the statue gleams And the moonbeam lolls in the lily white,— For her knightly lover who comes at night. Heigh-ho! they ceased, those meetings. I wot, Betrayed to the Baron by some of his crew Of menials who followed and saw and knew. For she loved too well to have once forgot The time and the place of their trysting true. Why and when? would ask Sir Hugh In the labored letters he used to lock —The lovers' post—in a coigne of that rock. She used to answer, but now did not. But, nearing Yule, love gat them again A twilight tryst—through frowardness sure!— They met. And the day was gray with rain,
  • 88. Welcome to our website – the perfect destination for book lovers and knowledge seekers. We believe that every book holds a new world, offering opportunities for learning, discovery, and personal growth. That’s why we are dedicated to bringing you a diverse collection of books, ranging from classic literature and specialized publications to self-development guides and children's books. More than just a book-buying platform, we strive to be a bridge connecting you with timeless cultural and intellectual values. With an elegant, user-friendly interface and a smart search system, you can quickly find the books that best suit your interests. Additionally, our special promotions and home delivery services help you save time and fully enjoy the joy of reading. Join us on a journey of knowledge exploration, passion nurturing, and personal growth every day! ebookbell.com