I implemented a basic Matrix
class for additions of matrices, multiplication of them, and multiplying every entry of matrix by constant, operator==
and operator!=
, overloads for I/O operations using istream
and ostream
.
Before you start reading it, please look at some rationale about horrible aspects of it:
- The reason for naming member functions
rows()
andcolumns()
was thatrowCount
andcolumnCount
were already reserved for private member variables, thus I couldn't name them that way. I was thinking between just returning the dimensions from vector itself, but chose the way the code is. - My knowledge about const member functions is not sufficient to be confident, that is why I made the code as messy as it is, but I hope it is safe, at least.
- I think that range loops are more explicit and names for the variables are clear enough, but I would like to receive an opinion of you.
- According to wikipedia page,
#pragma once
is supported wide enough to still use it.
Here is the header:
#pragma once
#include <vector>
#include <exception>
#include <iosfwd>
template <typename T>
class Matrix
{
std::vector<std::vector<T>> matrix;
size_t rowCount;
size_t columnCount;
public:
Matrix(size_t rowCount_, size_t columnCount_):
matrix(rowCount_),
rowCount(rowCount_),
columnCount(columnCount_)
{
for (auto& row : matrix)
{
row.resize(columnCount);
for (auto& cell : row)
{
cell = 0;
}
}
}
Matrix(size_t rowCount_, size_t columnCount_, const T& value) :
matrix(rowCount_),
rowCount(rowCount_),
columnCount(columnCount_)
{
for (auto& row : matrix)
{
row.resize(columnCount, value);
}
}
Matrix(const Matrix& other) = default;
Matrix(Matrix&& other) :
matrix(std::move(other.matrix))
{
rowCount = other.rowCount;
columnCount = other.columnCount;
}
Matrix& operator=(const Matrix& other) = default;
Matrix& operator=(Matrix&& other)
{
std::swap(matrix, other.matrix);
rowCount = other.rowCount;
columnCount = other.columnCount;
return *this;
}
Matrix& operator*=(const T& rhs)
{
for (auto& row : matrix)
{
for (auto& cell : row)
{
cell *= rhs;
}
}
return *this;
}
Matrix& operator*=(const Matrix& rhs)
{
if (columnCount != rhs.rowCount)
{
throw std::logic_error("column count of lhs and row count of rhs are not equal\n");
}
Matrix temp(rowCount, rhs.columnCount);
for (size_t i = 0; i < temp.rowCount; i++)
{
for (size_t j = 0; j < temp.columnCount; j++)
{
for (size_t k = 0; k < columnCount; k++)
{
temp[i][j] += matrix[i][k] * rhs[j][k];
}
}
}
std::swap(matrix, temp.matrix);
return *this;
}
Matrix& operator+=(const Matrix& rhs)
{
if (rowCount != rhs.rowCount || columnCount != rhs.columnCount)
{
throw std::logic_error("either or both of row count and column count of lhs and rhs are not equal\n");
}
for (size_t i = 0; i < rowCount; i++)
{
for (size_t j = 0; j < columnCount; j++)
{
matrix[i][j] += rhs[i][j];
}
}
return *this;
}
size_t rows()
{
return rowCount;
}
size_t columns()
{
return columnCount;
}
const size_t rows() const
{
return rowCount;
}
const size_t columns() const
{
return columnCount;
}
std::vector<T>& operator[](size_t row)
{
return matrix[row];
}
const std::vector<T>& operator[](size_t row) const
{
return matrix[row];
}
};
template <typename T>
bool operator==(const Matrix<T>& lhs, const Matrix<T>& rhs)
{
if (lhs.rows() != rhs.rows() || lhs.columns() != rhs.columns())
{
return false;
}
for (int i = 0; i < lhs.rows(); i++)
{
for (int j = 0; j < lhs.columns(); j++)
{
if (lhs[i][j] != rhs[i][j])
{
return false;
}
}
}
return true;
}
template <typename T>
bool operator!=(const Matrix<T>& lhs, const Matrix<T>& rhs)
{
return !(lhs == rhs);
}
template <typename T>
Matrix<T> operator+(Matrix<T> lhs, const Matrix<T>& rhs)
{
return lhs += rhs;
}
template <typename T>
Matrix<T> operator*(Matrix<T> lhs, const Matrix<T>& rhs)
{
return lhs *= rhs;
}
template <typename T>
Matrix<T> operator*(Matrix<T> lhs, const T& rhs)
{
return lhs *= rhs;
}
template <typename T>
Matrix<T> operator*(const T& lhs, Matrix<T> rhs)
{
return rhs *= lhs;
}
template <typename T>
std::istream& operator >> (std::istream& is, Matrix<T>& matrix)
{
for (size_t i = 0; i < matrix.rows(); i++)
{
for (size_t j = 0; j < matrix.columns(); j++)
{
is >> matrix[i][j];
}
}
return is;
}
template <typename T>
std::ostream& operator<< (std::ostream& os, const Matrix<T>& matrix)
{
for (size_t i = 0; i < matrix.rows(); i++)
{
for (size_t j = 0; j < matrix.columns(); j++)
{
os << matrix[i][j] << ' ';
}
os << "\n";
}
return os;
}
Example usage:
#include "matrix.h"
#include <iostream>
int main()
{
std::cout << "Enter the dimensions of the first matrix (non negative): ";
size_t rowCount = 0, columntCount = 0;
std::cin >> rowCount >> columntCount;
Matrix<double> firstMatrix(rowCount, columntCount);
std::cout << "Enter values of the matrix entries\n";
std::cin >> firstMatrix;
std::cout << "Enter the dimensions of the second matrix (non negative): ";
std::cin >> rowCount >> columntCount;
Matrix<double> secondMatrix(rowCount, columntCount);
std::cout << "Enter values of the matrix entries\n";
std::cin >> secondMatrix;
std::cout << "Choose operator (+ or *): ";
char op;
std::cin >> op;
switch (op)
{
case '*':
std::cout << firstMatrix * secondMatrix;
default:
std::cout << firstMatrix + secondMatrix;
}
std::system("pause");
}