|
||||
|
Section 16:
|
[16.18] But the above Matrix class is specific to Fred! Isn't there a way to make it generic?
Yep; just use templates: Here's how this can be used:
#include "Fred.h" // To get the definition for class Fred
// The code for Matrix<T> is shown below...
void someFunction(Fred& fred);
void manipulateArray(unsigned nrows, unsigned ncols)
{
Matrix<Fred> matrix(nrows, ncols); // Construct a Matrix<Fred> called matrix
for (unsigned i = 0; i < nrows; ++i) {
for (unsigned j = 0; j < ncols; ++j) {
// Here's the way you access the (i,j) element:
someFunction( matrix(i,j) );
// You can safely "return" without any special delete code:
if (today == "Tuesday" && moon.isFull())
return; // Quit early on Tuesdays when the moon is full
}
}
// No explicit delete code at the end of the function either
}
Now it's easy to use Matrix<T> for things other than Fred. For
example, the following uses a Matrix of std::string (where
std::string is the standard string class):
#include <string>
void someFunction(std::string& s);
void manipulateArray(unsigned nrows, unsigned ncols)
{
Matrix<std::string> matrix(nrows, ncols); // Construct a Matrix<std::string>
for (unsigned i = 0; i < nrows; ++i) {
for (unsigned j = 0; j < ncols; ++j) {
// Here's the way you access the (i,j) element:
someFunction( matrix(i,j) );
// You can safely "return" without any special delete code:
if (today == "Tuesday" && moon.isFull())
return; // Quit early on Tuesdays when the moon is full
}
}
// No explicit delete code at the end of the function either
}
You can thus get an entire family of classes from a
template. For example, Matrix<Fred>,
Matrix<std::string>, Matrix< Matrix<std::string> >, etc.
Here's one way that the template can be implemented: template<typename T> // See section on templates for more class Matrix { public: Matrix(unsigned nrows, unsigned ncols); // Throws a BadSize object if either size is zero class BadSize { }; // Based on the Law Of The Big Three: ~Matrix(); Matrix(const Matrix<T>& m); Matrix<T>& operator= (const Matrix<T>& m); // Access methods to get the (i,j) element: T& operator() (unsigned i, unsigned j); ← subscript operators often come in pairs T const& operator() (unsigned i, unsigned j) const; ← subscript operators often come in pairs // These throw a BoundsViolation object if i or j is too big class BoundsViolation { }; private: unsigned nrows_, ncols_; T* data_; }; template<typename T> inline T& Matrix<T>::operator() (unsigned row, unsigned col) { if (row >= nrows_ || col >= ncols_) throw BoundsViolation(); return data_[row*ncols_ + col]; } template<typename T> inline T const& Matrix<T>::operator() (unsigned row, unsigned col) const { if (row >= nrows_ || col >= ncols_) throw BoundsViolation(); return data_[row*ncols_ + col]; } template<typename T> inline Matrix<T>::Matrix(unsigned nrows, unsigned ncols) : nrows_ (nrows) , ncols_ (ncols) //, data_ <--initialized below (after the 'if/throw' statement) { if (nrows == 0 || ncols == 0) throw BadSize(); data_ = new T[nrows * ncols]; } template<typename T> inline Matrix<T>::~Matrix() { delete[] data_; } |
|||