I'm teaching myself c++ by creating my own data structure class (a matrix, to be exact) and I've changed it to a template class of type <T> from only using doubles. The overloaded matrix operators were pretty standard
// A snippet of code from when this matrix wasn't a template class
// Assignment
Matrix& operator=( const Matrix& other );
// Compound assignment
Matrix& operator+=( const Matrix& other ); // matrix addition
Matrix& operator-=( const Matrix& other ); // matrix subtracton
Matrix& operator&=( const Matrix& other ); // elem by elem product
Matrix& operator*=( const Matrix& other ); // matrix product
// Binary, defined in terms of compound
Matrix& operator+( const Matrix& other ) const; // matrix addition
Matrix& operator-( const Matrix& other ) const; // matrix subtracton
Matrix& operator&( const Matrix& other ) const; // elem by elem product
Matrix& operator*( const Matrix& other ) const; // matrix product
// examples of += and +, others similar
Matrix& Matrix::operator+=( const Matrix& rhs )
{
for( unsigned int i = 0; i < getCols()*getRows(); i++ )
{
this->elements.at(i) += rhs.elements.at(i);
}
return *this;
}
Matrix& Matrix::operator+( const Matrix& rhs ) const
{
return Matrix(*this) += rhs;
}
But now that Matrix can have a type, I'm having trouble determining which of the matrix references should be type <T> and what the consequences would be. Should I allow dissimilar types operate on each other (eg., Matrix<foo> a + Matrix<bar> b is valid)? I'm also a little fuzzy on how
One reason I'm interested in dissimilar types is to facilitate the use of complex numbers in the future. I'm a newbie at c++ but am happy to dive in over my head to learn. If you're familiar with any free online resources that deal with this problem I would find that most helpful.
Edit: no wonder no one thought this made sense all of my angle brackets in the body were treated as tags! I can't figure out how to escape them, so I'll inline code them.
I figure that I should illustrate my comment about parameterizing matrix dimensions, since you might not have seen this technique before.
template<class T, size_t NRows, size_t NCols>
class Matrix
{public:
Matrix() {} // `data` gets its default constructor, which for simple types
// like `float` means uninitialized, just like C.
Matrix(const T& initialValue)
{ // extra braces omitted for brevity.
for(size_t i = 0; i < NRows; ++i)
for(size_t j = 0; j < NCols; ++j)
data[i][j] = initialValue;
}
template<class U>
Matrix(const Matrix<U, NRows, NCols>& original)
{
for(size_t i = 0; i < NRows; ++i)
for(size_t j = 0; j < NCols; ++j)
data[i][j] = T(original.data[i][j]);
}
private:
T data[NRows][NCols];
public:
// Matrix copy -- ONLY valid if dimensions match, else compile error.
template<class U>
const Matrix<T, NRows, NCols>& (const Matrix<U, NRows, NCols>& original)
{
for(size_t i = 0; i < NRows; ++i)
for(size_t j = 0; j < NCols; ++j)
data[i][j] = T(original.data[i][j]);
return *this;
}
// Feel the magic: Matrix multiply only compiles if all dimensions
// are correct.
template<class U, size_t NOutCols>
Matrix<T, NRows, NOutCols> Matrix::operator*(
const Matrix<T, NCols, NOutCols>& rhs ) const
{
Matrix<T, NRows, NOutCols> result;
for(size_t i = 0; i < NRows; ++i)
for(size_t j = 0; j < NOutCols; ++j)
{
T x = data[i][0] * T(original.data[0][j]);
for(size_t k = 1; k < NCols; ++k)
x += data[i][k] * T(original.data[k][j]);
result[i][j] = x;
}
return result;
}
};
So you'd declare a 2x4 matrix of floats, initialized to 1.0, as:
Matrix<float, 2, 4> testArray(1.0);
Note that there is no requirement for the storage to be on the heap (i.e. using operator new) since the size is fixed. You could allocate this on the stack.
You can create another couple matrices of ints:
Matrix<int, 2, 4> testArrayIntA(2);
Matrix<int, 4, 2> testArrayIntB(100);
For copying, dimensions must match though types do not:
Matrix<float, 2, 4> testArray2(testArrayIntA); // works
Matrix<float, 2, 4> testArray3(testArrayIntB); // compile error
// No implementation for mismatched dimensions.
testArray = testArrayIntA; // works
testArray = testArrayIntB; // compile error, same reason
Multiplication must have the right dimensions:
Matrix<float, 2, 2> testArrayMult(testArray * testArrayIntB); // works
Matrix<float, 4, 4> testArrayMult2(testArray * testArrayIntB); // compile error
Matrix<float, 4, 4> testArrayMult2(testArrayIntB * testArray); // works
Note that, if there's a botch, it is caught at compile time. This is only possible if the matrix dimensions are fixed at compile time, though. Also note that this bounds checking results in no additional runtime code. It's the same code that you'd get if you just made the dimensions constant.
Resizing
If you don't know your matrix dimensions at compile time, but must wait until runtime, this code may not be of much use. You'll have to write a class that internally stores the dimensions and a pointer to the actual data, and it will need to do everything at runtime. Hint: write your operator [] to treat the matrix as a reshaped 1xN or Nx1 vector, and use operator () to perform multiple-index accesses. This is because operator [] can only take one parameter, but operator () has no such limit. It's easy to shoot yourself in the foot (force the optimizer to give up, at least) by trying to support a M[x][y] syntax.
That said, if there's some kind of standard matrix resizing that you do to resize one Matrix into another, given that all dimensions are known at compile time, then you could write a function to do the resize. For example, this template function will reshape any Matrix into a column vector:
template<class T, size_t NRows, size_t NCols>
Matrix<T, NRows * NCols, 1> column_vector(const Matrix<T, NRows, NCols>& original)
{ Matrix<T, NRows * NCols, 1> result;
for(size_t i = 0; i < NRows; ++i)
for(size_t j = 0; j < NCols; ++j)
result.data[i * NCols + j][0] = original.data[i][j];
// Or use the following if you want to be sure things are really optimized.
/*for(size_t i = 0; i < NRows * NCols; ++i)
static_cast<T*>(result.data)[i] = static_cast<T*>(original.data)[i];
*/
// (It could be reinterpret_cast instead of static_cast. I haven't tested
// this. Note that the optimizer may be smart enough to generate the same
// code for both versions. Test yours to be sure; if they generate the
// same code, prefer the more legible earlier version.)
return result;
}
... well, I think that's a column vector, anyway. Hope it's obvious how to fix it if not. Anyway, the optimizer will see that you're returning result and remove the extra copy operations, basically constructing the result right where the caller wants to see it.
Compile-Time Dimension Sanity Check
Say we want the compiler to stop if a dimension is 0 (normally resulting in an empty Matrix). There's a trick I've heard called "Compile-Time Assertion" which uses template specialization and is declared as:
template<bool Test> struct compiler_assert;
template<> struct compiler_assert<true> {};
What this does is let you write code such as:
private:
static const compiler_assert<(NRows > 0)> test_row_count;
static const compiler_assert<(NCols > 0)> test_col_count;
The basic idea is that, if the condition is true, the template turns into an empty struct that nobody uses and gets silently discarded. But if it's false, the compiler can't find a definition for struct compiler_assert<false> (just a declaration, which isn't enough) and errors out.
Better is Andrei Alexandrescu's version (from his book), which lets you use the declared name of the assertion object as an impromptu error message:
template<bool> struct CompileTimeChecker
{ CompileTimeChecker(...); };
template<> struct CompileTimeChecker<false> {};
#define STATIC_CHECK(expr, msg) { class ERROR_##msg {}; \
(void)sizeof(CompileTimeChecker<(expr)>(ERROR_##msg())); }
What you fill in for msg has to be a valid identifier (letters, numbers, and underscores only), but that's no big deal. Then we just replace the default constructor with:
Matrix()
{ // `data` gets its default constructor, which for simple types
// like `float` means uninitialized, just like C.
STATIC_CHECK(NRows > 0, NRows_Is_Zero);
STATIC_CHECK(NCols > 0, NCols_Is_Zero);
}
And voila, the compiler stops if we mistakenly set one of the dimensions to 0. For how it works, see page 25 of Andrei's book. Note that in the true case, the generated code gets discarded so long as the test has no side effects, so there's no bloat.
I'm not sure I understand what you're asking.
But I would point out that your operator declarations are incorrect and/or incomplete.
Firstly, the assignment operator should return the same type as its parameter; viz:
const Matrix& operator=(const Matrix& src);
Secondly, binary operators return a new object, so you can't return a reference. All binary operators should be declared thus:
Matrix operator+( const Matrix& other ) const; // matrix addition
Matrix operator-( const Matrix& other ) const; // matrix subtracton
Matrix operator&( const Matrix& other ) const; // elem by elem product
Matrix operator*( const Matrix& other ) const; // matrix product
Actually, it is considered better style to declare and implement binary operators as global friend functions instead:
class Matrix { ... };
inline Matrix operator+(const Matrix& lhs,const Matrix& rhs)
{ return Matrix(lhs)+=rhs; }
Hope this helps.
Now I understand what you're asking.
Presumably your implementation of various operators will in this case consist of operations on the composite types. So really the question of whether Matrix op Matrix is meaningful depends on whether string op int is meaningful (and whether such a thing might be useful). You would also need to determine what the return type might be.
Assuming the return type is the same as the LHS operand, the declarations would look something like:
template <typename T>
class Matrix
{
template <typename U>
Matrix<T>& operator+=(const Matrix<U>& rhs);
};
template <typename T,typename U>
Matrix<T> operator+(const Matrix<T>& lhs,const Matrix<U>& rhs)
{ return Matrix<T>(lhs)+=rhs; }
Matrix<double> x = ...;
Matrix<int> y = ...;
cout << x + y << endl; // prints a Matrix<double>?
OK, that's doable, but the problem gets tricky quickly.
Matrix<double> x = ...
Matrix<complex<float>> y = ...
cout << x + y << endl; // Matrix<complex<double>>?
You will most likely be happiest if you require that your binary operators use like-type operands and force your application builders to explicitly type-cast their values. For the latter case:
cout << ((Matrix<complex<double>>) x) + ((Matrix<complex<double>>) y) << endl;
You can provide a member template constructor (or a type conversion operator) to support the conversions.
template <typename T>
class Matrix {
...
public:
template <typename U>
Matrix(const Matrix<U>& that) {
// initialize this by performing U->T conversions for each element in that
}
...
};
The alternative, having your binary operator template deduce the correct Matrix return type based on the element types of the two operands, requires some moderately complex template meta-programming, probably not what you're looking to get into.
First of all, the copy assignment operator should not have const Matrix& as its return type; your interface is correct.
Grant's suggestion of how binary operators are implemented is the generally accepted way of doing these things.
It's a nice exercise, but one quickly sees why doing linear algebra in C++ is a bad idea to begin with. Operations like A+B and A*B are valid only if the dimensions of the matrices match up.
You don't have to add much at all, because inside a template, the class name itself refers to the current template parameter. So the following are equivalent:
template <typename T> struct Foo
{
Foo<T> bar(const Foo<T> &);
Foo bar2(const Foo *); // same
};
So all your operations just go through without change. What you should add is a constructor that converts one matrix type into another:
temlate <typename T> class Matrix
{
template <typename U> Matrix(const Matrix<U> &); // construct from another matrix
/*...*/
};
Using that conversion constructor, you can mix matrices in your operators, as Matrix<T>::operator+(Matrix<U>) will use the conversion to create an argument of type Matrix<T>, and then you use your already implemented operator.
In C++11 you can add static_assert(std::is_convertible<U, T>::value, "Boo"); to your converting constructor to give you a useful compile-time diagnostic if you call it with an incompatible type.
Related
I am building a template matrix class for use in my future c++ code. I have a few questions regarding value passing for overloaded operators, exceptions vs asserts, and general class design.
Am I passing the values correctly? Is it efficient? What can I do otherwise to make it better?
This library is built with future application design in mind (terminal or gui), where a user could define their own matrices and run calculations. Would using exceptions instead of asserts be better in this case?
I have looked up the rule of 5 for c++, where it states that:
Because the presence of a user-defined destructor, copy- constructor, or copy-assignment operator prevents implicit definition of the move constructor and the move assignment operator, any class for which move semantics are desirable, has to declare all five special member functions.
Can I get away with not implementing this rule by just not having any of those three?** What would be the standard way to make this class more functional?
I have subtraction, multiplication, and division (scalar) defined in my program with the same/similar structure as the provided addition operator definitions, so not all of that code is necessary here.
Any hard advice or criticism on the overall design is accepted!
#ifndef MACMATRICES_H
#define MACMATRICES_H
#include <vector>
#include <iomanip>
#include <iostream>
#include <exception>
#include "../../DMF-Terminal.h"
namespace DMF
{
template <typename T>
class matrix
{
public:
// Constructors
matrix();
matrix(int p_rows, int p_columns);
// Operators
std::vector<T>& operator[] (size_t i) { return m[i]; }
matrix<T> operator+(const matrix<T>& rhs);
matrix<T> operator+(const T& rhs);
matrix<T>& operator+=(const matrix<T>& rhs);
matrix<T>& operator+=(const T& rhs);
// Class Methods
void print() const;
matrix<T> inverse();
T determinant();
// Observers
bool isSquare() const;
int rowSize() const { return m_rows; }
int colSize() const { return m_cols; }
private:
int m_rows, m_cols;
std::vector< std::vector<T> > m;
};
/* Constructors -----------------------------------------------------------------------------------*/
template <typename T>
matrix<T>::matrix(){}
template <typename T>
matrix<T>::matrix(int p_rows, int p_cols) :
m(p_rows, std::vector<T>(p_cols)), m_rows(p_rows), m_cols(p_cols) {}
/* Addition ---------------------------------------------------------------------------------------*/
template <typename T>
matrix<T> matrix<T>::operator+(const matrix<T>& rhs)
{
try
{
if((this->rowSize() == rhs.rowSize()) && (this->colSize() == rhs.colSize()))
{
matrix<T> sum (this->rowSize(), this->colSize());
for(int i = 0; i < this->rowSize() ; ++i)
{
for(int j = 0; j < this->colSize(); ++j)
sum.m[i][j] = this->m[i][j] + rhs.m[i][j];
}
return sum;
}
else throw std::runtime_error("Cannot add matrices, invalid row/column sizes.");
}
catch (std::exception &e)
{
std::cout << "Error: " << e.what(); DMF::wait();
}
}
template <typename T>
matrix<T> matrix<T>::operator+(const T& rhs)
{
matrix<T> sum (this->rowSize(), this->colSize());
for(int i = 0; i < this->rowSize() ; ++i)
{
for(int j = 0; j < this->colSize(); ++j)
sum.m[i][j] = this->m[i][j] + rhs;
}
return sum;
}
template <typename T>
matrix<T>& matrix<T>::operator+=(const matrix<T>& rhs)
{
try
{
if((this->rowSize() == rhs.rowSize()) && (this->colSize() == rhs.colSize()))
{
for(int i = 0; i < this->rowSize() ; ++i)
{
for(int j = 0; j < this->colSize(); ++j)
this->m[i][j] += rhs.m[i][j];
}
return *this;
}
else throw std::runtime_error("Cannot add matrices, invalid row/column sizes.");
}
catch (std::exception &e)
{
std::cout << "Error: " << e.what(); DMF::wait();
}
}
template <typename T>
matrix<T>& matrix<T>::operator+=(const T& rhs)
{
matrix<T> sum (this->rowSize(), this->colSize());
for(int i = 0; i < this->rowSize() ; ++i)
{
for(int j = 0; j < this->colSize(); ++j)
this->m[i][j] += rhs;
}
return *this;
}
}
#endif /* MACMATRICES_H */
As of right now, this code works within a mini terminal program. I also have matrix * matrix and matrix *= matrix operators overloaded and it seems to be working correctly, with the result matrix size being correct.
Am I passing the values correctly? Is it efficient? What can I do otherwise to make it better?
You pass your matrix by (const) reference, so you avoid copy, so it is fine.
You have some "typo" as variable used as matrix<T> sum in operator+=.
You duplicate some information, std::vector knows its size.
linearise std::vector<std::vector<T>> into std::vector<T>> would be more cache friendly, but requires then a proxy class (with another operator[]) to handle operator[], or you might use instead an accessor as operator()(int, int) or operator[](std::pair<int, int>).
This library is built with future application design in mind (terminal or gui), where a user could define their own matrices and run calculations. Would using exceptions instead of asserts be better in this case?
Exceptions are to communicate the error to the user, but currently, you catch it directly to ignore the error with some log... So instead of throwing, you could directly log the error currently.
There are several to fix that issue:
Have matrix size in template argument and use type system to check those error at compilation. require to know size at compilation though.
If you consider that user might be able to ignore/recover from the error, then let's propagate exception. You might probably have dedicated exceptions.
If you consider that user might not be able to ignore/recover from the error, then assert/terminate/UB are possible way.
I have looked up the rule of 5 for c++, where it states that: "Because the presence of a user-defined destructor, copy- constructor, or copy-assignment operator prevents implicit definition of the move constructor and the move assignment operator, any class for which move semantics are desirable, has to declare all five special member functions." Can I get away with not implementing this rule by just not having any of those three? What would be the standard way to make this class more functional?
The rule of 3 has variants:
rule of 5 to also include move constructor and move assignation.
rule of 0, where all member are already RAII friendly and so default implementation is fine.
using std::vector allows to use rule of 0 :-)
So your are fine.
I have overloaded assignment operator for the class with a 2D array, but in order to do memory management and resizing correct I have to delete previous matrix first, then construct a new one, and only then I can start assigning.
Matrix& Matrix::operator = (const Matrix& m1){
for (int i = 0; i < m_rows; ++i)
delete[] m_matrix[i];
delete[] m_matrix;
m_matrix = new double*[m1.rows()];
for (int i = 0; i < m1.rows(); ++i)
m_matrix[i] = new double[m1.cols()]();
for (int k = 0; k < m1.rows(); ++k)
for (int j = 0; j < m1.cols(); ++j)
m_matrix[k][j] = m1.m_matrix[k][j];
m_rows = m1.rows();
m_cols = m1.cols();
return *this;
}
In fact, this part is destructor of my class:
for (int i = 0; i < m_rows; ++i)
delete[] m_matrix[i];
delete[] m_matrix;
And this part is similar to a constructor:
m_matrix = new double*[m1.rows()];
for (int i = 0; i < m_rows; ++i)
m_matrix[i] = new double[m1.cols()]();
What annoys me is that I have to copy constructors' and destructors' code in the assignment function (and some other functions too!) to make it work properly. Is there a better way to write it?
The ideal improvement would be Matrix& Matrix::operator=(const Matrix&) = default;.
If you switch to using std::vector for matrix storage you won't need to implement the copy/move constructors/assignments and destructor at all.
If what you are doing is a programming exercise, create your own dynamic array and use that in the implementation of your matrix.
I cannot recommend enough watching Better Code: Runtime Polymorphism by Sean Parent, he makes an effective demonstration of why you should strive to write classes that do not require non-default implementations of copy/move constructors/assignments and destructor.
Example:
template<class T>
class Matrix
{
std::vector<T> storage_;
unsigned cols_ = 0;
public:
Matrix(unsigned rows, unsigned cols)
: storage_(rows * cols)
, cols_(cols)
{}
// Because of the user-defined constructor above
// the default constructor must be provided.
// The default implementation is sufficient.
Matrix() = default;
unsigned columns() const { return cols_; }
unsigned rows() const { return storage_.size() / cols_; }
// Using operator() for indexing because [] can only take one argument.
T& operator()(unsigned row, unsigned col) { return storage_[row * cols_ + col]; }
T const& operator()(unsigned row, unsigned col) const { return storage_[row * cols_ + col]; }
// Canonical swap member function.
void swap(Matrix& b) {
using std::swap;
swap(storage_, b.storage_);
swap(cols_, b.cols_);
}
// Canonical swap function. Friend name injection.
friend void swap(Matrix& a, Matrix& b) { a.swap(b); }
// This is what the compiler does for you,
// not necessary to declare these at all.
Matrix(Matrix const&) = default;
Matrix(Matrix&&) = default;
Matrix& operator=(Matrix const&) = default;
Matrix& operator=(Matrix&&) = default;
~Matrix() = default;
};
The canonical implementation of the assignment operator leverages existing functionality (copy/move ctor, dtor, andswap(); note that using a non-specialized std::swap() would be bad). It looks like this:
T& T::operator= (T val) {
val.swap(*this);
return *this;
}
It nicely avoids reimplementing otherwise existing logic. It also deals gracefully with self-assignment which is a problem in your original code (it will do work but self-assignment is generally rather uncommon; optimizing it with a check against self-assignment typically pessimizes code).
The argument is passed by value to take advantage of copy elision.
The primary caveats with this approach are below. In general I prefer the canonical implementation as it is generally more correct and the outlined issue are often not really that relevant (e.g., when the object was just created anyway the transferred memory is actually "hot").
It does not attempt to reuse already allocated and possibly "hot" memory. Instead it always uses new memory.
If the amount of held data is huge, there are copies temporarily held which may exceed system limits. Reusing existing memory and/or release memory first would both address this issue.
I have a class meant to implement a matrix, here:
template<typename Comparable>
class Matrix {
private:
std::size_t num_cols_;
std::size_t num_rows_;
Comparable **array_;
public:
Matrix();
~Matrix(); // Destructor
Matrix(const Matrix<Comparable> & rhs);// Copy constructor
Matrix(Matrix<Comparable> && rhs); // Move constructor
Matrix<Comparable> & operator= (const Matrix<Comparable> & rhs);// Copy assignment
Matrix<Comparable> & operator= (Matrix<Comparable> && rhs); // Move assignment
template<typename buh> friend std::ostream &operator<< (std::ostream &os, Matrix<buh> &rhs);
void ReadMatrix();
};
(Vectors aren't an option for this particular problem.)
The array_ member in particular holds the matrix itself, and is populated using the following code:
array_ = new Comparable*[num_rows_];
for (int i = 0; i < num_rows_; ++i) {
array_[i] = new Comparable[num_cols_];
};
for(int i = 0;i < num_rows_; ++i) {
std::cout << "Enter items for row " << i << "." << std::endl;
for(int j = 0;j < num_cols_; ++j) {
std::cin >> array_[i][j];
}
}
I can fill the array with values and access them, and my copy constructor and move assignment operator are functional, but the move assignment operator throws out a strange bug. here's the definition.
template<typename Comparable>
Matrix<Comparable>& Matrix<Comparable>::operator= (Matrix<Comparable> && rhs) {
delete[] array_;
array_ = new Comparable*[rhs.num_rows_];
for(int i = 0;i < rhs.num_rows_;++i) {
std::swap(array_[i],rhs.array_[i]);
rhs.array_[i] = nullptr;
}
rhs.num_cols_ = 0;
rhs.num_rows_ = 0;
rhs.array_ = nullptr;
return *this;
}
Take the statement a = std::move(b);. If b is of a different size than a, the matrix data is deformed by the move. If b has more columns than a, the extra columns will be cut off; if b has fewer rows than a, the excess rows will remain in a; if a has more columns or rows than b, the excess columns will display memory address where there should be nothing at all. Is this a simple bug? Is there a problem with way I create the arrays? Any insight into what's causing this is appreciated.
"Move assign" doesn't mean "carefully modify the passed in object to become some sort of 'empty' value", it means "it's fine to modify the passed in object".
Move assignment here should have a very simple implementation: just swap.
template<typename Comparable>
Matrix<Comparable>& Matrix<Comparable>::operator= (Matrix<Comparable> && rhs) {
using std::swap;
swap(array_, rhs.array_);
swap(num_cols_, rhs.num_cols_);
swap(num_rows_, rhs.num_rows_);
return *this;
}
Not sure why you are using new Comparable* in your move assignment operator. The idea of a move assignment is to move the resources, not make new ones.
Your code could look like:
delete[] array_;
array_ = rhs.array_;
rhs.array_ = nullptr;
num_cols_ = rhs.num_cols_;
num_rows_ = rhs.num_rows_;
return *this;
However, consider using the copy-and-swap idiom. It's not always the most efficient option but it is a good starting point if you're not a guru.
Note: If you really want to use a pointer to pointer to implement your matrix, use vector<vector<Comparable>> instead. All the work is done for you; your code is just reinventing the wheel.
Usually it is simpler and more effective to represent a matrix with one contiguous allocation, instead of a separate allocation for each row, so you may want to give that idea some thought.
class matrix
{
int n;
double **a; //the "matrix"
public:
matrix(int);
~matrix();
int getN();
matrix& operator=(matrix&);
double& operator()(int,int);
friend matrix& operator+(matrix,matrix);
friend matrix& operator-(matrix,matrix);
friend matrix& operator*(matrix,matrix);
friend ostream& operator<<(ostream &,const matrix &);
};
matrix& operator+(matrix A,matrix B)
{
int i,j,n=A.getN();
assert(A.getN()==B.getN());
matrix *C=new matrix(A.getN());
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
(*C)(i,j)=A(i,j)+B(i,j);
}
}
return *C;
}
Is this the correct way to overload arithmetic operators?
Are there memory leaks in my code?
The constructor allocates memory in heap, first for an array of double pointers, then for each pointer an array of double.
You should be returning the new matrix by value, and passing (at leas one of) the arguments by const reference:
matrix operator+(const matrix& A, const matrix& B);
This means that you should not allocate it dynamically inside the body of the operator.
If you only call public member methods or member operators, then there is no need to declare the non-member operators as friend.
Also note that is is common practice to implement +=, *= etc. as member operators, and then implement the non-members in terms of those:
matrix operator+(matrix A, const matrix& B)
{
return A+=B;
}
As an aside, you have to check that the dimensions of the matrices are correct. With your design it is only possible to do this at run-time. Another approach is to enforce dimensional correctness at compile time by making the matrices class templates:
template <typename T, size_t ROWS, size_t COLS> matrix;
The trade-off is that matrices of different dimensions are different types.
That sort of design will quickly be inefficient. Think of what happens when you do:
matrix D = A + B + C ;
Template meta programming gives you some really nice possibilities to avoid making useless temporaries. Armadillo is a really good implementation of the idea.
I am writing a matrix class in c++ and trying to overload some operator like = and >> and << etc.
I was unable to overload operator [][] for matrix class.
if i have an object of class matrix like M1 then i can use this way for giving value to each element:
M1[1][2]=5;
OR
int X;
X=M1[4][5];
Just overload operator[] and make it return a pointer to the respective row or column of the matrix. Since pointers support subscripting by [], access by the 'double-square' notation [][] is possible then.
You can also overload operator() with two arguments.
There is no operator[][] in C++. You have to return a helper object and then overload operator[] for that too, to have this kind of access.
You could overload operator[]. So if you would like to use matrix that way, you should make matrix as array of vectors.
class Matrix
{
...
Vector & operator[]( int index );
...
};
and
class Vector
{
...
double & operator[]( int index );
...
};
Finally:
Matrix m;
...
double value = m[i][j];
...
there is no operator[][], you can implement operator[] to return a reference to the row/column object, in which you can implement the operator[] to return you the cell reference.
You can do something like the following to avoid all that hassle..
struct loc
{
int x;
int y;
};
then in your operator[] overload, accept a loc, something like
T& operator[](loc const& cLoc)
{
// now you have x/y you can return the object there.
}
To call, you can simply do something like:
matrix[loc(2,3)] = 5;
Actually, I did just that in my own matrix class a few years ago. In this case, I defined a matrix template class that contained the snippet, below.
I was then able to iterate and assign as follows:
for(size_t k=1; k<n; ++k) {
minor[p][k-1]=major[j][k];
}
I hope this helps.
// //////////////////////////////////////////////////////////////////////////////
// list is internal vector representation of n x m matrix
T* list;
// Proxy object used to provide the column operator
template < typename T >
class OperatorBracketHelper
{
Matrix < T > & parent ;
size_t firstIndex ;
public :
OperatorBracketHelper ( Matrix < T > & Parent , size_t FirstIndex ) :
parent ( Parent ), firstIndex ( FirstIndex ) {}
// method called for column operator
T & operator []( size_t SecondIndex )
{
// Call the parent GetElement method which will actually retrieve the element
return parent.GetElement ( firstIndex , SecondIndex );
}
};
// method called for row operator
OperatorBracketHelper < T > operator []( size_t FirstIndex )
{
// Return a proxy object that "knows" to which container it has to ask the element
// and which is the first index (specified in this call)
return OperatorBracketHelper < T >(* this , FirstIndex );
}
T & GetElement ( size_t FirstIndex , size_t SecondIndex )
{
return list[FirstIndex*cols+SecondIndex];
}
I am exactly working on a matrix class and I decided to first create an Array class which has a dynamic 2-D array. So, well just as you, I confronted this obstacle that how I can overload two square brackets. How I approached this case is very simple; I overloaded the square brackets operator twice as member functions. First, I overloaded [] so as to return a pointer pointing to the desired row, so to speak, and then the following member function (i.e. again operator [] overloaded) returns a lvalue of the same type as the array's elements.
However, note that the index you inter to invoke the former overloaded operator [] must be saved somewhere so that you may use it in the latter overloaded operator []. For this reason I simply added a new member of the type int to the class Array (which I've named it "test" in my code below).
class Array {
private:
double **ptr; int test;
... /* the rest of the members includes the number of rows and columns */
public:
Array(int=3,int=3); // Constructor
Array(Array &); // Copy Constructor
~Array(); // Destructor
void get_array();
void show_array();
double* operator[] (int);
double operator[] (short int);
...
};
...
double* Array::operator[] (int a) {
test = a;
double* p = ptr[test];
return p;
}
double Array::operator[] (short int b) {
return ((*this)[test][b]);
}
Therefor, as an example, in main I can simply write:
int main(){
Array example;
cout << example[1][2];
}
I hope this would help you.
You can't overload [][] as such, since there isn't such an
operator. You can overload [] to return something which also
has an [] defined on it (a proxy); in the simplest case,
something like a double* will work, but it's usually better,
although a bit more work, to use a full class. (Place to add
bounds checking, for example.)
Alternatively, you can overload (x,y). Depending on who you
ask, one format or the other is "better". (In fact, it's
strictly a question of style.)
Template matrix class
template <uint8_t rows, uint8_t cols, typename T>
class MatrixMxN {
public:
T* operator[](uint8_t f_row) {
return &m_val[f_row * cols];
}
protected:
T m_val[rows*cols];
};
and here is object of matrix with 3 row, 4 column and integer type.
MatrixMxN<3, 4, int32_t> M;
M[2][3] = 10;
std::cout << M[2][3] << std::endl;