Professional C__ - Marc Gregoire [91]
void Spreadsheet::copyFrom(const Spreadsheet& src)
{
mWidth = src.mWidth;
mHeight = src.mHeight;
mCells = new SpreadsheetCell* [mWidth];
for (int i = 0; i < mWidth; i++) {
mCells[i] = new SpreadsheetCell[mHeight];
}
for (int i = 0; i < mWidth; i++) {
for (int j = 0; j < mHeight; j++) {
mCells[i][j] = src.mCells[i][j];
}
}
}
Spreadsheet::Spreadsheet(const Spreadsheet& src)
{
copyFrom(src);
}
Spreadsheet& Spreadsheet::operator=(const Spreadsheet& rhs)
{
// Check for self-assignment.
if (this == &rhs) {
return *this;
}
// Free the old memory.
for (int i = 0; i < mWidth; i++) {
delete [] mCells[i];
}
delete [] mCells;
mCells = nullptr;
// Copy the new memory.
copyFrom(rhs);
return *this;
}
Code snippet from Spreadsheet\Spreadsheet.cpp
Disallowing Assignment and Pass-By-Value
Sometimes when you dynamically allocate memory in your class, it’s easiest just to prevent anyone from copying or assigning to your objects. You can do this by marking your operator= and copy constructor private. That way, if anyone tries to pass the object by value, return it from a function or method, or assign to it, the compiler will complain. Here is a Spreadsheet class definition that prevents assignment and pass-by-value:
class Spreadsheet
{
// Code omitted for brevity
private:
Spreadsheet(const Spreadsheet& src);
Spreadsheet& operator=(const Spreadsheet& rhs);
};
Code snippet from SpreadsheetNoCopyAssign\Spreadsheet.h
You don’t need to provide implementations for private copy constructors and assignment operators. The linker will never look for them because the compiler won’t allow code to call them. When you write code to copy or assign to a Spreadsheet object, the compiler will complain with a message like:
'=' : cannot access private member declared in class 'Spreadsheet'.
Instead of making your operator= and copy constructor private, you can make them protected if you want to allow subclasses to use them. Subclasses are discussed in Chapter 8.
DIFFERENT KINDS OF DATA MEMBERS
C++ gives you many choices for data members. In addition to declaring simple data members in your classes, you can create static data members that all objects of the class share, const members, reference members, const reference members, and more. This section explains the intricacies of these different kinds of data members.
static Data Members
Sometimes giving each object of a class a copy of a variable is overkill or won’t work. The data member might be specific to the class, but not appropriate for each object to have its own copy. For example, you might want to give each spreadsheet a unique numerical identifier. You would need a counter that starts at 0 from which each new object could obtain its ID. This spreadsheet counter really belongs to the Spreadsheet class, but it doesn’t make sense for each Spreadsheet object to have a copy of it because you would have to keep all the counters synchronized somehow. C++ provides a solution with static data members. A static data member is a data member associated with a class instead of an object. You can think of static data members as global variables specific to a class. Here is the Spreadsheet class definition, including the new static counter data member:
class Spreadsheet
{
// Omitted for brevity
protected:
static int sCounter = 0;
};
Code snippet from SpreadsheetDataMembers\Spreadsheet.h
With C++11, that’s all you need to do. If you are using C++ prior to C++11, it is a bit clumsier. In that case, you cannot do initialization in the class definition. In addition to listing static class members in the class definition, you will have to allocate space for them in a source file, usually the source file in which you place your class method definitions. You can initialize them at the same time, but note that unlike