Professional C__ - Marc Gregoire [331]
class GameBoard
{
public:
// The general-purpose GameBoard allows the user to specify its dimensions
GameBoard(size_t inWidth = kDefaultWidth,
size_t inHeight = kDefaultHeight);
GameBoard(const GameBoard& src); // Copy constructor
virtual ~GameBoard();
GameBoard& operator=(const GameBoard& rhs); // Assignment operator
void setPieceAt(size_t x, size_t y, const GamePiece& inPiece);
GamePiece& getPieceAt(size_t x, size_t y);
const GamePiece& getPieceAt(size_t x, size_t y) const;
size_t getHeight() const { return mHeight; }
size_t getWidth() const { return mWidth; }
static const size_t kDefaultWidth = 10;
static const size_t kDefaultHeight = 10;
protected:
void copyFrom(const GameBoard& src);
// Objects dynamically allocate space for the game pieces.
GamePiece** mCells;
size_t mWidth, mHeight;
};
Code snippet from GameBoard\GameBoard.h
getPieceAt() returns a reference to the piece at a specified spot instead of a copy of the piece. The GameBoard serves as an abstraction of a two-dimensional array, so it should provide array access semantics by giving the actual object at an index, not a copy of the object. Returning a reference from getPieceAt() works perfectly in this example. However, you should be careful when you write a method that returns something by reference, to make sure the reference stays valid. For example, if the GameBoard class would support a resize() method, this method would reallocate memory for mCells. By doing this, all references previously returned from getPieceAt() would become invalid. So, client code should not store such a reference for future use, but should call getPieceAt() right before using the returned reference.
This implementation of the class provides two versions of getPieceAt(), one of which returns a reference and one of which returns a const reference.
Here are the method definitions. The implementation is almost identical to the Spreadsheet class from Chapter 7. Production code would, of course, perform bounds checking in setPieceAt() and getPieceAt(). That code is omitted because it is not the point of this chapter:
GameBoard::GameBoard(size_t inWidth, size_t inHeight) :
mWidth(inWidth), mHeight(inHeight)
{
mCells = new GamePiece* [mWidth];
for (size_t i = 0; i < mWidth; i++) {
mCells[i] = new GamePiece[mHeight];
}
}
GameBoard::GameBoard(const GameBoard& src)
{
copyFrom(src);
}
GameBoard::~GameBoard()
{
// Free the old memory
for (size_t i = 0; i < mWidth; i++) {
delete [] mCells[i];
}
delete [] mCells;
mCells = nullptr;
}
void GameBoard::copyFrom(const GameBoard& src)
{
mWidth = src.mWidth;
mHeight = src.mHeight;
mCells = new GamePiece* [mWidth];
for (size_t i = 0; i < mWidth; i++) {
mCells[i] = new GamePiece[mHeight];
}
for (size_t i = 0; i < mWidth; i++) {
for (size_t j = 0; j < mHeight; j++) {
mCells[i][j] = src.mCells[i][j];
}
}
}
GameBoard& GameBoard::operator=(const GameBoard& rhs)
{
// Check for self-assignment
if (this == &rhs) {
return *this;
}
// Free the old memory
for (size_t i = 0; i < mWidth; i++) {
delete [] mCells[i];
}
delete [] mCells;
mCells = nullptr;
// Copy the new memory
copyFrom(rhs);
return *this;
}
void GameBoard::setPieceAt(size_t x, size_t y, const GamePiece& inElem)
{
mCells[x][y] = inElem;
}
GamePiece& GameBoard::getPieceAt(size_t x, size_t y)
{
return mCells[x][y];
}
const GamePiece& GameBoard::getPieceAt(size_t x, size_t y) const
{
return mCells[x][y];
}
Code snippet from GameBoard\GameBoard.cpp
This GameBoard class works pretty well. Assuming that you wrote a ChessPiece class, you can create GameBoard objects and use them like this:
GameBoard chessBoard(8, 8);
ChessPiece