I'm new to C++ and the way 2D arrays work is confusing me. I've been reading online and trying to understand what's causing my specific issue, but have come up with nothing.
According to this Stack Overflow answer, I should be able to get a value in my 2D array by doing this: (*myArrayObject)[row][col], but it throws the following error:
error: invalid types 'int[unsigned int]' for array subscript
return (*myArrayObject)[row][col];
^
And if I try to do myArrayObject[row][col] it throws the following error:
error: invalid initialization of non-const reference of types 'double&' from an rvalue of type 'double'
return myArrayObject[row][col];
^
Here's the full (related/concise) code in question:
main.cpp
#include "matrix.h"
using namespace std;
typedef unsigned int uint;
int main() {
Matrix * matrix; //This could be the problem, but not sure what else to do
matrix = new Matrix(10, 1);
for(uint i = 0; i < matrix->numRows(); ++i) {
for(uint j = 0; j < matrix->numCols(); ++j) {
cout << matrix->at(i,j) << " " << endl;
}
}
return 0;
}
matrix.h
typedef unsigned int uint;
class Matrix {
public:
Matrix(uint rows, uint cols); //Constructor
const uint numRows() const;
const uint numCols() const;
void setRows(const uint &);
void setCols(const uint &);
double & at(uint row, uint col);
private:
uint rows, cols;
int ** matrix; //This could also be the problem, but not sure what else to do
void makeArray() {
matrix = new int * [rows];
for(uint i = 0; i < rows; ++i) {
matrix[i] = new int [cols];
}
}
};
matrix.cpp
#include "matrix.h"
typedef unsigned int uint;
Matrix::Matrix(uint rows, uint cols) {
//Make matrix of desired size
this->setRows(rows);
this->setCols(cols);
//Initialize all elements to 0
for(uint i = 0; i < rows; ++i) {
for(uint j = 0; j < cols; ++j) {
this->matrix[i][j] = 0;
}
}
}
const uint Matrix::numRows() const {
return this->rows;
}
const uint Matrix::numCols() const {
return this->cols;
}
void Matrix::setRows(const uint & rows) {
this->rows = rows;
}
void Matrix::setCols(const uint & cols) {
this->cols = cols;
}
double & Matrix::at(uint row, uint col) {
return matrix[row][col]; //NOT WORKING
}
SOLUTION:
Changes made to matrix.h:
double ** matrix;
void makeArray() {
matrix = new double * [rows];
for(uint i = 0; i < rows; ++i) {
matrix[i] = new double [cols];
}
}
Changes made to matrix.cpp:
added makeArray() to constructor.
As WhozCraig and Rakete1111 said, your problem is, you can't return a reference to double, when your data is all int.
But you may have other problems even after fixing that.
You never call your makeArray function and/or in your constructor you never allocate (new) your array, like you did in makeArray.
Not a problem yet, but.
You don't need any setters for rows and cols values. Or, you need to change these setters in such a way, that a new matrix is reallocated to fit the new sizes.
#include is the same thing, as copy & pasting the contents of the named file. And you have a typedef for uint which simply get copy pasted from matrix.h to matrix.cpp and main.cpp, so it would work even, if you don't specify it again.
You have a using namespace std, but no std headers included. You may need that thing, e.g. if you #include <iostream> or <vector> or any other standard library header. Or if for some reason you wrote code in your own namespace std {...} block.
matrix[row][col] is the right way to do it, as matrix is an int**, and applying operator[] two times is like deferencing the pointer two times, and you'll get an individual int.
The reason though why you get an error because at returns a double&. Let me explain, matrix[row][col] returns an int, and this int gets promoted to a double. That double is a temporary variable, and you can't make a reference out of a temporary variable, and that's why the compiler complains.
Making at return an int& would obviously solve it :)
Related
int** transpose(int** matrix,int row, int column)
{
int** new_mat = new int*[column];
for(int i = 0; i < column; i++)
{
new_mat[i] = new int[row];
}
for(int i = 0; i < row; i++ )
{
for(int j = 0; j < column; j ++)
{
new_mat[j][i] = matrix[i][j];
}
}
return new_mat;
}
I have written this function but something feels wrong I couldn't decide whether should I delete new_mat somehow basically function returns this value how should I manage with memory without using any smart pointers or something?
The caller would use the returned matrix.
Moreover, it could acquire ownership. As a result, when the matrix would be no longer needed, it could delete it.
Another option, is for you, to provide another function, that will delete the matrix. The caller then, must call that function to de-allocate the dynamically allocated memory.
However, smart pointers is a nice C++ feature, and I encourage you to give them a shot.
Furthermore, since this C++, you could use a std::vector<std::vector<int>> for the type of your matrix. That way, you don't have to worry about memory management, since everything about it, will happen automatically.
To answer the question as asked, the caller would need to release the returned pointer. For every usage of operator new in the function, there needs to be a corresponding usage of operator delete in the caller. The caller would do this when the matrix returned is no longer needed i.e. anything that is deleted should not subsequently be used.
A better approach - in many respects, including no potential to forget to release memory - is to avoid using pointers directly, avoid using operator new (or variants) or operator delete directly. Instead, use a standard container, such as std::vector(std::vector<int> >. If used carefully, standard containers manage their own elements, and keep a record of their own size, so there is no possibility of memory leak (when a standard container ceases to exist, any dynamically allocated memory it uses also is released).
In principle, you should be able to simplify your function to something that is declared as
std::vector<std::vector<int> > transpose(const std::vector<std::vector<int> > &matrix);
rather than needing to pass numbers of rows and columns as separate arguments (the vectors will keep track). I'll leave implementing that function as an exercise, since you'll learn more of use that way.
You really should think about your matrix representation:
int** matrix = ...; // create matrix of 10x12
// doing quite a lot of stuff
delete[] matrix[7]; // possibly even forgotten -> memory leak
matrix[7] = new int[7];
and you now have a jagged array. Although std::vector will relieve you from all the memory management stuff, you still won't be able to prevent jagged arrays with:
std::vector<std::vector<int>> matrix = ...; // create matrix of 10x12
// doing quite a lot of stuff
matrix[7].resize(7);
Safest thing you can do is create your own class wrapping around the data; I'll be using std::vectors to hold the data, this will make the whole memory management stuff much easier:
template <typename T> // more flexibility: you can use arbitrary data types...
class Matrix // (but you don't _need_ to make a template from)
{
std::vector<std::vector<T>> data;
public:
Matrix(size_t rows, size_t columns)
: data(rows)
{
for(auto& d : data)
d.resize(columns);
}
// the nice thing about using std::vector as data container is
// that default generated move/copy constructors/assignment
// operators and destructor are fine already, so you can forget
// about rule of three or five respectively
// but you need ways to access your data:
size_t rows() { return data.size(); }
size_t columns() { return data.empty() ? 0 : data[0].size(); }
??? operator[](size_t index);
??? operator[](size_t index) const;
};
Well, the index operators... What you actually want to achieve is something that you can access the matrix just like you did with your arrays:
Matrix<int> m(10, 12);
m[7][7] = 7;
But what should we return? A reference to an inner vector would again allow to modify its size and to create a jagged array this way. Solution: A wrapper class around!
template <typename T>
class Matrix
{
// all we had so far...
template <typename Data>
class Row
{
Data& data;
friend class Matrix;
Row(std::vector<T>& data)
: data(data)
{ }
public:
// default constructed constructors/operators/destructor
// themselves and being public are fine again...
auto& operator[](size_t index) { return data[index]; }
};
auto operator[](size_t index) { return Row(data[index]); }
auto operator[](size_t index) const { return Row(data[index]); }
};
Why Row a template? Well, we need different Row types (mutable and immutable access to data) as return types for the two different index operators...
Finally: If you implement yourself, I'd reorder the private/public sections such that public comes first. This improves readability for users of your Matrix class, as they are interested in public interface only (normally) unless they intend to inherit from. But that (currently) is not a good idea here anyway as this class is not intended for, just as std::vector is not either. If you want that: make the destructor virtual:
virtual ~Matrix() = default;
If you feel more comfortable with, you could make the rule of three/five explicit:
Matrix(Matrix const& other) = default; // rule of three
Matrix& operator=(Matrix const& other) = default; // rule of three
Matrix(Matrix&& other) = default; // rule of five
Matrix& operator=(Matrix&& other) = default; // rule of five
Analogously for Row class. Be aware that if you insist on using raw arrays inside then you will have to write all of these explicitly!
Transposing matrices could then be done via a free function again:
Matrix transpose(Matrix const& m)
{
Matrix t(m.columns(), m.rows());
// loops as you had
return t;
}
You could even provide a member function that transposes the matrix itself, best: use above transpose function:
template <typename T>
class Matrix
{
public:
void transpose()
{
Matrix t(transpose(*this));
t.data.swap(data); // cleanup of previously owned data done in t's destructor...
}
If you don't want to use any smart pointers and vectors, then try like this.
matrix - is a wrapper for 2D-dynamic array with size [col][row].
#include <iostream>
#include <algorithm>
using namespace std;
class matrix
{
private:
unsigned int m_row;
unsigned int m_col;
int **m_data;
public:
matrix(unsigned int row, unsigned int col) : m_row(row), m_col(col), m_data(nullptr)
{
alloc();
}
matrix(const matrix &m) : m_row(m.get_rows_count()), m_col(m.get_cols_count()), m_data(nullptr)
{
alloc();
for(unsigned int i = 0; i < m_row; i++)
for(unsigned int j = 0; j < m_col; j++)
m_data[i][j] = m[i][j];
}
~matrix()
{
free();
}
unsigned int get_rows_count() const { return m_row; }
unsigned int get_cols_count() const { return m_col; }
const int* operator[](unsigned int ind) const
{
return m_data[ind];
}
int* operator[](unsigned int ind)
{
return m_data[ind];
}
matrix& operator=(const matrix &m)
{
free();
m_row = m.get_rows_count();
m_col = m.get_cols_count();
alloc();
for(unsigned int i = 0; i < m_row; i++)
for(unsigned int j = 0; j < m_col; j++)
m_data[i][j] = m[i][j];
return *this;
}
// you need move-operations:
//matrix(matrix&& other) = delete; // move constructor (rule of 5)
//matrix& operator=(matrix&& other); // move assignment (rule of 5)
void print()
{
for(unsigned int i = 0; i < m_row; i++)
{
for(unsigned int j = 0; j < m_col; j++)
cout << m_data[i][j] << " ";
cout << endl;
}
}
private:
void alloc()
{
if(m_data)
return;
m_data = new int*[m_row];
for(unsigned int i = 0; i < m_row; i++)
{
m_data[i] = new int[m_col];
std::fill(m_data[i], m_data[i] + m_col, 0);
}
}
void free()
{
if(!m_data)
return;
for(unsigned int i = 0; i < m_row; i++)
delete[]m_data[i];
delete[]m_data;
m_data = nullptr;
}
};
matrix transpose(const matrix matrix_in)
{
unsigned int M = matrix_in.get_rows_count();
unsigned int N = matrix_in.get_cols_count();
matrix out(N, M);
for(unsigned int i = 0; i < M; i++)
for(unsigned int j = 0; j < N; j++)
out[j][i] = matrix_in[i][j];
return out;
}
int main(int argc, char* argv[])
{
matrix m1(5, 7);
m1[0][1] = m1[0][2] = m1[0][3] = 7;
auto m2 = transpose(m1);
m1.print();
cout << endl;
m2.print();
}
In any case, it is better to free the memory in the same place where it was allocated. If you do not want to use some classes, you can do it like this:
void transpose(int **matr_in, int **matr_out, int M, int N)
{
for(int i = 0; i < M; i++)
for(int j = 0; j < N; j++)
matr_out[j][i] = matr_in[i][j];
}
int **create_matrix(int M, int N)
{
int **m = new int*[M];
for(int i = 0; i < M; i++)
m[i] = new int[N];
return m;
}
void delete_matrix(int **m, int M)
{
for(int i = 0; i < M; i++)
delete []m[i];
delete []m;
}
int main()
{
int M = 5, N = 4;
int **m1 = create_matrix(M, N);
// fill matrix m1
int **m2 = create_matrix(N, M);
transpose(m1, m2, M, N);
delete_matrix(m1, M);
delete_matrix(m2, N);
return 0;
}
you provide a function return a pointer array which holds seperated memory blocks (each represents one row). then you must also provide the free (or delete) function at the same time, and in the same module (to ensure the memory managerment functions matches exactly).
int** transpose(int** matrix, int row, int column)
{
int** new_mat = new int*[column];
...
return new_mat;
}
//when free the mat, cols is not concerned;
void free_mat(int** matrix, int rows)
{
int i;
for(i= 0; i< rows; i++)
delete[] matrix[i];
delete[] matrix;
}
//use the function as:
int** m2 = transpose(m1, rows, cols);
...
free_mat(m2, cols);
//after free_mat(), m2 still holds the address.
//so make it nullptr.
m2 = NULL;
also you can allcate one plane continuous memory block to present 2-dimention matrix:
int* mat = new int[rows * cols];
//transfer mat[iRow][iCol] to mat[iRow * cols + iCol];
return mat;
In my code I create a function outside of the main, which creates a 1D array and initializes to 0.
void create_grid(double *&y, int Npoints)
{
y = new double[Npoints];
for (int i = 0; i < Npoints; i++)
{
y[i] = 0;
}
}
If I didn't have the syntax of declaring in the function as double *&y I couldn't access the values of y.
I tried doing the same for a 2D array but i don't know the syntax. I tried &&**y and &*&*y but it didn't work. Does anyone know how to create a function outside of the main, which initializes a 2d dynamic array so I can use it in the main?
E.g.:
void create_grid_2D(double **&&y, int Npoints1, int Npoints2)
{
y = new double*[Npoints1];
for (int i = 0; i < Npoints1; i++)
{
y[i] = new double[Npoints2];
}
for (int i = 0; i < Npoints1; i++)
{
for (int j = 0; j < Npoints2; j++)
{
y[i][j] = 0;
}
}
}
int main()
{
int N = 10;
double **z;//correcting this line, i wrote z**
create_grid_2D(z, N, N);
delete[]z;
return 0;
}
C++ does not allow forming a pointer to reference or reference to reference. (And without a space between the characters, && is a single token meaning something entirely different.)
And your declaration double z**; is incorrect - you probably mean double **z;.
To write a function that takes the argument double **z by reference, you just want a reference to pointer to pointer:
void create_grid_2D(double **&y,int Npoints1,int Npoints2)
{
//...
}
Except don't use new and delete. Using them slightly wrong leads to memory leaks and bugs with dangling pointers and double deletes. For example, you tried to clean up your memory in main with delete []z;, but new-expressions were evaluated 11 times to your one delete-expression, so this misses out on deleting the row arrays z[0], z[1], ... z[9]. There's pretty much always a better and simpler way using std::unique_ptr, std::shared_ptr, std::vector, or other RAII (Resource Allocation Is Initialization) tools.
So I would change the function to:
void create_grid_2D(std::vector<std::vector<double>>& y,
unsigned int Npoints1,
unsigned int Npoints2)
{
y.assign(Npoints1, std::vector<double>(Npoints2, 0.0));
}
int main()
{
unsigned int N=10;
std::vector<std::vector<double>> z;
create_grid_2D(z, N, N);
// No manual cleanup necessary.
}
Or even use a return value rather than assigning an argument:
std::vector<std::vector<double>> create_grid_2D(
unsigned int Npoints1,
unsigned int Npoints2)
{
return std::vector<std::vector<double>>(
Npoints1, std::vector<double>(Npoints2, 0.0));
}
int main()
{
unsigned int N=10;
std::vector<std::vector<double>> z = create_grid_2D(N, N);
}
An easy trick to resolve/write such complicated references is (simplified version for the sake of this problem - it's a bit more complicated with braces present): start from the variable name and go to the left, step by step. In your case:
... y
y is ...
... & y
y is a reference ...
... *& y
y is a reference to a pointer ...
... **& y
y is a reference to a pointer to a pointer ...
double**& y
y is a reference to a pointer to a pointer to a double
So, the correct definition is:
void create_grid_2D(double**& y,int Npoints1,int Npoints2)
But as mentioned in the comments, please do really consider avoiding raw pointers in favor of std::vector and other standard containers.
So you want a reference on a pointer to a pointer.
2d pointer is int**, and the reference is int**&. That's what you want to use.
Then, you should a container or at least a smart pointer instead.
This approach would be a little different than what you currently have but basically you want a 2D grid and another name for this is simply a MxN Matrix! We can do this very easily with a simple template structure. This template class will hold all of the contents without having to put the data into dynamic memory directly. Then once you have your class object that you want to use we can then put that into dynamic memory with the use of smart pointers!
#include <iostream>
#include <memory>
template<class T, unsigned M, unsigned N>
class Matrix {
static const unsigned Row = M;
static const unsigned Col = N;
static const unsigned Size = Row * Col;
T data[Size] = {};
public:
Matrix() {};
Matrix( const T* dataIn ) {
fillMatrix( dataIn );
}
void fillMatrix( const T* dataIn );
void printMatrix() const;
};
template<class T, unsigned M, unsigned N>
void Matrix<T, M, N>::fillMatrix( const T* dataIn ) {
for ( unsigned i = 0; i < Size; i++ ) {
this->data[i] = dataIn[i];
}
}
template<class T, unsigned M, unsigned N>
void Matrix<T,M,N>::printMatrix() {
for ( unsigned i = 0; i < Row; i++ ) {
for ( unsigned j = 0; j < Col; j++ ) {
std::cout << this->data[i*Col + j] << " ";
}
std::cout << '\n';
}
}
int main() {
// our 1 day array of data
double data[6] = { 1,2,3,4,5,6 };
// create and print a MxN matrix - in memory still a 1 day array but represented as 2D array
Matrix<double,2,3> A;
A.fillMatrix( data );
A.printMatrix();
std::cout << '\n';
Matrix<double, 3,2> B( data );
B.printMatrix();
std::cout << '\n';
// Want this in dynamic memory? With shared_ptr the memory is handled for you
// and is cleaned up upon it's destruction. This helps to eliminate memory leaks
// and dangling pointers.
std::shared_ptr<Matrix<float,2,3>> pMatrix( new Matrix<float,2,3>( data ) );
pMatrix->printMatrix();
return 0;
}
Output:
1 2 3
4 5 6
1 2
3 4
5 6
1 2 3
4 5 6
I'm trying to use clear functions to do a matrix multiplication with random generated values. Therefore I'm hoping to use a function(mat_def) to generate the matrices and another function(mat_mul) to multiply them when the matrices are sent as parameters.
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
double mat_def(int n) //how to return the matrix
{
double a[n][n];
double f;
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
f= rand();
cout<<f ;
a[i][j]=f;
}
}
return 0;
}
double mat_mul( int n, double a[n][n], double b[n][n]) //how to send matrix as parameter
{
return 0;
}
int main()
{
/* initialize random seed: */
srand (time(NULL));
mat_def(10);
}
Here's a nice, standard C++ Matrix template for you.
Matrix.h
#include <vector>
class Matrix
{
class InnerM
{
private:
int ydim;
double* values;
public:
InnerM(int y) : ydim(y)
{
values = new double[y];
}
double& operator[](int y)
{
return values[y];
}
};
private:
int xdim;
int ydim;
std::vector<InnerM> inner;
public:
Matrix(int x, int y) : xdim(x), ydim(y), inner(xdim, InnerM(ydim))
{
}
InnerM& operator[](int x)
{
return inner[x];
}
};
All the memory leaks are there for you but you get the idea. From here you can handle the multiplication by overiding ::operator*() in the Matrix class.
I assume your problem is to define 2-D array and then pass it to mat_mul function to multiply the matrices. And the rest will be quite simple.
Defining the 2-D array(considering memory needs are known at run time):
int rows,cols;
cin >> rows;
cin >> cols;
int **arr = new int*[rows]; // rows X cols 2D-array
for(int i = 0; i < rows; ++i) {
arr[i] = new int[cols];
}
You can define another 2-D array exactly the same way with required rows and column.
now, Passing the 2-D array to function:
void mat_mul(int **arr1, int **arr2, int m, int n, int p, int q){
//define a 2-D array to store the result
//do the multiplication operation
//you could store the result in one of the two arrays
//so that you don't have to return it
//or else the return type should be modified to return the 2-D array
}
example:
void display(int **arr, int row, int col){
for (int i=0; i<row; i++){
for(int j=0;j<col; j++){
cout << arr[i][j] << '\t';
}
cout << endl;
}
}
Delete the memory if not required anymore with the following syntax:
for(int i=0; i<rows; i++){
delete[] array[i];
}
delete[] array;
hope this will be sufficient to get your work done!
there is already an answer on how to return a 2-D array on SO. Check the link below.
https://stackoverflow.com/a/8618617/8038009
Returning the raw allocation is a sucker bet. You need to manage all of the memory allocated yourself and pass it around with the matrix size parameters.
Why suffer? Use a matrix class
template<class Type>
class Matrix{
int rows;
int cols;
std::vector<type> data;
public:
Matrix(int row, int col):rows(row), cols(col), data(rows*cols)
{
// does nothing. All of the heavy lifting was in the initializer
}
// std::vector eliminates the need for destructor, assignment operators, and copy
//and move constructors.
//add a convenience method for easy access to the vector
type & operator()(size_t row, size_t col)
{
return data[row*cols+col];
}
type operator()(size_t row, size_t col) const
{
return data[row*cols+col];
}
};
Usage would be
Matrix<double> mat_mul(const Matrix<double> &a, const Matrix<double> &b)
{
Matrix<double> result;
// do multiplication
return result;
}
int main()
{
/* initialize random seed: */
srand (time(NULL));
Matrix<double> matA(10, 10);
matA(0,0) = 3.14; // sample assignment
matA(9,9) = 2.78;
double x = matA(0,0) * matA(9,9)
Matrix<double> matB(10, 10);
Matrix<double> matC = mat_mul(matA, matB) ;
}
More functionality, such as construction from an initializer list, can be added to the class to make your life easier. You can also specify an operator * overload for Matrix and use that in place of mat_mul if you chose. Read Operator overloading for more on that option.
There are other posts about common causes of segmentation faults, but I don't think the built-in array object I've created here (result) doesn't go out of bounds when I assign values to it.
I think this could be helpful to people in the future who have arrays not out of bounds, and I also haven't seen a lot of stuff about making 2D built-in array objects - examples I've seen are almost entirely vectors or std:array objects.
Here is runnable, relevant code:
matrix.h
#ifndef MATRIX_H
#define MATRIX_H
#include <initializer_list>
using std::initializer_list;
typedef unsigned int uint;
class Matrix {
public:
Matrix(uint rows, uint cols);
~Matrix();
Matrix add(double s) const;
const uint numRows() const;
const uint numCols() const;
double & at(uint row, uint col);
const double & at(uint row, uint col) const;
private:
uint rows, cols;
double ** matrix;
void makeArray() {
matrix = new double * [rows];
for(uint i = 0; i < rows; ++i) {
matrix[i] = new double [cols];
}
}
};
#endif
matrix.cpp
#include "matrix.h"
Matrix::Matrix(uint rows, uint cols) {
//Make matrix of desired size
this->rows = rows;
this->cols = cols;
makeArray();
//Initialize all elements to 0
for(uint i = 0; i < rows; ++i) {
for(uint j = 0; j < cols; ++j) {
this->matrix[i][j] = 0.0;
}
}
}
Matrix::~Matrix() {
for(uint i = 0; i < numRows(); ++i) {
delete[] matrix[i];
}
delete[] matrix;
}
const uint Matrix::numRows() const {
return this->rows;
}
const uint Matrix::numCols() const {
return this->cols;
}
double & Matrix::at(uint row, uint col) {
return matrix[row][col];
}
const double & Matrix::at(uint row, uint col) const {
return matrix[row][col];
}
Matrix Matrix::add(double s) const {
uint r = this->numRows();
uint c = this->numCols();
Matrix * result;
result = new Matrix(r, c);
for(uint i = 0; i < r; ++i) {
for(uint j = 0; j < c; ++j) {
result->at(i,j) = (this->at(i,j)) + s;
}
}
return * result;
}
main.cpp
#include <iostream>
#include <cstdlib>
#include "matrix.h"
using namespace std;
typedef unsigned int uint;
int main() {
Matrix * matrix;
matrix = new Matrix(3, 2); //Works fine
double scaler = 5;
matrix->at(2,1) = 5.0; //Works fine
Matrix r = matrix->add(scaler); //DOESN'T WORK
return EXIT_SUCCESS;
}
Any ideas why the add function is causing a segmentation fault error? The for-loop I used to fill the result Matrix object doesn't go out of bounds, and I'm not familiar enough with C++ to know what else could be causing it.
Thanks in advance.
The problem is lack of a manually defined copy constructor or assignment operator, given that the class manages a resource (memory).
If an instance of the class is assigned, or used to create a copy, the result will be two distinct objects that reference the same memory. When those two objects which refer to the same memory are destroyed, the memory is released twice. The result of that is undefined behaviour.
Look up the "rule of three" for a solution. In C++11, that often becomes a "rule of five" or a "rule of zero" (which involves using techniques to avoid the problem in the first place).
There is also a pretty significant problem in the add() function, since it dynamically creates a Matrix, then returns a copy of it. That causes a memory leak, even if the problem with copying the object is resolved. That function actually looks like something which would be written in a garbage collected language - the problem being that C++ is not garbage collected.
I was making some of my c++ exercises when I noticed following problem.
The given code will not run/compile in Visual Studio 2013 or Qt Creator 5.4.1
giving the error:
invalid types 'double[int]' for array subscript
test[0][0] = 2;
^
However when you first change the 16th (and 17th) line in the header file from
double &operator[]; to double operator[] and make the same changes in the source files -> then compile this (while getting multiple errors) -> and lastly changing it back to the original double &operator[];. Then in Qt Creator 5.4.1 it will compile and it will run while giving the expected results.
Edit: This does not always work, however changing it to double *operator[] instead of double operator[] will always reproduce the problem.
Why is this happening?
matrix.h
#ifndef MATRIX_H
#define MATRIX_H
#include <iostream>
using namespace std;
class Matrix
{
private:
double** m_elements;
int m_rows;
int m_columns;
public:
Matrix(int rows = 1, int columns = 1);
double &operator[](int index);
const double &operator[](int index) const;
friend ostream &operator<<(ostream &ostr, Matrix matrix);
};
#endif // MATRIX_H
matrix.cpp
#include "matrix.h"
Matrix::Matrix(int rows, int columns)
{
m_rows = rows;
m_columns = columns;
m_elements = new double*[rows];
for(int i=0; i<rows; i++)
{
m_elements[i] = new double[columns];
for(int j=0; j<columns; j++)
m_elements[i][j] = 0;
}
}
double &Matrix::operator[](int index)
{
return *(m_elements[index]);
}
const double &Matrix::operator[](int index) const
{
return *(m_elements[index]);
}
ostream &operator<<(ostream &ostr, Matrix matrix)
{
for(int i=0; i<matrix.m_rows; i++)
{
for(int j=0; j<matrix.m_columns; j++)
{
ostr << matrix.m_elements[i][j] << " ";
}
ostr << "\n";
}
return ostr;
}
main
#include <iostream>
#include "matrix.h"
using namespace std;
int main()
{
Matrix test(4,4);
test[0][0] = 2;
cout << test;
return 0;
}
double &Matrix::operator[](int index)
{
return *(m_elements[index]);
}
Will return a reference to the first element in the column rather than the column. So calling test[0][0] = 2; tries to apply the [] operator to a double, not an array of double.
Quick solution:
double * & Matrix::operator[](size_t index)
{
return m_elements[index];
}
This returns a reference to a pointer (read on to find out why I bother with the reference) and you can use [] on the returned pointer to get to the data element.
But...
There are better ways to do this.
Use std::vector, if possible, instead of the dynamic array.
std::vector<std::vector<double> > m_elements(m_rows, std::vector<double>(m_columns, 0.0));
This will solve a lot of potential problems and does the initializing of the matrix to 0 in one shot. It won't solve the [][] indexing though. That will still take a bit of work.
The simplest and safest way to do the indexing is to not use the [] operator at all. Instead define a new method. This way you have total control over what is exposed, and the input can be completely tested for validity before running out of bounds.
double &Matrix::at(size_t row, size_t column)
{
// optional overrun defence if desired
if (row < m_rows || column < m_columns)
{
return m_elements[row][column];
}
throw std::out_of_range("Matrix indices out of range");
}
double Matrix::at(size_t row, size_t column) const
{
// put overrun defence here if desired
return m_elements[row][column];
}
matrix.at(2,3) = 2;
constmatrix.at(2,3) = 2; // bad lvalue compiler error
Note the use of size_t in place of int. size_t is unsigned and removes the need to validity check for negative numbers. You can't have a negative array index, so why allow the possibility?
It is also worth noting that this approach makes it easy to define storage for your matrix as a 1 dimensional array like this:
std::vector<double> m_elements(m_rows * m_columns, 0.0);
or if you have to use an array
double m_elements = new double[m_rows* m_columns];
and access it like this:
double &Matrix::at(size_t row, size_t column)
{
return m_elements[row * m_rows + column];
}
Why? A number of good reasons. Easier to create, maintain and clean up one object than m_rows +1 is a good enough reason for me. Another excellent reason is locality. The whole matrix is guaranteed to be in one contiguous block, not one array here, another there, and yet another in the RAM equivalent of the bottom of the Marianas Trench. The odds of cache hits (and thus performance) go way up.
If you prefer the look and feel of an array, an operator() overload comes quite close.
double &Matrix::operator()(size_t row, size_t column)
{
return m_elements[row][column];
}
double Matrix::operator()(size_t row, size_t column) const
{
return m_elements[row][column];
}
matrix(2,3) = 2;
If you must have [][]
The suggested form of an [] operator returns a reference to the indexed data, in this case a vector or a pointer to a row array.
std::vector<double> & Matrix::operator[](size_t index)
{
return m_elements[index];
}
or
double * & Matrix::operator[](size_t index)
The array and vector internals are identical.
Caveat: This allows the user to get up to all sorts of trouble with that returned vector or pointer reference. Consider matrix[0].clear(); or matrix[0] = NULL; for example.
double * Matrix::operator[](size_t index)
will prevent most abuse by returning a copy of the pointer. Unfortunately this cannot be done to protect the vector because a copy of the vector will be a completely different vector with copies of the source's contents. Updating it and expecting persistence would be futile. The vector would have to be hidden from the user inside a wrapper class, and this is rapidly becoming too much work.
In addition, returning the copy or a wrapper will also block legitimate uses of a reference and violates the Law of Least Surprise: The Matrix [] operator does not work the same as other [] operators and may result in unexpected behaviour if an unsuspecting coder uses it as a regular [] operator.
My opinion is return the unprotected references, and if whoever is using the Matrix class wants to shoot themselves in the head... Well, you can only do so much. If you have to protect the user, use the at method or operator() approach described above.
Const [] operators are similar for the vector
std::vector<double> const & Matrix::operator[](size_t index) const
but different for the array because both the pointer and the values pointed at should be const
double const * const & Matrix::operator[](size_t index) const
My suggested implementation:
Matrix.h
#ifndef MATRIX_H
#define MATRIX_H
#include <iostream>
#include <vector>
// note that the using namespace std; is gone. One should never put it in the header
// and one should also think hard about putting it in the implementation file
class Matrix
{
private:
std::vector<double> m_elements;
size_t m_rows;
size_t m_columns;
public:
Matrix(int rows = 1, int columns = 1);
double &operator()(size_t row, size_t column);
double operator()(size_t row, size_t column) const;
friend std::ostream &operator<<(std::ostream &ostr, const Matrix & matrix);
};
#endif // MATRIX_H
Matrix.cpp
#include <stdexcept>
#include "Matrix.h"
Matrix::Matrix(int rows, int columns):
m_elements(rows * columns, 0.0),
m_rows(rows),
m_columns(columns)
{
}
std::ostream &operator<<(std::ostream &ostr, const Matrix &matrix)
{
for(size_t i=0; i<matrix.m_rows; i++)
{
for(size_t j=0; j<matrix.m_columns; j++)
{
ostr << matrix(i,j) << " ";
}
ostr << std::endl;
}
return ostr;
}
double &Matrix::operator()(size_t row, size_t column)
{
if (row < m_rows && column < m_columns)
{
return m_elements[row * m_rows + column];
}
throw std::out_of_range("Matrix indices out of range");
}
double Matrix::operator()(size_t row, size_t column) const
{
if (row < m_rows && column < m_columns)
{
return m_elements[row * m_rows + column];
}
throw std::out_of_range("Matrix indices out of range");
}
Just Because of the Meaning of the Return.
When You return Reference &, you could change it as you wanna, like test[2][2] = 2;Because it will make mean back to the source it had ever refer to
When You return By value, It just a temporary Object which will be destroyed after using, that means, You Could Not Change It !