Example: I want to extract the nested for loops from these operator functions that are the same except the one line.
// Add two matrices
Matrix& operator+=(const Matrix& other)
{
for (int i = 0; i < this->m_rows; i++)
{
for (int j = 0; j < this->m_cols; j++)
{
(*this)(i, j) = (*this)(i, j) + other(i, j); // Only difference
}
}
return *this;
}
// Subtract two matrices
Matrix& operator-=(const Matrix& other)
{
for (int i = 0; i < this->m_rows; i++)
{
for (int j = 0; j < this->m_cols; j++)
{
(*this)(i, j) = (*this)(i, j) - other(i, j); // Only different
}
}
return *this;
}
You can write a function template that accepts a binary function and applies it on all pairs of elements inside the loops
template<typename Op>
void loops(const Matrix& other, Op op)
{
for (int i = 0; i < this->m_rows; i++)
{
for (int j = 0; j < this->m_cols; j++)
{
(*this)(i, j) = op((*this)(i, j), other(i, j));
}
}
}
and then use it like this
// Add two matrices
Matrix& operator+=(const Matrix& other)
{
loops(other, std::plus{});
return *this;
}
// Subtract two matrices
Matrix& operator-=(const Matrix& other)
{
loops(other, std::minus{});
return *this;
}
I'd argue that this sort of problem often implies a poor abstraction.
In the example case, an efficient matrix will have a single contiguous array, where matrix(i,j) is translated to array[i*n_columns+j] behind the scenes. The i,j interface is simpler in many cases (otherwise you'd just use a vector), but there's no reason to restrict the user accessing the underlying array elements directly - let alone within your own matrix class!
Another way to put it is that you're using classes that create the (i,j) abstraction for you, and now you're wanting another layer of abstraction to undo the work. This is costly in cpu time and in your time, and makes for spaghetti code. Instead, make sure that you (and your user) has access to the underlying array by direct element access and by iterator:
public:
auto & operator [] (size_t i)
{
return data[i]; // our underlying array
}
const auto & operator [] (size_t i) const
{
return data[i];
}
Matrix& operator += (const Matrix& other)
{
for (size_t i = 0; i < size(); ++i)
data[i] += other[i];
return *this;
}
Matrix& operator -= (const Matrix& other)
{
for (size_t i = 0; i < size(); ++i)
data[i] -= other[i];
return *this;
}
You may think that this somehow breaks encapsulation, but it doesn't. The user has the same access to edit elements, but no access to change the matrix size or access to the raw pointer. However, for the sake of answering your more general question about avoiding loops, I'll pretend we only have access to the iterators:
Matrix& operator += (const Matrix& other)
{
auto it1 = begin();
auto it2 = other.begin();
for (size_t i = 0; i < size(); ++i)
it1[i] += it2[i];
return *this;
}
Matrix& operator -= (const Matrix& other)
{
auto it1 = begin();
auto it2 = other.begin();
for (size_t i = 0; i < size(); ++i)
it1[i] -= it2[i];
return *this;
}
Okay, that's better, but we can abstract the repetitive part for any iterator or container, like so:
template <class Func, class ... Its>
void ForArrays (size_t size, Func&& f, Its && ... its)
{
for (size_t i = 0; i < size; ++i)
f(its[i]...);
}
template <class Func, class ... Cs>
void ForContainers (Func&& f, Cs && ... cs)
{
size_t size = (cs.size(), ...);
assert(((size == cs.size()) && ...));
ForArrays(size, f, cs.begin()...);
}
Demo
And now we can rewrite our operators:
Matrix& operator += (const Matrix& other)
{
ForContainers([](auto& lhs, auto rhs){lhs += rhs}, *this, other);
return *this;
}
Matrix& operator -= (const Matrix& other)
{
ForContainers([](auto& lhs, auto rhs){lhs -= rhs}, *this, other);
return *this;
}
I think the lesson here is to never undo the work of an abstraction; instead, if it's not useful for part of your code then avoid it entirely (in that part of the code). A layer of abstraction to undo a layer of abstraction is why some people turn away from OOP.
Related
I have a matrix class with fields like this:
template <typename T>
class Matrix
{
private:
T **matrix = nullptr;
int rows;
int cols;
At this stage, I have written an assignment operator and a copy constructor. But firstly, there is code duplication, how can it be avoided, and secondly, they seem very similar to me, how can these methods be improved to look normal?
Matrix(const Matrix &matrix_) : rows(matrix_.rows), cols(matrix_.cols)
{
matrix = static_cast<T **>(new T *[rows]);
for (int i = 0; i < rows; i++)
{
matrix[i] = static_cast<T *>(new T[cols]);
}
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
matrix[i][j] = matrix_[i][j];
}
}
}
Matrix &operator=(const Matrix &matrix_)
{
if (&matrix == this)
{
return *this;
}
clean();
rows = matrix_.rows;
cols = matrix_.cols;
matrix = static_cast<T **>(new T *[rows]);
for (int i = 0; i < rows; i++)
{
matrix[i] = static_cast<T *>(new T[cols]);
}
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
matrix[i][j] = matrix_[i][j];
}
}
}
void clean()
{
if (cols > 0)
{
for (int i = 0; i < rows; i++)
{
delete[] matrix[i];
}
}
if (rows > 0)
{
delete[] matrix;
}
}
According to the condition of the assignment, it is forbidden to use STL containers, I must implement the controls myself
Added move semantics
Matrix(Matrix &&other) noexcept : rows(std::move(other.rows)), cols(std::move(other.cols)), data(new T(rows * cols))
{
other.data = nullptr;
rows = 0;
cols = 0;
}
Matrix &operator=(Matrix &&other) noexcept
{
if (&other == this)
{
return *this;
}
if (rows != other.rows && cols != other.cols)
{
std::cout << "Error assigning matrices of different sizes" << std::endl;
exit(-1);
}
clean();
std::swap(data, other.data);
std::swap(rows, other.rows);
std::swap(cols, other.cols);
return *this;
}
Generally, if you need common code between methods, the best way is to factor out the common code into (private) helper methods. For example, you might have a method alloc_ that has the loop calling new and a free_ that calls delete. Then you could have:
Matrix(const Matrix &a) : rows(a.rows), cols(a.cols) {
alloc_();
copy_(a);
}
Matrix &operator=(const Matrix &a) {
free_();
rows = a.rows;
cols = a.cols;
alloc_();
copy_(a);
}
~Matrix() { free_(); }
Use a 1D array then. It will be much cleaner and simpler and faster than an array of pointers to arrays... You can do something like:
template <typename T>
class Matrix
{
private:
// Note: Assuming T is a trivial type, most likely a fundamental type...
T* data; // Flattened matrix with size = rows*cols. Be careful,
// for really big matrices this multiplication could overflow!!!
// You can use unsigned type since negative size is a nonsense...
unsigned int rows;
unsigned int cols;
public:
Matrix(const Matrix& other)
: data(new T[other.rows*other.cols])
, rows(other.rows)
, cols(other.cols)
{
/// You can do this:
/// for(int i = 0; i < rows*cols; ++i)
/// data[i] = other.data[i];
/// or simply call the copy assign operator:
operator=(other);
}
Matrix& operator=(const Matrix& other)
{
// This is only for matrix with the same dimensions.
// Delete and allocate new array if the dimensions are different.
// This is up to the OP if he can have differently sized matrices or not...
// Also assert will "disappear" if NDEBUG (ie. in release) is defined.
assert(rows == other.rows);
assert(cols == other.cols);
for(int i = 0; i < rows*cols; ++i)
data[i] = other.data[i];
return *this;
}
};
Note: You will also need to define the constructor(s) and destructor of course...
I'm pretty sure you can't make it much simpler than this...
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
I have a class for complex numbers whose data members are im (the imaginary part) and re (the real part). Now I want to sum 2 matrix that come from the class Matrix (A and B) and put the sum in another matrix (C). This is what I tried. Any advice?
The error I get is
no match for 'operator[]' (operand types are 'Matrix' and 'int')
inline Matrix& operator+(Matrix& A, Matrix& B)
{
Matrix *C = new Matrix;
C->rows = A.rows;
C->columns = A.columns;
for(int i = 0; i < A.rows; i++)
for(int j = 0; j < A.columns; j++)
C[i][j] = A[i][j] + B[i][j];
}
return **C;
}
Add a operator '+' inside class like:
Matrix Matrix::operator+(const Matrix &other){
Matrix result = Matrix<T>(rows, other.columns);
if (this->rows == other.columns && this->columns == other.rows) {
for (int i = 0; i < this->rows; i++) {
for (int j = 0; j < this->columns; j++) {
result.n[i][j] = other.n[i][j] + this->n[i][j];
}
}
}
return result;
}
And the operator '=' to copy the result
Matrix& Matrix::operator=(const Matrix &other ){
if(this->columns<=other.columns && this->rows<=other.rows){
for(int i=0;i<this->rows;i++){
for(int j=0;j<this->columns;j++){
this->n[i][j]=other.n[i][j];
}
}
return (*this);
}else{
printf("ERROR");
}
}
Here's a basic Matrix implementation you could use.
Compatible with std::complex or whatever you own type is, as long as it's copy-constructible (see the main() in the link below).
template<class T>
struct Matrix {
std::vector<T> data;
size_t rows, cols;
Matrix(size_t rows_, size_t cols_, T value=T())
: data(rows_*cols_, value)
, rows(rows_)
, cols(cols_)
{
}
Matrix() = default;
Matrix(const Matrix& other) = default;
Matrix& operator=(const Matrix& other) = default;
Matrix(Matrix&& other) = default;
Matrix& operator=(Matrix&& other) = default;
T& operator()(size_t row, size_t col) {
return data.at(flatIndex(row, col));
}
const T& operator()(size_t row, size_t col) const {
return data.at(flatIndex(row, col));
}
Matrix operator+(const Matrix& other) const {
assert(data.size() == other.data.size());
assert(rows == other.rows);
assert(cols == other.cols);
Matrix sum = *this;
for (size_t i=0; i<data.size(); i++) {
sum.data.at(i) += other.data.at(i);
}
return sum;
}
protected:
size_t flatIndex(size_t row, size_t col) const {
return row * cols + col;
}
};
template<class T>
std::ostream& operator<<(std::ostream& os, const Matrix<T>& m) {
for (size_t r = 0; r<m.rows; r++) {
for (size_t c = 0; c<m.cols; c++) {
os << m(r, c) << " ";
}
os << '\n';
}
return os;
}
https://godbolt.org/z/Gvx5ac
If you really need performant/safe matrices, consider using a LinAlg library instead, like Eigen.
i have this class
class Matrix
{
int size;
std::unique_ptr<std::unique_ptr<int[]>[]> val;
public:
Matrix(int size1)
{
size=size1;
val=std::make_unique< std::unique_ptr<int[]>[] >(size);
...
}
... move constructor,move assignment operator
Matrix& operator+(Matrix &m)
{
Matrix sumMatrix(size);
for ( int i = 0; i < size; ++i)
{
for (int j = 0; j < size; ++j){
sumMatrix.val[i][j]=this->val[i][j]+m.val[i][j];
}
}
return sumMatrix;
}
and main :
...
Matrix e=b+c;
std::cout<<"e="<<std::endl;
e.print();
and i have this error :
warning: reference to local variable 'sumMatrix' returned
[-Wreturn-local-addr]
Matrix sumMatrix(size);
Can someone please help me with this ??
Return by value, as you should for operator+ most of the time:
// vvv--Removed & vvvvv-----vvvvv--Const is more appropriate here
Matrix operator+(Matrix const &m) const { ... }
This will require a copy constructor, make sure to add that. Also note that you should probably collect your for-loop logic into an operator+= and simplify operator+ significantly while providing more functionality for the end-user:
Matrix& operator+=(Matrix const& m) {
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
//vvv--No need for this-> in C++
val[i][j] += m.val[i][j];
}
}
return *this;
}
Matrix operator+(Matrix const& m) const {
Matrix sumMatrix{m}; // requires a copy constructor.
sumMatrix += *this;
return sumMatrix;
}
Here is a simplified addition operator overload for matrices that I've wrote, but my IDE (Clion) does not recognise it when I'm trying to use it like this: m1 = m2 + m3, and says: "binary operator + can't be applied to the expressions of type 'Matrix' and 'Matrix' "
Matrix& Matrix::operator += (Matrix& m1)
{
for (int i = 0; i < m_rows; ++i)
for (int j = 0; j < m_cols; ++j)
m_matrix[i][j] += m1.m_matrix[i][j];
return *this;
}
Matrix& operator + (Matrix m1, Matrix& m2){ return m1+=m2; }
What do I have to fix here?
P.S. I have also defined custom copy and move constructors and an assignment operator:
Matrix::Matrix(const Matrix& m1)
: m_matrix{new double*[m1.rows()]},
m_rows{m1.rows()},
m_cols{m1.cols()}
{
for (int i = 0; i < m1.rows(); ++i) {
m_matrix[i] = new double[m1.cols()];
for (int j = 0; j < m1.cols(); ++j)
m_matrix[i][j] = m1.m_matrix[i][j];
}
}
Matrix& Matrix::operator = (const Matrix& m1){
Matrix matrix(m1.rows(), m1.cols());
for (int i = 0; i < m1.rows(); ++i)
for (int j = 0; j < m1.cols(); ++j)
matrix.m_matrix[i][j] = m1.m_matrix[i][j];
for (int i = 0; i < m_rows; ++i)
delete m_matrix[i];
delete m_matrix;
m_matrix = matrix.m_matrix;
m_rows = m1.rows();
m_cols = m1.cols();
return *this;
}
Matrix::Matrix(Matrix&& m1) noexcept
: m_matrix{m1.m_matrix},
m_rows{m1.m_rows},
m_cols{m1.m_cols}
{
for (int i = 0; i < m1.m_rows; ++i)
delete m1.m_matrix[i];
delete m1.m_matrix;
m1.m_rows = m1.m_cols = 0;
}
Just guessing, as you did not provide the header file... Can it be that you forgot to declare the operator+ in your header file?
class Matrix
{
// ...
};
Matrix operator+ (Matrix m1, Matrix const& m2); // just declared, not implemented!
If so, you are able to use the operator only from within the Matrix implementation file (and only after the implementation), as it is simply not known elsewhere...
Have a look at my changes, too: really important, I don't return a reference any more; doing so would return m1 (result of operator += as reference), which is a local variable, so you get a dangling reference and undefined behaviour as soon as using it!
Additionally, you won't change m2, so you easily can pass it as const reference (provided you do the same change for your operator+= within class Matrix), this gives you more flexibility.
The first, your global operator + for Matrix should not return a refferece, bacause in that case you can write smth like this:
Matrix m1,m2,m3;
m1+m2=m3;
and this is ill formed.
Furthermore, I guess this Matrix& operator + (Matrix m1, Matrix& m2){ return m1+=m2; } is a returning m1 which is not a reference.
Also make sure you get all you operator arguments as const references.
I'm implementing my own matrix class in c++ to help me develop my understanding of the language. I read somewhere that if you've got a working += operator, to use it in your + operator. So that's what I've got:
template <class T>
const Matrix<T>& Matrix<T>::operator+(const Matrix<T> &R){
Matrix<T> copy(*this);
return copy += R;
}
And here is the += operator overload:
template <class T>
const Matrix<T>& Matrix<T>::operator+=(const Matrix<T> & second_matrix){
//Learn how to throw errors....
if (rows != second_matrix.getNumRows() || cols != second_matrix.getNumCols()){throw "Dimension mismatch.";}
int i,j;
for (i = 0; i < rows; i++){
for (j = 0; j < cols; j++){
data[i][j] += second_matrix.get(i,j);
}
}
return *this;
}
I can use the += just fine (eg, a += b; returns no errors). But calling the + operator (eg, a = b + c;) returns :
test.cpp.out(77055) malloc: *** error for object 0x300000004: pointer being freed was not allocated
Just for completeness, here's my destructor:
template <class T>
Matrix<T>::~Matrix(){
for (int i = 1; i < rows; i++){
delete[] data[i]; }
delete[] data;
}
I've been using C++ for a couple years on and off, and still have trouble sometimes keeping track of pointers. I hope that's normal...
Any help would be great. Thanks!
EDIT: here's my copy constructor. It was set to free the data arrays but i removed that. now I get segmentation faults.
template <class T>
Matrix<T>::Matrix(const Matrix<T>& second_matrix){
rows = second_matrix.getNumRows();
cols = second_matrix.getNumCols();
data = new T*[rows];
int i,j;
for (i = 0; i < rows; i++){
data[i] = new T[cols];
}
for (i = 0; i < rows; i++){
for (j = 0; j < cols; j++){
data[i][j] = second_matrix.get(i,j);
}
}
}
operator+() should not return a reference type as it is a new (locally declared) instance that holds the result of the operation.
This is how I have implemented such operators for a Matrix class, this is based on a Vector Class. Once you define some operators all other should be defined in terms of the simplest operators:
Matrix::Matrix(const Matrix& rMatrix) :
_iRows(rMatrix._iRows), _iColumns(rMatrix._iColumns), _pVector(0)
{
_pVector = new Vector[_iRows];
for (int i = 0; i < _iRows; i++) { _pVector[i] = rMatrix._pVector[i]; }
}
Matrix& Matrix::operator=(const Matrix& rMatrix)
{
if (this != &rMatrix)
{
if (0 != _pVector) { delete[] _pVector; pVector = 0; }
_iRows = rMatrix._iRows;
_iColumns = rMatrix._iColumns;
_pVector = new Vector[_iRows];
for (int i = 0; i < _iRows; i++) { _pVector[i] = rMatrix._pVector[i]; }
}
return *this;
}
Matrix& Matrix::operator+=(const Matrix& rMatrix)
{
*this = *this + rMatrix;
return *this;
}
Matrix Matrix::operator+(const Matrix& rMatrix) const
{
Matrix matrix(_iRows, _iColumns);
ValidateSizes(rMatrix);
for (int i = 0; i < _iRows; i++) { matrix._pVector[i] = _pVector[i] + rMatrix._pVector[i]; }
return matrix;
}
Matrix operator+(const Matrix& rMatrix, double dNum)
{
Matrix matrix(rMatrix._iRows, rMatrix._iColumns);
matrix.ValidateSizes(rMatrix);
for (int i = 0; i < matrix._iRows; i++) { matrix._pVector[i] = dNum + rMatrix._pVector[i]; }
return matrix;
}
Matrix operator+(double dNum, const Matrix& rMatrix)
{
return operator+(rMatrix, dNum);
}
bool Matrix::ValidateSizes(const Matrix& rMatrix) const
{
if (_iRows != rMatrix._iRows) { /* THROW EXCEPTION */ }
if (_iColumns != rMatrix._iColumns) { /* THROW EXCEPTION */ }
return true;
}
If this a matrix for 3D rendering/simulation I would recommend NOT dynamically allocating the memory like that. You can end up with the memory being spread all over the place which causes caching issues. It also leads to potential memory bugs.
template <typename T>
class Matrix
{
public:
T m_Data[4][4];
};
or if you want something non-4x4
template <typename T, unsigned int rows, unsigned int columns>
class Matrix
{
public:
T m_Data[rows][columns];
};
and then dynamically allocate the Matrix objects.