19.9.3.1. Exercises: The Function Call operator()

Complete the implementations of the member functions of the Matrix class and write client code that thoroughly tests the class.

[ fromfile: useroperators.xml id: None ]

 

Example 19.20. solution/matrix/matrix.h

[ . . . . ]
class Matrix {
public:
    Matrix(int rows, int cols); // allocates and zeros all cells
    Matrix(const Matrix& mat);  // copy constructor - clones mat
    ~Matrix();
    double& operator()(int i, int j);
    double operator()(int i, int j) const;
    Matrix& operator=(const Matrix& mat); // deletes host content and clones mat
    Matrix operator+(const Matrix& mat) const; // matrix addition
    Matrix operator*(const Matrix& mat) const; // matrix multiplication
    bool operator==(const Matrix& mat) const;
    int getRows() const;
    int getCols() const;
    QString toString() const;
private:
    int m_Rows, m_Cols;
    double  **m_NumArray;
    void sweepClean(); // Refactoring utility
    void clone(const Matrix& mat); // Refactoring utility
    double rcprod(int row, const Matrix& mat, int col) const; // host-row dot mat-col
};
[ . . . . ]

<include src="solution/matrix/matrix.h" href="solution/matrix/matrix.h" role="solution" mode="cpp"/>


Example 19.21. solution/matrix/matrix.cpp

[ . . . . ]
// Allocates memory for a rows x cols array of double and 
// initializes each cell to zero.
Matrix:: Matrix(int rows, int cols):m_Rows(rows), m_Cols(cols) {
    m_NumArray = new double*[rows];
    for (int r = 0; r < rows; ++r) {
        m_NumArray[r]  = new double[cols];
        for(int c = 0; c < cols; ++c)
           m_NumArray[r][c]  = 0;
    }
}

// Refactoring utility function
void Matrix::sweepClean() {
   for (int r = 0; r < m_Rows; ++r)
      delete[] m_NumArray[r] ;
   delete[] m_NumArray;
}

Matrix::~Matrix() {
   sweepClean();
}


// Refactoring utility function
void Matrix::clone(const Matrix& mat) {
   m_NumArray = new double*[mat.m_Rows];
   for (int r = 0; r < mat.m_Rows; ++r) {
       m_NumArray[r] = new double[mat.m_Cols];
       for(int c = 0; c < mat.m_Cols; ++c)
            m_NumArray[r][c] = mat(r,c);
   }
}   
   
// Clones mat
Matrix::Matrix(const Matrix& mat) {
   clone(mat);
}

double& Matrix::operator()(int r, int c) {
   assert (r >= 0 && r < m_Rows && c >= 0 && c < m_Cols);
   return m_NumArray[r][c];
}

double Matrix::operator()(int r, int c) const {
   assert (r >= 0 && r < m_Rows && c >= 0 && c < m_Cols);
   return m_NumArray[r][c];   
}

// Deletes host content and then clones mat
Matrix& Matrix::operator=(const Matrix& mat) {
   sweepClean();
   clone(mat);
   return *this;
}

// matrix addition - assumes size compatibility
Matrix Matrix::operator+(const Matrix& mat) const {
   assert(m_Rows == mat.m_Rows && m_Cols == mat.m_Cols);
   Matrix mat2(*this);
   for (int r = 0; r < m_Rows; ++r)
      for(int c = 0; c < m_Cols; ++c)
           mat2(r,c) += mat(r,c);
   return mat2;
}

// Dot product of host row times other Matrix col.
// Assumes both have same size.
double Matrix::rcprod(int row, const Matrix& mat, int col) const {
   double res(0);
   for (int i = 0; i < m_Cols; ++i)
      res += m_NumArray[row][i] * mat(i, col);
   return res;
}

// matrix multiplication - assumes size compatibility
Matrix Matrix::operator*(const Matrix& mat) const {
   assert(m_Cols == mat.m_Rows);
   Matrix mat2(m_Rows, mat.m_Cols);
   for (int r = 0; r < m_Rows; ++r)
      for(int c = 0; c < mat.m_Cols; ++c)
           mat2(r,c) = rcprod(r, mat, c);
   return mat2;
}

bool Matrix::operator==(const Matrix& mat) const {
   if(m_Rows != mat.m_Rows || m_Cols != mat.m_Cols)
      return false;
   for (int r = 0; r < m_Rows; ++r)
      for(int c = 0; c < m_Cols; ++c)
         if(m_NumArray[r][c] != mat(r,c))
            return false;
   return true;
}

int Matrix::getRows() const {
   return m_Rows;
}

int Matrix::getCols() const {
   return m_Cols;
}

QString Matrix::toString() const {
   QString res;
   int pos;
   for (int r = 0; r < m_Rows; ++r) {
      for(int c = 0; c < m_Cols; ++c)
         res += QString("%1\t").arg(m_NumArray[r][c]);
      pos = res.lastIndexOf('\t');
      res.replace(pos, 1, '\n');
   }
   return res;
}

<include src="solution/matrix/matrix.cpp" href="solution/matrix/matrix.cpp" role="solution" mode="cpp"/>


Example 19.22. solution/matrix/matrix-test.cpp

#include <QTextStream>
#include "matrix.h"

QTextStream cout(stdout);

void fill(Matrix& m, double d) {
   for(int r = 0; r < m.getRows(); ++r)
      for(int c = 0; c < m.getCols(); ++c)
         m(r,c) = d;
}

/* This client code does not test the Matrix class thoroughly.
   It gives some hints for making a more thorough test. */

int main() {
   Matrix m1(3,4), m2(4,3);
   fill(m1, 2);
   fill(m2, 3);
   Matrix m3(m1 * m2);
   cout << m3.toString() << endl;   
}

<include src="solution/matrix/matrix-test.cpp" href="solution/matrix/matrix-test.cpp" role="solution" mode="cpp"/>