Click here to Skip to main content
15,881,424 members
Articles / Programming Languages / C++

Rock Solid C++ 2D Templatized Array Class

Rate me:
Please Sign up or sign in to vote.
3.29/5 (8 votes)
12 Jan 2012CPOL2 min read 23.1K   10   10
A simple 2D array class utilizing C++ templates with iterators and indexing operators.

Introduction

I've been using variations of the following code in C++ for over twenty years and it is pretty much rock solid code. I will keep my description of this class very short. Please feel free to ask questions …

Background

The class encapsulates and controls access to a 2D array of just about any kind of data stored in row major order. It has methods to retrieve STL style iterators to the data sequence, operators to index the array in 1D and 2D, a method for filling the array with a single value, and a method for resizing the data bank. It can allocate memory for the array or accept and encapsulate data arrays allocated externally.

Why Use This Code?

Sure, there are many other implementations of array templates around. The advantage of using this particular class is that it was written with simplicity, ease of use, and raw speed in mind. I did not take the 'kitchen sink' approach and add everything I could imagine to it; I just implemented the basic necessary functions for a 2D array. Any special purpose functionality can be written by the user with template functions. Furthermore, this code has been tested over and over as part of possibly hundreds of simulation programs over the past twenty years.

Using the Code

There are two constructors. The first constructs the array given the row and column counts, as follows.

C++
TArray2<float> myArray(44,21);

The second constructor accepts a bank of external data with an option to adopt that data or to simply reference it. If the Boolean is set to true, then the array instance will assume ownership of the external data and will delete it as needed. If it is set to false, then the array will assume that the caller is managing the memory for the external data. In either case, a pointer to that external data is stored and used to store and retrieve data elements. For example, in the following code, the instance myArray will store a pointer to the data in pData and will delete the associated memory upon destruction or resizing.

C++
float pData = new float[512*512]
TArray2<float> myArray(pData,true,512,512); 

The Code

C++
#if !defined(TARRAY2_INCLUDED) 
#define TARRAY2_INCLUDED

#include <cassert>
#include <algorithm>

template<typename T>
class TArray2
{
   public:

      // Typedefs
      typedef T value_type;
      typedef value_type* iterator;
      typedef const value_type* const_iterator;

      // Constructor
      TArray2(size_t rows=1, size_t cols=1)
      : m_Rows(0),
        m_Cols(0),
        m_OwnsData(true),
        p_Data(0)
      {
         resizeTo(rows,cols);
      }

      // Construct given external data
      TArray2(T* pAry, bool ownsData, size_t rows=1, size_t cols=1)
      : m_Rows(rows),
        m_Cols(cols),
        m_OwnsData(ownsData),
        p_Data(pAry)
      {
         assert(0 != p_Data);
      }

      // Copy constructor
      TArray2(const TArray2<T>& a)
      {
         resizeTo( a.rows(),a.cols() );
         std::copy( a.begin(), a.end(), p_Data);
         m_OwnsData = true;
      }

      // Assignment operator
      TArray2& operator = (const TArray2<T>& a)
      {
         resizeTo( a.rows(),a.cols() );
         std::copy( a.begin(), a.end(), p_Data);
         m_OwnsData = true;
         return *this;
      }

      // Destructor
      ~TArray2()
      {
         if (m_OwnsData) { delete[] p_Data; }
      }

      // Dimensions retrieval
      inline size_t rows() const { return m_Rows; }
      inline size_t cols() const { return m_Cols; }
      inline size_t size() const { return rows()*cols(); }
      inline size_t bytes() const { return bytes()*sizeof(value_type); }

      // Fill with a value
      void fill(value_type val)
      {
         std::uninitialized_fill_n( p_Data, size(), val );
      }

      // Swap contents with another array
      void swap(TArray2<T>& ary)
      {
         std::swap( m_Rows, ary.m_Rows );
         std::swap( m_Cols, ary.m_Cols );
         std::swap( m_OwnsData, ary.m_OwnsData );
         std::swap( p_Data, ary.p_Data );
      }

      // Resize data bank
      void resizeTo(size_t rows, size_t cols)
      {
         // Not allowed for external data
         assert( m_OwnsData );

         // Sanity check
         size_t nElements = rows*cols;
         assert( nElements>0 );
         // If we have existing data
         if ( 0 != p_Data )
         {
            // No change, return
            if ( rows==m_Rows && cols==m_Cols )
            {
               return;
            }
            delete[] p_Data;
            p_Data = 0;
         }
         // Allocate data bank
         p_Data = new value_type[nElements];
         m_Rows = rows;
         m_Cols = cols;
      }

      // STL style iterators
      inline const_iterator begin() const { return p_Data; }
      inline const_iterator end() const { return p_Data + size(); }
      inline iterator begin() { return p_Data; }
      inline iterator end() { return p_Data + size(); }

      // Array indexing operators
      inline const T& operator () ( size_t i ) const { return p_Data[ checkedIndex(i) ]; }
      inline const T& operator () ( size_t i, size_t j ) const { return p_Data[ checkedIndex( i, j ) ]; }
      inline T& operator () ( size_t i ) { return p_Data[ checkedIndex(i) ]; }
      inline T& operator () ( size_t i, size_t j ) { return p_Data[ checkedIndex( i, j ) ]; }

      // Get pointers to internal data
      inline const T* c_data() const { return p_Data; }
      inline T* c_data() { return p_Data; }

   private:

      size_t checkedIndex(size_t indx) const
      {
         assert( indx < size() );
         return indx;
      }
      size_t checkedIndex(size_t iRow, size_t jCol) const
      {
         size_t k = m_Cols*iRow + jCol;
         assert( k < size() );
         return k;
      }

   private:

      size_t m_Rows; 
      size_t m_Cols;
      bool m_OwnsData;
      T* p_Data;

}; // class TArray2

#endif // #if !defined(TARRAY2_INCLUDED)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer Invariant Corp
United States United States
Paul Leopard has been working in the area of simulation and visualization since 1982.

Comments and Discussions

 
SuggestionThanks for your code,one suggest Pin
xiayingang28-Dec-13 22:14
xiayingang28-Dec-13 22:14 
GeneralMy vote of 3 Pin
Lorenzo Gatti18-Jan-12 0:14
Lorenzo Gatti18-Jan-12 0:14 
GeneralMy vote of 5 Pin
theisomizer12-Jan-12 19:06
theisomizer12-Jan-12 19:06 
QuestionThanks for the valuable feedback, glitches fixed Pin
pleopard12-Jan-12 8:25
pleopard12-Jan-12 8:25 
GeneralMy vote of 5 Pin
Snorri Kristjansson9-Jan-12 23:45
professionalSnorri Kristjansson9-Jan-12 23:45 
Thanks for sharing.
QuestionWhy? Pin
Snorri Kristjansson6-Jan-12 3:02
professionalSnorri Kristjansson6-Jan-12 3:02 
GeneralMy vote of 2 Pin
Ajay Vijayvargiya5-Jan-12 20:31
Ajay Vijayvargiya5-Jan-12 20:31 
GeneralRe: My vote of 2 Pin
Lorenzo Gatti18-Jan-12 0:07
Lorenzo Gatti18-Jan-12 0:07 
GeneralRe: My vote of 2 Pin
Ajay Vijayvargiya18-Jan-12 1:58
Ajay Vijayvargiya18-Jan-12 1:58 
GeneralRe: My vote of 2 Pin
Lorenzo Gatti19-Jan-12 5:29
Lorenzo Gatti19-Jan-12 5:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.