Your goal for this breakout is to create an easy-to-use Matrix class in C++ that
ID: 3591568 • Letter: Y
Question
Your goal for this breakout is to create an easy-to-use Matrix class in C++ that makes use of dynamic memory allocation and includes basic matrix operations both in the form of regular functions and via operator overloading.
Here is the basic prototype for the Matrix class (you may need to add more to it to support some of the additional features listed further down in this document):
}; // Matrix
Important Class Details
Your Matrix implementation will contain elements of type double. In the prototype presented above, the term scaler refers to a regular number. For example, if you add a scalar to a matrix, then each element in the matrix gets that number added to it. Contrast this with the member functions that take a Matrix as their parameter. These functions represent regular matrix operations. For some of these operations (e.g., multiplication, transpose, etc.), you may need to consult some sort of reference in order to recall the exact procedure/meaning behind the operation.
NOTE: You MAY assume valid input for all operations.
NOTE: You MAY NOT use library classes such as std::array, std::vector, std::list, etc. for this project. You must
implement your Matrix class internally using a dynamically allocated array. Example Usage 1
Matrix c = a.multiply(b); cout<<"["<<c.at(0,0)<<"]"<<endl //[7]
The usage of the member function at(uint, uint) is what facilities our ability to perform operations such as a.at(0, 0) = 1. If you implement this function carefully, then this behavior should work because the function returns a reference to an element. In order to support the constructor overload (i.e., matrix construction using an initializer list), you will need to use a standard template library (STL) class called std::initializer list1. The type signature for the << parameter representing the list should be std::initializer list<std::initializer list<double>> or simply i list if you use the provided typedef
Operator Overloading
You will also need to overload operators in order to support the following functionality. It is up to you whether or not these should be member or non-member overloads.
You should also support stream insertion (similar to overriding the toString method in Java) so that your matrices can easily be printed.
You should also support matrix assignment using an initializer list (to easily overwrite existing elements) that looks like the following:
{ 3, 4 }};
In order to support this last operator overload (i.e., matrix assignment using an initializer list), you will need to use a standard template library (STL) class called std::initializer list. The type signature for the << parameter representing the list should be std::initializer list<std::initializer list<double>> or simply i list if you use the provided typedef. It is preferred that you specify the parameter as a const i list & in order to avoid any unnecessary copying.
Additional Features
In addition to the requirements for listed above, you need to make sure your Matrix class supports the following features:
• Overloaded Function Call Operator (operator()(uint row, uint col)): After creating a (non-dynamically allocated) Matrix object, the user should be able to access the elements using the function call operator (as an alternative to using the at function):
• Overloaded Copy Assignment Operator (operator=(const Matrix &)): You should have already overloaded the assignment operator to take in a special kind of initializer list. Now you need to provide an additional overload that supports copy assignment. This will make your Matrix class more consistent since copy assignment parallels copy construction. Here is an example:
• Overloaded Non-Member Arithmetic Operators for Scalars: You should have already created overloads to support the basic arithmetic operations where the right-hand-side of an operation is a scaler value. Now you need to implement operator overloads so that scalers can be used on the left-hand-side of an operation. Here is an example showing the operators that you need to support:
• Overloaded Unary Minus Operator (operator-()): You need to support negating your Matrix objects:
Explanation / Answer
Given below is the code for the question with output. Please don't forget to rate if it helped. Thank you.
Matrix.h
#ifndef Matrix_h
#define Matrix_h
#include <initializer_list>
#include <iostream>
using namespace std;
typedef unsigned int uint;
typedef initializer_list<initializer_list<double>> i_list;
class Matrix {
private:
double **data;
uint rows, cols;
void deleteMatrix();
public:
Matrix(uint rows, uint cols);
Matrix(const i_list & list);
Matrix(const Matrix & m);
Matrix& operator =(const Matrix &M); //assignment operator
Matrix & operator = (const i_list & list); //assignment with initializer list
~Matrix();
Matrix add(double s) const;
Matrix add(const Matrix & m) const;
Matrix subtract(double s) const;
Matrix subtract(const Matrix & m) const;
Matrix multiply(double s) const;
Matrix multiply(const Matrix & m) const;
Matrix divide(double s) const;
friend ostream& operator << (ostream &out, const Matrix &mat);
Matrix operator +(double s) const;
Matrix operator +(const Matrix & m) const;
Matrix operator -(double s) const;
Matrix operator -(const Matrix & m) const;
Matrix operator *(double s) const;
Matrix operator *(const Matrix & m) const;
Matrix operator /(double s) const;
Matrix operator -() const; //unary minus
double& operator ()(uint row, uint col);
const double operator ()(uint row, uint col) const;
Matrix t() const;
const uint numRows() const;
const uint numCols() const;
double & at(uint row, uint col);
const double & at (uint row, uint col) const; // get element at row,col (when using a const object)
}; // Matrix
#endif /* Matrix_h*/
Matrix.cpp
#include "Matrix.h"
#include <iostream>
using namespace std;
Matrix::Matrix(uint rows, uint cols)
{
this->rows = rows;
this->cols = cols;
data = new double*[rows];
for(int i = 0; i < rows; i++)
{
data[i] = new double[cols];
for(int j = 0; j < cols; j++)
data[i][j] = 0;
}
}
Matrix::Matrix(const i_list & list)
{
data = NULL;
*this = list;
}
Matrix & Matrix::operator = (const i_list & list) //assignment with initializer list
{
deleteMatrix();
rows = list.size();
i_list::iterator it1 = list.begin();
cols = (*it1).size();
data = new double*[rows];
for(int i = 0;it1 != list.end();i++, it1++)
{
data[i] = new double[cols];
initializer_list<double>::iterator it2 = (*it1).begin();
for(int j = 0; it2 != (*it1).end(); it2++, j++)
data[i][j] = *it2;
}
return *this;
}
Matrix& Matrix::operator = (const Matrix &rhs)
{
if(this != &rhs) //avoid self copy
{
deleteMatrix();
rows = rhs.rows;
cols = rhs.cols;
data = new double*[rows];
for(int i = 0; i < rows; i++)
{
data[i] = new double[cols];
for(int j = 0; j < cols; j++)
data[i][j] = rhs.data[i][j];
}
}
return *this;
}
Matrix::Matrix(const Matrix& rhs)
{
data = NULL;
*this = rhs;
}
void Matrix::deleteMatrix()
{
if(data == NULL) return;
for(int i = 0; i < rows; i++)
delete []data[i];
delete []data;
rows = cols = 0;
data = NULL;
}
Matrix::~Matrix()
{
deleteMatrix();
}
Matrix Matrix:: add(double s) const
{
Matrix result(rows, cols);
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
result.data[i][j] = data[i][j] + s;
}
}
return result;
}
Matrix Matrix::add(const Matrix & m) const
{
Matrix result(rows, cols);
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
result.data[i][j] = data[i][j] + m.data[i][j];
}
}
return result;
}
Matrix Matrix::subtract(double s) const
{
Matrix result(rows, cols);
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
result.data[i][j] = data[i][j]- s;
}
}
return result;
}
Matrix Matrix::subtract(const Matrix & m) const
{
{
Matrix result(rows, cols);
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
result.data[i][j] = data[i][j] - m.data[i][j];
}
}
return result;
}
}
Matrix Matrix::multiply(double s) const
{
Matrix result(rows, cols);
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
result.data[i][j] = data[i][j] * s;
}
}
return result;
}
Matrix Matrix::multiply(const Matrix & m) const
{
Matrix result(rows, m.cols);
for(int i = 0; i < result.rows; i++)
{
for(int j = 0; j < result.cols; j++)
{
result.data[i][j] = 0;
for(int k = 0; k < cols; k++)
result.data[i][j] += data[i][k] * m.data[k][j];
}
}
return result;
}
Matrix Matrix::divide(double s) const
{
Matrix result(rows, cols);
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
result.data[i][j] = data[i][j] / s;
}
}
return result;
}
Matrix Matrix::operator -() const
{
Matrix result(rows, cols);
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
{
result.data[i][j] = -data[i][j] ;
}
}
return result;
}
Matrix Matrix::operator +(double s) const
{
return this->add (s);
}
Matrix Matrix::operator +(const Matrix & m) const
{
return this->add(m);
}
Matrix Matrix::operator -(double s) const
{
return this->subtract(s);
}
Matrix Matrix::operator -(const Matrix & m) const
{
return this->subtract(m);
}
Matrix Matrix::operator *(double s) const
{
return this->multiply(s);
}
Matrix Matrix:: operator *(const Matrix & m) const
{
return this->multiply(m);
}
Matrix Matrix::operator /(double s) const
{
return this->divide(s);
}
double& Matrix::operator ()(uint row, uint col)
{
return this->at(row, col);
}
const double Matrix::operator ()(uint row, uint col) const
{
return this->at(row, col);
}
Matrix Matrix::t() const
{
Matrix result(cols, rows);
for(int i = 0; i < rows; i++)
{
for(int j = 0; j < cols; j++)
result.data[j][i] = data[i][j];
}
return result;
}
const uint Matrix::numRows() const
{
return rows;
}
const uint Matrix:: numCols() const
{
return cols;
}
double & Matrix::at(uint row, uint col)
{
return data[row][col];
}
const double & Matrix::at (uint row, uint col) const // get element at row,col (when using a const object)
{
return data[row][col];
}
ostream& operator << (ostream &out, const Matrix &mat)
{
for(int i = 0; i < mat.rows; i++)
{
out << endl;
for(int j = 0; j < mat.cols; j++)
out << mat(i,j) << " ";
}
out << endl;
return out;
}
test.cpp
#include "Matrix.h"
#include <iostream>
using namespace std;
int main()
{
Matrix a(2, 2);
a.at(0, 0) = 1; // [ 1, 2 ]
a.at(0, 1) = 2; // [ 1, 3 ]
a.at(1, 0) = 1;
a.at(1, 1) = 3;
Matrix b(2, 1);
b.at(0, 0) = 3; // [ 3 ]
b.at(1, 0) = 2; // [ 2 ]
Matrix c = a.multiply(b);
cout<<"["<<c.at(0,0)<<"]"<<endl //[7]
<< "[ " << c.at(1, 0) << " ]" << endl; // [ 9 ]
Matrix d = {{1, 2}, // this will implicitly call the overloaded constructor
{3, 4}}; // that takes an initializer list
d = {{ 1, 2 },
{ 3, 4 }};//using initializer list
cout << "Matrix A " << endl << a << endl;
cout << "Matrix B " << endl << b << endl;
cout << "Matrix C" << endl << c << endl;
cout << "Matrix D" << endl << d << endl;
Matrix c0 = a + 5.2;
Matrix c1 = a + a; // NOTE: these examples actually end up calling the copy constructor
Matrix c2 = a - 3.5; // e.g., this line is the same as Matrix c2(a - 3.5);
Matrix c3 = b - b;
Matrix c4 = a * 2.1;
Matrix c5 = a * b;
Matrix c6 = a / 2.0;
cout << "Matrix c0" << endl << c0 << endl;
cout << "Matrix c1" << endl << c1 << endl;
cout << "Matrix c2" << endl << c2 << endl;
cout << "Matrix c3" << endl << c3 << endl;
cout << "Matrix c4" << endl << c4 << endl;
cout << "Matrix c5" << endl << c5 << endl;
cout << "Matrix c6" << endl << c6 << endl;
cout << "Matrix -A" << endl << -a << endl;
cout << "Matrix Transpose A" << endl << a.t() << endl;
}
output
[7]
[ 9 ]
Matrix A
1 2
1 3
Matrix B
3
2
Matrix C
7
9
Matrix D
1 2
3 4
Matrix c0
6.2 7.2
6.2 8.2
Matrix c1
2 4
2 6
Matrix c2
-2.5 -1.5
-2.5 -0.5
Matrix c3
0
0
Matrix c4
2.1 4.2
2.1 6.3
Matrix c5
7
9
Matrix c6
0.5 1
0.5 1.5
Matrix -A
-1 -2
-1 -3
Matrix Transpose A
1 1
2 3
Program ended with exit code: 0
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.