I'm currently working on a bigger project which involves implementing a linear algebra calculator. I have decided to not use any other already-existing libraries which might help me implement it as I thought it would be too easy.
I first started to code the Matrix class, which now looks like this:
class Matrix{
private:
int rows; // no. rows
int columns; // no. columns
double** matVal; //values of the matrix
char name; //only used when printing it out or by outside programs.
public:
//constructors and destructor
Matrix();
Matrix(int r,int c,char _name);
~Matrix();
//basic get functions for private access
int getNrRows();
int getNrCols();
char getName();
double getVal(int row,int col);
//basic set functions for private variables
void setVal(int row,int col,double value);
void setName(char _name);
//basic matrix operations
Matrix operator=(Matrix M);
Matrix operator+(Matrix M);
Matrix operator-(Matrix M);
Matrix operator*(Matrix M);
//Printing out the matrix
void Print();
};
At first it went smoothly, but then I stumbled into a fatal bug which wont allow me to progress further. For more information, here are my functions ( + some code to try to figure out what's wrong) and what I executed in main():
#define cout std::cout
Matrix::Matrix(){
rows = 0;
columns = 0;
matVal = nullptr;
}
Matrix::Matrix(int r,int c,char _name){
rows = r;
columns = c;
name = _name;
matVal = new double*[r];
for(int i = 0; i < r; i++){
matVal[i] = new double[c];
}
for(int i = 0; i < r; i++){
for(int j = 0; j < c; j++){
matVal[i][j] = 0;
}
}
}
Matrix::~Matrix(){
for (int i = 0; i < rows; i++)
delete[] matVal[i];
delete[] matVal;
}
int Matrix::getNrRows(){
return rows;
}
int Matrix::getNrCols(){
return columns;
}
char Matrix::getName(){
return name;
}
double Matrix::getVal(int row, int col){
return matVal[row-1][col-1];
}
void Matrix::setVal(int row,int col,double value){
matVal[row-1][col-1] = value;
}
void Matrix::setName(char _name){
name = _name;
}
Matrix Matrix::operator=(Matrix M){
for (int i = 0; i < rows; i++)
delete[] matVal[i];
delete[] matVal;
rows = M.rows;
columns = M.columns;
matVal = new double*[rows];
for(int i = 0; i < rows; i++){
matVal[i] = new double[M.columns];
}
for(int i = 0; i < M.rows; i++){
for(int j = 0; j < M.columns; j++){
matVal[i][j] = M.matVal[i][j];
cout<<matVal[i][j]<<' ';
}
cout<<'\n';
}
cout<<this<<std::endl;
return *this;
}
Matrix Matrix::operator+(Matrix M){
Matrix Rez;
Rez.rows = rows;
Rez.columns = columns;
for(int i = 0; i < rows; i++){
for(int j = 0; j < columns; j++){
Rez.matVal[i][j] = matVal[i][j] + M.matVal[i][j];
}
}
return Rez;
}
void Matrix::Print(){
cout<<'\n';
cout<<name<<": "<<"\n";
for(int i = 0; i < rows; i++){
for(int j = 0; j < columns; j++){
cout<<matVal[i][j]<<' ';
}
cout<<'\n';
}
cout<<'\n';
return;
}
Main:
Matrix M(4,3,'A');
M.setVal(1,1,2);
M.setVal(1,3,-1.1);
M.Print();
Matrix A(4,3,'B');
A.setVal(3,2,5);
A.Print();
Matrix C(4,3,'C');
C = A;
cout<<C.getVal(3,2)<<'\n';
cout<<C.getNrCols()<<" "<<C.getNrRows()<<endl;
C.Print();
cout<<"S"<<endl;
Printing the first 2 matrices works fine, when I print each element of C in the operator= function after I assigned it the proper value, again, it works fine, but when I use the Print() function on C it crashes. Here is the console output for the code above:
A:
2 0 -1.1
0 0 0
0 0 0
0 0 0
B:
0 0 0
0 0 0
0 5 0
0 0 0
0 0 0
0 0 0
0 5 0
0 0 0
0x69fed0
5
3 4
C:
At first I had absolutely no idea why it did that, but then I printed the pointer to each variable instead(it printed it all and returned 0 this time):
A:
0x850e38 0x850e40 0x850e48
0x851318 0x851320 0x851328
0x851338 0x851340 0x851348
0x851358 0x851360 0x851368
B:
0x851390 0x851398 0x8513a0
0x8513b0 0x8513b8 0x8513c0
0x8513d0 0x8513d8 0x8513e0
0x855b08 0x855b10 0x855b18
0x855b40 0x855b48 0x855b50
0x855b60 0x855b68 0x855b70
0x855b80 0x855b88 0x855b90
0x855ba0 0x855ba8 0x855bb0
0x69fed0
5
3 4
C:
0 0x8 0x10
0 0x8 0x10
0 0x8 0x10
0 0x8 0x10
S
Now I think that there is a problem with the Print function (because otherwise why would I be able to get the 5 printed out in main?). I still don't know exactly what is happening so I ask for your help. I'm sorry if this is a rookie mistake, I'm still quite inexperienced.
I also forgot to add that the class and class functions are in separate files (header and cpp), although I don't know how this could affect things.
The signature Matrix operator=(Matrix&); proposed in another answer it totally wrong. The correct signature should be
Matrix& operator=(const Matrix&);
or
void operator=(const Matrix&);
if you don't need chaining of assignments (a = b = c).
The bare minimum that you have to implement: copy constructor, copy assignment, and destructor. For a matrix class it is also reasonable to implement move operations. This is known as the rule of zero/three/five (it is five in our case):
If a class requires no user-defined constructors, no user-defined assignment operators and no user-defined destructor, don't define them; if a class requires a user-defined destructor, a user-defined copy (and move) constructor, or a user-defined copy (and move) assignment operator, it almost certainly requires all three (five).
Let's assume that matrix internally is represented as a 1D array. This approach avoids unnecessary indirection for matrix element access and simplifies code.
class Matrix {
public:
Matrix(const Matrix&);
Matrix(Matrix&&);
Matrix& operator=(const Matrix&);
Matrix& operator=(Matrix&&);
~Matrix();
private:
double* data_ = nullptr;
std::ptrdiff_t rows_ = 0;
std::ptrdiff_t cols_ = 0;
};
Copy operations should be deep, i.e. they should copy data, not just underlying pointer. Let's start with a copy constructor. It should allocate storage, then copy data from other to this storage:
Matrix(const Matrix& other)
: rows_(other.rows_), cols_(other.cols_) {
const auto n = other.rows_ * other.cols_;
data_ = new double[n];
std::copy(other.data_, other.data_ + n, data_);
}
Now let's implement swap:
void swap(Matrix& other) {
std::swap(rows_, other.rows_);
std::swap(cols_, other.cols_);
std::swap(data_, other.data_);
}
This function is very useful, because it allows us to implement move constructor, copy assignment and move assignment with almost no code:
Matrix(Matrix&& other) {
swap(other);
}
Matrix& operator=(const Matrix& other) {
Matrix(other).swap(*this);
return *this;
}
Matrix& operator=(Matrix&& other) {
Matrix(std::move(other)).swap(*this);
return *this;
}
With such canonical implementations you can be sure that these functions are implemented correctly (including self-assignment handling) once you have correct implementations of copy constructor and swap. Appreciate the elegance of the copy-and-swap idiom.
Now let's talk about operator+. Take a look at this implementation:
Matrix operator+(Matrix other) const {
assert(rows_ == other.rows_);
assert(cols_ == other.cols_);
const auto n = rows_ * cols_;
for (std::ptrdiff_t i = 0; i < n; ++i)
other.data_[i] += data_[i];
return other;
}
Here we take the argument by value and get a (deep) copy of it. Then we add this->data_ to the copy and return that copy. There is no need to introduce another local Matrix variable.
Complete demo
you can change the operator= function to this form:
Matrix operator=(Matrix &M);
and in definition you should change its return to:
return M;
i checked this and worked.
in operator+(and *) consider this note too.
Related
I'm trying to add two matrices using multidimensional arrays and overloading the addition operator '+' and the assignment operator '=' however, garbage data is passed to my overloaded assignment operator function.
I've been reading things about the Copy and Swap Idioms and so on but I can't seem to find a solution to my problem, and I'd prefer to use + and = separately, rather than '+=' which I've seen some people do, because I will use other arithmetic for matrices, but I want to get this solved first.
Here is my header:
#ifndef Matrix_h
#define Matrix_h
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <utility>
class Matrix
{
private:
int row;
int column;
double ** elements;
public:
Matrix();
Matrix(int r, int c); //constructor
Matrix(const Matrix& src); //copy constructor
~Matrix(); //destructor
Matrix& operator=(const Matrix& right); // assignment operator
int getRow() const;
int getColumn() const;
void setElement(int r, int c, double data);
double getElement(int r, int c) const;
Matrix& operator+(const Matrix& right); // calculates the sum of two matrices
};
#endif
Here are my function definitions:
#include <string.h>
#include "matrix.h"
using namespace std;
Matrix::Matrix(int r, int c){ //defines the constructor to create a new matrix
row = r;
column = c;
elements = new double*[row];
for (int i=0; i<row; i++){
elements[i] = new double[column];
}
for (int i=0; i<row; i++){
for (int j=0; j<column; j++){
elements[i][j] = 0;
}
}
}
Matrix::Matrix(const Matrix& src){ //defines the copying constructor
row = src.row;
column = src.column;
elements = new double*[row];
for (int i=0; i<row; i++){
elements[i] = new double[column];
}
for (int i=0; i<row; i++){
for (int j=0; j<column; j++){
elements[i][j] = src.elements[i][j];
}
}
}
Matrix::~Matrix() { //defines the destructor
for (int i=0; i<row; i++){
delete[] elements[i];
}
delete[] elements;
};
void Matrix::setElement(int r, int c, double data) {
elements[r][c] = data;
};
double Matrix::getElement(int r, int c) const {
return elements[r][c];
}
int Matrix::getColumn() const {
return column;
}
int Matrix::getRow() const {
return row;
}
Matrix& Matrix::operator =(const Matrix& right) {
if(this->elements != right.elements && column==right.column && row==right.row)
{
memcpy ( &this->elements, &right.elements, sizeof(this->elements) );
}
return *this;
}
Matrix& Matrix::operator +(const Matrix& right) {
// first, make sure matrices can be added. if not, return original matrix
if (this->row != right.row || this->column != right.column){
cout << "Matrix sizes do not match.";
return (*this);
}
Matrix sum(row, column);
for (int i=0; i<this->row; i++){
for (int j=0; j<this->column; j++){
sum.elements[i][j] = this->elements[i][j] + right.elements[i][j];
}
}
return sum;
}
My main simply creates matrixA with a 4x4 matrix of 1s and matrixB with a 4x4 matrix of 2s and adds them where the sum = matrixC which should result in a 4x4 matrix of 3s. However, this is my result:
matrixA
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
matrixB
2 2 2 2
2 2 2 2
2 2 2 2
2 2 2 2
matrixC before addition
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
matrixC after addition
1.0147e-316 3 3 3
1.0147e-316 3 3 3
1.0147e-316 3 3 3
1.0147e-316 3 3 3
When I test it to see what elements are passed to 'right' in the '=' function I noticed similar data as the first column of matrixC. I believe I'm not understanding the passing of references involved here fully so I'd like some help on that.
Your assignment operator is broken (and logically flawed).
It's logically flawed because an assignment like m1 = m2 should result in m1 == m2, no matter what the previous state of m1 was. Your implementation only attempts to copy when they have the same size. You should resize the matrix on the left side of the assignment.
Your operator also isn't using memcpy correctly. memcpy(dest, src, count) will copy contiguous data from the address src to the address dest. The addresses you're providing are the addresses of pointers to pointers to int, not pointers to a contiguous block of ints. There are two levels of indirection here that memcpy simply has no clue about. Also, sizeof(this->elements) is sizeof(int**), which will be a constant of probably 4 or 8 bytes, which should also seem wrong. To fix this, I would just use a plain old set of for loops.
Matrix& Matrix::operator=(const Matrix& other){
// attempt to acquire memory before modifying (for exception safety)
int** temp = new int*[other.row];
for (int i = 0; i < other.row; ++i){
temp[i] = new int[other.col];
}
// cleanup
for (int i = 0; i < row; ++i){
delete[] elements[i];
}
delete[] elements;
// copy data
elements = temp;
row = other.row;
col = other.col;
for (int i = 0; i < row; ++i){
for (int j = 0; j < col; ++j){
elements[i][j] = other.elements[i][j];
}
}
return *this
}
Another important note: Your operator+ returns a reference to a local variable. This is very bad, but C++ doesn't protect you from it. You should just return sum by value.
In modern C++, we generally prefer to use safer alternatives to manual memory management like std::vector<std::vector<int>>, which go a long way to seamlessly enforce correct usage and to save you from yourself.
Hmm, my CLang compiler warns me that operator +(const Matrix& right) return a reference to a temporary which is Undefined Behaviour.
You must return a plain object and not a reference:
Matrix operator=(const Matrix& right); // assignment operator
Matrix& Matrix::operator +(const Matrix& right) {
// ...
Matrix sum(row, column);
// ...
return sum;
}
You're returning a reference to an automatic local (sum).
You can see the canonical forms of the arithmetic operators here, and addition should look like
Matrix Matrix::operator+(const Matrix &b) const;
There are various other issues, some mentioned in other answers, but the only thing your current operator+ reliably produces is Undefined Behaviour.
A hint: if you store the matrix as one array double m[rows * columns] the implementation is going to be much simpler.
Indexing [row][column] is m[row * columns + column].
Summing is just summing two arrays element-wise (vector sum essentially).
Im working on matrix template class, and now i should write the "=" operator overload.
What Im trying to do is to delete the matrix that appears in the left side of the '=', and return new one that equals to the matrix that appears in the right side of the '='.
because i can't delete "this" with a distructor, I delete it "manually" in the function. but now i should make a new matrix, therefor i make a new one ("temp") and return it.
The prblem is that "temp" is really return, but it doesn't set in the matrix that appears in the left side of the '='.
The code:
Matrix<int> m (3, 4);
Matrix<int> m2(2, 5);
m2 = m;
This was the main part.
The function:
template<class T>
Matrix<T> & Matrix<T>::operator=(Matrix<T>& mat)
{
if (this==&mat)
{
return *this;
}
for (int i = 0; i < this->rows; i++)
{
delete[] this->mat[i];
}
delete[] this->mat;
Matrix<T> * temp = new Matrix<T>(mat.rows, mat.cols);
for (int i = 0; i < temp->rows; i++)
for (int j = 0; j < temp->cols; j++)
{
temp->mat[i][j] = mat.mat[i][j];
}
return *temp;
}
template<class T>
Matrix<T>::Matrix(int row, int col)
{
rows = row;
cols = col;
mat = new T*[rows];
for (int i = 0; i < rows; i++)
{
mat[i] = new T[cols];
}
rester(*this);
}
Thx!!
Use std::vector as storage (instead of manal new and delete), and just accept the copy assignment operator generated by the compiler. It's that easy.
If you absolutely want to implement the copy assignment yourself, for learning, then just express copy assignment in terms of copy construction.
To do that, first define a noexcept swap operation:
// In class definition:
friend
void swap( Matrix& a, Matrix& b )
noexcept
{
using std::swap;
// swap all data members here
}
Then the copy assignment operator can be expressed as simply
// In class definition
auto operator=( Matrix other )
-> Matrix&
{
swap( *this, other );
return *this;
}
It's popular, an idiom, because it's very simple yet exception safe.
Instead of returning a reference, which adds verbosity and some possible marginal inefficiency for no gain, you might want to just use void as return type. However, probably for historical reasons, the containers in the standard library require the copy assignment operator to return a reference to self.
You need to allocate memory for this instead of creating a temp.
template<class T>
Matrix<T> & Matrix<T>::operator=(Matrix<T>& rhs)
{
if (this==&rhs)
{
return *this;
}
// Delete current memory
for (int i = 0; i < this->rows; i++)
{
delete[] this->mat[i];
}
delete[] this->mat;
this->rows = rhs.rows;
this->cols = rhs.cols;
// Allocate new memory
// Assign values to newly allocated memory.
this->mat = new int*[rhs.rows];
for (int = 0; i < rhs.rows; ++i )
{
this->mat[i] = new int[rhs.cols];
for (int j = 0; j < rhs.cols; j++)
{
this->mat[i][j] = rhs.mat[i][j];
}
}
// Return *this.
return *this;
}
I would recommend using the suggestion given in the answer by #Cheersandhth.
Also use a different name of the argument. Don't confuse with the member variable mat and the argument mat.
I am making a class to do matrix (and vector) math for a test I am running and to learn more C++. The class looks like this:
class utlMatrix
{
private:
int num_rows;
int num_cols; // number of columns
double **data; // array of pointers to the data
public:
// default constructor, with initialization
utlMatrix() : num_rows(0), num_cols(0), data(NULL) {};
// constructor with size
utlMatrix(int, int);
// destructor
~utlMatrix();
// copy constructor
utlMatrix(const utlMatrix&);
void copy(const utlMatrix &old); // copy 'old' to 'this'
void zero(); // sets all values to zero
void fill_rand(); //fills the data with random stuff
void print(std::ostream&); // prints the matrix to a file
// Operators
utlMatrix& operator=(const utlMatrix&); // copies matrices
friend utlMatrix operator+(const utlMatrix&, const utlMatrix&); // adds 2 matrices
utlMatrix operator*(const utlMatrix&) const;
//friend utlMatrix operator*(const utlMatrix&, const utlMatrix&); // multiplies 2 matrices
};
Copy Constructors, assignment operator and destructor
// copy constructor
utlMatrix::utlMatrix(const utlMatrix &old) {
copy(old);
}
utlMatrix& utlMatrix::operator=(const utlMatrix &old) {
copy(old);
return *this;
}
void utlMatrix::copy(const utlMatrix &old) {
num_rows = old.num_rows;
num_cols = old.num_cols;
data = new float*[num_rows];
for (int i = 0; i < num_cols; i++)
data[i] = new float[num_cols];
for (int i = 0; i < num_rows; i++)
{
for (int j = 0; j < num_cols; j++)
data[i][j] = old.data[i][j];
}
}
utlMatrix::~utlMatrix()
{
for (int i = 0; i < num_rows; i++)
delete [] data[i];
delete [] data;
}
Multiplication operators, I tried both, both failed if used twice in a row.
/*
utlMatrix operator*(const utlMatrix &left, const utlMatrix &right)
{
// first determine if the matrices can be multiplied
if (left.num_cols != right.num_rows)
{
std::cout << "Error using *, Inner dimensions must agree." << std::endl;
exit(-1);
}
// create the new matrix
utlMatrix newmat(left.num_rows, right.num_cols);
for (int i = 0; i < left.num_rows; i++)
for (int j = 0; j < right.num_cols; j++)
for (int k = 0; k < right.num_rows; k++)
newmat.data[i][j] += left.data[i][k] * right.data[k][j];
return newmat;
}
*/
utlMatrix utlMatrix::operator*(const utlMatrix &right) const
{
if ( this->num_cols != right.num_rows)
{
std::cout << "Error using *, Inner dimensions must agree." << std::endl;
return utlMatrix();
}
utlMatrix newmat(this->num_rows, right.num_cols);
for (int i = 0; i < this->num_rows; i++)
for (int j = 0; j < right.num_cols; j++)
for (int k = 0; k < right.num_rows; k++)
newmat.data[i][j] += this->data[i][k] * right.data[k][j];
return newmat;
}
The copy constructor, assignment and addition operators all work fine. When I try to multiply 1 matrix by another it works the first time but fails on the second. I altered the code to write out when it enter a constructor, operator and destructor in addition to printing the matrices after the multiplication. The math is good for the first matrix and the code fails with:
Unhandled exception at 0x776015de in sandbox.exe: 0xC0000005: Access violation writing location 0xcdcdcdcd.
From the screen output I know this is happening after the copy constructor is called following the second multiplication. The addition operator mirrors the first multiplication operator and appears to work fine, no exceptions, the copy constructor and destructor happen as expected. I found 2 answers on SO, Matrix class operator overloading,destructor problem and Matrix Multiplication with operator overloading. I checked to make sure that my pointers were not copied. The copy constructor does create a new object with a new pointer to data. If I run:
int main()
{
utlMatrix A(3,3);
utlMatrix B(2,2);
A.fill_rand();
B.fill_rand();
B = A;
return 0;
}
The debugger shows:
A {num_rows=3 num_cols=3 data=0x000365c0 ...} utlMatrix
B {num_rows=3 num_cols=3 data=0x00037c50 ...} utlMatrix
What did I miss? Here is how I actually used the operator. Failure occurs after D = A * B;
#include "utlMatrix.h"
int main()
{
// create the first matrices
utlMatrix A(3,3);
utlMatrix B(3,3);
utlMatrix C(3,2);
// fill them with random numbers
A.fill_rand();
B.fill_rand();
C.fill_rand();
utlMatrix D = A * B;
utlMatrix E = A * C;
utlMatrix F = B * C;
}
The error appeared to only show itself after the second or third call because that was when the matrices produced non-square output. These lines:
for (int i = 0; i < num_cols; i++)
data[i] = new float[num_cols];
in the copy constructor meant the non-square matrix was actually being built as a square matrix of size columns of the old one. Since my case was a matrix with more rows than columns it tried to put data into a non-existing memory location. Following the suggestions of Igor Tandetnik and Dave S, fixing the indexes and using SWAP fixed the problem.
I have some problem about operator overloading. I looked everywhere but couldn't find a proper solution for this error. Here is some parts of my code :
Matrix<type> Matrix<type>::operator/(const Matrix& denom){
if(num_of_rows != denom.num_of_rows || num_of_cols != denom.num_of_cols)
throw string("Unable to divide (Different size).");
if(denom.contains(0))
throw string("Unable to divide (Divide by zero).");
for(int i = 0; i < num_of_rows; i++)
for(int j = 0; j < num_of_cols; j++)
values[i][j] /= denom.values[i][j];
// I KNOW THIS IS NOT HOW TO DIVIDE TWO MATRICES
return *this;
}
void Matrix<type>::operator=(const Matrix& m) const {
delete [][] values;
num_of_rows = m.num_of_rows;
num_of_cols = m.num_of_cols;
values = new type*[num_of_rows];
for(int i = 0; i < num_of_rows; i++){
*(values + i) = new type[num_of_cols];
for(int j = 0; j < num_of_cols; j++)
values[i][j] = m.values[i][j];
}
}
And this is the Matrix class and the constructor takes 2 arguments :
class Matrix{
private:
type** values;
int num_of_rows, num_of_cols;
public:
Matrix(){}
Matrix(int, int);
type getElement(int, int);
void print();
bool contains(type);
Matrix<type> operator/(const Matrix&);
void operator=(const Matrix&) const;
};
template <class type>
Matrix<type>::Matrix(int rows, int cols){
values = new type*[rows];
num_of_rows = rows;
num_of_cols = cols;
for(int i = 0; i < rows; i++){
*(values + i) = new type[cols];
for(int j = 0; j < cols; j++){
type random = (type)rand() / 3276.71;
values[i][j] = random;
}
}
}
And this piece of code in main gives this error :
srand(time(NULL));
Matrix<int> m1(3,5); // creating some objects
Matrix<double> m2(3,5); // matrices’ elements are assigned randomly from 0 to 10
Matrix<double> m3(5,5);
Matrix<double> m4(5,6);
if(!(m2.contains(0))){
Matrix<double> m8(3,5);
m8=m1/m2; // THIS LINE GIVES ERROR
m8.print();
}
m1 has type Matrix<int>, so when looking up for a suitable overload of operator/ we find:
Matrix<int> Matrix<int>::operator/(const Matrix& denom);
Note that the parameter type Matrix here makes use of the so-called injected class name. That means Matrix stands in this case for Matrix<int>, since that's the (template) class in question. However m2, the argument to the call to operator/, has type Matrix<double>. There is no suitable conversion from Matrix<double> to Matrix<int> so the call is invalid.
A possible fix is to change operator/ to also be a template:
// Declared inside Matrix<type>:
template<typename Other>
Matrix& operator/=(Matrix<Other> const& other);
(I also took the liberty of fixing the operator to better reflect what it's actually doing.)
However you'll then run into the problem that you're using a Matrix<int> (the result of the call to operator/) to assign to m8 which has type Matrix<double>. So perhaps you need an operator= to do a conversion (in which case I'd recommend a converting constructor as well, or perhaps even just a converting constructor without a converting operator=).
The error message quite clearly states that you haven't defined a division operator taking the two types as arguments you are passing. Looking at the code excerpt this is the case: There is an operator taking two Matrix<T> but none taking aMatrix<T1> and a Matrix<T2> (for different type T1 and T2).
BTW, what is your question?
I have a little bit of a problem... I understand what a EXC_BAD_ACCESS error is and I generally know how to fix it but this one has got me completely stuffed. I have this all within a class, here is one method:
double Matrix::get_element(int r, int c) const {
//Retrieve the element at row r and column c
//Should not modify the value stored in Matrix but return a double copy of the value
double currentValue = matrix[r][c];
return currentValue;
}
Now, I have another piece of my code that calls this method:
std::string Matrix::to_string() const {
std::string result;
double current;
Matrix working = *this;
std::ostringstream oss;
oss << "[";
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
current = 0.0;
current = working.get_element(i, j);
oss << " " << current << " ";
}
oss << "; ";
}
oss << "]";
result = oss.str();
return result;
}
I know that the working object has 3 rows and 3 cols at the point where working.get_element(i, j); is called. The variable list shows me just before the get_element() method, that both rows and cols are set to 3. In the method, I'm able to get the value at get_element(0, 0) but not get_element(0, 1).
I can't see why this is the case... Anyone know why or require more of my code to understand why these methods are being called?
EDIT:
Here is the header file:
class Matrix {
private:
//Any variables required
int rows;
int cols;
double **matrix;
public:
Matrix(); //Working M
~Matrix(); //Working M
Matrix(int r, int c); //Working M
int getRows();
int getCols();
void set_element(int r, int c, double val); //Working M
double get_element(int r, int c) const; //Working M
void clear(); //Working M
bool is_empty(); //Working M
bool is_identity(); //Working M
const Matrix transpose(); //Working M
int minorMat(double **dest, const int row, const int col, int order); //Working M
double get_determinent(); //Working M
double higherDeterminents(int order); //Working M
const Matrix operator+(const Matrix &rhs); //Working M
const Matrix operator-(const Matrix &rhs); //Working M
const Matrix operator*(const Matrix &rhs);
bool operator==(const Matrix &rhs); //NOT assessed
const Matrix operator*(const double &rhs);
const Matrix operator/(const double &rhs);
Matrix & operator=(const Matrix &rhs);
std::string to_string() const;
};
Do ignore the comments sorry. And this is the constructors/destructors:
Matrix::Matrix() {
//Basic Constructor
rows = 1;
cols = 1;
matrix = new double*[rows];
for (int i = 0; i < rows; ++i) {
matrix[i] = new double[cols];
}
}
Matrix::~Matrix() {
//Basic Deconstructor
for (int i = 0; i < rows; ++i) {
delete[] matrix[i];
}
delete[] matrix;
rows = NULL;
cols = NULL;
matrix = NULL;
}
Matrix::Matrix(int r, int c) {
//Empty matrix (all 0's) with r rows and c columns, if they are -ve, set to 1
rows = r;
cols = c;
if (cols < 0)
cols = 1;
if (rows < 0)
rows = 1;
matrix = NULL;
matrix = new double*[rows];
for (int i = 0; i < rows; i++) {
matrix[i] = new double[cols];
}
}
EDIT2:
Matrix & Matrix::operator=(const Matrix &rhs) {
//rhs is matrix to be copied
//rhs compied into Matrix called on
double toCopy;
for (int i = 0; i < rhs.rows; i++) {
for (int j = 0; j < rhs.cols; j++) {
toCopy = rhs.get_element(i, j);
this->set_element(i, j, toCopy);
}
}
return *this;
}
It is impossible for us to say when you do not state how you declare and initialize the matrix element. Using something like that in your CTOR should be fine:
class Matrix {
float matrix[3][3];
...
}
Don't forget to initialize it in your CTOR to something that makes sense.
Btw: why do you do this: Matrix working = *this; ?? You could simply this->get_element(i, j); instead, which would not invoke copying of your whole object. [1]
EDIT: Update since you updated your answer. You should be careful with your copy CTORs and operator=() statements. It is easily possible to make double deletes or something ugly like that.
EDIT2: I think the problem is this line:
Matrix working = *this;
You are creating a new copy working of your this object. But working gets initialized with only 1 column and 1 row (as defined in your standard CTOR). I'm not sure if you are checking the bounds when calling set_element or get_element so I guess you are writing over the bounds of your arrays.
I think the best idea is to just remove the line Matrix working = *this; and to adhere to my tip in above:
this->get_element(i, j); in std::string Matrix::to_string() const.
Your Matrix(int r, int c) allocates memory for matrix, but leaves the values it points to uninitialized. Add something like this to the constructor:
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
this->set_element(i, j, 0);
}
}
Doing like this:
int main()
{
Matrix m(3,3);
std::cout << m.to_string();
}
Output:
[ 0 0 0; 0 0 0; 0 0 0; ].
Same thing in the default constructor:
Matrix::Matrix() {
//Basic Constructor
rows = 1;
cols = 1;
matrix = new double*[rows];
for (int i = 0; i < rows; ++i) {
matrix[i] = new double[cols];
}
}
You allocated memory, but wherever matrix[0][0] points to, it's uninitialized garbage value. Do something like matrix[0][0] = 0; or whatever default value you want it to have.
Hope that helps.
Your class is violating the "big three" rule. If a class has one of a destructor, assignment operator or copy constructor then it most probably you need to have all three.
In your case you have a destructor, but no assignment operator or copy constructor, and this is going to create an UB condition when you do Matrix working = *this.
By the way there are two other problems with your code
From the comment seems that you think that new double[size] will initialize the elements to 0 and this is not true.
Most of your code is technically very bad. Your matrix class would be much easier (less code) to implement correctly using std::vector instead of pointers and dynamic memory. Of course if this is just an exercise then it make sense to avoid using std::vector.
By the way if you never heard about the "big three" rule chances are that you're trying to learn C++ with experimentation instead that by reading.
With C++ this is not a smart move... logic can be used as substitute for study if 1) the topic is highly logical, 2) if you can be told when you get it wrong.
Instead C++ is very very complex and in a few place is also quite illogical (for historical reasons) so there are parts in which logic will simply misguide you.
Moreover when you make a mistake in C++ you don't get in general an error message, but "Undefined Behavior". This basically makes very very hard to learn C++ with experimentation because even wrong code may apparently work. It is also very easy to write code that looks good and that is instead quite wrong for subtle reasons.
Instead of just experimenting you should grab a good book and read it cover to cover...