
- •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

Understanding C++ Quirks and Oddities
int Demo::x = 3;
int y = 4;
However, C++ provides no specifications or guarantees about the initialization ordering of nonlocal variables in different source files. If you have a global variable x in one source file and a global variable y in another, you have no way of knowing which will be initialized first. Normally, this lack of specification isn’t cause for concern. However, it can be problematic if one global or static variable depends on another. Recall that initialization of objects implies running their constructors. The constructor of one global object might access another global object, assuming that it is already constructed. If these two global objects are declared in two different source files, you cannot count on one being constructed before the other.
Initialization order of nonlocal variables in different source files is undefined.
Types and Casts
Chapter 1 reviewed the basic types in C++. Chapter 8 showed you how to write your own types with classes. This section explores two of the trickier aspects of types: typedefs and casts.
typedefs
A typedef provides a new name for an existing type. You can think of a typedef simply as syntax for introducing a synonym for an existing type name. typedefs do not create new types — they only provide a new way to refer to an old type. You can use the new type name and the old type name interchangeably. Variables created with the new type name are completely compatible with those created with the original type name.
You might be surprised at the simplicity of the previous paragraph’s definition for typedefs. You’ve probably used typedefs in your code, or at least seen code that uses them, and they didn’t seem that easy. However, if you examine all the uses, you will see that they are simply providing alternate typenames.
The most common use of typenames is to provide manageable names when the real typenames become too unwieldy. This situation commonly arises with templates. For example, suppose you want to use the Grid template from Chapter 11 to create a spreadsheet, which is a Grid of SpreadsheetCells. Without typedefs, anytime you want to refer to the type of this Grid, for declaring variables, specifying function parameters, and so on, you would have to write Grid<SpreadsheetCell>:
int main(int argc, char** argv)
{
Grid<SpreadsheetCell> mySpreadsheet; // Rest of the program . . .
}
void processSpreadsheet(const Grid<SpreadsheetCell>& spreadsheet)
{
// Body omitted
}
337

Chapter 12
With a typedef, you can create a shorter, more meaningful, name:
typedef Grid<SpreadsheetCell> Spreadsheet;
int main(int argc, char** argv)
{
Spreadsheet mySpreadsheet; // Rest of the program . . .
}
void processSpreadsheet(const Spreadsheet& spreadsheet)
{
// Body omitted
}
One tricky aspect of typedefs is that the typenames can include the scope qualifiers. For example, in Chapter 9, you saw this typedef:
typedef Spreadsheet::SpreadsheetCell SCell;
This typedef creates a short name SCell to refer to the SpreadsheetCell type inside the
Spreadsheet scope.
The STL uses typedefs extensively to provide shorter names for types. For example, string is actually a typdef that looks like this:
typedef basic_string<char> string;
Casts
As explained in Chapter 1, the old-style C casts with () still work in C++. However, C++ also provides four new casts: static_cast, dynamic_cast, const_cast, and reinterpret_cast. You should use the C++ style casts instead of the old C-style casts because they perform more type checking and stand out better syntactically in your code.
This section describes the purposes for each cast and specifies when you would use each of them.
const_cast
The const_cast is the most straightforward. You can use it to cast away const-ness of a variable. It is the only cast of the four that is allowed to cast away const-ness. Theoretically, of course, there should be no need for a const cast. If a variable is const, it should stay const. In practice, however, you sometimes find yourself in a situation where a function is specified to take a const variable, which it must then pass to a function that takes a non-const variable. The “correct” solution would be to make const consistent in the program, but that is not always an option, especially if you are using third-party libraries. Thus, you sometimes need to cast away the const-ness of a variable. Here is an example:
void g(char* str)
{
// Function body omitted for brevity
}
338

Understanding C++ Quirks and Oddities
void f(const char* str)
{
//Function body omitted for brevity g(const_cast<char*>(str));
//Function body omitted for brevity
}
static_cast
You can use the static_cast to perform explicitly conversions that are supported directly by the language. For example, if you write an arithmetic expression in which you need to convert an int to a double in order to avoid integer division, use a static_cast:
int i = 3;
double result = static_cast<double>(i) / 10;
You can also use static_cast to perform explicitly conversions that are allowed because of userdefined constructors or conversion routines. For example, if class A has a constructor that takes an object of class B, you can convert a B object to an A object with a static_cast. In most situations where you want this behavior, however, the compiler will perform the conversion automatically.
Another use for the static_cast is to perform downcasts in an inheritance hierarchy. For example:
class Base
{
public: Base() {};
virtual ~Base() {}
};
class Derived : public Base
{
public: Derived() {}
virtual ~Derived() {}
};
int main(int argc, char** argv)
{
Base* b;
Derived* d = new Derived();
b = d; // Don’t need a cast to go up the inheritance hierarchy
d = static_cast<Derived*>(b); // Need a cast to go down the hierarchy
Base base;
Derived derived;
Base& br = base;
Derived& dr = static_cast<Derived&>(br);
return (0);
}
These casts work with both pointers and references. They do not work with objects themselves.
339

Chapter 12
Note that these casts with static_cast do not perform runtime type checking. They allow you to convert any Base pointer to a Derived pointer or Base reference to a Derived reference, even if the Base really isn’t a Derived at run time. To perform the cast safely, with runtime type checking, use the dynamic_cast.
static_casts are not all-powerful. You can’t static_cast pointers of one type to pointers of another unrelated type. You can’t static_cast pointers to ints. You can’t static_cast directly objects of one type to objects of another type. You can’t static_cast a const type to a non-const type. Basically, you can’t do anything that doesn’t make sense according to the type rules of C++.
reinterpret_cast
The reinterpret_cast is a bit more powerful, and concomitantly less safe, than the static_cast. You can use it to perform some casts that are not technically allowed by C++ type rules, but which might make sense to the programmer in some circumstances. For example, you can cast a pointer type to any other pointer type, even if they are unrelated by an inheritance hierarchy. Similarly, you can cast a reference to one type to a reference to another type, even if the types are unrelated. You can also cast pointers to ints and ints to pointers. Here are some examples:
class X {}; class Y {};
int main(int argc, char** argv)
{
int i = 3;
X x;
Y y;
X* xp;
Y* yp;
//Need reinterpret cast to perform pointer conversion from unrelated classes
//static_cast doesn’t work.
xp = reinterpret_cast<X*>(yp);
//Need reinterpret_cast to go from pointer to int and from int to pointer i = reinterpret_cast<int>(xp);
xp = reinterpret_cast<X*>(i);
//Need reinterpret cast to perform reference conversion from unrelated classes
//static_cast doesn’t work.
X& xr = x;
Y& yr = reinterpret_cast<Y&>(x);
return (0);
}
You should be very careful with the reinterpret_cast because it “reinterprets” raw bits as a different type without performing any type checking.
340

Understanding C++ Quirks and Oddities
dynamic_cast
As mentioned in the discussion of static_cast, the dynamic_cast provides a run-time check on casts within an inheritance hierarchy. You can use it to cast pointers or references. dynamic_cast checks the runtime type information of the underlying object at run time. If the cast doesn’t make sense, dynamic_cast returns NULL (for the pointer version) or throws a bad_cast exception (for the reference version).
Note that the runtime-type information is stored in the vtable of the object. Therefore, in order to use dynamic_cast, your classes must have at least one virtual function.
Here are some examples:
#include <typeinfo> #include <iostream> using namespace std;
class Base
{
public: Base() {};
virtual ~Base() {}
};
class Derived : public Base
{
public: Derived() {}
virtual ~Derived() {}
};
int main(int argc, char** argv)
{
Base* b;
Derived* d = new Derived();
b = d;
d = dynamic_cast<Derived*>(b);
Base base;
Derived derived;
Base& br = base;
try {
Derived& dr = dynamic_cast<Derived&>(br); } catch (bad_cast&) {
cout << “Bad cast!\n”;
}
return (0);
}
341

Chapter 12
In the preceding example, the first cast should succeed, while the second should throw an exception. Chapter 15 covers the details of exception handling.
Note that you can perform the same casts down the inheritance hierarchy with a static_cast or reinterpret_cast. The difference with dynamic_cast is that it performs runtime (dynamic) type checking.
Summary of Casts
The following table summarizes the casts you should use for difference situations.
Situation |
Cast |
|
|
Remove const-ness |
const_cast |
Explicit cast supported by language |
static_cast |
(e.g., int to double, int to bool) |
|
Explicit cast supported by user-defined |
|
constructors or conversions |
static_cast |
Object of one class to object of another |
Can’t be done |
(unrelated) class |
|
Pointer-to-object of one class to pointer-to-object of |
static_cast |
another class in the same inheritance hierarchy |
or dynamic_cast |
Reference-to-object of one class to reference-to-object |
static_cast or |
of another class in the same inheritance hierarchy |
dynamic_cast |
Pointer-to-type to unrelated pointer-to-type |
reinterpret_cast |
Reference-to-type to unrelated reference-to-type |
reinterpret_cast |
Pointer to int/ int to pointer |
reinterpret_cast |
Pointer-to-function to pointer-to-function |
reinterpret_cast |
|
|
342