
- •Introduction
- •Saving Time with This Book
- •Conventions Used in This Book
- •Part II: Working with the Pre-Processor
- •Part III: Types
- •Part IV: Classes
- •Part V: Arrays and Templates
- •Part VI: Input and Output
- •Part VII: Using the Built-in Functionality
- •Part VIII: Utilities
- •Part IX: Debugging C++ Applications
- •Part X: The Scary (or Fun!) Stuff
- •Icons Used in This Book
- •Creating and Implementing an Encapsulated Class
- •Creating a Mailing-List Application
- •Testing the Mailing-List Application
- •Customizing a Class with Polymorphism
- •Testing the Virtual Function Code
- •Why Do the Destructors Work?
- •Delayed Construction
- •The cDate Class
- •Testing the cDate Class
- •Creating the Header File
- •Testing the Header File
- •The Assert Problem
- •Fixing the Assert Problem
- •Using the const Construct
- •Identifying the Errors
- •Fixing the Errors
- •Fixing What Went Wrong with the Macro
- •Using Macros Appropriately
- •Using the sizeof Function
- •Evaluating the Results
- •Using sizeof with Pointers
- •Implementing the Range Class
- •Testing the Range Class
- •Creating the Matrix Class
- •Matrix Operations
- •Multiplying a Matrix by a Scalar Value
- •Multiplying a Matrix by Scalar Values, Take 2
- •Testing the Matrix Class
- •Implementing the Enumeration Class
- •Testing the Enumeration Class
- •Implementing Structures
- •Interpreting the Output
- •Defining Constants
- •Testing the Constant Application
- •Using the const Keyword
- •Illustrating Scope
- •Interpreting the Output
- •Using Casts
- •Addressing the Compiler Problems
- •Testing the Changes
- •Implementing Member-Function Pointers
- •Updating Your Code with Member-Function Pointers
- •Testing the Member Pointer Code
- •Customizing Functions We Wrote Ourselves
- •Testing the Default Code
- •Fixing the Problem
- •Testing the Complete Class
- •Implementing Virtual Inheritance
- •Correcting the Code
- •Rules for Creating Overloaded Operators
- •Using Conversion Operators
- •Using Overloaded Operators
- •Testing the MyString Class
- •Rules for Implementing new and delete Handlers
- •Overloading new and delete Handlers
- •Testing the Memory Allocation Tracker
- •Implementing Properties
- •Testing the Property Class
- •Implementing Data Validation with Classes
- •Testing Your SSN Validator Class
- •Creating the Date Class
- •Testing the Date Class
- •Some Final Thoughts on the Date Class
- •Creating a Factory Class
- •Testing the Factory
- •Enhancing the Manager Class
- •Implementing Mix-In Classes
- •Testing the Template Classes
- •Implementing Function Templates
- •Creating Method Templates
- •Using the Vector Class
- •Creating the String Array Class
- •Working with Vector Algorithms
- •Creating an Array of Heterogeneous Objects
- •Creating the Column Class
- •Creating the Row Class
- •Creating the Spreadsheet Class
- •Testing Your Spreadsheet
- •Working with Streams
- •Testing the File-Reading Code
- •Creating the Test File
- •Reading Delimited Files
- •Testing the Code
- •Creating the XML Writer
- •Testing the XML Writer
- •Creating the Configuration-File Class
- •Setting Up Your Test File
- •Building the Language Files
- •Creating an Input Text File
- •Reading the International File
- •Testing the String Reader
- •Creating a Translator Class
- •Testing the Translator Class
- •Creating a Virtual File Class
- •Testing the Virtual File Class
- •Using the auto_ptr Class
- •Creating a Memory Safe Buffer Class
- •Throwing and Logging Exceptions
- •Dealing with Unhandled Exceptions
- •Re-throwing Exceptions
- •Creating the Wildcard Matching Class
- •Testing the Wildcard Matching Class
- •Creating the URL Codec Class
- •Testing the URL Codec Class
- •Testing the Rot13 Algorithm
- •Testing the XOR Algorithm
- •Implementing the transform Function to Convert Strings
- •Testing the String Conversions
- •Implementing the Serialization Interface
- •Creating the Buffer Class
- •Testing the Buffer Class
- •Creating the Multiple-Search-Path Class
- •Testing the Multiple-Search-Path Class
- •Testing the Flow Trace System
- •The assert Macro
- •Logging
- •Testing the Logger Class
- •Design by Contract
- •Adding Logging to the Application
- •Making Functions Inline
- •Avoiding Temporary Objects
- •Passing Objects by Reference
- •Choosing Initialization Instead of Assignment
- •Learning How Code Operates
- •Testing the Properties Class
- •Creating the Locking Mechanism
- •Testing the Locking Mechanism
- •Testing the File-Guardian Class
- •Implementing the Complex Class
- •Creating the Conversion Code
- •Testing the Conversion Code
- •A Sample Program
- •Componentizing
- •Restructuring
- •Specialization
- •Index

38 Working with Arrays of Object
Technique Pointers
Save Time By
Understanding arrays of object pointers
Implementing arrays of object pointers
Interpreting output
Although simple arrays of objects are easy to work with, arrays of pointers that indicate objects are slightly more complicated to handle. The syntax for creating and deleting these arrays is a little
more difficult; heterogeneous arrays of pointers that point to a common base object require a bit more work — and this technique guides you through what must be done. C++ allows you to store pointers to all related classes — that is, those derived from a common base — in a single array, while keeping track of their types and sizes. This can save you a lot of time, by allowing you to place all of the related objects in a single array while processing them differently using their derived types.
You can save a lot of time by storing all objects that derive from a common base in a single array for access — as long as you have a way to access the objects consistently. If you do so, you must use a virtual destructor in the base class to insure that all de-allocations are done properly. If you do not do this, the destructors for the derived classes will not be called, and potential memory leaks can occur.
Creating an Array of Heterogeneous Objects
If you are working with a batch of different classes, all derived from a single base class, it can be advantageous to store them all in one place. For one thing, you only have one array to work with. For another, because the objects are all related, it is likely that you will be doing the same processing on them all at the same time. Let’s look at an example of creating a heterogeneous array that stores multiple classes of objects.
1. In the code editor of your choice, create a new file to hold the code for the implementation of the source file.
In this example, the file is named ch38.cpp, although you can use whatever you choose.
2. Type the code from Listing 38-1 into your file.
Better yet, copy the code from the source file on this book’s companion Web site.

214 Technique 38: Working with Arrays of Object Pointers
LISTING 38-1: CREATING AN ARRAY OF OBJECT POINTERS
#include <stdio.h> #include <string.h>
class Base
{
char *ptr; public:
Base(void)
{
ptr = NULL;
}
Base( const char *str )
{
setString( str );
}
virtual ~Base()
{
printf(“Base::~Base called\n”); delete ptr;
}
void setString( const char *str )
{
ptr = new char[strlen(str)+1]; strcpy( ptr, str );
}
const char *getString()
{
return ptr;
}
};
class Derived : public Base
{
private: int _num;
public:
Derived(void)
: Base(“DerivedVoid”)
{
_num = 0;
}
Derived( int nVal )
: Base(“DerivedFull”)
{
_num = nVal;
}
virtual ~Derived()
{
printf(“Derived::~Derived called\n”);
}
void setVal( int nVal )
{
_num = nVal;
}
int getVal ( void )
{
return _num;
}
};
const int NumElements = 3;
int main(void) |
|
|
{ |
|
1 |
Base **bArray = new Base*[10]; |
||
for ( int i=0; |
i<NumElements; ++i ) |
2 |
bArray[i] = |
new Derived(i); |
|
// Print them out |
|
|
for ( int j=0; |
j<NumElements; ++j ) |
|
printf(“Object %s - %d\n”, bArray[j]- >getString(), ((Derived *)bArray[j])- >getVal());
// Delete them
for ( int i=0; i<NumElements; ++i ) delete bArray[i];
delete [] bArray; return 0;
}
The above code listing illustrates how we create an array of pointers and store data in that array. As you can see at 1, allocating an array of pointers is no different than allocating any other sort of array in C++. The difference here is that while the array space is allocated, no actual objects are created. This is because we are allocating space for pointers to the objects, not objects themselves. The actual allocation of objects and the space they consume is illustrated at the line marked 2. Note that even though we have an array of Base pointers, we can create and store Derived pointers in the array, since they are a derived form of Base.

Creating an Array of Heterogeneous Objects |
215 |
3. Save the source file in your code editor and close the code editor.
4. Compile the source code with the compiler of your choice on the operating system of your choice.
When the program is run, if you have done everything properly, you should see the following output in the shell window:
$ ./a.exe
Object DerivedFull - 0 Object DerivedFull - 1 Object DerivedFull - 2 Derived::~Derived called Base::~Base called Derived::~Derived called Base::~Base called Derived::~Derived called Base::~Base called
The output here illustrates that the array of pointers is created as we expected. The string stored in the Base class was created from the Derived constructor, which is what we anticipated. The destruction of the objects does chain upward to call both the Base class and Derived class destructors. In short, this code works exactly as advertised.
The ability to work with an array of heterogeneous pointers is quite powerful in C++, because it means that you need not know what sort of object you are working with. Had we created virtual methods for getting and setting the values in the Base class, we would not even have to cast the object in the printf in the main function.

39 Implementing a
Spreadsheet
Technique
Save Time By
Creating a simple spreadsheet implementation
Creating the Column class
Creating the Row class
Creating the
Spreadsheet class
Testing your spreadsheet
One of the most famous (or perhaps infamous) applications to help make the personal computer popular was the spreadsheet — nothing more than a grid of cells arranged in rows and columns —
in other words, a two-dimensional array. Spreadsheets have more functionality than simple arrays (for example, you can build in formulae), but at its heart, a spreadsheet is an array of rows and columns. This technique uses the Standard Template Library (STL) to set up and implement a spreadsheet shell. The result can easily be used to create a real spreadsheet implementation. Spreadsheets are common elements of applications these days, from doing presentations of data to what-if analysis. By having a generic spreadsheet class that you can drop into your next project, you will find that you save a lot of time in both the design and implementation phase of the project.
The implementation shown here isn’t designed to work with or interpret formulae, but it will do everything else. If you want a complete spreadsheet that can handle formulae, all you need to do is incorporate a simple expression parser.
The basics of the spreadsheet are three elements: the column (or cell), the row, and the sheet itself. Each column contains a piece of data and the information for formatting that piece of data for display. The column contains methods to copy itself, clear itself out, and modify the data or formatting information in itself. The row is simply an array of columns that makes up a single row of the spreadsheet. The Row class needs to be able to modify any existing column in the row, as well as add new columns and remove columns. A row should be able to return the contents of any given column within that row so that the end user can modify the contents directly.
Finally, the Spreadsheet class will contain an array of rows. This array knows nothing about the individual columns in the sheet, nor does it know anything about formatting or data. This data encapsulation is consistent with the object-oriented paradigm, certainly, but is also important in terms of being able to easily modify the basic layers of the system with minimal change to the upper layers.