Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

2D Matrix Container with [][] indexing

0.00/5 (No votes)
5 Jun 2002 1  
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;
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here