c++ same code compiles/runs never in Visual Studio and sometimes in Qt Creator -
i making of c++ exercises when noticed following problem. given code not run/compile in visual studio 2013 or qt creator 5.4.1
giving error:
invalid types 'double[int]' array subscript test[0][0] = 2; ^
however when first change 16th (and 17th) line in header file double &operator[];
double operator[]
, make same changes in source files -> compile (while getting multiple errors) -> , lastly changing original double &operator[];
. in qt creator 5.4.1 compile , run while giving expected results.
edit: not work, changing double *operator[]
instead of double operator[]
reproduce problem.
why 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 reference first element in column rather column. calling test[0][0] = 2;
tries apply []
operator double, not array of double.
quick solution:
double * & matrix::operator[](size_t index) { return m_elements[index]; }
this returns reference pointer (read on find out why bother reference) , can use [] on returned pointer data element.
but...
there better ways this.
use std::vector, if possible, instead of dynamic array.
std::vector<std::vector<double> > m_elements(m_rows, std::vector<double>(m_columns, 0.0));
this solve lot of potential problems , initializing of matrix 0 in 1 shot. won't solve [][] indexing though. still take bit of work.
the simplest , safest way indexing not use [] operator @ all. instead define new method. way have total control on exposed, , input can tested 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 use of size_t
in place of int
. size_t unsigned , removes need validity check negative numbers. can't have negative array index, why allow possibility?
it worth noting approach makes easy define storage matrix 1 dimensional array this:
std::vector<double> m_elements(m_rows * m_columns, 0.0);
or if have use array
double m_elements = new double[m_rows* m_columns];
and access this:
double &matrix::at(size_t row, size_t column) { return m_elements[row * m_rows + column]; }
why? number of reasons. easier create, maintain , clean 1 object m_rows +1 enough reason me. excellent reason locality. whole matrix guaranteed in 1 contiguous block, not 1 array here, there, , yet in ram equivalent of bottom of marianas trench. odds of cache hits (and performance) go way up.
if prefer , feel of array, 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 must have [][]
the suggested form of [] operator returns reference indexed data, in case vector or pointer row array.
std::vector<double> & matrix::operator[](size_t index) { return m_elements[index]; }
or
double * & matrix::operator[](size_t index)
the array , vector internals identical.
caveat: allows user sorts of trouble returned vector or pointer reference. consider matrix[0].clear();
or matrix[0] = null;
example.
double * matrix::operator[](size_t index)
will prevent abuse returning copy of pointer. unfortunately cannot done protect vector because copy of vector different vector copies of source's contents. updating , expecting persistence futile. vector have hidden user inside wrapper class, , rapidly becoming work.
in addition, returning copy or wrapper block legitimate uses of reference , violates law of least surprise: matrix [] operator not work same other [] operators , may result in unexpected behaviour if unsuspecting coder uses regular [] operator.
my opinion return unprotected references, , if whoever using matrix class wants shoot in head... well, can much. if have protect user, use at
method or operator()
approach described above.
const [] operators similar vector
std::vector<double> const & matrix::operator[](size_t index) const
but different array because both pointer , values pointed @ should 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 using namespace std; gone. 1 should never put in header // , 1 should think hard putting in 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"); }
Comments
Post a Comment