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.
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.
float pData = new float[512*512]
TArray2<float> myArray(pData,true,512,512);
The Code
#if !defined(TARRAY2_INCLUDED)
#define TARRAY2_INCLUDED
#include <cassert>
#include <algorithm>
template<typename T>
class TArray2
{
public:
typedef T value_type;
typedef value_type* iterator;
typedef const value_type* const_iterator;
TArray2(size_t rows=1, size_t cols=1)
: m_Rows(0),
m_Cols(0),
m_OwnsData(true),
p_Data(0)
{
resizeTo(rows,cols);
}
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);
}
TArray2(const TArray2<T>& a)
{
resizeTo( a.rows(),a.cols() );
std::copy( a.begin(), a.end(), p_Data);
m_OwnsData = true;
}
TArray2& operator = (const TArray2<T>& a)
{
resizeTo( a.rows(),a.cols() );
std::copy( a.begin(), a.end(), p_Data);
m_OwnsData = true;
return *this;
}
~TArray2()
{
if (m_OwnsData) { delete[] p_Data; }
}
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); }
void fill(value_type val)
{
std::uninitialized_fill_n( p_Data, size(), val );
}
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 );
}
void resizeTo(size_t rows, size_t cols)
{
assert( m_OwnsData );
size_t nElements = rows*cols;
assert( nElements>0 );
if ( 0 != p_Data )
{
if ( rows==m_Rows && cols==m_Cols )
{
return;
}
delete[] p_Data;
p_Data = 0;
}
p_Data = new value_type[nElements];
m_Rows = rows;
m_Cols = cols;
}
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(); }
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 ) ]; }
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;
};
#endif // #if !defined(TARRAY2_INCLUDED)
Paul Leopard has been working in the area of simulation and visualization since 1982.