Re: Simple 2D Array
On 18 Des, 21:07, "(2b|!2b)==?" <void-s...@ursa-major.com> wrote:
I quickly hacked this together for some stuff I am writing - I havent
used the STL for a while (been programming in another language) and I'd
appreciate any feedback on how I may improve it/any gotchas (an obvious
one being if constructed with (0,0) :
#ifndef SIMPLE_2D_ARRAY_Header
#define SIMPLE_2D_ARRAY_Header
#include <vector>
You need a [stddef.h] here for unqualified size_t.
template <typename T>
class Array2D
{
public:
typedef std::vector<T> Row ;
Is this implementation detail intentionally exposed?
Array2D(const size_t numrows=1, const size_t numcols==
1)
:m_rows(numrows)
{
if (numrows)
{
for (size_t i=0; i < nu=
mrows; i++)
m_rows[i]=
..resize(numcols);
}
}
You don't need that 'if'.
Array2D(const Array2D& tbl)
{
m_rows = tbl.m_rows ;
}
You don't need to define a copy constructor either: the one you get by
default works nicely.
But if you do define a copy constructor, use member initialization
list for initializations wherever possible.
The above constructor first default-constructs m_rows, and then
assigns, instead of copy-constructing it directly.
Array2D& operator= (const Array2D& rhs)
{
if (this != &rhs)
m_rows = rhs.m_rows ;
return *this ;
}
What happens if one of the vector element assignments throw an
exception?
Instead use the 'swap-idiom'.
That means defining a swap method, which you need anyway.
~Array2D()
{}
This destructor is not needed.
void reset(const size_t numrows, const size_t numcols)
{
m_rows.clear();
m_rows.resize(numrows);
for (size_t i = 0; i < numrows; i++)
{
m_rows[i].clear();
m_rows[i].resize(numcols)=
;
}
}
size_t numrows() const { return m_rows.size() ; }
size_t numcols() const { return m_rows.size() ? m_rows[0]=
..size() : 0 ; }
The standard library uses size_t, an unsigned type for this, so you're
in good company.
However, it's very impractical (I'd written "plain stupid" except then
narrow-minded conformists would jump on me like a bee-swarm).
For one, the aspect that annoys me the most, it forces use of size_t
on client code in order to avoid silly-warnings about signed/unsigned
comparisions -- and sometimes causes great havoc.
Basic types in C++ do *not* guarantee range checking.
On the contrary, the unsigned types guarantee no range checking. Which
means that they're completely unsuitable for indicating value ranges.
There's no advantage, and many pitfalls.
T& value_at(const size_t row, const size_t col)
{
if (m_rows.empty() || (row >= m_rows.si=
ze()))
throw std::logic_error("r=
ow array bounds exceeded") ;
if (m_rows[row].empty() || (col >= m_ro=
ws[row].size()))
throw std::logic_error("c=
ol array bounds exceeded") ;
return m_rows[row][col] ;
}
Why not just use the 'at' method here?
const T& value_at(const size_t row, const size_t col) con=
st
{
if (m_rows.empty() || (row >= m_rows.si=
ze()))
throw std::logic_error("r=
ow array bounds exceeded") ;
if (m_rows[row].empty() || (col >= m_ro=
ws[row].size()))
throw std::logic_error("c=
ol array bounds exceeded") ;
return m_rows[row][col] ;
}
const std::vector<T>& row(const unsigned int index) const
{
if (m_rows.empty() || (index >= m_rows.=
size()))
throw std::logic_error("r=
ow array bounds exceeded") ;
return m_rows[index] ;
}
size_t add_col()
{
size_t newsize = m_rows.size() ? m_rows=
[0].size() + 1 : 1;
if (newsize == 1)
{
Row row ;
m_rows.push_back(row);
}
else
{
for (size_t rownum = 0;=
rownum < m_rows.size(); rownum++)
m_rows[ro=
wnum].resize(newsize);
}
return newsize;
}
size_t add_row()
{
if (!m_rows.size())
return 0;
Row row(m_rows[0].size()) ;
m_rows.push_back(row);
return m_rows.size();
}
private:
std::vector<Row> m_rows ;
};
#endif
MTIA
Uhm, I see no way to get data into such a 2D array?
Cheers & hth.,
- Alf