
- •Contents
- •Introduction
- •Who This Book Is For
- •What This Book Covers
- •How This Book Is Structured
- •What You Need to Use This Book
- •Conventions
- •Source Code
- •Errata
- •p2p.wrox.com
- •The Basics of C++
- •The Obligatory Hello, World
- •Namespaces
- •Variables
- •Operators
- •Types
- •Conditionals
- •Loops
- •Arrays
- •Functions
- •Those Are the Basics
- •Diving Deeper into C++
- •Pointers and Dynamic Memory
- •Strings in C++
- •References
- •Exceptions
- •The Many Uses of const
- •C++ as an Object-Oriented Language
- •Declaring a Class
- •Your First Useful C++ Program
- •An Employee Records System
- •The Employee Class
- •The Database Class
- •The User Interface
- •Evaluating the Program
- •What Is Programming Design?
- •The Importance of Programming Design
- •Two Rules for C++ Design
- •Abstraction
- •Reuse
- •Designing a Chess Program
- •Requirements
- •Design Steps
- •An Object-Oriented View of the World
- •Am I Thinking Procedurally?
- •The Object-Oriented Philosophy
- •Living in a World of Objects
- •Object Relationships
- •Abstraction
- •Reusing Code
- •A Note on Terminology
- •Deciding Whether or Not to Reuse Code
- •Strategies for Reusing Code
- •Bundling Third-Party Applications
- •Open-Source Libraries
- •The C++ Standard Library
- •Designing with Patterns and Techniques
- •Design Techniques
- •Design Patterns
- •The Reuse Philosophy
- •How to Design Reusable Code
- •Use Abstraction
- •Structure Your Code for Optimal Reuse
- •Design Usable Interfaces
- •Reconciling Generality and Ease of Use
- •The Need for Process
- •Software Life-Cycle Models
- •The Stagewise and Waterfall Models
- •The Spiral Method
- •The Rational Unified Process
- •Software-Engineering Methodologies
- •Extreme Programming (XP)
- •Software Triage
- •Be Open to New Ideas
- •Bring New Ideas to the Table
- •Thinking Ahead
- •Keeping It Clear
- •Elements of Good Style
- •Documenting Your Code
- •Reasons to Write Comments
- •Commenting Styles
- •Comments in This Book
- •Decomposition
- •Decomposition through Refactoring
- •Decomposition by Design
- •Decomposition in This Book
- •Naming
- •Choosing a Good Name
- •Naming Conventions
- •Using Language Features with Style
- •Use Constants
- •Take Advantage of const Variables
- •Use References Instead of Pointers
- •Use Custom Exceptions
- •Formatting
- •The Curly Brace Alignment Debate
- •Coming to Blows over Spaces and Parentheses
- •Spaces and Tabs
- •Stylistic Challenges
- •Introducing the Spreadsheet Example
- •Writing Classes
- •Class Definitions
- •Defining Methods
- •Using Objects
- •Object Life Cycles
- •Object Creation
- •Object Destruction
- •Assigning to Objects
- •Distinguishing Copying from Assignment
- •The Spreadsheet Class
- •Freeing Memory with Destructors
- •Handling Copying and Assignment
- •Different Kinds of Data Members
- •Static Data Members
- •Const Data Members
- •Reference Data Members
- •Const Reference Data Members
- •More about Methods
- •Static Methods
- •Const Methods
- •Method Overloading
- •Default Parameters
- •Inline Methods
- •Nested Classes
- •Friends
- •Operator Overloading
- •Implementing Addition
- •Overloading Arithmetic Operators
- •Overloading Comparison Operators
- •Building Types with Operator Overloading
- •Pointers to Methods and Members
- •Building Abstract Classes
- •Using Interface and Implementation Classes
- •Building Classes with Inheritance
- •Extending Classes
- •Overriding Methods
- •Inheritance for Reuse
- •The WeatherPrediction Class
- •Adding Functionality in a Subclass
- •Replacing Functionality in a Subclass
- •Respect Your Parents
- •Parent Constructors
- •Parent Destructors
- •Referring to Parent Data
- •Casting Up and Down
- •Inheritance for Polymorphism
- •Return of the Spreadsheet
- •Designing the Polymorphic Spreadsheet Cell
- •The Spreadsheet Cell Base Class
- •The Individual Subclasses
- •Leveraging Polymorphism
- •Future Considerations
- •Multiple Inheritance
- •Inheriting from Multiple Classes
- •Naming Collisions and Ambiguous Base Classes
- •Interesting and Obscure Inheritance Issues
- •Special Cases in Overriding Methods
- •Copy Constructors and the Equals Operator
- •The Truth about Virtual
- •Runtime Type Facilities
- •Non-Public Inheritance
- •Virtual Base Classes
- •Class Templates
- •Writing a Class Template
- •How the Compiler Processes Templates
- •Distributing Template Code between Files
- •Template Parameters
- •Method Templates
- •Template Class Specialization
- •Subclassing Template Classes
- •Inheritance versus Specialization
- •Function Templates
- •Function Template Specialization
- •Function Template Overloading
- •Friend Function Templates of Class Templates
- •Advanced Templates
- •More about Template Parameters
- •Template Class Partial Specialization
- •Emulating Function Partial Specialization with Overloading
- •Template Recursion
- •References
- •Reference Variables
- •Reference Data Members
- •Reference Parameters
- •Reference Return Values
- •Deciding between References and Pointers
- •Keyword Confusion
- •The const Keyword
- •The static Keyword
- •Order of Initialization of Nonlocal Variables
- •Types and Casts
- •typedefs
- •Casts
- •Scope Resolution
- •Header Files
- •C Utilities
- •Variable-Length Argument Lists
- •Preprocessor Macros
- •How to Picture Memory
- •Allocation and Deallocation
- •Arrays
- •Working with Pointers
- •Array-Pointer Duality
- •Arrays Are Pointers!
- •Not All Pointers Are Arrays!
- •Dynamic Strings
- •C-Style Strings
- •String Literals
- •The C++ string Class
- •Pointer Arithmetic
- •Custom Memory Management
- •Garbage Collection
- •Object Pools
- •Function Pointers
- •Underallocating Strings
- •Memory Leaks
- •Double-Deleting and Invalid Pointers
- •Accessing Out-of-Bounds Memory
- •Using Streams
- •What Is a Stream, Anyway?
- •Stream Sources and Destinations
- •Output with Streams
- •Input with Streams
- •Input and Output with Objects
- •String Streams
- •File Streams
- •Jumping around with seek() and tell()
- •Linking Streams Together
- •Bidirectional I/O
- •Internationalization
- •Wide Characters
- •Non-Western Character Sets
- •Locales and Facets
- •Errors and Exceptions
- •What Are Exceptions, Anyway?
- •Why Exceptions in C++ Are a Good Thing
- •Why Exceptions in C++ Are a Bad Thing
- •Our Recommendation
- •Exception Mechanics
- •Throwing and Catching Exceptions
- •Exception Types
- •Throwing and Catching Multiple Exceptions
- •Uncaught Exceptions
- •Throw Lists
- •Exceptions and Polymorphism
- •The Standard Exception Hierarchy
- •Catching Exceptions in a Class Hierarchy
- •Writing Your Own Exception Classes
- •Stack Unwinding and Cleanup
- •Catch, Cleanup, and Rethrow
- •Use Smart Pointers
- •Common Error-Handling Issues
- •Memory Allocation Errors
- •Errors in Constructors
- •Errors in Destructors
- •Putting It All Together
- •Why Overload Operators?
- •Limitations to Operator Overloading
- •Choices in Operator Overloading
- •Summary of Overloadable Operators
- •Overloading the Arithmetic Operators
- •Overloading Unary Minus and Unary Plus
- •Overloading Increment and Decrement
- •Overloading the Subscripting Operator
- •Providing Read-Only Access with operator[]
- •Non-Integral Array Indices
- •Overloading the Function Call Operator
- •Overloading the Dereferencing Operators
- •Implementing operator*
- •Implementing operator->
- •What in the World Is operator->* ?
- •Writing Conversion Operators
- •Ambiguity Problems with Conversion Operators
- •Conversions for Boolean Expressions
- •How new and delete Really Work
- •Overloading operator new and operator delete
- •Overloading operator new and operator delete with Extra Parameters
- •Two Approaches to Efficiency
- •Two Kinds of Programs
- •Is C++ an Inefficient Language?
- •Language-Level Efficiency
- •Handle Objects Efficiently
- •Use Inline Methods and Functions
- •Design-Level Efficiency
- •Cache as Much as Possible
- •Use Object Pools
- •Use Thread Pools
- •Profiling
- •Profiling Example with gprof
- •Cross-Platform Development
- •Architecture Issues
- •Implementation Issues
- •Platform-Specific Features
- •Cross-Language Development
- •Mixing C and C++
- •Shifting Paradigms
- •Linking with C Code
- •Mixing Java and C++ with JNI
- •Mixing C++ with Perl and Shell Scripts
- •Mixing C++ with Assembly Code
- •Quality Control
- •Whose Responsibility Is Testing?
- •The Life Cycle of a Bug
- •Bug-Tracking Tools
- •Unit Testing
- •Approaches to Unit Testing
- •The Unit Testing Process
- •Unit Testing in Action
- •Higher-Level Testing
- •Integration Tests
- •System Tests
- •Regression Tests
- •Tips for Successful Testing
- •The Fundamental Law of Debugging
- •Bug Taxonomies
- •Avoiding Bugs
- •Planning for Bugs
- •Error Logging
- •Debug Traces
- •Asserts
- •Debugging Techniques
- •Reproducing Bugs
- •Debugging Reproducible Bugs
- •Debugging Nonreproducible Bugs
- •Debugging Memory Problems
- •Debugging Multithreaded Programs
- •Debugging Example: Article Citations
- •Lessons from the ArticleCitations Example
- •Requirements on Elements
- •Exceptions and Error Checking
- •Iterators
- •Sequential Containers
- •Vector
- •The vector<bool> Specialization
- •deque
- •list
- •Container Adapters
- •queue
- •priority_queue
- •stack
- •Associative Containers
- •The pair Utility Class
- •multimap
- •multiset
- •Other Containers
- •Arrays as STL Containers
- •Strings as STL Containers
- •Streams as STL Containers
- •bitset
- •The find() and find_if() Algorithms
- •The accumulate() Algorithms
- •Function Objects
- •Arithmetic Function Objects
- •Comparison Function Objects
- •Logical Function Objects
- •Function Object Adapters
- •Writing Your Own Function Objects
- •Algorithm Details
- •Utility Algorithms
- •Nonmodifying Algorithms
- •Modifying Algorithms
- •Sorting Algorithms
- •Set Algorithms
- •The Voter Registration Audit Problem Statement
- •The auditVoterRolls() Function
- •The getDuplicates() Function
- •The RemoveNames Functor
- •The NameInList Functor
- •Testing the auditVoterRolls() Function
- •Allocators
- •Iterator Adapters
- •Reverse Iterators
- •Stream Iterators
- •Insert Iterators
- •Extending the STL
- •Why Extend the STL?
- •Writing an STL Algorithm
- •Writing an STL Container
- •The Appeal of Distributed Computing
- •Distribution for Scalability
- •Distribution for Reliability
- •Distribution for Centrality
- •Distributed Content
- •Distributed versus Networked
- •Distributed Objects
- •Serialization and Marshalling
- •Remote Procedure Calls
- •CORBA
- •Interface Definition Language
- •Implementing the Class
- •Using the Objects
- •A Crash Course in XML
- •XML as a Distributed Object Technology
- •Generating and Parsing XML in C++
- •XML Validation
- •Building a Distributed Object with XML
- •SOAP (Simple Object Access Protocol)
- •. . . Write a Class
- •. . . Subclass an Existing Class
- •. . . Throw and Catch Exceptions
- •. . . Read from a File
- •. . . Write to a File
- •. . . Write a Template Class
- •There Must Be a Better Way
- •Smart Pointers with Reference Counting
- •Double Dispatch
- •Mix-In Classes
- •Object-Oriented Frameworks
- •Working with Frameworks
- •The Model-View-Controller Paradigm
- •The Singleton Pattern
- •Example: A Logging Mechanism
- •Implementation of a Singleton
- •Using a Singleton
- •Example: A Car Factory Simulation
- •Implementation of a Factory
- •Using a Factory
- •Other Uses of Factories
- •The Proxy Pattern
- •Example: Hiding Network Connectivity Issues
- •Implementation of a Proxy
- •Using a Proxy
- •The Adapter Pattern
- •Example: Adapting an XML Library
- •Implementation of an Adapter
- •Using an Adapter
- •The Decorator Pattern
- •Example: Defining Styles in Web Pages
- •Implementation of a Decorator
- •Using a Decorator
- •The Chain of Responsibility Pattern
- •Example: Event Handling
- •Implementation of a Chain of Responsibility
- •Using a Chain of Responsibility
- •Example: Event Handling
- •Implementation of an Observer
- •Using an Observer
- •Chapter 1: A Crash Course in C++
- •Chapter 3: Designing with Objects
- •Chapter 4: Designing with Libraries and Patterns
- •Chapter 5: Designing for Reuse
- •Chapter 7: Coding with Style
- •Chapters 8 and 9: Classes and Objects
- •Chapter 11: Writing Generic Code with Templates
- •Chapter 14: Demystifying C++ I/O
- •Chapter 15: Handling Errors
- •Chapter 16: Overloading C++ Operators
- •Chapter 17: Writing Efficient C++
- •Chapter 19: Becoming Adept at Testing
- •Chapter 20: Conquering Debugging
- •Chapter 24: Exploring Distributed Objects
- •Chapter 26: Applying Design Patterns
- •Beginning C++
- •General C++
- •I/O Streams
- •The C++ Standard Library
- •C++ Templates
- •Integrating C++ and Other Languages
- •Algorithms and Data Structures
- •Open-Source Software
- •Software-Engineering Methodology
- •Programming Style
- •Computer Architecture
- •Efficiency
- •Testing
- •Debugging
- •Distributed Objects
- •CORBA
- •XML and SOAP
- •Design Patterns
- •Index

Demystifying C++ I/O
A program’s fundamental job is to accept input and produce output. A program that produces no output of any sort would not be very useful. All languages provide some mechanism for I/O, either as a built-in part of the language or through OS-specific hooks. A good I/O system is both flexible and easy to use. Part of being flexible is polymorphism: flexible I/O systems support input and output through a variety of devices, such as files and the user console. They also support the reading and writing of different types of data. I/O is error-prone because data coming from a user can be incorrect or the underlying file system or other data source can be inaccessible. Thus, a good I/O system is also capable of handling error conditions.
If you are familiar with the C language, you have undoubtedly used printf() and scanf(). As I/O mechanisms, printf() and scanf() are certainly flexible. Through escape codes and variable placeholders, they can be customized to read in specially formatted data or output anything from an integer to a string. printf() and scanf(), however, falter on other measures of good I/O systems. They do not handle errors particularly well, they are not flexible enough to handle custom data types, and, worst of all in an object-oriented language like C++, they are not at all object oriented!
C++ provides a more refined method of input and output through a mechanism known as streams. Streams are a flexible and object-oriented approach to I/O. In this chapter, you will learn how to use streams for data output and input. You will also learn how to use the stream mechanism to read from various sources and write to various destinations, such as the user console, files, and even strings. This chapter covers the most commonly used I/O features. This chapter also covers the increasingly important topic of writing programs that can be localized to different regions around the world.
Using Streams
The stream metaphor takes a bit of getting used to. At first, streams may seem more complex than traditional C-style I/O, such as printf(). In reality, they seem complicated initially only because there is a deeper metaphor behind streams than there is behind printf(). Don’t worry though; after a few examples, you’ll never look back.

Chapter 14
What Is a Stream, Anyway?
As you read in Chapter 1, the cout stream is like a laundry chute for data. You throw some variables down the stream, and they are written to the user’s screen, or console. More generally, all streams can be viewed as data chutes. Streams vary in their direction and their associated source or destination. For example, the cout stream that you are already familiar with is an output stream so its direction is “out.” It writes data to the console so its associated destination is “console.” There is another standard stream called cin that accepts input from the user. Its direction is in, and its associated source is console. cout and cin are predefined instances of streams that are defined within the std namespace in C++.
Every input stream has an associated source. Every output stream has an associated destination.
Stream Sources and Destinations
Streams as a concept can be applied to any object that accepts data or emits data. You could write a stream-based network class or stream-based access to a MIDI-based instrument. In C++, there are three common sources and destinations for streams.
You have already read many examples of user, or console, streams. Console input streams make programs interactive by allowing input from the user during run time. Console output streams provide feedback to the user and output results.
File streams, as the name implies, read data from a file system and write data to a file system. File input streams are useful for reading in configuration data and saved files or for batch processing file-based data. File output streams are useful for saving state and providing output.
String streams are an application of the stream metaphor to the string type. With a string stream, you can treat character data just as you would treat any other stream. For the most part, this is merely a handy syntax for functionality that could be handled through methods on the string class. However, using stream syntax provides opportunities for optimization and can be far more convenient than direct use of the string class.
The rest of this section deals with console streams (cin and cout). Examples of file and string streams are provided later in this chapter. Other types of streams, such as printer output or network I/O are provided by the operating system and are not built into the language.
Output with Streams
Output using streams was introduced in Chapter 1 and has been used in almost every chapter in this book. This section will briefly revisit some of those basics then will introduce material that is more advanced.
Output Basics
Output streams are defined in the <ostream> header file. Most programmers include <iostream> in their programs, which in turn includes the headers for both input streams and output streams. The <iostream> header also declares the standard console output stream, cout.
380

Demystifying C++ I/O
The << operator is the simplest way to use output streams. C++ basic types, such as ints, pointers, doubles, and characters, can be output using <<. In addition, the C++ string class is compatible with <<, and C-style strings are properly output as well. Following are some examples of using << and their corresponding output.
int i = 7;
cout << i;
7
char ch = ‘a’;
cout << ch;
a
string myString = “Marni is adorable.”;
cout << myString;
Marni is adorable.
The cout stream is the built-in stream for writing to the console, or standard output. Recall that you can “chain” uses of << together to output multiple pieces of data. This is because the << operator returns the stream as its result so you can immediately use << again on the same stream. For example:
int i = 11;
cout << “On a scale of 1 to cute, Marni ranks “ << i << “!”;
On a scale of 1 to cute, Marni ranks 11!
C++ streams will correctly parse C-style escape codes, such as strings that contain \n, but it is much more hip to use the built-in endl mechanism for this purpose. The following example uses endl, which is defined in the std namespace to represent an end-of-line character and to flush the output buffer. Several lines of text are output using one line of code.
cout << “Line 1” << endl << “Line 2” << endl << “Line 3” << endl;
Line 1
Line 2
Line 3
Methods of Output Streams
The << operator is, without a doubt, the most useful part of output streams. However, there is additional functionality to be explored. If you take a peek at the <ostream> header file, you’ll see many lines of overloaded definitions of the << operator. You’ll also find some useful public methods.
put() and write()
put() and write() are raw output methods. Instead of taking an object or variable that has some defined behavior for output, these methods accept a character or character array, respectively. The data passed to these methods are output as is, without any special formatting or processing. Escape characters, such as \n are still output in their correct form (i.e., a carriage return), but no polymorphic output will occur. The following function takes a C-style string and outputs it to the console without using the << operator:
381

Chapter 14
void rawWrite(const char* data, int dataSize)
{
cout.write(data, dataSize);
}
The next function writes the prescribed index of a C-style string to the console using the put method.
void rawPutChar(const char* data, int charIndex)
{
cout.put(data[charIndex]);
}
flush()
When you write to an output stream, the stream does not necessarily write the data to its destination right away. Most output streams buffer, or accumulate data instead of writing it out as it comes in. The stream will flush, or write out the accumulated data, when one of the following conditions occurs:
A sentinel, such as the endl marker, is reached.
The stream goes out of scope and is destructed.
Input is requested from a corresponding input stream (i.e., when you make use of cin for input, cout will flush). In the section on file streams, you will learn how to establish this type of link.
The stream buffer is full.
You explicitly tell the stream to flush its buffer.
One way to explicitly tell a stream to flush is to call its flush() method, as in the code that follows.
cout << “abc”; |
|
|
|
|
cout.flush(); |
// |
abc is |
written to the console. |
|
cout << |
“def”; |
|
|
|
cout << |
endl; |
// |
def is |
written to the console. |
|
|
|
|
|
Not all output streams are buffered. The cerr stream, for example, does not buffer its output.
Handling Output Errors
Output errors can arise in a variety of situations. Perhaps you are attempting to write to a file that does not exist or has been given read-only permissions. Maybe a disk error has prevented a write operation from succeeding or the console has somehow gotten into a locked-up state. None of the streams code you have read up until this point has considered these possibilities, mainly for brevity. However, in professional C++ programs, it is vital that you address any error conditions that occur.
When a stream is in its normal usable state, it is said to be “good.” The good() method can be called directly on a stream to determine whether or not the stream is currently good.
382

Demystifying C++ I/O
if (cout.good()) {
cout << “All good” << endl;
}
The good() method provides an easy way to obtain basic information about the validity of the steam, but it does not tell you why the steam is unusable. There is a method called bad() that provides a bit more information. If bad() returns true, it means that a fatal error has occurred (as opposed to any nonfatal condition like end-of-file). Another method, fail(), returns true if the most recent operation has failed, implying that the next operation will also fail. For example, after calling flush() on an output stream, you could call fail() to make sure the stream is still usable.
cout.flush();
if (cout.fail()) {
cerr << “Unable to flush to standard out” << endl;
}
To reset the error state of a stream, use the clear() method:
cout.clear();
Error checking is performed less frequently for console output streams than for file output streams or input streams. The methods discussed here apply for other types of streams as well and are revisited below as each type is discussed.
Output Manipulators
One of the unusual features of streams is that you can throw more than just data down the chute. C++ streams also recognize manipulators, objects that make a change to the behavior of the stream instead of, or in addition to, providing data for the stream to work with.
You have already seen one manipulator: endl. The endl manipulator encapsulates data and behavior. It tells the stream to output a carriage return and to flush its buffer. Following are some other useful manipulators, many of which are defined in the <ios> and <iomanip> standard header files.
boolalpha and noboolalpha. Tells the stream to output bool values as true and false
(boolalpha) or 1 and 0 (noboolalpha). The default is noboolalpha.
hex, oct, and dec. Outputs numbers in hexadecimal, octal, and base 10, respectively.
setprecision. Sets the number of decimal places that are output for fractional numbers. This is a parameterized manipulator (meaning that it takes an argument).
setw. Sets the field width for outputting numerical data. This is a parameterized manipulator.
setfill. Specifies the character that is used to pad numbers that are smaller than the specified width. This is a parameterized manipulator.
showpoint and noshowpoint. Forces the stream to always or never show the decimal point for floats and doubles with no fractional part.
The following program uses several of these manipulators to customize its output.
383