Как реализовать многомерный массив динамического размера?
Один из подходов — разворачивание его на одномерный массив, то есть задача сводится к проекции вектора индексов на множество целых чисел.
Тема возникла по результатам треда
http://www.rsdn.ru/forum/?mid=104547Автор: Harut
Дата: 23.09.02
Для 2- и 3-мерных случаев
class index2d
{
size_t m_sizeX, m_sizeY;
public:
index2d(size_t sizeX, size_t sizeY): m_sizeX(sizeX), m_sizeY(sizeY) {}
// размер линейного массива
size_t sizeTotal() const { return m_sizeX * m_sizeY; }
// линейный индекс
size_t operator() (size_t x, size_t y) const { return x * m_sizeY + y; }
};
class index3d
{
size_t m_sizeX, m_sizeY, m_sizeZ;
public:
index3d(size_t sizeX, size_t sizeY, size_t sizeZ): m_sizeX(sizeX), m_sizeY(sizeY), m_sizeZ(sizeZ) {}
// размер линейного массива
size_t sizeTotal() const { return m_sizeX * m_sizeY * m_sizeZ; }
// линейный индекс
size_t operator() (size_t x, size_t y, size_t z) const { return (x * m_sizeY + y) * m_sizeZ + z; }
};
способ применения:
index2d i2d(10, 15); // объект-хелпер
std::vector<int> vec; // контейнер данных
vec.setsize(i2d.sizeTotal()); // установка размера
vec[i2d(2, 3)] = 100; // доступ к элементам
Для произвольной размерности: используем синтаксис с переменным числом параметров (...)
#include <stdarg.h>
#include <assert.h>
template <size_t Arity> // "арность", т.е. число измерений
class indexNd
{
size_t m_sizes[Arity];
public:
indexNd(const size_t *sizes)
{
assert(Arity > 0);
assert(sizes != NULL);
for(size_t n = 0; n < Arity; n++)
{
assert(sizes[n] > 0);
m_sizes[n] = sizes[n];
}
}
indexNd(size_t size0, ...)
{
assert(Arity > 0);
va_list args;
va_start(args, size0);
initv(size0, args);
}
static size_t arity() { return Arity; }
size_t sizeTotal() const
{
size_t t = 1;
for(size_t n = 0; n < Arity; n++) t *= m_sizes[n];
return t;
}
size_t operator()(const size_t* indices) const
{
assert(indices != NULL);
assert(indices[0] < m_sizes[0]);
size_t t = indices[0];
for(int n = 1; n < Arity; n++)
{
assert(indices[n] < m_sizes[n]);
t *= m_sizes[n-1];
t += indices[n];
}
return t;
}
size_t operator()(size_t index0, ...) const
{
va_list args;
va_start(args, index0);
size_t t = indexv(index0, args);
va_end(args);
return t;
}
protected:
indexNd() {} // только для наследников
void initv(size_t size0, va_list args)
{
assert(size0 > 0);
m_sizes[0] = size0;
for(size_t n = 1; n < Arity; n++)
{
m_sizes[n] = va_arg(args, size_t);
}
va_end(args);
}
size_t indexv(size_t index0, va_list args) const
{
assert(index0 < m_sizes[0]);
size_t i = index0;
for(int n = 1; n < Arity; n++)
{
size_t indexN = va_arg(args, size_t);
assert(indexN < m_sizes[n]);
i *= m_sizes[n-1];
i += indexN;
}
return i;
}
};
и способ применения:
indexNd<4> i4d( 10, 20, 30, 40 );
std::vector<int> vec;
vec.setsize(i4d.sizeTotal());
vec[i4d(1, 2, 3, 4)] = 1234;
Можно сделать арность параметром не шаблона, а экземпляра объекта.
Наконец, индексатор можно срастить с контейнером:
template<class TYPE, size_t Arity>
class vectorNd : private indexNd<Arity>
{
std::vector<TYPE> m_vec;
public:
vectorNd(size_t size0, ...)
{
va_list args;
va_start(args, size0);
initv(size0, args);
va_end(args);
m_vec.setsize(sizeTotal());
}
TYPE& operator()(size_t index0, ...) // увы, круглые скобки вместо квадратных
{
va_list args;
va_start(args, index0);
TYPE& v = m_vec[indexv(index0, args)];
va_end(args);
return v;
}
const TYPE& operator()(size_t index0, ...) const
{
va_list args;
va_start(args, index0);
TYPE& v = m_vec[indexv(index0, args)];
va_end(args);
return v;
}
};
Благодарности:
Harut — за вопрос
Bell — за первотолчок