- •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
Testing the Mailing-List Application |
17 |
{
printf(“Enter the next name for the mailing list:\n”); return GetAnEntry();
}
};
Testing the Mailing-List
Application
1. In the code editor of your choice, reopen the source file to hold the code for your test program.
In this example, I named the test program
After you create a class, it is important to create a test driver that not only ensures that your code is correct, but also shows people how to use your code. The following steps show you how:
ch02.cpp.
2. Type the code from Listing 2-4 into your file, substituting your own names for the italicized constants, variables, and filenames.
A more efficient approach is to copy the code from the source file on this book’s companion Web site.
LISTING 2-4: THE MAILING-LIST TEST PROGRAM
void ProcessEntries( BaseMailingListEntry *pEntry )
{
bool not_done = pEntry->First(); while ( not_done )
{
//Do something with the entry here.
//Get the next one
not_done = pEntry->Next();
}
}
int main(int argc, char **argv)
{
int choice = 0;
printf(“Enter 1 to use a file-based mailing list\n”); printf(“Enter 2 to enter data from the command line\n”); scanf(“%d”, &choice );
if ( choice == 1 )
{
char szBuffer[ 256 ]; printf(“Enter the file name: “); gets(szBuffer);
FileMailingListEntry fmle(szBuffer); ProcessEntries( &fmle );
}
(continued)
18 Technique 2: Using Abstraction to Extend Functionality
LISTING 2-4 (continued)
else
if ( choice == 2 )
{
CommandLineMailingListEntry cmle; ProcessEntries( &cmle );
}
else
printf(“Invalid option\n”);
return 0;
}
The main function for the driver really isn’t very busy — all it’s doing is creating whichever type of object you want to use. The ProcessEntries function is the fascinating one because it is a function that is working on a class type that doesn’t do anything — it has no idea which type of mailing-list entry object it is processing. Rather, it works from a pointer to the base class. If you run this program, you will find that it works as advertised, as you can see in Listing 2-5.
You could likewise create a file containing all entries that we just typed into the various fields above to
enter those fields into the system. You can do all of this without changing a single line of the ProcessEntries function! This is the power of pure virtual functions, and thus the power of abstraction.
When you create a set of classes that are all doing the same general thing, look for the common elements of the class and abstract them into a common base class. Then you can build on that common base in the future, more easily creating new versions of the classes as the need arises.
LISTING 2-5: THE MAILING-LIST PROGRAM IN OPERATION
Enter 1 to use a file-based mailing list Enter 2 to enter data from the command line 2
Enter the first name for the mailing list: Enter the last name of the person: Telles Enter the first name of the person: Matt Enter the first address line: 10 Main St Enter the second address line:
Enter the city: Anytown Enter the state: NY
Enter the zip code: 11518
Enter the next name for the mailing list: Enter the last name of the person:
3 Customizing a Class
with Virtual
Technique Functions
Save Time By
Understanding polymorphism
Overriding selected pieces of a class
Customizing classes at run-time
Using destructors with virtual functions
P olymorphism (from the Greek for “having many forms”) is what happens when you assign different meanings to a symbol or operator in different contexts. All well and good — but what does it
mean to us as C++ programmers?
Granted, the pure virtual function in C++ (discussed in Technique 2) is very useful, but C++ gives us an additional edge: The programmer can override only selected pieces of a class without forcing us to override the entire class. Although a pure virtual function requires the programmer to implement functionality, a virtual function allows you to override that functionality only if you wish to, which is an important distinction.
Allowing the programmer to customize a class by changing small parts of the functionality makes C++ the fastest development language. You should seriously consider making the individual functions in your classes virtual whenever possible. That way the next developer can modify the functionality with a minimum of fuss.
Small changes to the derived class are called virtual functions — in effect, they allow a derived class to override the functionality in a base class without making you tinker with the base class. You can use this capability to define a given class’s default functionality, while still letting end users of the class fine-tune that functionality for their own purposes. This approach might be used for error handling, or to change the way a given class handles printing, or just about anything else. In the next section, I show you how you can customize a class, using virtual functions to change the behavior of a base-class method at run-time.
20 Technique 3: Customizing a Class with Virtual Functions
Customizing a Class with Polymorphism
In order to understand how base classes can be customized using the polymorphic ability offered by virtual functions, let’s look at a simple example of customizing a base class in C++.
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 ch03.cpp, although you can use whatever you choose.
2. Type the code from Listing 3-1 into your file, substituting your own names for the italicized constants, variables, and filenames.
Better yet, copy the code from the source file on this book’s companion Web site.
LISTING 3-1: THE VIRTUAL FUNCTION BASE-CLASS SOURCE CODE
#include <string> #include <stdio.h>
class Fruit
{
public:
Fruit()
{
}
virtual ~Fruit()
{
printf(“Deleting a fruit\n”);
}
virtual std::string Color()
{
return “Unknown”;
}
void Print()
{
printf(“My color is: %s\n”, Color().c_str() );
}
};
class Orange : public Fruit
{
public:
Orange()
{
}
virtual std::string Color()
{
return “Orange”;
}
};
class Apple : public Fruit
{
public:
Apple()
{
}
virtual std::string Color()
{
return “Reddish”;
}
};
class Grape : public Fruit
{
public:
Grape()
{
}
virtual std::string Color()
{
return “Red”;
}
};
class GreenGrape : public Grape
{
public:
GreenGrape()
{
}
virtual std::string Color()
{
return “Green”;
}
};