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

Delving into the STL: Containers and Iterators
#include <stack> #include <stdexcept>
// Details of Error class omitted for brevity
//
// Simple ErrorCorrelator class that returns most recent errors first
//
class ErrorCorrelator
{
public: ErrorCorrelator() {}
//
// Add an error to be correlated.
//
void addError(const Error& error);
//
// Retrieve the next error to be processed.
//
Error getError() throw (std::out_of_range);
protected:
std::stack<Error> mErrors;
private:
// Prevent assignment and pass-by-reference. ErrorCorrelator(const ErrorCorrelator& src); ErrorCorrelator& operator=(const ErrorCorrelator& rhs);
};
Associative Containers
Unlike the sequential containers, the associative containers do not store elements in a linear configuration. Instead, they provide a mapping of keys to values. They generally offer insertion, deletion, and lookup times that are equivalent to each other.
The four associative containers provided by the STL are map, multimap, set, and multiset. Each of these containers stores its elements in a sorted, treelike, data structure.
The pair Utility Class
Before learning about the associative containers, you must become familiar with the pair class, which is defined in the <utility> header file. pair is a class template that groups together two values of possibly different types. The values are accessible through the first and second public data members. operator== and operator< are defined for pairs to compare both the first and second elements. Here are some examples:
#include <utility> #include <string> #include <iostream>
595

Chapter 21
using namespace std;
int main(int argc, char** argv)
{
//Two-argument ctor and default ctor pair<string, int> myPair(“hello”, 5), myOtherPair;
//Can assign directly to first and second myOtherPair.first = “hello”; myOtherPair.second = 6;
//Copy ctor.
pair<string, int> myThirdPair(myOtherPair);
// operator<
if (myPair < myOtherPair) {
cout << “myPair is less than myOtherPair\n”; } else {
cout << “myPair is greater than or equal to myOtherPair\n”;
}
// operator==
if (myOtherPair == myThirdPair) {
cout << “myOtherPair is equal to myThirdPair\n”; } else {
cout << “myOtherPair is not equal to myThirdPair\n”;
}
return (0);
}
The library also provides a utility function template, make_pair(), that constructs a pair from two variables. For example, you could use it like this:
pair<int, int> aPair = make_pair(5, 10);
Of course, in this case you could have just used the two-argument constructor. However, make_pair() is more useful when you want to pass a pair to a function. Unlike class templates, function templates can infer types from parameters, so you can use make_pair() to construct a pair without explicitly specifying the types.
Using pointer types in pairs is risky because the pair copy constructor and assignment operator perform only shallow copies and assignments of pointer types.
map
The map is one of the most useful containers. It stores key/value pairs instead of just a single value. Insertion, lookup, and deletion are all based on the key; the value is just “along for the ride.” The term “map” comes from the conceptual understanding that the container “maps” keys to values. You might be more familiar with the concept of a hash table. The map provides a similar interface; the differences are in the underlying data structure and the algorithmic complexity of the operations.
596

Delving into the STL: Containers and Iterators
The map keeps elements in sorted order, based on the keys, so that insertion, deletion, and lookup all take logarithmic time. It is usually implemented as some form of balanced tree, such as a red-black tree. However, the tree structure is not exposed to the client.
You should use a map whenever you need to store and retrieve elements based on a “key” value.
Constructing Maps
The map template takes four types: the key type, the value type, the comparison type, and the allocator type. As usual, we ignore the allocator in this chapter; see Chapter 23 for details. The comparison type is similar to the comparison type for priority_queue described above. It allows you to specify a different comparison class than the default. You usually shouldn’t need to change the sorting criteria. In this chapter, we use only the default less comparison. When using the default, make sure that your keys all respond to operator< appropriately.
If you’re interested in further detail, Chapter 22 explains how to write your own comparison classes.
If you ignore the comparison and allocator parameters (which we urge you to do), constructing a map is just like constructing a vector or list, except that you specify the key and value types separately in the template. For example, the following code constructs a map that uses ints as the key and stores objects of the Data class (whose full definition is not shown):
#include <map> using namespace std;
class Data
{
public:
Data(int val = 0) { mVal = val; } int getVal() const { return mVal; } void setVal(int val) {mVal = val; } // Remainder of definition omitted
protected: int mVal;
};
int main(int argc, char** argv)
{
map<int, Data> dataMap; return (0);
}
Inserting Elements
Inserting an element into the sequential containers such as vector and list always requires you to specify the position at which the element is to be added. The map, along with the other associative containers, is different. The map internal implementation determines the position in which to store the new element; you need only to supply the key and the value.
map and the other associative containers do provide a version of insert() that takes an iterator position. However, that position is only a “hint” to the container as to the correct position. The container is not required to insert the element at that position.
597

Chapter 21
When inserting elements, it is important to keep in mind that maps support so-called “unique keys:” every element in the map must have a different key. If you want to support multiple elements with the same key, you must use multimaps, which are described below.
There are two ways to insert an element into the map: one clumsy and one not so clumsy.
The insert() Method
The clumsy mechanism to add an element to a map is the insert() method. One problem is that you must specify the key/value pair as a pair object. The second problem is that the return value from the basic form of insert() is a pair of an iterator and a bool. The reason for the complicated return value is that insert() does not overwrite an element value if one already exists with the specified key. The bool element of the return pair specifies whether the insert() actually inserted the new key/ value pair. The iterator refers to the element in the map with the specified key (with a new or old value, depending on whether the insert succeeded or failed). Continuing the map example from the previous section, here is how to use insert():
#include <map> #include <iostream> using namespace std;
class Data
{
public:
Data(int val = 0) { mVal = val; } int getVal() const { return mVal; } void setVal(int val) {mVal = val; } // Remainder of definition omitted
protected: int mVal;
};
int main(int argc, char** argv)
{
map<int, Data> dataMap;
pair<map<int, Data>::iterator, bool> ret;
ret = dataMap.insert(make_pair(1, Data(4))); if (ret.second) {
cout << “Insert succeeded!\n”; } else {
cout << “Insert failed!\n”;
}
ret = dataMap.insert(make_pair(1, Data(6))); if (ret.second) {
cout << “Insert succeeded!\n”; } else {
cout << “Insert failed!\n”;
}
return (0);
}
598

Delving into the STL: Containers and Iterators
Note the use of make_pair() to construct the pair to pass to the insert() method. The output from the program is:
Insert succeeded!
Insert failed!
operator[]
The less clumsy way to insert an element into the map is through the overloaded operator[]. The difference is mainly in the syntax: you specify the key and value separately. Additionally, operator[] always succeeds. If no element value with the given key exists, it creates a new element with that key and value. If an element with the key exists already, operator[] replaces the element value with the newly specified value. Here is the previous example using operator[] instead of insert():
#include <map> #include <iostream> using namespace std;
class Data
{
public:
Data(int val = 0) { mVal = val; } int getVal() const { return mVal; } void setVal(int val) {mVal = val; } // Remainder of definition omitted
protected: int mVal;
};
int main(int argc, char** argv)
{
map<int, Data> dataMap; dataMap[1] = Data(4);
dataMap[1] = Data(6); // Replaces the element with key 1 return (0);
}
There is, however, one major caveat to operator[]: it always constructs a new value object, even if it doesn’t need to use it. Thus, it requires a default constructor for your element values, and can be less efficient than insert().
Map Iterators
map iterators work similarly to the iterators on the sequential containers. The major difference is that the iterators refer to key/value pairs instead of just the values. In order to access the value, you must retrieve the second field of the pair object. Here is how you can iterate through the map from the previous example:
#include <map> #include <iostream> using namespace std;
class Data
{
599

Chapter 21
public:
Data(int val = 0) { mVal = val; } int getVal() const { return mVal; } void setVal(int val) {mVal = val; } // Remainder of definition omitted
protected: int mVal;
};
int main(int argc, char** argv)
{
map<int, Data> dataMap;
dataMap[1] = Data(4);
dataMap[1] = Data(6); // Replaces the element with key 1
for (map<int, Data>::iterator it = dataMap.begin(); it != dataMap.end(); ++it) {
cout << it->second.getVal() << endl;
}
return (0);
}
Take another look at the expression used to access the value:
it->second.getVal()
it refers to a key/value pair, so you can use the -> operator to access the second field of that pair, which is a Data object. You can then call the getVal() method on that data object.
Note that the following code is functionally equivalent:
(*it).second.getVal()
You still see a lot of code that like around because -> didn’t used to be required for iterators.
You can modify element values through non-const iterators, but you cannot modify the key of an element, even through a non-const iterator, because it would destroy the sorted order of the elements in the map.
map iterators are bidirectional.
Looking Up Elements
The map provides logarithmic lookup of elements based on a supplied key. If you already know that an element with a given key is in the map, the simplest way to look it up is through operator[]. The nice thing about operator[] is that it returns a reference to the element that you can use (or modify on a non-const map) directly, without worrying about pulling the value out of a pair object. Here is an extension to the preceding example to call the setVal() method on the Data object value at key 1:
600

Delving into the STL: Containers and Iterators
#include <map> #include <iostream> using namespace std;
class Data
{
public:
Data(int val = 0) { mVal = val; } int getVal() const { return mVal; } void setVal(int val) {mVal = val; } // Remainder of definition omitted
protected: int mVal;
};
int main(int argc, char** argv)
{
map<int, Data> dataMap; dataMap[1] = Data(4); dataMap[1] = Data(6); dataMap[1].setVal(100);
return (0);
}
However, if you don’t know whether the element exists, you may not want to use operator[], because it will insert a new element with that key if it doesn’t find one already. As an alternative, the map provides a find() method that returns an iterator referring to the element with the specified key, if it exists, or the end() iterator if its not in the map. Here is an example using find() to perform the same modification to the Data object with key 1:
#include <map> #include <iostream> using namespace std;
class Data
{
public:
Data(int val = 0) { mVal = val; } int getVal() const { return mVal; } void setVal(int val) {mVal = val; }
// Remainder of definition omitted protected:
int mVal;
};
int main(int argc, char** argv)
{
map<int, Data> dataMap; dataMap[1] = Data(4); dataMap[1] = Data(6);
601

Chapter 21
map<int, Data>::iterator it = dataMap.find(1); if (it != dataMap.end()) {
it->second.setVal(100);
}
return (0);
}
As you can see, using find() is a bit clumsier, but it’s sometimes necessary.
If you only want to know whether or not an element with a certain key is in the map, you can use the count() member function. It returns the number of elements in the map with a given key. For maps, the result will always be 0 or 1 because there can be no elements with duplicate keys. The following section shows an example using count().
Removing Elements
The map allows you to remove an element at a specific iterator position or to remove all elements in a given iterator range, in amortized constant and logarithmic time, respectively. From the client perspective, these two erase() methods are equivalent to those in the sequential containers. A great feature of the map, however, is that it also provides a version of erase() to remove an element matching a key. Here is an example:
//#includes, Data class definition, and beginning of main function omitted.
//See previous examples for details.
map<int, Data> dataMap; dataMap[1] = Data(4);
cout << “There are “ << dataMap.count(1) << “ elements with key 1\n”; dataMap.erase(1);
cout << “There are “ << dataMap.count(1) << “ elements with key 1\n”;
Map Example: Bank Account
You can implement a simple bank account database using a map. A common pattern is for the key to be one field of a class or struct that is stored in the map. In this case, the key is the account number. Here are simple BankAccount and BankDB classes:
#include <map> #include <string> #include <stdexcept> using std::map; using std::string;
using std::out_of_range;
class BankAccount
{
public:
BankAccount(int acctNum, const string& name) : mAcctNum(acctNum), mClientName(name) {}
void setAcctNum(int acctNum) { mAcctNum = acctNum; } int getAcctNum() const {return (mAcctNum); }
void setClientName(const string& name) { mClientName = name; } string getClientName() const { return mClientName; }
602

Delving into the STL: Containers and Iterators
// Other public methods omitted
protected:
int mAcctNum; string mClientName;
// Other data members omitted
};
class BankDB
{
public: BankDB() {}
//Adds acct to the bank database. If an account
//exists already with that number, the new account is
//not added. Returns true if the account is added, false
//if it’s not.
bool addAccount(const BankAccount& acct);
//Removes the account acctNum from the database void deleteAccount(int acctNum);
//Returns a reference to the account represented
//by its number or the client name.
//Throws out_of_range if the account is not found BankAccount& findAccount(int acctNum) throw(out_of_range);
BankAccount& findAccount(const string& name) throw(out_of_range);
//Adds all the accounts from db to this database.
//Deletes all the accounts in db.
void mergeDatabase(BankDB& db);
protected:
map<int, BankAccount> mAccounts;
};
Here are implementations of the BankDB methods:
#include “BankDB.h” #include <utility> using namespace std;
bool BankDB::addAccount(const BankAccount& acct)
{
//Declare a variable to store the return from insert(). pair<map<int, BankAccount>::iterator, bool> res;
//Do the actual insert, using the account number as the key. res = mAccounts.insert(make_pair(acct.getAcctNum(), acct));
//Return the bool field of the pair specifying success or failure. return (res.second);
}
void BankDB::deleteAccount(int acctNum)
{
603