- •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
Creating the Column Class 217
When you’re implementing a complex system, you can save immense time by breaking it down into the most discrete simple components you can make. This way, when change is needed later on, the amount of required effort is smaller and the ripple effect throughout the system is minimal.
Creating the Column Class
The first element of the spreadsheet is the column. Let’s build a simple class that maintains information about the column, and contains methods to work with that information. Here’s how:
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 ch39.cpp, although you can use whatever you choose.
2. Type the code from Listing 39-1 into your file.
Better yet, copy the code from the source file on this book’s companion Web site.
LISTING 39-1: THE COLUMN CLASS
#include <stdio.h> #include <string> #include <vector>
class Column
{
std::string _format; std::string _value;
public:
Column(void)
{
_format = “%s”; _value = “”;
}
Column( const char *format, const char *value )
{
_format = format; _value = value;
}
Column( const Column& aCopy )
{
_format = aCopy._format; _value = aCopy._value;
}
virtual ~Column()
{
}
Column operator=( const Column& aCopy )
{
_format = aCopy._format; _value = aCopy._value; return *this;
}
Column operator=(const char *value)
{
_value = value; return *this;
}
void setValue( const char *value )
{
_value = value;
}
std::string getValue( void )
{
return _value;
}
void setFormat( const char *format )
{
_format = format; |
|
|
} |
|
|
std::string getFormat( void ) |
|
|
{ |
|
|
return _format; |
|
|
} |
|
|
virtual std::string getFormattedString |
1 |
|
( void ) const |
|
|
{ |
|
char szBuffer[ 100 ]; sprintf(szBuffer, _format.c_str(), _value.c_str());
std::string sRet = szBuffer; return sRet;
}
};
218 Technique 39: Implementing a Spreadsheet
This code implements the most basic element of the spreadsheet system, the column. As you can see, we implement a complete class by adding methods for the constructors, destructors, assignment operators, and accessor methods. It also implements a virtual method (shown at
1) for returning the contents of the column in a formatted manner. Doing so allows other column types to be defined later on down the line, if you so desire.
3. Save your file in your code editor.
The next step is to implement the Row class that will hold an array of columns.
Creating the Row Class
After we have created a Column class, the next thing to do is to create a Row class that contains the columns we wish to store in the spreadsheet. You can think of the Column class as the data for a single cell, and the Row class as a list of cells for a given row.
1. Append the code from Listing 39-2 to the end of your file.
LISTING 39-2: THE ROW CLASS
class Row
{
std::vector< Column > _columns;
void Copy( const Row& aCopy )
{
std::vector< Column >::const_ iterator iter;
for ( iter = aCopy._columns.begin(); iter != aCopy._columns.end(); ++iter )
_columns.insert( _columns.end(), (*iter) );
}
public: Row(void)
{
}
Row( unsigned int numColumns )
{
for ( int i=0; i<numColumns; ++i )
{
Column c;
_columns.insert( _columns.end(),
c ); |
|
|
} |
|
|
} |
|
|
Row( const Row& aCopy ) |
|
|
{ |
|
|
Copy( aCopy ); |
|
|
} |
|
|
Row operator=( const Row& aCopy ) |
|
|
{ |
|
|
Copy( aCopy ); |
|
|
} |
|
3 |
Column& operator[]( int idx ) |
|
|
{ |
|
|
if ( idx < 0 || idx > _columns. |
|
|
size()-1 ) |
|
|
throw “Row: Index out |
|
2 |
of range”; |
|
|
return _columns[ idx ]; |
|
}
int NumColumns( void )
{
return _columns.size();
}
void Clear()
{
std::vector< Column >::iterator iter;
for ( iter = _columns.begin(); iter != _columns.end(); ++iter )
(*iter).setValue( “” );
}
void Print() const
{
std::vector< Column >::const_ iterator iter;
for ( iter = _columns.begin(); iter != _columns.end(); ++iter )
printf(“%s “, (*iter).getFormattedString().c_str() );
printf(“\n”);
}
};
Creating the Spreadsheet Class 219
Note that the Row class does not do anything with the columns, except to store them and give the end-user access to the ones they want. Note also that we use exception handling (shown at
2) to deal with the exceptional cases of arrayindices out of bounds. There are no good defaults possible here, so we just assume that it is a fatal error to ask for an invalid column number.
One thing that could be changed here is that the Row class does not handle resizing. Instead, the Row class simply assumes that the array of columns is always being instantiated from scratch. To properly resize a row, you would need to create a new array of columns of the right size, and then copy the existing columns into that row.
2. Save your file.
The final step of the process of implementing the class is to put together the actual Spreadsheet class. The next section shows how.
Creating the Spreadsheet Class
Finally, we come to the important part for the enduser: the Spreadsheet class itself. A spreadsheet, of course, is simply a list of the rows that make up the sheet, which in turn is a list of the columns that make up each row. Our spreadsheet will always be “square” — that is, it will contain an equal number of columns in each row.
1. Append the code from Listing 39-3 to the end of your file.
LISTING 39-3: THE SPREADSHEET CLASS
class Spreadsheet |
|
{ |
|
int |
_cols; |
std::vector< Row > _rows; |
|
std::string |
_name; |
void _BuildSheet( int nRows, int nCols )
{
//If there is anything already here, remove it.
_rows.erase(_rows.begin(), _rows.end());
// Now, add in the rows.
for ( int i=0; i<nRows; ++i )
{
Row row(nCols); _rows.insert( _rows.end(),
row );
}
}
void _InternalSetRows( const unsigned int nRows )
{
_BuildSheet( nRows, _cols );
}
void _InternalSetCols( const unsigned int nCols )
{
//Save the number of rows, so we can rebuild it.
int nRowCount = _rows.size();
//Set the number of columns. _cols = nCols;
//Now rebuild the rows.
_BuildSheet( nRowCount, nCols );
}
void Copy( const Spreadsheet& aCopy )
{
_InternalSetCols( aCopy. NumColumns() );
std::vector< Row >::const_iterator iter;
for ( iter = aCopy._rows.begin(); iter != aCopy._rows.end(); ++iter )
_rows.insert( _rows.end(), (*iter) );
_name = aCopy._name;
}
public:
Spreadsheet(void)
{
}
Spreadsheet( const char *name )
{
_name = name;
}
Spreadsheet( const char *name, unsigned int nRows, unsigned int nCols )
{
(continued)
220 Technique 39: Implementing a Spreadsheet
LISTING 39-3 (continued)
_name = name; _InternalSetCols( nCols ); _InternalSetRows( nRows );
}
Spreadsheet( const Spreadsheet& aCopy )
{
Copy( aCopy ); |
|
|
} |
|
|
Spreadsheet operator=( const |
|
|
Spreadsheet& aCopy ) |
|
|
{ |
|
|
Copy( aCopy ); |
|
|
return *this; |
|
|
} |
|
4 |
Row& operator[]( int idx ) |
|
|
{ |
|
if ( idx < 0 || idx > _rows. size()-1 )
throw “Spreadsheet: Index out of range”;
return _rows[idx];
}
Spreadsheet operator()(int r1, int c1, int r2, int c2)
{
Spreadsheet ret;
//Assign the pieces. ret.setNumColumns( c2-c1+1 ); ret.setNumRows( r2-r1+1 );
//Now copy over the chunk they want. try
{
for ( int r = r1; r <= r2; ++r ) for ( int c = c1; c <= c2;
++c ) ret[r-r1][c-c1] =
(*this)[r][c];
}
catch ( ... )
{
throw “Spreadsheet: Index out of range”;
}
return ret;
}
void setNumColumns( int nCols )
{
_InternalSetCols( nCols );
}
void setNumRows( int nRows )
{
_InternalSetRows( nRows );
}
int NumColumns() const
{
return _cols;
}
int NumRows() const
{
return _rows.size();
}
void setName( const char *name )
{
_name = name;
}
std::string getName( void ) const
{
return _name;
}
void Print() const
{
std::vector< Row >::const_iterator iter;
printf(“Sheet: %s\n”, _name.c_str() );
for ( iter = _rows.begin(); iter != _rows.end(); ++iter )
{
(*iter).Print();
}
}
void Clear()
{
std::vector< Row >::iterator iter; for ( iter = _rows.begin(); iter !=
_rows.end(); ++iter ) (*iter).Clear();
}
};
As I mentioned earlier in this technique, the spreadsheet is really just a holder of rows, which in turn are a holder of columns. The Column class is the only one that “understands” what the data being stored looks like, or how it is formatted, or how it will be displayed. The Spreadsheet class