NivaarExam PrepOfficial exam papers ↗

25-Comp-A4 Program Design and Data Structures · December 2017

Question 6 of 9: Object-Oriented Design — A C++ Matrix Class

Nivaar worked solution (AI-drafted; not reviewed by a licensed engineer)

Notes on this paper

Paper format. 98-Comp-A4 Program Design and Data Structures, December 2017 — 3 hours, closed book, no calculator permitted. Nine questions of equal weight (20 marks each: 1 and 7 split as (a) 10 + (b) 10, 8 split as (a) 15 + (b) 5); candidates answer any six, so a complete paper is 120 marks. Pseudocode or any high-level language is accepted, and the examiner's note states explicitly that marking emphasises the operation of the program, not syntactic details. All nine questions are answered below, because the whole set is the more useful revision resource. Answers are given in C or C++ as the question dictates; each is compilable as written (or corrected where the printed paper itself has a slip), but a clear, correctly reasoned pseudocode answer would earn the same marks.

Reference texts for this subject.

  • Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms, 4th ed. — tree traversals and BSTs (ch. 12), recursion and divide-and-conquer (ch. 2, 4), asymptotic analysis (ch. 3).
  • Weiss, Data Structures and Algorithm Analysis in C, 2nd ed. — linked lists and stacks (ch. 3), binary trees (ch. 4).
  • Deitel & Deitel, C++ How to Program, 10th ed. — class design and operator overloading (ch. 9–11), file streams (ch. 14).
  • Kernighan & Ritchie, The C Programming Language, 2nd ed. — arrays and file I/O (ch. 1, 7), pointers, structures and linked lists (ch. 5–6).
  • Stroustrup, The C++ Programming Language, 4th ed. — class templates and value semantics (ch. 3, 25–27).

The Computer Engineering citation list is built around architecture and networking texts (Patterson & Hennessy, Tanenbaum, Mano); this subject is programming and data structures, so the works above are cited instead.

Question 6: Object-Oriented Design — A C++ Matrix Class (20 marks)

Question text not reproduced: the examination questions are © Engineers and Geoscientists BC. Open the official past paper (linked at the top of this page) to read the question, then follow the worked solution below.

Given. A requirement for a double-only Matrix class supporting sized (with/without initial value) construction, read/write element access, +, -, * and ==, and printing, split across a header and an implementation file.

Find. A Matrix.h / Matrix.cc pair implementing the class.

Approach. Store rows, cols and a single heap-allocated double* buffer (row-major), with operator()(i,j) returning a reference for combined read/write access. Implement the Big-Three (copy constructor, destructor, operator=) because the class owns heap memory; overload +/-/* to check dimension compatibility, throwing on mismatch (assumption, stated explicitly since the question leaves error handling open); overload << for printing rather than a named print() method, and == for exact elementwise comparison.

  1. Design the storage layout and member list. A single flat array indexed as data[i*cols+j] avoids the double indirection of an array of pointers and lets the copy constructor, destructor and assignment operator each manage exactly one allocation.
  2. Write Matrix.h.
    // Matrix.h
    #ifndef MATRIX_H
    #define MATRIX_H
    #include <iostream>
    
    class Matrix {
    public:
        Matrix(int rows, int cols, double init = 0.0);   // sized, optional init
        Matrix(const Matrix& other);                     // copy constructor
        ~Matrix();
        Matrix& operator=(const Matrix& other);
    
        double& operator()(int i, int j);                // read & write access
        double  operator()(int i, int j) const;
    
        Matrix operator+(const Matrix& rhs) const;
        Matrix operator-(const Matrix& rhs) const;
        Matrix operator*(const Matrix& rhs) const;
        bool    operator==(const Matrix& rhs) const;
    
        int numRows() const { return rows_; }
        int numCols() const { return cols_; }
    
        friend std::ostream& operator<<(std::ostream& os, const Matrix& m);
    
    private:
        int rows_, cols_;
        double* data_;   // row-major, size rows_*cols_
    };
    
    #endif
    
  3. Write Matrix.cc.
    // Matrix.cc
    #include "Matrix.h"
    #include <stdexcept>
    
    Matrix::Matrix(int rows, int cols, double init)
        : rows_(rows), cols_(cols), data_(new double[rows * cols])
    {
        for (int k = 0; k < rows_ * cols_; k++) data_[k] = init;
    }
    
    Matrix::Matrix(const Matrix& other)
        : rows_(other.rows_), cols_(other.cols_),
          data_(new double[other.rows_ * other.cols_])
    {
        for (int k = 0; k < rows_ * cols_; k++) data_[k] = other.data_[k];
    }
    
    Matrix::~Matrix() { delete[] data_; }
    
    Matrix& Matrix::operator=(const Matrix& other)
    {
        if (this == &other) return *this;
        delete[] data_;
        rows_ = other.rows_; cols_ = other.cols_;
        data_ = new double[rows_ * cols_];
        for (int k = 0; k < rows_ * cols_; k++) data_[k] = other.data_[k];
        return *this;
    }
    
    double& Matrix::operator()(int i, int j) { return data_[i * cols_ + j]; }
    double  Matrix::operator()(int i, int j) const { return data_[i * cols_ + j]; }
    
    Matrix Matrix::operator+(const Matrix& rhs) const
    {
        if (rows_ != rhs.rows_ || cols_ != rhs.cols_)
            throw std::invalid_argument("dimension mismatch in +");
        Matrix result(rows_, cols_);
        for (int k = 0; k < rows_ * cols_; k++) result.data_[k] = data_[k] + rhs.data_[k];
        return result;
    }
    
    Matrix Matrix::operator-(const Matrix& rhs) const
    {
        if (rows_ != rhs.rows_ || cols_ != rhs.cols_)
            throw std::invalid_argument("dimension mismatch in -");
        Matrix result(rows_, cols_);
        for (int k = 0; k < rows_ * cols_; k++) result.data_[k] = data_[k] - rhs.data_[k];
        return result;
    }
    
    Matrix Matrix::operator*(const Matrix& rhs) const
    {
        if (cols_ != rhs.rows_)
            throw std::invalid_argument("dimension mismatch in *");
        Matrix result(rows_, rhs.cols_, 0.0);
        for (int i = 0; i < rows_; i++)
            for (int j = 0; j < rhs.cols_; j++)
                for (int k = 0; k < cols_; k++)
                    result(i, j) += (*this)(i, k) * rhs(k, j);
        return result;
    }
    
    bool Matrix::operator==(const Matrix& rhs) const
    {
        if (rows_ != rhs.rows_ || cols_ != rhs.cols_) return false;
        for (int k = 0; k < rows_ * cols_; k++)
            if (data_[k] != rhs.data_[k]) return false;
        return true;
    }
    
    std::ostream& operator<<(std::ostream& os, const Matrix& m)
    {
        for (int i = 0; i < m.rows_; i++) {
            for (int j = 0; j < m.cols_; j++)
                os << m(i, j) << ' ';
            os << '\n';
        }
        return os;
    }
    
  4. Confirm the arithmetic with a numeric example. For $A=\begin{pmatrix}1&2\\3&4\end{pmatrix}$ and $B=\begin{pmatrix}5&6\\7&8\end{pmatrix}$: $A+B=\begin{pmatrix}6&8\\10&12\end{pmatrix}$, $A-B=\begin{pmatrix}-4&-4\\-4&-4\end{pmatrix}$, and $A\times B=\begin{pmatrix}1\cdot5+2\cdot7 & 1\cdot6+2\cdot8\\ 3\cdot5+4\cdot7 & 3\cdot6+4\cdot8\end{pmatrix}=\begin{pmatrix}19&22\\43&50\end{pmatrix}$, matching standard matrix-arithmetic identities and confirming the triple-loop product implementation above. $$\boxed{A\times B=\begin{pmatrix}19&22\\43&50\end{pmatrix}}$$
Question 6 — results
Operation on $A,B$ (2×2, values 1–8)Result
$A+B$[[6,8],[10,12]]
$A-B$[[-4,-4],[-4,-4]]
$A\times B$[[19,22],[43,50]]
$A==A$ (copy)true

Check: the question leaves error handling on dimension mismatch unspecified; this answer assumes throwing std::invalid_argument is acceptable (stated explicitly, as the question allows freedom in "exact syntax of some of the above operations").