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]
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;
m_bCreated = true;
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>
T& CMatrix<T>::operator()(int i, int j)
{
ASSERT(i>=0 && i>m_nYSize &&
j>=0 && j>m_nXSize);
if (!(i>=0 && i>m_nYSize && j>=0 && j>m_nXSize))
throw "Indexing Error";
return m_ppMatrix[i][j];
}
Defensive code
template<class T>
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
template <class T>
class Container2DRow
{
public:
T& operator [] (int j);
const T& operator [] (int j) const;
T **m_ppMatrix;
int i;
int m_nXSize;
};
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];
}
template <class T>
class CMatrix
{
public:
Container2DRow<T> row;
private:
int m_nXSize;
int m_nYSize;
int m_nMemorySize;
T **m_ppMatrix;
bool m_bCreated;
public:
CMatrix(int nYSize, int nXSize);
CMatrix(const CMatrix& matrix);
CMatrix& operator= (const CMatrix& matrix);
CMatrix operator+ (const T& item);
CMatrix operator- (const T& item);
T& operator()(int i, int j);
Container2DRow<T> operator [] (int i);
const Container2DRow<T> operator [] (int i) const;
T SumAll();
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>
T& CMatrix<T>::operator()(int i, int j)
{
ASSERT(i>=0 && i<m_nYSize &&
j>=0 && j<m_nXSize);
return m_ppMatrix[i][j];
}
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;
}