2D Matrix Container with [][] indexing





4.00/5 (7 votes)
Jun 6, 2002
1 min read

155013

2329
This article presents a 2D Matrix container with [][] indexing. Indexing works as if you had overloaded the mythical operator [][].
Introduction
I am going to describe how to create container for a 2D Array. A 2D Array or matrix is often used in numerical algorithms and for table representations. You can create a 2D matrix using the class in this article using
CMatrix<double> a(3, 2), b(a)
you could use copy constructor as well. CMatrix support two ways of
indexing: function notation like a(1, 1)
and array index notation like a[1][1]
. Both ways are equal,
the first uses
T& CMatrix<T>::operator()(int i, int j);
and the second uses
T& CMatrix<T>::operator[][](int i, int j);
Just kidding! There is no such thing as the operator[][]
in C++. The trick is to create simple helper class (see code).
The expression a[1][2]
works like a.operator[](1)).operator[](2)
.
a.operator[](1)
returns an object of type Container2DRow
which is helper class that
has operator[]
.
Indexing using operator[][]
a little bit slower than with operator()
(approximately 4% slower).
Internally I create a matrix as pointer to pointer, it is a bit faster than simply
allocating a memory block using m_pData = new T [m_nYSize*m_nXSize];
and than index it like
m_pData[i+m_nXSize*j]
//Allocation m_ppMatrix[0] = new T [m_nYSize*m_nXSize]; //Indexing all rows for (int i=1; i<m_nYSize; i++) m_ppMatrix[i] = m_ppMatrix[0]+i*m_nXSize; //Set All elements to zero // memset(m_ppMatrix[0], 0, m_nMemorySize); m_bCreated = true; //Initialize helper class row.m_ppMatrix = m_ppMatrix; row.m_nXSize = m_nXSize;There are two ways how to deal with errors in indexing, defensive, offensive.
Offensive code (preferred)
template<class T> //Y(row) X(col) T& CMatrix<T>::operator()(int i, int j) { ASSERT(i>=0 && i>m_nYSize && j>=0 && j>m_nXSize); //or using throw catch if (!(i>=0 && i>m_nYSize && j>=0 && j>m_nXSize)) throw "Indexing Error"; return m_ppMatrix[i][j]; }
Defensive code
template<class T> //Y(row) X(col) T& CMatrix<T>::operator()(int i, int j) { if(i>=0 && i>m_nYSize && j>=0 && j>m_nXSize) { TRACE("Indexes are incorect (%d\t%d)\n", i, j); return m_ppMatrix[i][j]; } else { return m_ppMatrix[0][0]; } }
Full Source Code
// // Definition and Declaration of Container2DRow class // if you do not like templates for any reason you can // create two version of this class double and int that // should be enough for 99% of applications template <class T> class Container2DRow { public: T& operator [] (int j); const T& operator [] (int j) const; T **m_ppMatrix; int i; //ROW (Y coord) int m_nXSize; }; ///Class container template<class T> const T& Container2DRow<T>::operator [] (int j) const { ASSERT(j>=0 && j<m_nXSize); return m_ppMatrix[i][j]; } template<class T> T& Container2DRow<T>::operator [] (int j) { ASSERT(j>=0 && j<m_nXSize); return m_ppMatrix[i][j]; } // // Defenition of CMatrix class // template <class T> class CMatrix { public: //Helper class for [][] indexing, it is not neccesarily // to agragated by CMatrix it could be just a friend Container2DRow<T> row; private: int m_nXSize; int m_nYSize; int m_nMemorySize; T **m_ppMatrix; bool m_bCreated; public: //Constructor & Copy Constructor CMatrix(int nYSize, int nXSize); CMatrix(const CMatrix& matrix); //operator = returns reference in order to enable //expressions like this a=b=c=d; //a=b a.operator=(b) //a=b+c a.operator=(b.operator+(c)); //a=b-c a.operator=(b.operator-(c)); CMatrix& operator= (const CMatrix& matrix); CMatrix operator+ (const T& item); CMatrix operator- (const T& item); //Indexing //Y(row) X(col) T& operator()(int i, int j); // i - row //operator [] returns object of type Container2DRow //with have operator [] overloaded and know how to access //matrix data Container2DRow<T> operator [] (int i); const Container2DRow<T> operator [] (int i) const; //Helper functions, you can expand this section to do //LU decomposition, determinant evaluation and so on, T SumAll(); //Get Size int GetXSize(); int GetYSize(); T GetMinValue(); T GetMaxValue(); virtual ~CMatrix(); }; template<class T> CMatrix<T>::CMatrix(int nYSize, int nXSize) { m_bCreated = false; ASSERT(nXSize>0 && nYSize>0); m_nXSize = nXSize; m_nYSize = nYSize; m_nMemorySize = m_nYSize*m_nXSize*sizeof(T); m_ppMatrix = new T* [m_nYSize]; m_ppMatrix[0] = new T [m_nYSize*m_nXSize]; for (int i=1; i<m_nYSize; i++) m_ppMatrix[i] = m_ppMatrix[0]+i*m_nXSize; memset(m_ppMatrix[0], 0, m_nMemorySize); m_bCreated = true; row.m_ppMatrix = m_ppMatrix; row.m_nXSize = m_nXSize; } template<class T> CMatrix<T>::CMatrix(const CMatrix& matrix) { m_nXSize = matrix.m_nXSize; m_nYSize = matrix.m_nYSize; m_nMemorySize = m_nYSize*m_nXSize*sizeof(T); m_ppMatrix = new T* [m_nYSize]; ASSERT(m_ppMatrix!=NULL); m_ppMatrix[0] = new T [m_nYSize*m_nXSize]; ASSERT(m_ppMatrix[0]!=NULL); for (int i=1; i<m_nYSize; i++) m_ppMatrix[i] = m_ppMatrix[0]+i*m_nXSize; memcpy(m_ppMatrix[0],matrix.m_ppMatrix[0], m_nMemorySize); m_bCreated = true; } template<class T> CMatrix<T>& CMatrix<T>::operator= (const CMatrix& matrix) { if (this == &matrix) return *this; ASSERT(m_nXSize == matrix.m_nXSize && m_nYSize == matrix.m_nYSize); memcpy(m_ppMatrix[0],matrix.m_ppMatrix[0], m_nMemorySize); return *this; } template<class T> T CMatrix<T>::GetMinValue() { T minValue = m_ppMatrix[0][0]; int i,j; for (i=0; i<m_nYSize; i++) for (j=0; j<m_nXSize; j++) { if(m_ppMatrix[i][j]<minValue) minValue = m_ppMatrix[i][j]; } return minValue; } template<class T> T CMatrix<T>::GetMaxValue() { T maxValue = m_ppMatrix[0][0]; int i,j; for (i=0; i<m_nYSize; i++) for (j=0; j<m_nXSize; j++) { if(m_ppMatrix[i][j]>maxValue) maxValue = m_ppMatrix[i][j]; } return maxValue; } template<class T> CMatrix<T> CMatrix<T>::operator+ (const T& item) { int i, j; CMatrix<T> mtrx(m_nYSize, m_nXSize); for (i=0; i<m_nYSize; i++) for (j=0; j<m_nXSize; j++) { mtrx.m_ppMatrix[i][j] = m_ppMatrix[i][j]+item ; } return mtrx; } template<class T> CMatrix<T> CMatrix<T>::operator- (const T& item) { int i, j; CMatrix<T> mtrx(m_nYSize, m_nXSize); for (i=0; i<m_nYSize; i++) for (j=0; j<m_nXSize; j++) { mtrx.m_ppMatrix[i][j] = m_ppMatrix[i][j]-item ; } return mtrx; } template<class T> CMatrix<T>::~CMatrix() { if (m_bCreated) { delete [] m_ppMatrix[0]; delete [] m_ppMatrix; } } template<class T> int CMatrix<T>::GetXSize() { return m_nXSize; } template<class T> T CMatrix<T>::SumAll() { T sum = 0; int i, j; for (i=0; i<m_nYSize; i++) for (j=0; j<m_nXSize; j++) { sum += m_ppMatrix[i][j]; } return sum; } template<class T> int CMatrix<T>::GetYSize() { return m_nYSize; } template<class T> //Y(row) X(col) T& CMatrix<T>::operator()(int i, int j) { ASSERT(i>=0 && i<m_nYSize && j>=0 && j<m_nXSize); return m_ppMatrix[i][j]; } //Fancy Indexing template<class T> Container2DRow<T> CMatrix<T>::operator [] (int i) { ASSERT(i>=0 && i<m_nYSize); row.i = i; return row; } template<class T> const Container2DRow<T> CMatrix<T>::operator [] (int i) const { ASSERT(i>=0 && i<m_nYSize); row.i = i; return row; }