Professional C__ - Marc Gregoire [88]
Many of the concepts in this chapter arise in advanced C++ programming, especially in the standard template library.
DYNAMIC MEMORY ALLOCATION IN OBJECTS
Sometimes you don’t know how much memory you will need before your program actually runs. As you know, the solution is to dynamically allocate as much space as you need during program execution. Classes are no exception. Sometimes you don’t know how much memory an object will need when you write the class. In that case, the object should dynamically allocate memory.
Dynamically allocated memory in objects provides several challenges, including freeing the memory, handling object copying, and handling object assignment.
The Spreadsheet Class
Chapter 6 introduced the SpreadsheetCell class. This chapter moves on to write the Spreadsheet class. As with the SpreadsheetCell class, the Spreadsheet class will evolve throughout this chapter. Thus, the various attempts do not always illustrate the best way to do every aspect of class writing. To start, a Spreadsheet is simply a two-dimensional array of SpreadsheetCells, with methods to set and retrieve cells at specific locations in the Spreadsheet. Although most spreadsheet applications use letters in one direction and numbers in the other to refer to cells, this Spreadsheet uses numbers in both directions. Here is a first attempt at a class definition for a simple Spreadsheet class:
#include "SpreadsheetCell.h"
class Spreadsheet
{
public:
Spreadsheet(int inWidth, int inHeight);
void setCellAt(int x, int y, const SpreadsheetCell& cell);
SpreadsheetCell getCellAt(int x, int y);
protected:
bool inRange(int val, int upper);
int mWidth, mHeight;
SpreadsheetCell** mCells;
};
Code snippet from Spreadsheet\Spreadsheet.h
The Spreadsheet class uses normal pointers for the mCells array. This is done throughout this chapter to show the consequences and to explain how you should handle dynamic memory in classes. In production code, you should use one of the standard C++ containers, like std::vector which is briefly introduced in Chapter 1 and discussed in detail in Chapter 12.
Note that the Spreadsheet class does not contain a standard two-dimensional array of SpreadsheetCells. Instead, it contains a SpreadsheetCell**. The reason is that each Spreadsheet object might have different dimensions, so the constructor of the class must dynamically allocate the two-dimensional array based on the client-specified height and width. In order to allocate dynamically a two-dimensional array you need to write the following code:
#include "Spreadsheet.h"
Spreadsheet::Spreadsheet(int inWidth, int inHeight) :
mWidth(inWidth), mHeight(inHeight)
{
mCells = new SpreadsheetCell* [mWidth];
for (int i = 0; i < mWidth; i++) {
mCells[i] = new SpreadsheetCell[mHeight];
}
}
Code snippet from Spreadsheet\Spreadsheet.cpp
The resultant memory for a Spreadsheet called s1 on the stack with width four and height three is shown in Figure 7-1.
FIGURE 7-1
The implementations of the set and retrieval methods are straightforward:
void Spreadsheet::setCellAt(int x, int y, const SpreadsheetCell& cell)
{
if (!inRange(x, mWidth) || !inRange(y, mHeight)) {
throw std::out_of_range("");
}
mCells[x][y] = cell;
}
SpreadsheetCell Spreadsheet::getCellAt(int x, int y)
{
if (!inRange(x, mWidth) || !inRange(y, mHeight)) {
throw std::out_of_range("");
}
return mCells[x][y];
}
Code snippet from Spreadsheet\Spreadsheet.cpp
Note that these two methods use a helper method inRange() to check that x and y represent valid coordinates in the spreadsheet. Attempting to access an array element at an out-of-range index will cause the program to malfunction. This example uses exceptions which are mentioned in Chapter 1 and described in detail in Chapter 10.
Freeing