Online Book Reader

Home Category

Professional C__ - Marc Gregoire [351]

By Root 1395 0

psGrid.setElementAt(1, 1, &x);

Grid psGrid2(psGrid);

Grid psGrid3;

psGrid3 = psGrid2;

const Grid& psGrid4 = psGrid2;

cout << *(psGrid4.getElementAt(1, 1)) << endl;

x=6;

cout << *(psGrid4.getElementAt(1, 1)) << endl;

Code snippet from GridPartialPtr\GridPtrTest.cpp

The psGrid4 grid is storing pointers, so psGrid4.getElementAt(1, 1) will return a pointer to x. Changing the value of the variable x will change the result of dereferencing the pointer returned by getElementAt(1, 1). The output of the above code should be as follows:

3

6

At this point, you’re probably wondering whether this really works. We sympathize with your skepticism. One of the authors was so surprised by this syntax when he first read about it that he didn’t believe it actually worked until he was able to try it out. If you don’t believe us, try it out yourself. Here are the method implementations. Pay close attention to the template line syntax before each method:

template

Grid::Grid(size_t inWidth, size_t inHeight) :

mWidth(inWidth), mHeight(inHeight)

{

mCells = new T** [mWidth];

for (size_t i = 0; i < mWidth; i++) {

mCells[i] = new T*[mHeight];

}

}

template

Grid::Grid(const Grid& src)

{

copyFrom(src);

}

template

Grid::~Grid()

{

// Free the old memory.

for (size_t i = 0; i < mWidth; i++) {

delete [] mCells[i];

}

delete [] mCells;

mCells = nullptr;

}

template

void Grid::copyFrom(const Grid& src)

{

mWidth = src.mWidth;

mHeight = src.mHeight;

mCells = new T** [mWidth];

for (size_t i = 0; i < mWidth; i++) {

mCells[i] = new T*[mHeight];

}

for (size_t i = 0; i < mWidth; i++) {

for (size_t j = 0; j < mHeight; j++) {

mCells[i][j] = src.mCells[i][j];

}

}

}

template

Grid& Grid::operator=(const Grid& 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;

}

template

void Grid::setElementAt(size_t x, size_t y, T* inElem)

{

mCells[x][y] = inElem;

}

template

T* Grid::getElementAt(size_t x, size_t y) const

{

return mCells[x][y];

}

Code snippet from GridPartialPtr\GridPtr.h

For brevity, this example is not implementing any deep copying of the pointers. This would be a good exercise for the reader.

EMULATING FUNCTION PARTIAL SPECIALIZATION WITH OVERLOADING


The C++ standard does not permit partial template specialization of functions. Instead, you can overload the function with another template. The difference is subtle. Suppose that you want to write a specialization of the Find() function, presented in the previous chapter, that dereferences the pointers to use operator== directly on the objects pointed to. Following the syntax for class template partial specialization, you might be tempted to write this:

template

size_t Find(T*& value, T** arr, size_t size)

{

for (size_t i = 0; i < size; i++) {

if (*arr[i] == *value) {

return i; // Found it; return the index

}

}

return NOT_FOUND; // failed to find it; return NOT_FOUND

}

Code snippet from FunctionTemplatePtr\FindTemplatePtr.cpp

However, that syntax declares a partial specialization of the function template, which the C++ standard does not allow. The standard way to implement the behavior you want is to write a new template for Find():

template

size_t Find(T*& value, T** arr, size_t size)

{

for (size_t i = 0; i < size; i++) {

if (*arr[i] == *value) {

return i; // Found it; return the index

}

}

return NOT_FOUND; // failed to find it; return NOT_FOUND

}

Code snippet from FunctionTemplatePtr\FindTemplatePtr.cpp

The difference might seem trivial and academic, but it makes the difference between portable standard code and code that is not standard compliant.

More on Deduction

You can define in one program the original Find() template,

Return Main Page Previous Page Next Page

®Online Book Reader